Skip to main content

Release & CI Workflows

A one-glance map of every GitHub Actions workflow across the Playroll repos — what triggers it and what it does — plus which skill drives which workflow. Use it to answer "what actually happens when I push this tag?" without reading YAML.

How a release flows (the short version)

  1. cpp is tagged vX.Y.Z → its CI builds the core and uploads a zip to S3 (no user impact yet).
  2. installer is tagged playroll/<track>/vX.Y.Z (pinned to specific cpp/ui tags via release-manifest.yml) → its CI builds + signs the installer, publishes to the CDN, and writes a row in Supabase app_releases_v2.
  3. A rollout (rollout.yml / an override) decides who receives it. Publishing and audience are separate steps — a published release reaches nobody until a rollout or override targets them.

The user-facing artifact is the signed Playroll_Setup_X.Y.Z.exe, not the cpp core zip. The UI never releases on its own — it is pinned and built by the installer.

Workflows by repo

RepoWorkflowTriggerWhat it does
playroll-cppbuild-check.ymlpush/PR on main,develop,ci/**Compiles the core in Release as a check. No publish.
playroll-cppbuild-release.ymltag v*, or manual dispatchBuilds the core and uploads playroll-core-build.zip to S3. If the artifact already exists for that tag, it skips the build (claim check). Does not roll out to users.
playroll-cpplambda-deploy.ymlpush to main touching lambda_src/**, or dispatchDeploys the AWS Lambda functions.
playroll-installerbuild-check.ymlPR on mainBuilds the full installer (no signing, no publish) as a check. Checks out cpp/ui at main.
playroll-installerdry-run.ymlmanual dispatchBuild-only trial of the installer; publishes nothing.
playroll-installerrelease.ymltag playroll/{stable,test,dev}/v*Builds + signs + publishes the installer, writes app_releases_v2. On the test track only, it auto-rolls 100% forced to the entire staff tester cohort. dev/stable do not auto-roll.
playroll-installerrollout.ymlmanual dispatchDecides who receives a published release — writes app_rollouts_v2 / overrides.
playroll-installerpromote.ymlmanual dispatch from a tag refRe-publishes an existing release on another track without rebuilding or re-signing.
playroll-installerhotfix-tester-smoke.ymlpush/PR touching hotfix-tester.ps1, or dispatchRuns hotfix-tester.ps1 -DryRun and asserts the plan invariants. A safety net so a rename/schema change breaks CI, not the next real hotfix.
playroll-uibuild-check.ymlpush/PR on main,develop,ci/**Lint + typecheck + test + build. The UI ships only as part of an installer release.

Skills and the scripts/workflows they drive

The canonical, CI-driven release flow lives in workspace-level skills (ec-workspace/.claude/skills/), inherited by every repo.

Skill / scriptLives inDrivesConcern
playroll-doctorworkspace skillnothing (read-only)Verifies the release environment is set up. Run before releasing.
playroll-releaseworkspace skillgit-flow on cpp/ui + installer release.yml; also promotionWhat ships. Includes a "Targeted hotfix to specific tester(s)" section.
playroll-rolloutworkspace skillinstaller rollout.ymlapp_rollouts_v2Who receives it.
/hotfix-releaseplayroll-cpp commandthe cpp-side hotfix detail (branch from the release tag, verify it builds on the old base, pre-stage the S3 artifact)Defers to playroll-release / hotfix-tester.ps1 for the installer release + rollout.
hotfix-tester.ps1playroll-installer scriptdev-track release + forced override to an explicit user listDeterministic single/multi-tester hotfix. Track is hardcoded dev, so it can never auto-roll to everyone. -DryRun writes nothing.
release-testers.ps1, rollout-testers.ps1, local-release.ps1playroll-installer scriptslocal publish/rolloutDev-machine fallbacks, not the default path.

Gotchas worth knowing

  • test track fans out. Tagging playroll/test/v* auto-rolls the build to all staff testers (hard-wired in release.yml). To target one or a few testers, use track dev + an override — hotfix-tester.ps1 does exactly this and removes the choice (no -Track parameter).
  • The cpp tag is not a rollout. It only stages the core artifact on S3. Nobody gets anything until the installer release + a rollout.
  • Publishing ≠ audience. release.yml/playroll-release publish; rollout.yml/playroll-rollout choose who receives it. Keep them separate.
  • promote.yml must be dispatched from a tag ref. The OIDC trust policy on AWS_RELEASE_ROLE_ARN only trusts tag refs, so gh workflow run promote.yml --ref "playroll/<from_track>/v<X.Y.Z>" ... works while a dispatch from main (the default) fails at the "Configure AWS credentials" step.
  • promote.yml's CloudFront wait is best-effort. If aws cloudfront wait invalidation-completed times out, the run logs [WARN] Timed out waiting and still succeeds — the cache refreshes on its own within minutes. (Before 2026-07-09 the timed-out waiter's exit code leaked into the step result, failing the run after a successful promotion.)