From d5e1fd65e1878936e2950ff37f1c12a871dd767a Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 10:22:23 -1000 Subject: [PATCH 01/18] Document cpflow downstream setup --- .controlplane/readme.md | 108 +++++++++++++++++- .github/cpflow-help.md | 65 +++++++++-- .../cpflow-cleanup-stale-review-apps.yml | 4 +- .../workflows/cpflow-delete-review-app.yml | 4 +- .../workflows/cpflow-deploy-review-app.yml | 4 +- .github/workflows/cpflow-deploy-staging.yml | 4 +- .github/workflows/cpflow-help-command.yml | 2 +- .../cpflow-promote-staging-to-production.yml | 16 ++- .github/workflows/cpflow-review-app-help.yml | 2 +- 9 files changed, 185 insertions(+), 24 deletions(-) diff --git a/.controlplane/readme.md b/.controlplane/readme.md index fff76eeb..81781952 100644 --- a/.controlplane/readme.md +++ b/.controlplane/readme.md @@ -19,6 +19,104 @@ In a real app, you would likely use persistent, external resources, such as AWS You can see the definition of Postgres and Redis in the `.controlplane/templates` directory. +## GitHub Review App Setup + +For normal generated review apps in this repo, GitHub needs only one repository +secret: + +| Secret | Notes | +| --- | --- | +| `CPLN_TOKEN_STAGING` | Control Plane service-account token for `shakacode-open-source-examples-staging`. | + +No GitHub repository variables are required for the standard review-app path. +The generated workflow infers the review app prefix +`qa-react-webpack-rails-tutorial` and staging org +`shakacode-open-source-examples-staging` from `.controlplane/controlplane.yml` +because that file defines exactly one app with +`match_if_app_name_starts_with: true`. + +Optional review-app variables: + +| Variable | Notes | +| --- | --- | +| `CPLN_ORG_STAGING` | Override the inferred staging org. | +| `REVIEW_APP_PREFIX` | Override or disambiguate the inferred review app prefix. | +| `PRIMARY_WORKLOAD` | Public workload name used to discover the review URL; defaults to `rails`. | + +For staging auto-deploys, also configure: + +| Secret or variable | Value | +| --- | --- | +| `CPLN_TOKEN_STAGING` | Same staging Control Plane token used by review apps. | +| `CPLN_ORG_STAGING` | `shakacode-open-source-examples-staging` | +| `STAGING_APP_NAME` | `react-webpack-rails-tutorial-staging` | + +For production promotion, configure a protected GitHub Environment named +`production`: + +| Secret or variable | Value | +| --- | --- | +| `CPLN_TOKEN_PRODUCTION` | Environment secret on `production`, not a repository or organization secret. | +| `CPLN_ORG_PRODUCTION` | Environment variable on `production`: `shakacode-open-source-examples-production` | +| `PRODUCTION_APP_NAME` | Environment variable on `production`: `react-webpack-rails-tutorial-production` | + +Protect the `production` environment with required reviewers, enable prevent +self-review, and consider disabling administrator bypass. Only release managers +or similarly trusted maintainers should be able to approve the promotion job. +The promotion workflow uses that environment before it can access +`CPLN_TOKEN_PRODUCTION`, so the production token is not exposed to ordinary +review-app or staging runs. + +Advanced optional variables: + +| Name | Notes | +| --- | --- | +| `REVIEW_APP_DEPLOYING_ICON_URL` | Cosmetic custom animated icon for review-app comments. Ignore this for the standard setup. | +| `CPLN_CLI_VERSION` | Pin only when Control Plane CLI compatibility requires it. | +| `CPFLOW_VERSION` | Runtime gem override. Leave unset when workflow wrappers are pinned to a GitHub commit SHA for upstream PR testing. | + +## Control Plane Setup + +The GitHub secret is only the automation credential. The Control Plane org also +needs the app resources and runtime secrets that the workloads read at boot. + +For review-app testing, the standard setup is: + +| Control Plane item | Where | Notes | +| --- | --- | --- | +| Staging/review org | `shakacode-open-source-examples-staging` | The `CPLN_TOKEN_STAGING` service account must be able to create and update GVCs, workloads, images, identities, policies, and secrets in this org. | +| Review app prefix | `qa-react-webpack-rails-tutorial` | Review apps are named `qa-react-webpack-rails-tutorial-`. This is inferred from `.controlplane/controlplane.yml`. | +| Review app secret dictionary | `qa-react-webpack-rails-tutorial-secrets` | Shared by generated review apps because the QA app entry uses `match_if_app_name_starts_with: true`. | + +For staging deploys later, also use: + +| Control Plane item | Where | Notes | +| --- | --- | --- | +| Staging app | `react-webpack-rails-tutorial-staging` | The `CPLN_TOKEN_STAGING` token deploys this app from `master`. | +| Staging app secret dictionary | `react-webpack-rails-tutorial-staging-secrets` | Same required keys as the review app secret dictionary. | + +For production promotion later, use a separate production org and token: + +| Control Plane item | Where | Notes | +| --- | --- | --- | +| Production org | `shakacode-open-source-examples-production` | Do not give the staging token access to this org. | +| Production app | `react-webpack-rails-tutorial-production` | Promotion copies the staging image into this app. | +| Production app secret dictionary | `react-webpack-rails-tutorial-production-secrets` | Create before the first promotion. Use production-only values. | +| Production service-account token | GitHub Environment secret `CPLN_TOKEN_PRODUCTION` | Keep this token in the protected `production` GitHub Environment only. | + +The app secret dictionaries must include: + +- `SECRET_KEY_BASE` +- `RENDERER_PASSWORD` +- `REACT_ON_RAILS_PRO_LICENSE` + +Generate `SECRET_KEY_BASE` with `openssl rand -hex 64` and +`RENDERER_PASSWORD` with `openssl rand -hex 32`. The review/staging template +currently contains a test placeholder for `SECRET_KEY_BASE`; replace it with a +secret-backed value before promoting production. The demo Postgres and Redis +workloads are useful for review apps and staging demos. For real production, +prefer managed services and update `DATABASE_URL` and `REDIS_URL` accordingly. + ## Prerequisites 1. Ensure your [Control Plane](https://shakacode.controlplane.com) account is set up. @@ -388,11 +486,11 @@ The production promotion workflow checks that production has all environment variable names present in staging; it does not compare secret values, workload environment variables, or Control Plane secret references. -The repository variables and secrets must match the app names in -`.controlplane/controlplane.yml`. In particular, `REVIEW_APP_PREFIX` should -include the `-pr` suffix for this app, such as -`qa-react-webpack-rails-tutorial-pr`, so generated review apps are named -`qa-react-webpack-rails-tutorial-pr-1234`. +The GitHub settings and Control Plane resources must match the app names in +`.controlplane/controlplane.yml`. For the standard review-app path, leave +`REVIEW_APP_PREFIX` unset and let the workflow infer +`qa-react-webpack-rails-tutorial`; generated review apps are named +`qa-react-webpack-rails-tutorial-`. This allows teams to: - Preview changes in a production-like environment diff --git a/.github/cpflow-help.md b/.github/cpflow-help.md index f608d4e5..663b97c3 100644 --- a/.github/cpflow-help.md +++ b/.github/cpflow-help.md @@ -35,26 +35,77 @@ You asked for review app help. These commands are generated by [cpflow](https:// | Name | Required | Notes | | --- | --- | --- | | `CPLN_TOKEN_STAGING` | yes | Service-account token scoped to the staging Control Plane org on controlplane.com. | -| `CPLN_TOKEN_PRODUCTION` | yes (for promote) | Service-account token scoped to the production Control Plane org on controlplane.com. | +| `CPLN_TOKEN_PRODUCTION` | yes (for promote) | Store this as a secret on the protected `production` GitHub Environment, not as a repository or organization secret. | | `DOCKER_BUILD_SSH_KEY` | optional | Private SSH key used when Docker builds fetch private deps via `RUN --mount=type=ssh`. | +For normal generated review apps, `CPLN_TOKEN_STAGING` is the only required +GitHub setting. The review app prefix and staging org are inferred from +`.controlplane/controlplane.yml` when it defines exactly one app with +`match_if_app_name_starts_with: true`. + +For production promotion, create a GitHub Environment named `production`, add +required reviewers, enable prevent self-review, and store +`CPLN_TOKEN_PRODUCTION` as an environment secret there. The generated promotion +workflow uses that environment before it can access production secrets. + ### GitHub Actions variables | Name | Required | Notes | | --- | --- | --- | -| `CPLN_ORG_STAGING` | yes | Control Plane org on controlplane.com for staging and review apps. | -| `CPLN_ORG_PRODUCTION` | yes (for promote) | Control Plane org on controlplane.com for production. | +| `CPLN_ORG_STAGING` | optional for review apps; yes for staging | Override the staging/review Control Plane org inferred from `controlplane.yml`. | +| `CPLN_ORG_PRODUCTION` | yes (for promote) | Control Plane org on controlplane.com for production. Prefer a `production` environment variable. | | `STAGING_APP_NAME` | yes | App name in `controlplane.yml` used as the staging deploy target. | -| `PRODUCTION_APP_NAME` | yes (for promote) | App name in `controlplane.yml` used as the production deploy target. | -| `REVIEW_APP_PREFIX` | yes | Prefix for per-PR review app names (e.g. `review-app`). | -| `REVIEW_APP_DEPLOYING_ICON_URL` | optional | Custom image URL for the animated deploying icon in review-app PR comments. Set to `none` to use the text fallback icon. | +| `PRODUCTION_APP_NAME` | yes (for promote) | App name in `controlplane.yml` used as the production deploy target. Prefer a `production` environment variable. | +| `REVIEW_APP_PREFIX` | optional | Override or disambiguate the review app prefix inferred from `controlplane.yml`. | +| `REVIEW_APP_DEPLOYING_ICON_URL` | optional, advanced | Cosmetic custom image URL for the animated deploying icon in review-app PR comments. Set to `none` to use the text fallback icon. | | `STAGING_APP_BRANCH` | optional | Custom staging branch. Custom branches must also appear in `cpflow-deploy-staging.yml`'s push filter. | | `PRIMARY_WORKLOAD` | optional | Workload polled for health and rollback (defaults to `rails`). | | `DOCKER_BUILD_EXTRA_ARGS` | optional | Newline-delimited extra docker build tokens (e.g. `--build-arg=FOO=bar`). | | `DOCKER_BUILD_SSH_KNOWN_HOSTS` | optional | SSH known_hosts entries when SSH build hosts are not GitHub.com. | | `HEALTH_CHECK_ACCEPTED_STATUSES` | optional | Space-separated HTTP statuses considered healthy on promote (default `200 301 302`). | | `CPLN_CLI_VERSION` | optional | Pin a specific `@controlplane/cli` version; falls back to the action default when unset. | -| `CPFLOW_VERSION` | optional | Runtime gem-install override. When unset, cpflow is built from the pinned upstream workflow ref. When set, use the RubyGems version without a leading `v`. | +| `CPFLOW_VERSION` | optional | Pin a published RubyGems version such as `5.0.0` or `5.0.0.rc.1`. Leave unset for normal generated workflows so the setup action builds `cpflow` from the same `control-plane-flow` GitHub ref used by the reusable workflow. | + + + +
+Advanced: testing unreleased control-plane-flow changes + +Generated workflow wrappers have two related pins: + +- The `uses: shakacode/control-plane-flow/...@` GitHub ref selects the reusable workflow code. +- The `control_plane_flow_ref: ` input tells the setup action which `control-plane-flow` source to check out and build when `CPFLOW_VERSION` is empty. + +The GitHub ref is the runtime lock for workflow and action behavior. The +RubyGems version is used to generate/update these wrappers and, only when +`CPFLOW_VERSION` is set, to install the `cpflow` CLI at runtime. A downstream +repo cannot rely on the gem alone because GitHub loads reusable workflow YAML +from `shakacode/control-plane-flow`, not from RubyGems. + +For normal releases, point both pins at a release tag such as `v5.0.0`. +You may leave `CPFLOW_VERSION` unset, or set it to the matching RubyGems version +without the leading `v`, such as `5.0.0`. If you set `CPFLOW_VERSION` for a +prerelease, use RubyGems dot syntax such as `5.0.0.rc.1`; the release tag may +use either `v5.0.0.rc.1` or `v5.0.0-rc.1`. + +To update to a new stable release, install or bundle the new `cpflow` gem, run +`cpflow generate-github-actions`, and commit the regenerated wrappers. Use +`bin/pin-cpflow-github-ref vX.Y.Z` only when the generated files are already +current and you only need to move the upstream GitHub ref. + +For temporary downstream testing of an upstream PR before a gem is released, pin +both values to the exact 40-character commit SHA and leave `CPFLOW_VERSION` +unset: + +```sh +bin/pin-cpflow-github-ref <40-character-control-plane-flow-commit-sha> +bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow +``` + +Do not leave downstream apps pinned to a moving branch such as `main`. A branch +can pick up beta or work-in-progress changes without a downstream PR changing. +Use commit SHAs for short-lived PR testing, then switch to the release tag once +the gem and tag exist.
diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index 167f3f37..2c6c51b3 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -10,7 +10,7 @@ permissions: jobs: cleanup: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@db013e139af4ee8741f791c14ff825f13c0a1021 with: - control_plane_flow_ref: 3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021 secrets: inherit diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index 6c9cbf5e..a012741b 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -26,7 +26,7 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@db013e139af4ee8741f791c14ff825f13c0a1021 with: - control_plane_flow_ref: 3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021 secrets: inherit diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index 0693116d..88e4dad2 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,7 +30,7 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@db013e139af4ee8741f791c14ff825f13c0a1021 with: - control_plane_flow_ref: 3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021 secrets: inherit diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index 32cad245..84cdf115 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,8 +16,8 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@db013e139af4ee8741f791c14ff825f13c0a1021 with: - control_plane_flow_ref: 3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021 staging_app_branch_default: "" secrets: inherit diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index 2c8c5b9d..db8438a2 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,4 +23,4 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@db013e139af4ee8741f791c14ff825f13c0a1021 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index 8efeb249..61f5becc 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -9,12 +9,24 @@ on: type: string permissions: + # The upstream reusable workflow's create-github-release job needs + # contents: write, and callers must grant the union of callee permissions. contents: write jobs: promote-to-production: if: github.event.inputs.confirm_promotion == 'promote' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the + # first selects the reusable workflow, the second selects its shared actions. + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@db013e139af4ee8741f791c14ff825f13c0a1021 with: - control_plane_flow_ref: 3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021 + # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub + # Environment. Required reviewers approve the job before GitHub exposes + # environment secrets to the upstream reusable workflow. + production_environment: production + # `secrets: inherit` passes all caller repository secrets to the trusted + # upstream workflow. The upstream workflow only reads the named secrets it + # references, but GitHub does not enforce that boundary. Strict consumers can + # set CPFLOW_GITHUB_ACTIONS_REF to an immutable commit SHA. secrets: inherit diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 8a25e25d..980d6de9 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -15,4 +15,4 @@ permissions: jobs: show-help: if: vars.REVIEW_APP_PREFIX != '' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@3e0e7e1f0a35c15648cc9254b573b058d77ca8c4 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@db013e139af4ee8741f791c14ff825f13c0a1021 From e3f03a0ed2545c39321841e15f3604de14566d80 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 10:28:59 -1000 Subject: [PATCH 02/18] Address cpflow setup review feedback --- .controlplane/shakacode-team.md | 32 ++++++++++++------- .../cpflow-promote-staging-to-production.yml | 3 +- .github/workflows/cpflow-review-app-help.yml | 1 - 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index bb864651..c3789149 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -6,10 +6,10 @@ Deployments are handled by Control Plane configuration in this repo and GitHub A ### Review Apps - Add a comment `+review-app-deploy` to any PR to deploy a review app -- The generated app name is `${REVIEW_APP_PREFIX}-${PR_NUMBER}`. Keep - `REVIEW_APP_PREFIX` set to `qa-react-webpack-rails-tutorial-pr` so review - apps use names like `qa-react-webpack-rails-tutorial-pr-1234`, matching the - prefix-backed config in `.controlplane/controlplane.yml`. +- Leave `REVIEW_APP_PREFIX` unset for the standard path. The workflow infers + `qa-react-webpack-rails-tutorial` from `.controlplane/controlplane.yml`, so + generated review apps use names like + `qa-react-webpack-rails-tutorial-1234`. - New pushes to a PR redeploy only after the review app already exists. - Add `+review-app-delete` to delete a review app manually; closing the PR also deletes it automatically. Use `+review-app-help` for the command reference. @@ -30,21 +30,31 @@ Deployments are handled by Control Plane configuration in this repo and GitHub A ### GitHub Repository Settings -Required repository secrets: +Required repository secret for review apps and staging: - `CPLN_TOKEN_STAGING` -- `CPLN_TOKEN_PRODUCTION` -Required repository variables: +Required repository variables for staging deploys: - `CPLN_ORG_STAGING=shakacode-open-source-examples-staging` -- `CPLN_ORG_PRODUCTION=shakacode-open-source-examples-production` - `STAGING_APP_NAME=react-webpack-rails-tutorial-staging` -- `PRODUCTION_APP_NAME=react-webpack-rails-tutorial-production` -- `REVIEW_APP_PREFIX=qa-react-webpack-rails-tutorial-pr` - `STAGING_APP_BRANCH=master` - `PRIMARY_WORKLOAD=rails` +Review apps infer `CPLN_ORG_STAGING`, `REVIEW_APP_PREFIX`, and +`PRIMARY_WORKLOAD` from `.controlplane/controlplane.yml` and workflow defaults, +so those values do not need to be set just to test review apps. + +Production promotion uses a protected GitHub Environment named `production`: + +- Environment secret `CPLN_TOKEN_PRODUCTION` +- Environment variable `CPLN_ORG_PRODUCTION=shakacode-open-source-examples-production` +- Environment variable `PRODUCTION_APP_NAME=react-webpack-rails-tutorial-production` + +Protect the `production` environment with required reviewers, enable prevent +self-review, and consider disabling administrator bypass. Do not store +`CPLN_TOKEN_PRODUCTION` as a repository or organization secret. + Optional repository settings: - `DOCKER_BUILD_SSH_KEY`: secret for private SSH dependencies during Docker builds. @@ -66,7 +76,7 @@ filter in `.github/workflows/cpflow-deploy-staging.yml`. When the upstream `control-plane-flow` repo changes the generated GitHub Actions flow, regenerate the `cpflow-*` actions/workflows in this repo from the target `cpflow` version or branch using `--staging-branch master`, review the diff, and -keep the repository variables above aligned with `.controlplane/controlplane.yml`. Validate with +keep the GitHub settings above aligned with `.controlplane/controlplane.yml`. Validate with `cpflow github-flow-readiness`, `actionlint .github/workflows/cpflow-*.yml`, and the normal CI checks before merging. For review-app workflow changes, remember that the deploy workflow checks out trusted local actions from `master` before diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index 61f5becc..72a9f3e0 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -28,5 +28,6 @@ jobs: # `secrets: inherit` passes all caller repository secrets to the trusted # upstream workflow. The upstream workflow only reads the named secrets it # references, but GitHub does not enforce that boundary. Strict consumers can - # set CPFLOW_GITHUB_ACTIONS_REF to an immutable commit SHA. + # keep both the `uses:` ref and `control_plane_flow_ref` pinned to an + # immutable commit SHA. secrets: inherit diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 980d6de9..6c661981 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -14,5 +14,4 @@ permissions: jobs: show-help: - if: vars.REVIEW_APP_PREFIX != '' uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@db013e139af4ee8741f791c14ff825f13c0a1021 From c78d0f1cf6f7802157939f9b7826d4d629efb79b Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 11:11:54 -1000 Subject: [PATCH 03/18] Clarify inferred review app overrides --- .controlplane/readme.md | 16 ++++++++++++---- .controlplane/shakacode-team.md | 4 +++- .github/cpflow-help.md | 11 +++++++++-- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/.controlplane/readme.md b/.controlplane/readme.md index 81781952..7e948dac 100644 --- a/.controlplane/readme.md +++ b/.controlplane/readme.md @@ -35,13 +35,21 @@ The generated workflow infers the review app prefix because that file defines exactly one app with `match_if_app_name_starts_with: true`. -Optional review-app variables: +These inferred values come from `.controlplane/controlplane.yml`: the review-app +prefix is the app key with `match_if_app_name_starts_with: true`, and the +staging org is the `cpln_org` value on that app or its shared alias. The +variables below are escape hatches for forks and clones, so someone can test +this repo against their own Control Plane org, choose a different review-app +prefix, or expose a different public workload without editing the generated +workflow. Leave them unset for the standard setup. + +Optional review-app override variables: | Variable | Notes | | --- | --- | -| `CPLN_ORG_STAGING` | Override the inferred staging org. | -| `REVIEW_APP_PREFIX` | Override or disambiguate the inferred review app prefix. | -| `PRIMARY_WORKLOAD` | Public workload name used to discover the review URL; defaults to `rails`. | +| `CPLN_ORG_STAGING` | Override the staging org inferred from `cpln_org` in `.controlplane/controlplane.yml`. | +| `REVIEW_APP_PREFIX` | Override the review-app app key inferred from the `match_if_app_name_starts_with: true` entry. | +| `PRIMARY_WORKLOAD` | Override the public workload used to discover the review URL; leave unset for `rails`. | For staging auto-deploys, also configure: diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index c3789149..78bb1ee4 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -43,7 +43,9 @@ Required repository variables for staging deploys: Review apps infer `CPLN_ORG_STAGING`, `REVIEW_APP_PREFIX`, and `PRIMARY_WORKLOAD` from `.controlplane/controlplane.yml` and workflow defaults, -so those values do not need to be set just to test review apps. +so those values do not need to be set just to test review apps. Set them only +when testing a fork or clone against a different Control Plane org, review-app +prefix, or public workload. Production promotion uses a protected GitHub Environment named `production`: diff --git a/.github/cpflow-help.md b/.github/cpflow-help.md index 663b97c3..53ada007 100644 --- a/.github/cpflow-help.md +++ b/.github/cpflow-help.md @@ -43,6 +43,13 @@ GitHub setting. The review app prefix and staging org are inferred from `.controlplane/controlplane.yml` when it defines exactly one app with `match_if_app_name_starts_with: true`. +Those inferred values come from `.controlplane/controlplane.yml`: `cpln_org` +selects the Control Plane org and the app key with +`match_if_app_name_starts_with: true` becomes the review-app prefix. The +optional review-app variables below are override hooks for forks and clones that +want to test against their own Control Plane org, use a different prefix, or +expose a different public workload. Leave them unset for the standard setup. + For production promotion, create a GitHub Environment named `production`, add required reviewers, enable prevent self-review, and store `CPLN_TOKEN_PRODUCTION` as an environment secret there. The generated promotion @@ -56,10 +63,10 @@ workflow uses that environment before it can access production secrets. | `CPLN_ORG_PRODUCTION` | yes (for promote) | Control Plane org on controlplane.com for production. Prefer a `production` environment variable. | | `STAGING_APP_NAME` | yes | App name in `controlplane.yml` used as the staging deploy target. | | `PRODUCTION_APP_NAME` | yes (for promote) | App name in `controlplane.yml` used as the production deploy target. Prefer a `production` environment variable. | -| `REVIEW_APP_PREFIX` | optional | Override or disambiguate the review app prefix inferred from `controlplane.yml`. | +| `REVIEW_APP_PREFIX` | optional | Override the review-app app key inferred from the `match_if_app_name_starts_with: true` entry in `controlplane.yml`. | | `REVIEW_APP_DEPLOYING_ICON_URL` | optional, advanced | Cosmetic custom image URL for the animated deploying icon in review-app PR comments. Set to `none` to use the text fallback icon. | | `STAGING_APP_BRANCH` | optional | Custom staging branch. Custom branches must also appear in `cpflow-deploy-staging.yml`'s push filter. | -| `PRIMARY_WORKLOAD` | optional | Workload polled for health and rollback (defaults to `rails`). | +| `PRIMARY_WORKLOAD` | optional | Override the public workload used for the review URL, health checks, and rollback (defaults to `rails`). | | `DOCKER_BUILD_EXTRA_ARGS` | optional | Newline-delimited extra docker build tokens (e.g. `--build-arg=FOO=bar`). | | `DOCKER_BUILD_SSH_KNOWN_HOSTS` | optional | SSH known_hosts entries when SSH build hosts are not GitHub.com. | | `HEALTH_CHECK_ACCEPTED_STATUSES` | optional | Space-separated HTTP statuses considered healthy on promote (default `200 301 302`). | From 8e2722ff91614051c59638c8e58fad0d5cff3b8f Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 11:31:20 -1000 Subject: [PATCH 04/18] Pin cpflow workflows to latest upstream review fix --- .github/workflows/cpflow-cleanup-stale-review-apps.yml | 4 ++-- .github/workflows/cpflow-delete-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-staging.yml | 4 ++-- .github/workflows/cpflow-help-command.yml | 2 +- .github/workflows/cpflow-promote-staging-to-production.yml | 4 ++-- .github/workflows/cpflow-review-app-help.yml | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index 2c6c51b3..e3bdc07e 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -10,7 +10,7 @@ permissions: jobs: cleanup: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@db013e139af4ee8741f791c14ff825f13c0a1021 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@cfe494bf32925d49508380e03856d97bd71f6689 with: - control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021 + control_plane_flow_ref: cfe494bf32925d49508380e03856d97bd71f6689 secrets: inherit diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index a012741b..7fd347d4 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -26,7 +26,7 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@db013e139af4ee8741f791c14ff825f13c0a1021 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@cfe494bf32925d49508380e03856d97bd71f6689 with: - control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021 + control_plane_flow_ref: cfe494bf32925d49508380e03856d97bd71f6689 secrets: inherit diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index 88e4dad2..8e4b2ae9 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,7 +30,7 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@db013e139af4ee8741f791c14ff825f13c0a1021 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@cfe494bf32925d49508380e03856d97bd71f6689 with: - control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021 + control_plane_flow_ref: cfe494bf32925d49508380e03856d97bd71f6689 secrets: inherit diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index 84cdf115..3faa784a 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,8 +16,8 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@db013e139af4ee8741f791c14ff825f13c0a1021 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@cfe494bf32925d49508380e03856d97bd71f6689 with: - control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021 + control_plane_flow_ref: cfe494bf32925d49508380e03856d97bd71f6689 staging_app_branch_default: "" secrets: inherit diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index db8438a2..d25f8c89 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,4 +23,4 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@db013e139af4ee8741f791c14ff825f13c0a1021 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@cfe494bf32925d49508380e03856d97bd71f6689 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index 72a9f3e0..2ee2b8bb 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -18,9 +18,9 @@ jobs: if: github.event.inputs.confirm_promotion == 'promote' # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the # first selects the reusable workflow, the second selects its shared actions. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@db013e139af4ee8741f791c14ff825f13c0a1021 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@cfe494bf32925d49508380e03856d97bd71f6689 with: - control_plane_flow_ref: db013e139af4ee8741f791c14ff825f13c0a1021 + control_plane_flow_ref: cfe494bf32925d49508380e03856d97bd71f6689 # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. Required reviewers approve the job before GitHub exposes # environment secrets to the upstream reusable workflow. diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 6c661981..30ba6659 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -14,4 +14,4 @@ permissions: jobs: show-help: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@db013e139af4ee8741f791c14ff825f13c0a1021 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@cfe494bf32925d49508380e03856d97bd71f6689 From 31a8103f397b24cdd8a8faf059dc204012eddae5 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 15:44:17 -1000 Subject: [PATCH 05/18] Harden cpflow workflow secret forwarding --- .controlplane/controlplane.yml | 2 +- .../docs/testing-cpflow-github-actions.md | 4 ++-- .controlplane/readme.md | 4 ++++ .controlplane/shakacode-team.md | 5 ++++- .github/cpflow-help.md | 14 +++++++++++--- .../cpflow-cleanup-stale-review-apps.yml | 7 ++++--- .github/workflows/cpflow-delete-review-app.yml | 7 ++++--- .github/workflows/cpflow-deploy-review-app.yml | 8 +++++--- .github/workflows/cpflow-deploy-staging.yml | 8 +++++--- .github/workflows/cpflow-help-command.yml | 4 +++- .../cpflow-promote-staging-to-production.yml | 15 +++++++-------- .github/workflows/cpflow-review-app-help.yml | 4 +++- bin/test-cpflow-github-flow | 6 +++++- 13 files changed, 58 insertions(+), 30 deletions(-) diff --git a/.controlplane/controlplane.yml b/.controlplane/controlplane.yml index 74dc1583..2ef6d0d2 100644 --- a/.controlplane/controlplane.yml +++ b/.controlplane/controlplane.yml @@ -54,7 +54,7 @@ apps: <<: *common # QA Apps are like Heroku review apps, but the use `prefix` so you can run a commmand like # this to create a QA app for the tutorial app. - # `cpflow setup gvc postgres redis rails -a qa-react-webpack-rails-tutorial-pr-1234` + # `cpflow setup gvc postgres redis rails -a qa-react-webpack-rails-tutorial-1234` qa-react-webpack-rails-tutorial: <<: *common # Order matters! diff --git a/.controlplane/docs/testing-cpflow-github-actions.md b/.controlplane/docs/testing-cpflow-github-actions.md index ffd37395..5ef5da6d 100644 --- a/.controlplane/docs/testing-cpflow-github-actions.md +++ b/.controlplane/docs/testing-cpflow-github-actions.md @@ -151,8 +151,8 @@ examples such as `${{ vars.SOME_VALUE }}` can fail action loading before any shell step starts. The wrapper runs `cpflow github-flow-readiness`, parses the generated YAML, checks action input descriptions for literal GitHub expressions, checks that every generated wrapper keeps `uses:` and `control_plane_flow_ref` -on the same upstream ref across all `cpflow-*` wrappers, checks that any -secret-inheriting reusable workflow passes `control_plane_flow_ref`, and runs +on the same upstream ref across all `cpflow-*` wrappers, rejects broad `secrets: inherit` in generated cpflow wrappers, checks that +any secret-passing reusable workflow passes `control_plane_flow_ref`, and runs `actionlint -ignore 'SC2129' .github/workflows/cpflow-*.yml`. ## PR Checks diff --git a/.controlplane/readme.md b/.controlplane/readme.md index 7e948dac..f516d0fc 100644 --- a/.controlplane/readme.md +++ b/.controlplane/readme.md @@ -74,6 +74,9 @@ or similarly trusted maintainers should be able to approve the promotion job. The promotion workflow uses that environment before it can access `CPLN_TOKEN_PRODUCTION`, so the production token is not exposed to ordinary review-app or staging runs. +Generated caller workflows pass only the named secrets each upstream workflow +needs. They do not use `secrets: inherit`; `CPLN_TOKEN_PRODUCTION` is supplied +only by the protected `production` Environment after approval. Advanced optional variables: @@ -499,6 +502,7 @@ The GitHub settings and Control Plane resources must match the app names in `REVIEW_APP_PREFIX` unset and let the workflow infer `qa-react-webpack-rails-tutorial`; generated review apps are named `qa-react-webpack-rails-tutorial-`. +If you have older review apps from the previous `qa-react-webpack-rails-tutorial-pr-` naming, delete them manually after this flow lands; cleanup targets the current prefix convention. This allows teams to: - Preview changes in a production-like environment diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index 78bb1ee4..5e588988 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -39,7 +39,6 @@ Required repository variables for staging deploys: - `CPLN_ORG_STAGING=shakacode-open-source-examples-staging` - `STAGING_APP_NAME=react-webpack-rails-tutorial-staging` - `STAGING_APP_BRANCH=master` -- `PRIMARY_WORKLOAD=rails` Review apps infer `CPLN_ORG_STAGING`, `REVIEW_APP_PREFIX`, and `PRIMARY_WORKLOAD` from `.controlplane/controlplane.yml` and workflow defaults, @@ -56,9 +55,13 @@ Production promotion uses a protected GitHub Environment named `production`: Protect the `production` environment with required reviewers, enable prevent self-review, and consider disabling administrator bypass. Do not store `CPLN_TOKEN_PRODUCTION` as a repository or organization secret. +Generated caller workflows pass only the named secrets each upstream workflow +needs. They do not use `secrets: inherit`; `CPLN_TOKEN_PRODUCTION` is supplied +only by the protected `production` Environment after approval. Optional repository settings: +- `PRIMARY_WORKLOAD`: public workload used for review URLs and promote health checks; defaults to `rails`. - `DOCKER_BUILD_SSH_KEY`: secret for private SSH dependencies during Docker builds. - `DOCKER_BUILD_EXTRA_ARGS`: newline-delimited Docker build tokens, such as `--build-arg=FOO=bar`. - `DOCKER_BUILD_SSH_KNOWN_HOSTS`: custom `known_hosts` entries when SSH build hosts are not GitHub.com. diff --git a/.github/cpflow-help.md b/.github/cpflow-help.md index 53ada007..fa802202 100644 --- a/.github/cpflow-help.md +++ b/.github/cpflow-help.md @@ -35,7 +35,7 @@ You asked for review app help. These commands are generated by [cpflow](https:// | Name | Required | Notes | | --- | --- | --- | | `CPLN_TOKEN_STAGING` | yes | Service-account token scoped to the staging Control Plane org on controlplane.com. | -| `CPLN_TOKEN_PRODUCTION` | yes (for promote) | Store this as a secret on the protected `production` GitHub Environment, not as a repository or organization secret. | +| `CPLN_TOKEN_PRODUCTION` | yes for promote, as an environment secret | Store this as a secret on the protected `production` GitHub Environment, not as a repository or organization secret. | | `DOCKER_BUILD_SSH_KEY` | optional | Private SSH key used when Docker builds fetch private deps via `RUN --mount=type=ssh`. | For normal generated review apps, `CPLN_TOKEN_STAGING` is the only required @@ -54,15 +54,18 @@ For production promotion, create a GitHub Environment named `production`, add required reviewers, enable prevent self-review, and store `CPLN_TOKEN_PRODUCTION` as an environment secret there. The generated promotion workflow uses that environment before it can access production secrets. +Generated caller workflows pass only the named secrets each upstream workflow +needs. They do not use `secrets: inherit`; the production token is supplied by +the protected `production` Environment after approval. ### GitHub Actions variables | Name | Required | Notes | | --- | --- | --- | | `CPLN_ORG_STAGING` | optional for review apps; yes for staging | Override the staging/review Control Plane org inferred from `controlplane.yml`. | -| `CPLN_ORG_PRODUCTION` | yes (for promote) | Control Plane org on controlplane.com for production. Prefer a `production` environment variable. | +| `CPLN_ORG_PRODUCTION` | yes for promote, preferably as environment variable | Control Plane org on controlplane.com for production. Prefer a `production` environment variable. | | `STAGING_APP_NAME` | yes | App name in `controlplane.yml` used as the staging deploy target. | -| `PRODUCTION_APP_NAME` | yes (for promote) | App name in `controlplane.yml` used as the production deploy target. Prefer a `production` environment variable. | +| `PRODUCTION_APP_NAME` | yes for promote, preferably as environment variable | App name in `controlplane.yml` used as the production deploy target. Prefer a `production` environment variable. | | `REVIEW_APP_PREFIX` | optional | Override the review-app app key inferred from the `match_if_app_name_starts_with: true` entry in `controlplane.yml`. | | `REVIEW_APP_DEPLOYING_ICON_URL` | optional, advanced | Cosmetic custom image URL for the animated deploying icon in review-app PR comments. Set to `none` to use the text fallback icon. | | `STAGING_APP_BRANCH` | optional | Custom staging branch. Custom branches must also appear in `cpflow-deploy-staging.yml`'s push filter. | @@ -75,6 +78,11 @@ workflow uses that environment before it can access production secrets. +Generated review app names use `-`, for example +`my-app-review-123`. If you are migrating from older local workflow glue that +created names like `-pr-123`, delete those old review apps +manually after merging this flow. +
Advanced: testing unreleased control-plane-flow changes diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index e3bdc07e..bc3e5631 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -10,7 +10,8 @@ permissions: jobs: cleanup: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@cfe494bf32925d49508380e03856d97bd71f6689 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 with: - control_plane_flow_ref: cfe494bf32925d49508380e03856d97bd71f6689 - secrets: inherit + control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + secrets: + CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index 7fd347d4..5ac01dc6 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -26,7 +26,8 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@cfe494bf32925d49508380e03856d97bd71f6689 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 with: - control_plane_flow_ref: cfe494bf32925d49508380e03856d97bd71f6689 - secrets: inherit + control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + secrets: + CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index 8e4b2ae9..14618de9 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,7 +30,9 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@cfe494bf32925d49508380e03856d97bd71f6689 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 with: - control_plane_flow_ref: cfe494bf32925d49508380e03856d97bd71f6689 - secrets: inherit + control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + secrets: + CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} + DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index 3faa784a..153eaed5 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,8 +16,10 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@cfe494bf32925d49508380e03856d97bd71f6689 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 with: - control_plane_flow_ref: cfe494bf32925d49508380e03856d97bd71f6689 + control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 staging_app_branch_default: "" - secrets: inherit + secrets: + CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} + DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index d25f8c89..830201a0 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,4 +23,6 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@cfe494bf32925d49508380e03856d97bd71f6689 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + with: + control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index 2ee2b8bb..c6314c80 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -18,16 +18,15 @@ jobs: if: github.event.inputs.confirm_promotion == 'promote' # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the # first selects the reusable workflow, the second selects its shared actions. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@cfe494bf32925d49508380e03856d97bd71f6689 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 with: - control_plane_flow_ref: cfe494bf32925d49508380e03856d97bd71f6689 + control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. Required reviewers approve the job before GitHub exposes # environment secrets to the upstream reusable workflow. production_environment: production - # `secrets: inherit` passes all caller repository secrets to the trusted - # upstream workflow. The upstream workflow only reads the named secrets it - # references, but GitHub does not enforce that boundary. Strict consumers can - # keep both the `uses:` ref and `control_plane_flow_ref` pinned to an - # immutable commit SHA. - secrets: inherit + # Only pass the staging token explicitly. CPLN_TOKEN_PRODUCTION must live on + # the protected production Environment, where GitHub exposes it only after + # the required reviewers approve this job. + secrets: + CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 30ba6659..cf1861bd 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -14,4 +14,6 @@ permissions: jobs: show-help: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@cfe494bf32925d49508380e03856d97bd71f6689 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + with: + control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 diff --git a/bin/test-cpflow-github-flow b/bin/test-cpflow-github-flow index 91c8ed46..804a0011 100755 --- a/bin/test-cpflow-github-flow +++ b/bin/test-cpflow-github-flow @@ -55,6 +55,10 @@ Dir[".github/workflows/cpflow-*.yml"].sort.each do |path| input_ref = with["control_plane_flow_ref"] uses_match = job["uses"].to_s.match(CONTROL_PLANE_FLOW_WORKFLOW) + if job["secrets"] == "inherit" + abort "#{path}:#{job_name} uses secrets: inherit; pass only the named secrets the upstream workflow needs" + end + unless uses_match abort "#{path}:#{job_name} has control_plane_flow_ref but no control-plane-flow reusable workflow" if input_ref @@ -68,7 +72,7 @@ Dir[".github/workflows/cpflow-*.yml"].sort.each do |path| refs[input_ref] << "#{path}:#{job_name}" abort "#{path}:#{job_name} mismatched cpflow refs: #{uses_ref}, #{input_ref}" if uses_ref != input_ref elsif job.key?("secrets") - abort "#{path}:#{job_name} inherits secrets but is missing control_plane_flow_ref for #{uses_ref}" + abort "#{path}:#{job_name} passes secrets but is missing control_plane_flow_ref for #{uses_ref}" end end end From e230f63492dfade866c99c9c4e36609a070f60e7 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 15:53:32 -1000 Subject: [PATCH 06/18] Pin cpflow workflows to shared resolver --- .github/workflows/cpflow-cleanup-stale-review-apps.yml | 4 ++-- .github/workflows/cpflow-delete-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-staging.yml | 4 ++-- .github/workflows/cpflow-help-command.yml | 4 ++-- .github/workflows/cpflow-promote-staging-to-production.yml | 4 ++-- .github/workflows/cpflow-review-app-help.yml | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index bc3e5631..a0784088 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -10,8 +10,8 @@ permissions: jobs: cleanup: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 with: - control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index 5ac01dc6..6693bfe8 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -26,8 +26,8 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 with: - control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index 14618de9..87423666 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,9 +30,9 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 with: - control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index 153eaed5..b12a2507 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,9 +16,9 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 with: - control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 staging_app_branch_default: "" secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index 830201a0..119029ef 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,6 +23,6 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 with: - control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index c6314c80..f30f2151 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -18,9 +18,9 @@ jobs: if: github.event.inputs.confirm_promotion == 'promote' # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the # first selects the reusable workflow, the second selects its shared actions. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 with: - control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. Required reviewers approve the job before GitHub exposes # environment secrets to the upstream reusable workflow. diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index cf1861bd..084e30f0 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -14,6 +14,6 @@ permissions: jobs: show-help: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 with: - control_plane_flow_ref: 7d9b80dcb55b243d0cc78b0a85bcb3d568ce8e02 + control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 From 5bc5ef8c64551bb6f4c8a10c3444583544520836 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 15:59:23 -1000 Subject: [PATCH 07/18] Clarify cpflow production secret docs --- .controlplane/controlplane.yml | 2 +- .controlplane/readme.md | 14 +++++++---- .controlplane/shakacode-team.md | 6 ++++- .github/cpflow-help.md | 24 ++++++++++++++----- .../cpflow-promote-staging-to-production.yml | 5 ++-- bin/test-cpflow-github-flow | 12 ++++++++++ 6 files changed, 49 insertions(+), 14 deletions(-) diff --git a/.controlplane/controlplane.yml b/.controlplane/controlplane.yml index 2ef6d0d2..0d30b91b 100644 --- a/.controlplane/controlplane.yml +++ b/.controlplane/controlplane.yml @@ -52,7 +52,7 @@ apps: react-webpack-rails-tutorial-staging: <<: *common - # QA Apps are like Heroku review apps, but the use `prefix` so you can run a commmand like + # QA Apps are like Heroku review apps, but they use `prefix` so you can run a command like # this to create a QA app for the tutorial app. # `cpflow setup gvc postgres redis rails -a qa-react-webpack-rails-tutorial-1234` qa-react-webpack-rails-tutorial: diff --git a/.controlplane/readme.md b/.controlplane/readme.md index f516d0fc..5d084070 100644 --- a/.controlplane/readme.md +++ b/.controlplane/readme.md @@ -71,9 +71,11 @@ For production promotion, configure a protected GitHub Environment named Protect the `production` environment with required reviewers, enable prevent self-review, and consider disabling administrator bypass. Only release managers or similarly trusted maintainers should be able to approve the promotion job. -The promotion workflow uses that environment before it can access -`CPLN_TOKEN_PRODUCTION`, so the production token is not exposed to ordinary -review-app or staging runs. +The generated caller passes `production_environment: production`; the upstream +reusable workflow runs its production job in that environment, so GitHub injects +`CPLN_TOKEN_PRODUCTION` only after the environment approval gate passes. The +production token is not exposed to ordinary review-app or staging runs. + Generated caller workflows pass only the named secrets each upstream workflow needs. They do not use `secrets: inherit`; `CPLN_TOKEN_PRODUCTION` is supplied only by the protected `production` Environment after approval. @@ -502,7 +504,11 @@ The GitHub settings and Control Plane resources must match the app names in `REVIEW_APP_PREFIX` unset and let the workflow infer `qa-react-webpack-rails-tutorial`; generated review apps are named `qa-react-webpack-rails-tutorial-`. -If you have older review apps from the previous `qa-react-webpack-rails-tutorial-pr-` naming, delete them manually after this flow lands; cleanup targets the current prefix convention. +If you have older review apps from the previous `qa-react-webpack-rails-tutorial-pr-` naming, delete them manually after this flow lands; cleanup targets the current prefix convention. To inventory old apps, run: + +```sh +cpln gvc query --org shakacode-open-source-examples-staging -o yaml --prop name~qa-react-webpack-rails-tutorial-pr- +``` This allows teams to: - Preview changes in a production-like environment diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index 5e588988..7009478d 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -54,7 +54,11 @@ Production promotion uses a protected GitHub Environment named `production`: Protect the `production` environment with required reviewers, enable prevent self-review, and consider disabling administrator bypass. Do not store -`CPLN_TOKEN_PRODUCTION` as a repository or organization secret. +`CPLN_TOKEN_PRODUCTION` as a repository or organization secret. The caller +passes `production_environment: production`; the upstream reusable workflow runs +its production job in that environment, and GitHub injects the production token +only after approval. + Generated caller workflows pass only the named secrets each upstream workflow needs. They do not use `secrets: inherit`; `CPLN_TOKEN_PRODUCTION` is supplied only by the protected `production` Environment after approval. diff --git a/.github/cpflow-help.md b/.github/cpflow-help.md index fa802202..9121945e 100644 --- a/.github/cpflow-help.md +++ b/.github/cpflow-help.md @@ -35,13 +35,15 @@ You asked for review app help. These commands are generated by [cpflow](https:// | Name | Required | Notes | | --- | --- | --- | | `CPLN_TOKEN_STAGING` | yes | Service-account token scoped to the staging Control Plane org on controlplane.com. | -| `CPLN_TOKEN_PRODUCTION` | yes for promote, as an environment secret | Store this as a secret on the protected `production` GitHub Environment, not as a repository or organization secret. | +| `CPLN_TOKEN_PRODUCTION` | yes for promote, as a production environment secret | Store this as a secret on the protected `production` GitHub Environment, not as a repository or organization secret. | | `DOCKER_BUILD_SSH_KEY` | optional | Private SSH key used when Docker builds fetch private deps via `RUN --mount=type=ssh`. | For normal generated review apps, `CPLN_TOKEN_STAGING` is the only required GitHub setting. The review app prefix and staging org are inferred from `.controlplane/controlplane.yml` when it defines exactly one app with `match_if_app_name_starts_with: true`. +If more than one app has that flag, set `CPLN_ORG_STAGING` and +`REVIEW_APP_PREFIX` explicitly to disambiguate. Those inferred values come from `.controlplane/controlplane.yml`: `cpln_org` selects the Control Plane org and the app key with @@ -52,8 +54,10 @@ expose a different public workload. Leave them unset for the standard setup. For production promotion, create a GitHub Environment named `production`, add required reviewers, enable prevent self-review, and store -`CPLN_TOKEN_PRODUCTION` as an environment secret there. The generated promotion -workflow uses that environment before it can access production secrets. +`CPLN_TOKEN_PRODUCTION` as an environment secret there. The generated caller +passes `production_environment: production`; the upstream reusable workflow +runs its production job in that environment, so GitHub injects +`CPLN_TOKEN_PRODUCTION` only after the environment approval gate passes. Generated caller workflows pass only the named secrets each upstream workflow needs. They do not use `secrets: inherit`; the production token is supplied by the protected `production` Environment after approval. @@ -63,9 +67,9 @@ the protected `production` Environment after approval. | Name | Required | Notes | | --- | --- | --- | | `CPLN_ORG_STAGING` | optional for review apps; yes for staging | Override the staging/review Control Plane org inferred from `controlplane.yml`. | -| `CPLN_ORG_PRODUCTION` | yes for promote, preferably as environment variable | Control Plane org on controlplane.com for production. Prefer a `production` environment variable. | +| `CPLN_ORG_PRODUCTION` | yes for promote, preferably as production environment variable | Control Plane org on controlplane.com for production. Prefer a `production` environment variable. | | `STAGING_APP_NAME` | yes | App name in `controlplane.yml` used as the staging deploy target. | -| `PRODUCTION_APP_NAME` | yes for promote, preferably as environment variable | App name in `controlplane.yml` used as the production deploy target. Prefer a `production` environment variable. | +| `PRODUCTION_APP_NAME` | yes for promote, preferably as production environment variable | App name in `controlplane.yml` used as the production deploy target. Prefer a `production` environment variable. | | `REVIEW_APP_PREFIX` | optional | Override the review-app app key inferred from the `match_if_app_name_starts_with: true` entry in `controlplane.yml`. | | `REVIEW_APP_DEPLOYING_ICON_URL` | optional, advanced | Cosmetic custom image URL for the animated deploying icon in review-app PR comments. Set to `none` to use the text fallback icon. | | `STAGING_APP_BRANCH` | optional | Custom staging branch. Custom branches must also appear in `cpflow-deploy-staging.yml`'s push filter. | @@ -81,7 +85,15 @@ the protected `production` Environment after approval. Generated review app names use `-`, for example `my-app-review-123`. If you are migrating from older local workflow glue that created names like `-pr-123`, delete those old review apps -manually after merging this flow. +manually after merging this flow. To inventory old apps, run: + +```sh +cpln gvc query --org -o yaml --prop name~-pr- +``` + +The PR-open help workflow posts the short command reference whenever the +generated wrapper exists. Remove `.github/workflows/cpflow-review-app-help.yml` +or add a wrapper-level `if:` guard if a repo should not advertise review apps.
Advanced: testing unreleased control-plane-flow changes diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index f30f2151..678f184d 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -22,8 +22,9 @@ jobs: with: control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub - # Environment. Required reviewers approve the job before GitHub exposes - # environment secrets to the upstream reusable workflow. + # Environment. The caller passes the environment name, the upstream + # reusable workflow runs its production job in that environment, and + # GitHub exposes environment secrets only after required reviewers approve. production_environment: production # Only pass the staging token explicitly. CPLN_TOKEN_PRODUCTION must live on # the protected production Environment, where GitHub exposes it only after diff --git a/bin/test-cpflow-github-flow b/bin/test-cpflow-github-flow index 804a0011..a968dce9 100755 --- a/bin/test-cpflow-github-flow +++ b/bin/test-cpflow-github-flow @@ -43,6 +43,7 @@ bin/conductor-exec ruby <<'RUBY' require "yaml" CONTROL_PLANE_FLOW_WORKFLOW = %r{\Ashakacode/control-plane-flow/\.github/workflows/[^@\s]+@([^\s]+)\z} +PROMOTE_WORKFLOW = "/.github/workflows/cpflow-promote-staging-to-production.yml@" refs = Hash.new { |hash, key| hash[key] = [] } @@ -68,6 +69,17 @@ Dir[".github/workflows/cpflow-*.yml"].sort.each do |path| uses_ref = uses_match[1] refs[uses_ref] << "#{path}:#{job_name}" + if job["uses"].to_s.include?(PROMOTE_WORKFLOW) + if with["production_environment"].to_s.strip.empty? + abort "#{path}:#{job_name} must set production_environment so GitHub can expose production environment secrets after approval" + end + + secrets = job["secrets"].is_a?(Hash) ? job["secrets"] : {} + if secrets.key?("CPLN_TOKEN_PRODUCTION") + abort "#{path}:#{job_name} must not pass CPLN_TOKEN_PRODUCTION as a caller secret; store it on the protected production environment" + end + end + if input_ref refs[input_ref] << "#{path}:#{job_name}" abort "#{path}:#{job_name} mismatched cpflow refs: #{uses_ref}, #{input_ref}" if uses_ref != input_ref From 9be9140dd91cb7b5a1b5cd2dc5662161a6eced78 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 16:11:10 -1000 Subject: [PATCH 08/18] Pin cpflow workflows to final review polish --- .github/workflows/cpflow-cleanup-stale-review-apps.yml | 6 ++++-- .github/workflows/cpflow-delete-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-staging.yml | 4 ++-- .github/workflows/cpflow-help-command.yml | 4 ++-- .github/workflows/cpflow-promote-staging-to-production.yml | 4 ++-- .github/workflows/cpflow-review-app-help.yml | 4 ++-- 7 files changed, 16 insertions(+), 14 deletions(-) diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index a0784088..81bd359e 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -10,8 +10,10 @@ permissions: jobs: cleanup: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + # Cleanup targets qa-react-webpack-rails-tutorial-. Manually delete any + # legacy qa-react-webpack-rails-tutorial-pr- apps during migration. + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 with: - control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index 6693bfe8..0e8ac0d1 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -26,8 +26,8 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 with: - control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index 87423666..5b8100d0 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,9 +30,9 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 with: - control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index b12a2507..fb60dc85 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,9 +16,9 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 with: - control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 staging_app_branch_default: "" secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index 119029ef..48474ae9 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,6 +23,6 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 with: - control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index 678f184d..8613a69e 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -18,9 +18,9 @@ jobs: if: github.event.inputs.confirm_promotion == 'promote' # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the # first selects the reusable workflow, the second selects its shared actions. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 with: - control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. The caller passes the environment name, the upstream # reusable workflow runs its production job in that environment, and diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 084e30f0..751f6c44 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -14,6 +14,6 @@ permissions: jobs: show-help: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 with: - control_plane_flow_ref: b0c5759050c5c79a05826cd7a5d3ae711cd22a40 + control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 From f443270f9011d7a1d3da733f2497dcbcc25f4f5f Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 16:33:09 -1000 Subject: [PATCH 09/18] Document final cpflow PR pin --- .controlplane/readme.md | 5 ++++- .controlplane/shakacode-team.md | 5 +++++ .github/workflows/cpflow-cleanup-stale-review-apps.yml | 4 ++-- .github/workflows/cpflow-delete-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-staging.yml | 4 ++-- .github/workflows/cpflow-help-command.yml | 4 ++-- .github/workflows/cpflow-promote-staging-to-production.yml | 4 ++-- .github/workflows/cpflow-review-app-help.yml | 7 +++++-- 9 files changed, 26 insertions(+), 15 deletions(-) diff --git a/.controlplane/readme.md b/.controlplane/readme.md index 5d084070..20527bb1 100644 --- a/.controlplane/readme.md +++ b/.controlplane/readme.md @@ -504,7 +504,10 @@ The GitHub settings and Control Plane resources must match the app names in `REVIEW_APP_PREFIX` unset and let the workflow infer `qa-react-webpack-rails-tutorial`; generated review apps are named `qa-react-webpack-rails-tutorial-`. -If you have older review apps from the previous `qa-react-webpack-rails-tutorial-pr-` naming, delete them manually after this flow lands; cleanup targets the current prefix convention. To inventory old apps, run: +If you have older review apps from the previous +`qa-react-webpack-rails-tutorial-pr-` naming, delete them manually +after this flow lands; cleanup targets the current prefix convention. To +inventory old apps, run: ```sh cpln gvc query --org shakacode-open-source-examples-staging -o yaml --prop name~qa-react-webpack-rails-tutorial-pr- diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index 7009478d..e78ed121 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -77,6 +77,11 @@ Optional repository settings: - `HEALTH_CHECK_RETRIES` / `HEALTH_CHECK_INTERVAL`: production health polling controls; defaults to `24` retries and `15` seconds. - `ROLLBACK_READINESS_RETRIES` / `ROLLBACK_READINESS_INTERVAL`: post-rollback health polling controls; defaults to `24` retries and `15` seconds. +Current workflow wrappers are pinned to upstream `control-plane-flow` PR #318 at +`f3f410ebe622fd60af09b8bdf6eca4617685c64a` for downstream testing. After that +upstream work is released, regenerate or repin the wrappers to the release tag +instead of keeping this PR commit SHA long term. + If staging moves off `master`, update both `STAGING_APP_BRANCH` and the branch filter in `.github/workflows/cpflow-deploy-staging.yml`. diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index 81bd359e..9258e273 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -12,8 +12,8 @@ jobs: cleanup: # Cleanup targets qa-react-webpack-rails-tutorial-. Manually delete any # legacy qa-react-webpack-rails-tutorial-pr- apps during migration. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a with: - control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 + control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index 0e8ac0d1..e365c1ee 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -26,8 +26,8 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a with: - control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 + control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index 5b8100d0..a2b54545 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,9 +30,9 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a with: - control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 + control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index fb60dc85..57241ba2 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,9 +16,9 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a with: - control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 + control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a staging_app_branch_default: "" secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index 48474ae9..d7b968b8 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,6 +23,6 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a with: - control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 + control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index 8613a69e..e2de4a4d 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -18,9 +18,9 @@ jobs: if: github.event.inputs.confirm_promotion == 'promote' # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the # first selects the reusable workflow, the second selects its shared actions. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a with: - control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 + control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. The caller passes the environment name, the upstream # reusable workflow runs its production job in that environment, and diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 751f6c44..8241d28c 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -14,6 +14,9 @@ permissions: jobs: show-help: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@1d82a6dc510745c3fe9b40b01ea348875a4f1923 + # This is intentionally unconditional: adding this wrapper opts the repo in + # to PR-open help. Remove the wrapper or add a repo-specific `if:` guard if + # review apps should not be advertised before Control Plane is configured. + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a with: - control_plane_flow_ref: 1d82a6dc510745c3fe9b40b01ea348875a4f1923 + control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a From 8bec1859b552d290e7df10cb4599c39b78e6e341 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 16:41:18 -1000 Subject: [PATCH 10/18] Pin cpflow workflows to latest PR head --- .controlplane/shakacode-team.md | 2 +- .github/workflows/cpflow-cleanup-stale-review-apps.yml | 4 ++-- .github/workflows/cpflow-delete-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-staging.yml | 4 ++-- .github/workflows/cpflow-help-command.yml | 4 ++-- .github/workflows/cpflow-promote-staging-to-production.yml | 4 ++-- .github/workflows/cpflow-review-app-help.yml | 4 ++-- bin/test-cpflow-github-flow | 4 ++-- 9 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index e78ed121..62411b4d 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -78,7 +78,7 @@ Optional repository settings: - `ROLLBACK_READINESS_RETRIES` / `ROLLBACK_READINESS_INTERVAL`: post-rollback health polling controls; defaults to `24` retries and `15` seconds. Current workflow wrappers are pinned to upstream `control-plane-flow` PR #318 at -`f3f410ebe622fd60af09b8bdf6eca4617685c64a` for downstream testing. After that +`a83a38fa3e60538cd54d193791302d0dc52df695` for downstream testing. After that upstream work is released, regenerate or repin the wrappers to the release tag instead of keeping this PR commit SHA long term. diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index 9258e273..2c4386e7 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -12,8 +12,8 @@ jobs: cleanup: # Cleanup targets qa-react-webpack-rails-tutorial-. Manually delete any # legacy qa-react-webpack-rails-tutorial-pr- apps during migration. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@a83a38fa3e60538cd54d193791302d0dc52df695 with: - control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a + control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index e365c1ee..c4e73c45 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -26,8 +26,8 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@a83a38fa3e60538cd54d193791302d0dc52df695 with: - control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a + control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index a2b54545..33067881 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,9 +30,9 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@a83a38fa3e60538cd54d193791302d0dc52df695 with: - control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a + control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index 57241ba2..62869649 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,9 +16,9 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@a83a38fa3e60538cd54d193791302d0dc52df695 with: - control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a + control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 staging_app_branch_default: "" secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index d7b968b8..5199d484 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,6 +23,6 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@a83a38fa3e60538cd54d193791302d0dc52df695 with: - control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a + control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index e2de4a4d..893bb0dc 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -18,9 +18,9 @@ jobs: if: github.event.inputs.confirm_promotion == 'promote' # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the # first selects the reusable workflow, the second selects its shared actions. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@a83a38fa3e60538cd54d193791302d0dc52df695 with: - control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a + control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. The caller passes the environment name, the upstream # reusable workflow runs its production job in that environment, and diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 8241d28c..109b5cd8 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -17,6 +17,6 @@ jobs: # This is intentionally unconditional: adding this wrapper opts the repo in # to PR-open help. Remove the wrapper or add a repo-specific `if:` guard if # review apps should not be advertised before Control Plane is configured. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@f3f410ebe622fd60af09b8bdf6eca4617685c64a + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@a83a38fa3e60538cd54d193791302d0dc52df695 with: - control_plane_flow_ref: f3f410ebe622fd60af09b8bdf6eca4617685c64a + control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 diff --git a/bin/test-cpflow-github-flow b/bin/test-cpflow-github-flow index a968dce9..6294e5f9 100755 --- a/bin/test-cpflow-github-flow +++ b/bin/test-cpflow-github-flow @@ -43,7 +43,7 @@ bin/conductor-exec ruby <<'RUBY' require "yaml" CONTROL_PLANE_FLOW_WORKFLOW = %r{\Ashakacode/control-plane-flow/\.github/workflows/[^@\s]+@([^\s]+)\z} -PROMOTE_WORKFLOW = "/.github/workflows/cpflow-promote-staging-to-production.yml@" +PROMOTE_WORKFLOW = %r{\Ashakacode/control-plane-flow/\.github/workflows/cpflow-promote-staging-to-production\.yml@[^\s]+\z} refs = Hash.new { |hash, key| hash[key] = [] } @@ -69,7 +69,7 @@ Dir[".github/workflows/cpflow-*.yml"].sort.each do |path| uses_ref = uses_match[1] refs[uses_ref] << "#{path}:#{job_name}" - if job["uses"].to_s.include?(PROMOTE_WORKFLOW) + if job["uses"].to_s.match?(PROMOTE_WORKFLOW) if with["production_environment"].to_s.strip.empty? abort "#{path}:#{job_name} must set production_environment so GitHub can expose production environment secrets after approval" end From 11033a9219ee7e326687886a29da653d62ec0929 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 16:55:57 -1000 Subject: [PATCH 11/18] Pin cpflow workflows to latest upstream PR --- .controlplane/shakacode-team.md | 2 +- .github/workflows/cpflow-cleanup-stale-review-apps.yml | 4 ++-- .github/workflows/cpflow-delete-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-staging.yml | 4 ++-- .github/workflows/cpflow-help-command.yml | 4 ++-- .github/workflows/cpflow-promote-staging-to-production.yml | 4 ++-- .github/workflows/cpflow-review-app-help.yml | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index 62411b4d..3a8bf303 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -78,7 +78,7 @@ Optional repository settings: - `ROLLBACK_READINESS_RETRIES` / `ROLLBACK_READINESS_INTERVAL`: post-rollback health polling controls; defaults to `24` retries and `15` seconds. Current workflow wrappers are pinned to upstream `control-plane-flow` PR #318 at -`a83a38fa3e60538cd54d193791302d0dc52df695` for downstream testing. After that +`1fd7de511099278dc9dccd4c28b146d809cf36bc` for downstream testing. After that upstream work is released, regenerate or repin the wrappers to the release tag instead of keeping this PR commit SHA long term. diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index 2c4386e7..db4068bd 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -12,8 +12,8 @@ jobs: cleanup: # Cleanup targets qa-react-webpack-rails-tutorial-. Manually delete any # legacy qa-react-webpack-rails-tutorial-pr- apps during migration. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@a83a38fa3e60538cd54d193791302d0dc52df695 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc with: - control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 + control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index c4e73c45..6e598805 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -26,8 +26,8 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@a83a38fa3e60538cd54d193791302d0dc52df695 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc with: - control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 + control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index 33067881..d5930ec2 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,9 +30,9 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@a83a38fa3e60538cd54d193791302d0dc52df695 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc with: - control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 + control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index 62869649..3d247789 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,9 +16,9 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@a83a38fa3e60538cd54d193791302d0dc52df695 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc with: - control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 + control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc staging_app_branch_default: "" secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index 5199d484..a569c633 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,6 +23,6 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@a83a38fa3e60538cd54d193791302d0dc52df695 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc with: - control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 + control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index 893bb0dc..eb6f54ec 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -18,9 +18,9 @@ jobs: if: github.event.inputs.confirm_promotion == 'promote' # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the # first selects the reusable workflow, the second selects its shared actions. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@a83a38fa3e60538cd54d193791302d0dc52df695 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc with: - control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 + control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. The caller passes the environment name, the upstream # reusable workflow runs its production job in that environment, and diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 109b5cd8..e492abf0 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -17,6 +17,6 @@ jobs: # This is intentionally unconditional: adding this wrapper opts the repo in # to PR-open help. Remove the wrapper or add a repo-specific `if:` guard if # review apps should not be advertised before Control Plane is configured. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@a83a38fa3e60538cd54d193791302d0dc52df695 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc with: - control_plane_flow_ref: a83a38fa3e60538cd54d193791302d0dc52df695 + control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc From 84ea6b3992c9d3590e32248b33c7bccdeb4c7fea Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 17:08:58 -1000 Subject: [PATCH 12/18] Pin cpflow workflows to safety guard fixes --- .controlplane/shakacode-team.md | 2 +- .github/workflows/cpflow-cleanup-stale-review-apps.yml | 4 ++-- .github/workflows/cpflow-delete-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-staging.yml | 4 ++-- .github/workflows/cpflow-help-command.yml | 4 ++-- .github/workflows/cpflow-promote-staging-to-production.yml | 4 ++-- .github/workflows/cpflow-review-app-help.yml | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index 3a8bf303..fd20e20e 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -78,7 +78,7 @@ Optional repository settings: - `ROLLBACK_READINESS_RETRIES` / `ROLLBACK_READINESS_INTERVAL`: post-rollback health polling controls; defaults to `24` retries and `15` seconds. Current workflow wrappers are pinned to upstream `control-plane-flow` PR #318 at -`1fd7de511099278dc9dccd4c28b146d809cf36bc` for downstream testing. After that +`19501451b7e08a48a917fb04a439171881558f82` for downstream testing. After that upstream work is released, regenerate or repin the wrappers to the release tag instead of keeping this PR commit SHA long term. diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index db4068bd..2d70cb96 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -12,8 +12,8 @@ jobs: cleanup: # Cleanup targets qa-react-webpack-rails-tutorial-. Manually delete any # legacy qa-react-webpack-rails-tutorial-pr- apps during migration. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@19501451b7e08a48a917fb04a439171881558f82 with: - control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc + control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index 6e598805..f91eeaed 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -26,8 +26,8 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@19501451b7e08a48a917fb04a439171881558f82 with: - control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc + control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index d5930ec2..863b8220 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,9 +30,9 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@19501451b7e08a48a917fb04a439171881558f82 with: - control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc + control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index 3d247789..3e5cd431 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,9 +16,9 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@19501451b7e08a48a917fb04a439171881558f82 with: - control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc + control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 staging_app_branch_default: "" secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index a569c633..95e7561f 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,6 +23,6 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@19501451b7e08a48a917fb04a439171881558f82 with: - control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc + control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index eb6f54ec..15806227 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -18,9 +18,9 @@ jobs: if: github.event.inputs.confirm_promotion == 'promote' # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the # first selects the reusable workflow, the second selects its shared actions. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@19501451b7e08a48a917fb04a439171881558f82 with: - control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc + control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. The caller passes the environment name, the upstream # reusable workflow runs its production job in that environment, and diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index e492abf0..8e082ca4 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -17,6 +17,6 @@ jobs: # This is intentionally unconditional: adding this wrapper opts the repo in # to PR-open help. Remove the wrapper or add a repo-specific `if:` guard if # review apps should not be advertised before Control Plane is configured. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@1fd7de511099278dc9dccd4c28b146d809cf36bc + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@19501451b7e08a48a917fb04a439171881558f82 with: - control_plane_flow_ref: 1fd7de511099278dc9dccd4c28b146d809cf36bc + control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 From 2a62d01b6d149cb29b4aff883a2aad85c0a33be7 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 17:19:31 -1000 Subject: [PATCH 13/18] Pin cpflow workflows to final PR head --- .controlplane/shakacode-team.md | 2 +- .github/workflows/cpflow-cleanup-stale-review-apps.yml | 4 ++-- .github/workflows/cpflow-delete-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-review-app.yml | 4 ++-- .github/workflows/cpflow-deploy-staging.yml | 4 ++-- .github/workflows/cpflow-help-command.yml | 4 ++-- .github/workflows/cpflow-promote-staging-to-production.yml | 4 ++-- .github/workflows/cpflow-review-app-help.yml | 4 ++-- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index fd20e20e..59a7683d 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -78,7 +78,7 @@ Optional repository settings: - `ROLLBACK_READINESS_RETRIES` / `ROLLBACK_READINESS_INTERVAL`: post-rollback health polling controls; defaults to `24` retries and `15` seconds. Current workflow wrappers are pinned to upstream `control-plane-flow` PR #318 at -`19501451b7e08a48a917fb04a439171881558f82` for downstream testing. After that +`19ca93eadcba81e7438f26ab55a0f2ca7ace82b0` for downstream testing. After that upstream work is released, regenerate or repin the wrappers to the release tag instead of keeping this PR commit SHA long term. diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index 2d70cb96..5ededc8b 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -12,8 +12,8 @@ jobs: cleanup: # Cleanup targets qa-react-webpack-rails-tutorial-. Manually delete any # legacy qa-react-webpack-rails-tutorial-pr- apps during migration. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@19501451b7e08a48a917fb04a439171881558f82 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 with: - control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 + control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index f91eeaed..73dbca68 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -26,8 +26,8 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@19501451b7e08a48a917fb04a439171881558f82 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 with: - control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 + control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index 863b8220..ed256892 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,9 +30,9 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@19501451b7e08a48a917fb04a439171881558f82 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 with: - control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 + control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index 3e5cd431..bd139c65 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,9 +16,9 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@19501451b7e08a48a917fb04a439171881558f82 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 with: - control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 + control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 staging_app_branch_default: "" secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index 95e7561f..1c54801e 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,6 +23,6 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@19501451b7e08a48a917fb04a439171881558f82 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 with: - control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 + control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index 15806227..644f1fc3 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -18,9 +18,9 @@ jobs: if: github.event.inputs.confirm_promotion == 'promote' # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the # first selects the reusable workflow, the second selects its shared actions. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@19501451b7e08a48a917fb04a439171881558f82 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 with: - control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 + control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. The caller passes the environment name, the upstream # reusable workflow runs its production job in that environment, and diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 8e082ca4..b52d0305 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -17,6 +17,6 @@ jobs: # This is intentionally unconditional: adding this wrapper opts the repo in # to PR-open help. Remove the wrapper or add a repo-specific `if:` guard if # review apps should not be advertised before Control Plane is configured. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@19501451b7e08a48a917fb04a439171881558f82 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 with: - control_plane_flow_ref: 19501451b7e08a48a917fb04a439171881558f82 + control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 From eb25cc6c6fac3945a6a5a30c7e786bf4ddd0850b Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 18:12:08 -1000 Subject: [PATCH 14/18] Simplify downstream cpflow docs --- .../docs/testing-cpflow-github-actions.md | 262 +++--------------- .controlplane/readme.md | 225 ++++++--------- .controlplane/shakacode-team.md | 40 +-- .github/cpflow-help.md | 188 +++++-------- 4 files changed, 188 insertions(+), 527 deletions(-) diff --git a/.controlplane/docs/testing-cpflow-github-actions.md b/.controlplane/docs/testing-cpflow-github-actions.md index 5ef5da6d..a6ab1df0 100644 --- a/.controlplane/docs/testing-cpflow-github-actions.md +++ b/.controlplane/docs/testing-cpflow-github-actions.md @@ -1,258 +1,74 @@ # Testing cpflow GitHub Actions Changes -Use this guide when changing generated `cpflow-*` GitHub Actions, updating the -`cpflow` generator version, or debugging review-app automation. - -## What To Test - -Test the flow in three layers: - -1. Local generated-file checks catch YAML, metadata, and lint problems before a PR. -2. GitHub workflow checks prove GitHub can load the workflow and run CI. -3. A real review-app deploy proves the default-branch trusted actions, GitHub - secrets, Docker build, and Control Plane deploy all work together. - -The third layer matters because the review-app workflow intentionally checks out -trusted workflow sources from the repository default branch before passing -Control Plane secrets to local composite actions. A PR branch can contain fixed -`.github/actions/*` files, but the deploy job still loads those local actions -from `master` until the fix is merged there. - -## How Upstream Code Is Pinned - -Generated `cpflow-*` workflows are thin wrappers around reusable workflows in -`shakacode/control-plane-flow`. GitHub reusable workflows cannot be loaded from -a Ruby gem; GitHub resolves them from a repository ref. Each wrapper therefore -has two upstream pins that must stay in sync: - -```yaml -uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@ -with: - control_plane_flow_ref: -``` - -`uses: ...@` chooses the reusable workflow file. `control_plane_flow_ref` -chooses the same upstream checkout for shared composite actions and, when -`CPFLOW_VERSION` is unset, the source used to build and install the `cpflow` gem -inside the workflow. - -The stable release path is still gem-driven: - -1. Install or update to a released `cpflow` gem. -2. Run `cpflow generate-github-actions --staging-branch master`. -3. The generated wrappers use `v` as the upstream ref. - -That tag should point to the same source that produced the RubyGems release, so -the workflow code is locked to a release tag rather than a moving branch. Do not -pin production workflows to `main` or a feature branch. For unreleased testing, -pin to an immutable commit SHA, not a branch name, then regenerate or repin to -the release tag after the upstream release is published. - -`CPFLOW_VERSION` is separate from the GitHub workflow ref. If the repository -variable is set, the setup action runs `gem install cpflow -v `. If it -is unset, the setup action builds `cpflow` from the checked-out upstream ref. -For normal releases, either leave `CPFLOW_VERSION` unset while using the matching -`v` upstream tag, or set `CPFLOW_VERSION` to the same released gem -version without the leading `v`. - -This repo is currently pinned to an upstream commit SHA because the reusable -workflow change was tested before a new `cpflow` gem release existed. That is -safer than pinning a branch, but it should be treated as a temporary test pin -until the next upstream release tag is available. - -What is tied to the upstream repo: - -- The downstream workflow wrapper hardcodes `shakacode/control-plane-flow` in - `uses:`. -- The reusable workflow file comes from the ref after `@`. -- The reusable workflow checks out `control-plane-flow` at - `control_plane_flow_ref` to load shared composite actions. -- When `CPFLOW_VERSION` is empty, the setup action builds and installs the - `cpflow` gem from that checked-out repository ref. - -What is tied to RubyGems: - -- A released `cpflow` gem is the normal source used to generate downstream - workflow wrappers. -- The `CPFLOW_VERSION` repository variable is a runtime override that runs - `gem install cpflow -v ` inside workflows. - -For stable downstream automation, prefer the release path: generate from a -released gem and pin wrappers to the matching upstream release tag. For -pre-release validation, pin to a full commit SHA from the upstream PR, never a -moving branch. - -## Testing An Unmerged Upstream PR Downstream - -You can test an upstream `control-plane-flow` PR in this downstream app before -merging upstream, without publishing a gem. Use an immutable commit SHA from the -upstream PR branch: - -1. Push the upstream PR branch and copy its head commit SHA. -2. In a downstream test branch, pin every generated wrapper ref: - - ```sh - bin/pin-cpflow-github-ref - ``` - - The helper accepts release tags and full 40-character commit SHAs by default. - It rejects branch names such as `main` or `feature/foo`; use - `--allow-moving-ref` only for short-lived local experiments that will not be - committed. The resulting diff should replace both pins in each reusable - workflow call: - - ```yaml - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@ - with: - control_plane_flow_ref: - ``` - -3. Keep `CPFLOW_VERSION` unset unless you intentionally want to test a released - RubyGems version instead of building `cpflow` from the upstream PR SHA. -4. Run `bin/test-cpflow-github-flow`. -5. Open a downstream PR and trigger a real review app with a comment whose body - is exactly: - - ```text - +review-app-deploy - ``` - -6. Verify the deploy logs show the expected upstream commit SHA, the setup step - prints the expected `cpflow` version/source, and the review app URL returns - HTTP 200. -7. After the upstream PR merges and releases, regenerate or repin downstream to - the release tag instead of leaving the temporary commit SHA forever. - -This tests the real reusable workflow and shared composite actions from the -upstream PR. It avoids merging upstream blind while also avoiding a mutable -branch ref in downstream automation. +Generic reusable-workflow behavior belongs upstream in the +[`control-plane-flow` CI automation guide](https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md). +Use this repo note only as the canary checklist for +`react-webpack-rails-tutorial`. ## Local Checks -After regenerating the flow, run these checks from the repository root. If -`cpflow` is installed as a gem, use `cpflow` directly: +After regenerating the generated `cpflow-*` wrappers, run: ```sh -bin/conductor-exec cpflow generate-github-actions --staging-branch master -bin/test-cpflow-github-flow +bin/conductor-exec bin/test-cpflow-github-flow ``` -When testing an unreleased upstream `control-plane-flow` checkout, replace -`cpflow` with that checkout's `bin/cpflow`: +When testing an unreleased upstream `control-plane-flow` checkout, pass that +checkout's `bin/cpflow`: ```sh -bin/conductor-exec ruby /path/to/control-plane-flow/bin/cpflow generate-github-actions --staging-branch master -bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow +bin/conductor-exec bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow ``` -Why the explicit description check exists: GitHub parses expression-like snippets -inside composite action metadata, including `description:` fields. Literal -examples such as `${{ vars.SOME_VALUE }}` can fail action loading before any -shell step starts. The wrapper runs `cpflow github-flow-readiness`, parses the -generated YAML, checks action input descriptions for literal GitHub expressions, -checks that every generated wrapper keeps `uses:` and `control_plane_flow_ref` -on the same upstream ref across all `cpflow-*` wrappers, rejects broad `secrets: inherit` in generated cpflow wrappers, checks that -any secret-passing reusable workflow passes `control_plane_flow_ref`, and runs -`actionlint -ignore 'SC2129' .github/workflows/cpflow-*.yml`. - -## PR Checks +## Testing An Upstream PR Downstream -Open a normal PR for the generated-file diff and wait for CI. The workflow PR -itself is useful for syntax and CI validation, but it does not fully prove -review-app deployment changes that live under `.github/actions/`. - -For top-level workflow edits, you can manually dispatch the PR branch workflow: +Use an immutable upstream commit SHA, not a branch: ```sh -gh workflow run cpflow-deploy-review-app.yml --ref -f pr_number= +bin/pin-cpflow-github-ref <40-character-control-plane-flow-commit-sha> +bin/conductor-exec bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow ``` -This loads the workflow file from ``, but the deploy workflow's -`Checkout trusted workflow sources` step still checks out `master` before using -local composite actions with secrets. Treat this as a partial smoke test, not as -proof that PR-branch composite action changes work. - -## Post-Merge Review-App Test +Leave `CPFLOW_VERSION` unset while testing a commit SHA. After the upstream gem +and tag ship, repin wrappers to the release tag, such as `v5.0.1`. -After the workflow PR merges to `master`, test a real review-app deployment: +## Review App Canary -1. Pick a same-repository PR to use as the canary. -2. If the review app does not exist yet, comment exactly `+review-app-deploy` on - that PR. -3. If a previous deploy run failed, rerun the failed deploy run after the - workflow PR is merged. -4. Confirm the deploy run checks out `master` at the merge commit in - `Checkout trusted workflow sources`. -5. Confirm `Setup environment` succeeds and prints the expected `cpflow` version. -6. Confirm `Check if review app exists`, `Build Docker image`, and - `Deploy to Control Plane` all run as expected. -7. Open the review-app URL from the PR comment or deployment status and verify - it returns HTTP 200. +1. Open or reuse a same-repository PR. +2. Comment exactly `+review-app-deploy`. +3. Confirm the deploy job checks out the expected upstream + `control_plane_flow_ref`. +4. Confirm `Setup environment`, `Check if review app exists`, + `Build Docker image`, and `Deploy to Control Plane` all pass. +5. Open the review-app URL from the PR comment and verify it returns HTTP 200. -Use the generated app name from the workflow log: +Comment-triggered workflows run from the repository default branch. If you are +testing edits to a workflow file before merging, manually dispatch the PR branch +workflow: -```text -APP_NAME: ${REVIEW_APP_PREFIX}-${PR_NUMBER} +```sh +gh workflow run cpflow-deploy-review-app.yml --ref -f pr_number= ``` -This is a template from the workflow output, not a literal command to evaluate -unless those environment variables are already set. For this repo, verify the -actual `REVIEW_APP_PREFIX` repository variable before assuming the final app -name. - ## Troubleshooting Signals -### Composite action metadata fails before setup - -Error shape: - -```text -Unrecognized named-value: 'vars' -Failed to load ./.github/actions/cpflow-setup-environment/action.yml -``` - -Cause: GitHub parsed a literal expression inside composite action metadata, -usually an input description. Because trusted local actions come from `master`, -fix and merge the generated action metadata on `master`, then rerun the deploy. - -### Setup succeeds, then `cpflow exists` reports token format - -Error shape: +### Token Format Error ```text ERROR: Unknown API token format. Please re-run 'cpln profile login' or set the correct CPLN_TOKEN env variable. ``` -Cause: the workflow can read `CPLN_TOKEN_STAGING`, but the value is not a valid -Control Plane service-account token for the installed Control Plane CLI. Rotate -the GitHub secret, then rerun the failed deploy job. - -### PR pushes do not create a new review app +The workflow can read `CPLN_TOKEN_STAGING`, but the secret value is not a valid +Control Plane service-account token. Rotate the GitHub secret and rerun the +deploy. -This is expected. Pushes redeploy only after the review app already exists. -Create the first review app by commenting exactly: +### No Deploy After Push -```text -+review-app-deploy -``` +Pushes redeploy only after the review app already exists. Create the first one +with an exact `+review-app-deploy` PR comment. -## Ways To Make This Easier +### No Visible Workflow Changes -- Extend `bin/pin-cpflow-github-ref` so it can also run - `bin/test-cpflow-github-flow`, open a downstream PR, and print or post the - exact `+review-app-deploy` command needed to start the canary deploy. -- Add CI coverage that runs `bin/test-cpflow-github-flow` on generated workflow - changes, so ref mismatches and action metadata parsing issues are caught - before review. -- Add a no-secret GitHub Actions smoke workflow that loads generated local - composite actions from the PR branch and fails fast on action metadata parsing. -- Extend `bin/test-cpflow-github-flow` as more local cpflow GitHub Actions - checks become worth standardizing. -- Add an early token sanity step after `Setup environment` so invalid - `CPLN_TOKEN_STAGING` and `CPLN_TOKEN_PRODUCTION` values fail with a named - "validate Control Plane token" step instead of surfacing later during - `cpflow exists`. -- Keep a tiny canary PR open for review-app workflow testing so post-merge - deploy verification does not depend on whichever feature PR happens to exist. -- Upstream the metadata-description check to `cpflow github-flow-readiness` so - downstream repos get the guard automatically. +Comment-triggered runs use workflow files from `master`. For PR-branch workflow +edits, use `workflow_dispatch` as shown above or merge first and test with a +real review-app deploy. diff --git a/.controlplane/readme.md b/.controlplane/readme.md index 20527bb1..67b794f6 100644 --- a/.controlplane/readme.md +++ b/.controlplane/readme.md @@ -19,116 +19,91 @@ In a real app, you would likely use persistent, external resources, such as AWS You can see the definition of Postgres and Redis in the `.controlplane/templates` directory. -## GitHub Review App Setup +## GitHub and Control Plane Setup -For normal generated review apps in this repo, GitHub needs only one repository -secret: +This repo uses the generated `cpflow-*` GitHub Actions wrappers. Keep the +generic behavior documented upstream in the +[`control-plane-flow` CI automation guide](https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md); +this section only lists the values that are specific to this app. -| Secret | Notes | -| --- | --- | -| `CPLN_TOKEN_STAGING` | Control Plane service-account token for `shakacode-open-source-examples-staging`. | - -No GitHub repository variables are required for the standard review-app path. -The generated workflow infers the review app prefix -`qa-react-webpack-rails-tutorial` and staging org -`shakacode-open-source-examples-staging` from `.controlplane/controlplane.yml` -because that file defines exactly one app with -`match_if_app_name_starts_with: true`. - -These inferred values come from `.controlplane/controlplane.yml`: the review-app -prefix is the app key with `match_if_app_name_starts_with: true`, and the -staging org is the `cpln_org` value on that app or its shared alias. The -variables below are escape hatches for forks and clones, so someone can test -this repo against their own Control Plane org, choose a different review-app -prefix, or expose a different public workload without editing the generated -workflow. Leave them unset for the standard setup. - -Optional review-app override variables: - -| Variable | Notes | -| --- | --- | -| `CPLN_ORG_STAGING` | Override the staging org inferred from `cpln_org` in `.controlplane/controlplane.yml`. | -| `REVIEW_APP_PREFIX` | Override the review-app app key inferred from the `match_if_app_name_starts_with: true` entry. | -| `PRIMARY_WORKLOAD` | Override the public workload used to discover the review URL; leave unset for `rails`. | +### Review Apps and Staging -For staging auto-deploys, also configure: +For review apps, GitHub needs one repository secret: -| Secret or variable | Value | +| Name | Value | | --- | --- | -| `CPLN_TOKEN_STAGING` | Same staging Control Plane token used by review apps. | -| `CPLN_ORG_STAGING` | `shakacode-open-source-examples-staging` | -| `STAGING_APP_NAME` | `react-webpack-rails-tutorial-staging` | - -For production promotion, configure a protected GitHub Environment named -`production`: +| `CPLN_TOKEN_STAGING` | Service-account token for `shakacode-open-source-examples-staging`. | -| Secret or variable | Value | -| --- | --- | -| `CPLN_TOKEN_PRODUCTION` | Environment secret on `production`, not a repository or organization secret. | -| `CPLN_ORG_PRODUCTION` | Environment variable on `production`: `shakacode-open-source-examples-production` | -| `PRODUCTION_APP_NAME` | Environment variable on `production`: `react-webpack-rails-tutorial-production` | +No review-app repository variables are required for the standard path. The +workflow infers `qa-react-webpack-rails-tutorial` and +`shakacode-open-source-examples-staging` from `.controlplane/controlplane.yml`, +because that file has one app with `match_if_app_name_starts_with: true`. +`PRIMARY_WORKLOAD` also stays unset because the public workload is `rails`. -Protect the `production` environment with required reviewers, enable prevent -self-review, and consider disabling administrator bypass. Only release managers -or similarly trusted maintainers should be able to approve the promotion job. -The generated caller passes `production_environment: production`; the upstream -reusable workflow runs its production job in that environment, so GitHub injects -`CPLN_TOKEN_PRODUCTION` only after the environment approval gate passes. The -production token is not exposed to ordinary review-app or staging runs. +For staging auto-deploys, also set these repository variables: -Generated caller workflows pass only the named secrets each upstream workflow -needs. They do not use `secrets: inherit`; `CPLN_TOKEN_PRODUCTION` is supplied -only by the protected `production` Environment after approval. +| Name | Value | +| --- | --- | +| `CPLN_ORG_STAGING` | `shakacode-open-source-examples-staging` | +| `STAGING_APP_NAME` | `react-webpack-rails-tutorial-staging` | +| `STAGING_APP_BRANCH` | `master` | -Advanced optional variables: +The matching Control Plane resources are: -| Name | Notes | +| Resource | Name | | --- | --- | -| `REVIEW_APP_DEPLOYING_ICON_URL` | Cosmetic custom animated icon for review-app comments. Ignore this for the standard setup. | -| `CPLN_CLI_VERSION` | Pin only when Control Plane CLI compatibility requires it. | -| `CPFLOW_VERSION` | Runtime gem override. Leave unset when workflow wrappers are pinned to a GitHub commit SHA for upstream PR testing. | +| Review app prefix | `qa-react-webpack-rails-tutorial` | +| Review app secret dictionary | `qa-react-webpack-rails-tutorial-secrets` | +| Staging app | `react-webpack-rails-tutorial-staging` | +| Staging app secret dictionary | `react-webpack-rails-tutorial-staging-secrets` | -## Control Plane Setup +### Production Promotion -The GitHub secret is only the automation credential. The Control Plane org also -needs the app resources and runtime secrets that the workloads read at boot. +Production promotion is part of the default demo flow, but the production token +must be gated by a protected GitHub Environment named `production`: -For review-app testing, the standard setup is: - -| Control Plane item | Where | Notes | +| Name | Where | Value | | --- | --- | --- | -| Staging/review org | `shakacode-open-source-examples-staging` | The `CPLN_TOKEN_STAGING` service account must be able to create and update GVCs, workloads, images, identities, policies, and secrets in this org. | -| Review app prefix | `qa-react-webpack-rails-tutorial` | Review apps are named `qa-react-webpack-rails-tutorial-`. This is inferred from `.controlplane/controlplane.yml`. | -| Review app secret dictionary | `qa-react-webpack-rails-tutorial-secrets` | Shared by generated review apps because the QA app entry uses `match_if_app_name_starts_with: true`. | +| `CPLN_TOKEN_PRODUCTION` | `production` Environment secret | Production Control Plane service-account token. | +| `CPLN_ORG_PRODUCTION` | `production` Environment variable | `shakacode-open-source-examples-production` | +| `PRODUCTION_APP_NAME` | `production` Environment variable | `react-webpack-rails-tutorial-production` | -For staging deploys later, also use: +Protect the `production` environment with required reviewers, prevent +self-review, and consider disabling administrator bypass. Do not store +`CPLN_TOKEN_PRODUCTION` as a repository or organization secret. The generated +promotion wrapper does not use `secrets: inherit`; GitHub exposes the production +token only after the environment approval gate passes. -| Control Plane item | Where | Notes | -| --- | --- | --- | -| Staging app | `react-webpack-rails-tutorial-staging` | The `CPLN_TOKEN_STAGING` token deploys this app from `master`. | -| Staging app secret dictionary | `react-webpack-rails-tutorial-staging-secrets` | Same required keys as the review app secret dictionary. | +The matching Control Plane resources are: -For production promotion later, use a separate production org and token: - -| Control Plane item | Where | Notes | -| --- | --- | --- | -| Production org | `shakacode-open-source-examples-production` | Do not give the staging token access to this org. | -| Production app | `react-webpack-rails-tutorial-production` | Promotion copies the staging image into this app. | -| Production app secret dictionary | `react-webpack-rails-tutorial-production-secrets` | Create before the first promotion. Use production-only values. | -| Production service-account token | GitHub Environment secret `CPLN_TOKEN_PRODUCTION` | Keep this token in the protected `production` GitHub Environment only. | +| Resource | Name | +| --- | --- | +| Production app | `react-webpack-rails-tutorial-production` | +| Production app secret dictionary | `react-webpack-rails-tutorial-production-secrets` | -The app secret dictionaries must include: +All review, staging, and production secret dictionaries need these app runtime +secrets: - `SECRET_KEY_BASE` - `RENDERER_PASSWORD` - `REACT_ON_RAILS_PRO_LICENSE` Generate `SECRET_KEY_BASE` with `openssl rand -hex 64` and -`RENDERER_PASSWORD` with `openssl rand -hex 32`. The review/staging template -currently contains a test placeholder for `SECRET_KEY_BASE`; replace it with a -secret-backed value before promoting production. The demo Postgres and Redis -workloads are useful for review apps and staging demos. For real production, -prefer managed services and update `DATABASE_URL` and `REDIS_URL` accordingly. +`RENDERER_PASSWORD` with `openssl rand -hex 32`. For real production, prefer +managed Postgres and Redis services and update `DATABASE_URL` and `REDIS_URL` +accordingly. + +### Advanced Overrides + +Most repos should leave these unset. They exist so forks and clones can test +against their own Control Plane org, prefix, workload, or toolchain: + +- `CPLN_ORG_STAGING` +- `REVIEW_APP_PREFIX` +- `PRIMARY_WORKLOAD` +- `REVIEW_APP_DEPLOYING_ICON_URL` +- `CPLN_CLI_VERSION` +- `CPFLOW_VERSION` ## Prerequisites @@ -524,63 +499,27 @@ each review app getting its own resources as defined in the controlplane.yml configuration. -### Workflow for Developing GitHub Actions for Review Apps - -1. Create a PR with changes to the GitHub Actions workflow -2. Make edits to files such as `.github/actions/cpflow-build-docker-image/action.yml` or `.github/workflows/cpflow-deploy-review-app.yml` -3. Run a script like `ga .github && gc -m fixes && gp` to commit and push changes (ga = git add, gc = git commit, gp = git push) -4. Check the GitHub Actions tab in the PR to see the status of the workflow - -### Keeping Generated cpflow Workflows Updated - -Treat `.github/actions/cpflow-*` and `.github/workflows/cpflow-*` as generated -workflow files with project-specific settings layered on top. When `cpflow` -releases generator fixes or the upstream `control-plane-flow` repo changes the -GitHub Actions flow, update a project by regenerating the flow from the desired -`cpflow` version or branch, reviewing the diff, and keeping any local app names, -repository variables, secrets, and docs aligned with `.controlplane/controlplane.yml`. - -The generated workflows are intentionally thin wrappers around reusable -workflows in `shakacode/control-plane-flow`. GitHub loads reusable workflows from -a repository ref, not from the Ruby gem, so each wrapper has a `uses: ...@` -line plus a matching `control_plane_flow_ref: `. For stable releases, -generate these files from a released `cpflow` gem; the default ref is the -matching upstream release tag, `v`. Avoid `main` or feature-branch -refs in production. Use an immutable commit SHA only when testing unreleased -upstream changes, then move back to the release tag after the upstream release -is published. - -`CPFLOW_VERSION` is a runtime gem-install override. When it is unset, the setup -action builds `cpflow` from the checked-out upstream ref. When it is set, the -setup action installs that RubyGems version instead. For normal release pins, -either leave it unset while using the matching `v` workflow tag, or set -it to the same gem version without the leading `v`. - -To test unreleased upstream workflow changes before merging `control-plane-flow`, -pin a downstream PR to the upstream PR's full commit SHA in both `uses:` and -`control_plane_flow_ref`, leave `CPFLOW_VERSION` unset, and trigger a real review -app deploy. That tests the upstream reusable workflow, shared composite actions, -and source-built `cpflow` gem from the same immutable commit. After the upstream -change is released, regenerate or repin back to the matching release tag. -Use `bin/pin-cpflow-github-ref ` to update the generated wrapper refs -together. The helper accepts release tags and full commit SHAs by default; -`--allow-moving-ref` is only for short-lived local experiments. - -For this app, validate a regenerated flow with: +### Updating Generated cpflow Workflows + +Keep the reusable-workflow mechanics in the upstream +[`control-plane-flow` CI automation guide](https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md). +For this repo, the update loop is: + +1. Generate from the desired `cpflow` release with `--staging-branch master`. +2. Keep generated refs on a release tag such as `v5.0.1`. Use a full upstream + commit SHA only for short-lived downstream testing of an unreleased upstream + PR, and leave `CPFLOW_VERSION` unset in that case. +3. Keep app names and GitHub settings aligned with `.controlplane/controlplane.yml`. +4. Validate locally: ```bash bin/conductor-exec ruby /path/to/control-plane-flow/bin/cpflow generate-github-actions --staging-branch master -bin/conductor-exec ruby /path/to/control-plane-flow/bin/cpflow github-flow-readiness -actionlint -ignore 'SC2129' .github/workflows/cpflow-*.yml -bin/conductor-exec bundle exec rubocop +bin/conductor-exec bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow ``` -Then open a normal PR and let GitHub Actions prove the generated review-app, -staging, lint, JS, and RSpec workflows before merging. For review-app workflow -changes, test both the local workflow syntax and a real deployment. GitHub runs -`issue_comment` workflows from the default branch, so a `+review-app-deploy` -comment on the PR does not fully exercise command changes that are only on the -PR branch. For top-level workflow edits, run the PR branch workflow explicitly: +Then open a normal PR, wait for GitHub Actions, and test a real review-app +deploy. Comment-triggered workflows run from `master`; for PR-branch workflow +edits, dispatch the workflow explicitly: ```bash gh workflow run cpflow-deploy-review-app.yml --ref -f pr_number= @@ -589,13 +528,5 @@ gh workflow run cpflow-deploy-review-app.yml --ref -f pr_number=`, but trusted local composite actions still come from the default branch before secrets are used. Treat it as a partial smoke test, then verify a real deploy after the workflow changes land -on `master`. - -After the workflow reports a review-app URL, verify the URL returns HTTP 200. -If a project needs to track generator changes automatically, use a scheduled -maintenance PR or Renovate-style workflow that bumps the `cpflow` version, -regenerates these files, and runs the same validation commands. - -For a fuller checklist, including the gotcha that review-app deploys load local -composite actions from `master` before using Control Plane secrets, see -[Testing cpflow GitHub Actions Changes](docs/testing-cpflow-github-actions.md). +on `master`. See the short +[testing checklist](docs/testing-cpflow-github-actions.md) for the canary steps. diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index 59a7683d..a834e307 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -63,24 +63,12 @@ Generated caller workflows pass only the named secrets each upstream workflow needs. They do not use `secrets: inherit`; `CPLN_TOKEN_PRODUCTION` is supplied only by the protected `production` Environment after approval. -Optional repository settings: - -- `PRIMARY_WORKLOAD`: public workload used for review URLs and promote health checks; defaults to `rails`. -- `DOCKER_BUILD_SSH_KEY`: secret for private SSH dependencies during Docker builds. -- `DOCKER_BUILD_EXTRA_ARGS`: newline-delimited Docker build tokens, such as `--build-arg=FOO=bar`. -- `DOCKER_BUILD_SSH_KNOWN_HOSTS`: custom `known_hosts` entries when SSH build hosts are not GitHub.com. -- `CPLN_CLI_VERSION`: pin a specific `@controlplane/cli` version; defaults to the generated action pin. -- `CPFLOW_VERSION`: optional runtime gem-install override. When unset, workflows build - `cpflow` from the checked-out upstream workflow ref. When set, use the RubyGems - version number without a leading `v`. -- `HEALTH_CHECK_ACCEPTED_STATUSES`: production promotion health statuses; defaults to `200 301 302`. -- `HEALTH_CHECK_RETRIES` / `HEALTH_CHECK_INTERVAL`: production health polling controls; defaults to `24` retries and `15` seconds. -- `ROLLBACK_READINESS_RETRIES` / `ROLLBACK_READINESS_INTERVAL`: post-rollback health polling controls; defaults to `24` retries and `15` seconds. +Advanced optional settings are documented upstream in the +[`control-plane-flow` CI automation guide](https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md). Current workflow wrappers are pinned to upstream `control-plane-flow` PR #318 at -`19ca93eadcba81e7438f26ab55a0f2ca7ace82b0` for downstream testing. After that -upstream work is released, regenerate or repin the wrappers to the release tag -instead of keeping this PR commit SHA long term. +`19ca93eadcba81e7438f26ab55a0f2ca7ace82b0` for downstream testing. After 5.0.1 +ships, repin the wrappers to `v5.0.1`. If staging moves off `master`, update both `STAGING_APP_BRANCH` and the branch filter in `.github/workflows/cpflow-deploy-staging.yml`. @@ -88,22 +76,10 @@ filter in `.github/workflows/cpflow-deploy-staging.yml`. ### Keeping cpflow Automation Current When the upstream `control-plane-flow` repo changes the generated GitHub Actions -flow, regenerate the `cpflow-*` actions/workflows in this repo from the target -`cpflow` version or branch using `--staging-branch master`, review the diff, and -keep the GitHub settings above aligned with `.controlplane/controlplane.yml`. Validate with -`cpflow github-flow-readiness`, `actionlint .github/workflows/cpflow-*.yml`, and -the normal CI checks before merging. For review-app workflow changes, remember -that the deploy workflow checks out trusted local actions from `master` before -passing Control Plane secrets; PR-branch composite action changes are not fully -tested until they land on `master` and a real review-app deploy is rerun. - -Generated workflow wrappers point to upstream reusable workflows with -`uses: shakacode/control-plane-flow/...@` and pass the same -`control_plane_flow_ref`. For stable releases, `` should be the -`v` tag produced by the released gem. Do not use `main` or a -feature branch for production automation. A commit SHA is acceptable for -unreleased testing, but regenerate or repin to the release tag once the upstream -release exists. +flow, regenerate from the target `cpflow` version with `--staging-branch master`, +review the diff, and validate with `bin/test-cpflow-github-flow` plus the normal +CI checks. Stable automation should use release tags such as `v5.0.1`, not +`main` or a feature branch. See [readme.md](readme.md) and [Testing cpflow GitHub Actions Changes](docs/testing-cpflow-github-actions.md) diff --git a/.github/cpflow-help.md b/.github/cpflow-help.md index 9121945e..69032091 100644 --- a/.github/cpflow-help.md +++ b/.github/cpflow-help.md @@ -1,152 +1,90 @@ -# Review app help +# Review App Commands -You asked for review app help. These commands are generated by [cpflow](https://github.com/shakacode/control-plane-flow). +These commands are generated by [cpflow](https://github.com/shakacode/control-plane-flow). +For full setup, version-pinning, and troubleshooting details, see the upstream +[CI automation guide](https://github.com/shakacode/control-plane-flow/blob/19ca93eadcba81e7438f26ab55a0f2ca7ace82b0/docs/ci-automation.md). -## PR commands +## Pull Request Commands -`+review-app-deploy` -- Creates the review app if it does not exist -- Builds the PR commit image -- Deploys the image and comments with the review URL -- Comment body must be exactly `+review-app-deploy`, with no surrounding text or trailing spaces. A single trailing newline from GitHub's comment editor is accepted. Comments like `please +review-app-deploy now` or `+review-app-deploy ` (with a trailing space) silently no-op. +Comment with exactly one command, with no surrounding text or trailing spaces. +A single trailing newline from GitHub's comment editor is accepted. -`+review-app-delete` -- Deletes the review app when the PR is done -- This also runs automatically when the PR closes -- Comment body must be exactly `+review-app-delete`, with no surrounding text or trailing spaces. A single trailing newline from GitHub's comment editor is accepted. Same command-match rule as `+review-app-deploy`. +| Command | What it does | +| --- | --- | +| `+review-app-deploy` | Builds the PR image, creates the review app if needed, deploys, and comments with the review URL. | +| `+review-app-delete` | Deletes the review app. This also runs automatically when the PR closes. | +| `+review-app-help` | Posts this help message on the PR. | -`+review-app-help` -- Posts this message on the PR. -- Comment body must be exactly `+review-app-help`, with no surrounding text or trailing spaces. A single trailing newline from GitHub's comment editor is accepted. Same command-match rule as `+review-app-deploy`. +## Standard Setup -## Workflow behavior +For the normal generated review-app path, GitHub needs one repository secret: -- Review apps are opt-in and created with `+review-app-deploy` -- New commits redeploy existing review apps automatically -- Pushes to the staging branch deploy staging automatically -- Promotion to production is manual via the Actions tab -- A nightly workflow removes stale review apps - -
-Advanced: GitHub Actions secrets and variables +| Name | Where | Notes | +| --- | --- | --- | +| `CPLN_TOKEN_STAGING` | Repository secret | Control Plane service-account token for the staging/review org. | -### GitHub Actions secrets +No repository variables are required for the standard review-app path when +`.controlplane/controlplane.yml` has exactly one review app entry with +`match_if_app_name_starts_with: true`. cpflow infers the review-app prefix and +staging org from that config. -| Name | Required | Notes | -| --- | --- | --- | -| `CPLN_TOKEN_STAGING` | yes | Service-account token scoped to the staging Control Plane org on controlplane.com. | -| `CPLN_TOKEN_PRODUCTION` | yes for promote, as a production environment secret | Store this as a secret on the protected `production` GitHub Environment, not as a repository or organization secret. | -| `DOCKER_BUILD_SSH_KEY` | optional | Private SSH key used when Docker builds fetch private deps via `RUN --mount=type=ssh`. | - -For normal generated review apps, `CPLN_TOKEN_STAGING` is the only required -GitHub setting. The review app prefix and staging org are inferred from -`.controlplane/controlplane.yml` when it defines exactly one app with -`match_if_app_name_starts_with: true`. -If more than one app has that flag, set `CPLN_ORG_STAGING` and -`REVIEW_APP_PREFIX` explicitly to disambiguate. - -Those inferred values come from `.controlplane/controlplane.yml`: `cpln_org` -selects the Control Plane org and the app key with -`match_if_app_name_starts_with: true` becomes the review-app prefix. The -optional review-app variables below are override hooks for forks and clones that -want to test against their own Control Plane org, use a different prefix, or -expose a different public workload. Leave them unset for the standard setup. - -For production promotion, create a GitHub Environment named `production`, add -required reviewers, enable prevent self-review, and store -`CPLN_TOKEN_PRODUCTION` as an environment secret there. The generated caller -passes `production_environment: production`; the upstream reusable workflow -runs its production job in that environment, so GitHub injects -`CPLN_TOKEN_PRODUCTION` only after the environment approval gate passes. -Generated caller workflows pass only the named secrets each upstream workflow -needs. They do not use `secrets: inherit`; the production token is supplied by -the protected `production` Environment after approval. - -### GitHub Actions variables - -| Name | Required | Notes | -| --- | --- | --- | -| `CPLN_ORG_STAGING` | optional for review apps; yes for staging | Override the staging/review Control Plane org inferred from `controlplane.yml`. | -| `CPLN_ORG_PRODUCTION` | yes for promote, preferably as production environment variable | Control Plane org on controlplane.com for production. Prefer a `production` environment variable. | -| `STAGING_APP_NAME` | yes | App name in `controlplane.yml` used as the staging deploy target. | -| `PRODUCTION_APP_NAME` | yes for promote, preferably as production environment variable | App name in `controlplane.yml` used as the production deploy target. Prefer a `production` environment variable. | -| `REVIEW_APP_PREFIX` | optional | Override the review-app app key inferred from the `match_if_app_name_starts_with: true` entry in `controlplane.yml`. | -| `REVIEW_APP_DEPLOYING_ICON_URL` | optional, advanced | Cosmetic custom image URL for the animated deploying icon in review-app PR comments. Set to `none` to use the text fallback icon. | -| `STAGING_APP_BRANCH` | optional | Custom staging branch. Custom branches must also appear in `cpflow-deploy-staging.yml`'s push filter. | -| `PRIMARY_WORKLOAD` | optional | Override the public workload used for the review URL, health checks, and rollback (defaults to `rails`). | -| `DOCKER_BUILD_EXTRA_ARGS` | optional | Newline-delimited extra docker build tokens (e.g. `--build-arg=FOO=bar`). | -| `DOCKER_BUILD_SSH_KNOWN_HOSTS` | optional | SSH known_hosts entries when SSH build hosts are not GitHub.com. | -| `HEALTH_CHECK_ACCEPTED_STATUSES` | optional | Space-separated HTTP statuses considered healthy on promote (default `200 301 302`). | -| `CPLN_CLI_VERSION` | optional | Pin a specific `@controlplane/cli` version; falls back to the action default when unset. | -| `CPFLOW_VERSION` | optional | Pin a published RubyGems version such as `5.0.0` or `5.0.0.rc.1`. Leave unset for normal generated workflows so the setup action builds `cpflow` from the same `control-plane-flow` GitHub ref used by the reusable workflow. | - -
- -Generated review app names use `-`, for example -`my-app-review-123`. If you are migrating from older local workflow glue that -created names like `-pr-123`, delete those old review apps -manually after merging this flow. To inventory old apps, run: +Optional overrides exist for forks, clones, and unusual apps: -```sh -cpln gvc query --org -o yaml --prop name~-pr- -``` +| Name | Notes | +| --- | --- | +| `CPLN_ORG_STAGING` | Override the staging/review Control Plane org inferred from `controlplane.yml`. | +| `REVIEW_APP_PREFIX` | Override the review-app prefix inferred from `controlplane.yml`. | +| `PRIMARY_WORKLOAD` | Public workload used for review URLs and health checks; defaults to `rails`. | -The PR-open help workflow posts the short command reference whenever the -generated wrapper exists. Remove `.github/workflows/cpflow-review-app-help.yml` -or add a wrapper-level `if:` guard if a repo should not advertise review apps. +## Staging And Production -
-Advanced: testing unreleased control-plane-flow changes +Staging deploys use the same `CPLN_TOKEN_STAGING` secret plus `STAGING_APP_NAME`. -Generated workflow wrappers have two related pins: +Production promotion is part of the generated flow, but keep it protected: -- The `uses: shakacode/control-plane-flow/...@` GitHub ref selects the reusable workflow code. -- The `control_plane_flow_ref: ` input tells the setup action which `control-plane-flow` source to check out and build when `CPFLOW_VERSION` is empty. +| Name | Where | Notes | +| --- | --- | --- | +| `CPLN_TOKEN_PRODUCTION` | `production` GitHub Environment secret | Do not store this as a repository or organization secret. | +| `CPLN_ORG_PRODUCTION` | Prefer `production` Environment variable | Production Control Plane org. | +| `PRODUCTION_APP_NAME` | Prefer `production` Environment variable | Production app name from `controlplane.yml`. | -The GitHub ref is the runtime lock for workflow and action behavior. The -RubyGems version is used to generate/update these wrappers and, only when -`CPFLOW_VERSION` is set, to install the `cpflow` CLI at runtime. A downstream -repo cannot rely on the gem alone because GitHub loads reusable workflow YAML -from `shakacode/control-plane-flow`, not from RubyGems. +Configure the `production` GitHub Environment with required reviewers and +prevent self-review. The generated promotion wrapper passes only the staging +token from repository secrets; GitHub injects `CPLN_TOKEN_PRODUCTION` only after +the environment approval gate passes. -For normal releases, point both pins at a release tag such as `v5.0.0`. -You may leave `CPFLOW_VERSION` unset, or set it to the matching RubyGems version -without the leading `v`, such as `5.0.0`. If you set `CPFLOW_VERSION` for a -prerelease, use RubyGems dot syntax such as `5.0.0.rc.1`; the release tag may -use either `v5.0.0.rc.1` or `v5.0.0-rc.1`. +## Version Locking -To update to a new stable release, install or bundle the new `cpflow` gem, run -`cpflow generate-github-actions`, and commit the regenerated wrappers. Use -`bin/pin-cpflow-github-ref vX.Y.Z` only when the generated files are already -current and you only need to move the upstream GitHub ref. +Current wrappers pin both the reusable workflow `uses:` ref and +`control_plane_flow_ref` to `19ca93eadcba81e7438f26ab55a0f2ca7ace82b0` for +upstream PR testing. After `cpflow` 5.0.1 ships, switch both refs to `v5.0.1`. +Leave `CPFLOW_VERSION` unset so the workflow builds cpflow from the same +checked-out upstream source. If you set `CPFLOW_VERSION`, it must match the +release tag, for example `CPFLOW_VERSION=5.0.1` with +`control_plane_flow_ref: v5.0.1`. -For temporary downstream testing of an upstream PR before a gem is released, pin -both values to the exact 40-character commit SHA and leave `CPFLOW_VERSION` -unset: +Do not leave downstream apps pinned to a moving branch such as `main`. For a +short-lived test of an unreleased upstream PR, pin to a full 40-character commit +SHA and leave `CPFLOW_VERSION` unset: ```sh bin/pin-cpflow-github-ref <40-character-control-plane-flow-commit-sha> bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow ``` -Do not leave downstream apps pinned to a moving branch such as `main`. A branch -can pick up beta or work-in-progress changes without a downstream PR changing. -Use commit SHAs for short-lived PR testing, then switch to the release tag once -the gem and tag exist. - -
- -
-Advanced: testing changes to generated workflows +## Advanced Variables -When iterating on the generated workflow YAML on a PR branch, comment-triggered runs (`+review-app-deploy`, `+review-app-delete`, `+review-app-help`) execute the workflow code from the repository's default branch — not your PR branch. To exercise the PR-branch workflow code before merging, dispatch the workflow manually with `gh`: - -```sh -gh workflow run cpflow-deploy-review-app.yml --ref -f pr_number= -gh workflow run cpflow-delete-review-app.yml --ref -f pr_number= -gh workflow run cpflow-help-command.yml --ref -f pr_number= -``` +Most apps do not need these: -`workflow_dispatch` runs use the workflow file from the `--ref` you pass, so this is the supported way to test PR-branch workflow edits before merge. After merge, comment triggers go back to running the default-branch workflow code as usual. +| Name | Notes | +| --- | --- | +| `DOCKER_BUILD_EXTRA_ARGS` | Newline-delimited extra Docker build tokens. | +| `DOCKER_BUILD_SSH_KEY` | Private SSH key for Docker builds that fetch private dependencies. | +| `DOCKER_BUILD_SSH_KNOWN_HOSTS` | SSH known_hosts entries when SSH build hosts are not GitHub.com. | +| `REVIEW_APP_DEPLOYING_ICON_URL` | Cosmetic custom image URL for the animated deploying icon. Set to `none` to use the text fallback icon. | +| `STAGING_APP_BRANCH` | Custom staging branch. The branch must also appear in `cpflow-deploy-staging.yml`'s push filter. | +| `CPLN_CLI_VERSION` | Pin a specific `@controlplane/cli` version; normally leave unset. | -
+The PR-open help workflow posts a short command reference whenever the generated +wrapper exists. Remove `.github/workflows/cpflow-review-app-help.yml` or add a +wrapper-level `if:` guard if a repo should not advertise review apps. From 4eea355cdd3ced65a6394a3fd59a7f2680518d46 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 18:32:31 -1000 Subject: [PATCH 15/18] Address cpflow docs review follow-up --- .controlplane/shakacode-team.md | 3 ++- .github/cpflow-help.md | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index a834e307..993e87a1 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -68,7 +68,8 @@ Advanced optional settings are documented upstream in the Current workflow wrappers are pinned to upstream `control-plane-flow` PR #318 at `19ca93eadcba81e7438f26ab55a0f2ca7ace82b0` for downstream testing. After 5.0.1 -ships, repin the wrappers to `v5.0.1`. +ships, repin the wrappers to `v5.0.1`; do not treat the temporary SHA as the +steady-state master configuration. If staging moves off `master`, update both `STAGING_APP_BRANCH` and the branch filter in `.github/workflows/cpflow-deploy-staging.yml`. diff --git a/.github/cpflow-help.md b/.github/cpflow-help.md index 69032091..8f64e933 100644 --- a/.github/cpflow-help.md +++ b/.github/cpflow-help.md @@ -2,7 +2,7 @@ These commands are generated by [cpflow](https://github.com/shakacode/control-plane-flow). For full setup, version-pinning, and troubleshooting details, see the upstream -[CI automation guide](https://github.com/shakacode/control-plane-flow/blob/19ca93eadcba81e7438f26ab55a0f2ca7ace82b0/docs/ci-automation.md). +[CI automation guide](https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md). ## Pull Request Commands @@ -56,8 +56,9 @@ the environment approval gate passes. ## Version Locking Current wrappers pin both the reusable workflow `uses:` ref and -`control_plane_flow_ref` to `19ca93eadcba81e7438f26ab55a0f2ca7ace82b0` for -upstream PR testing. After `cpflow` 5.0.1 ships, switch both refs to `v5.0.1`. +`control_plane_flow_ref` to `19ca93eadcba81e7438f26ab55a0f2ca7ace82b0` only for +upstream PR testing. Do not leave production automation on this temporary SHA: +after `cpflow` 5.0.1 ships, switch both refs to `v5.0.1`. Leave `CPFLOW_VERSION` unset so the workflow builds cpflow from the same checked-out upstream source. If you set `CPFLOW_VERSION`, it must match the release tag, for example `CPFLOW_VERSION=5.0.1` with From 9e5a0dddc8dcfd0cd4afc4f4945554ce727cd7fc Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 19:50:50 -1000 Subject: [PATCH 16/18] Regenerate cpflow wrappers from merged upstream --- .../docs/testing-cpflow-github-actions.md | 4 +-- .controlplane/shakacode-team.md | 8 +++--- .github/cpflow-help.md | 18 +++++++------ .github/testing-github-actions.md | 5 ++-- .../cpflow-cleanup-stale-review-apps.yml | 8 +++--- .../workflows/cpflow-delete-review-app.yml | 9 ++++--- .../workflows/cpflow-deploy-review-app.yml | 4 +-- .github/workflows/cpflow-deploy-staging.yml | 7 +++--- .github/workflows/cpflow-help-command.yml | 4 +-- .../cpflow-promote-staging-to-production.yml | 5 +--- .github/workflows/cpflow-review-app-help.yml | 4 +-- bin/pin-cpflow-github-ref | 10 +++++--- bin/test-cpflow-github-flow | 25 +++++++++---------- 13 files changed, 54 insertions(+), 57 deletions(-) diff --git a/.controlplane/docs/testing-cpflow-github-actions.md b/.controlplane/docs/testing-cpflow-github-actions.md index a6ab1df0..ee78e5f4 100644 --- a/.controlplane/docs/testing-cpflow-github-actions.md +++ b/.controlplane/docs/testing-cpflow-github-actions.md @@ -36,8 +36,8 @@ and tag ship, repin wrappers to the release tag, such as `v5.0.1`. 1. Open or reuse a same-repository PR. 2. Comment exactly `+review-app-deploy`. -3. Confirm the deploy job checks out the expected upstream - `control_plane_flow_ref`. +3. Confirm the deploy job checks out the expected upstream Control Plane Flow + source selected by the generated wrapper's `uses:` ref. 4. Confirm `Setup environment`, `Check if review app exists`, `Build Docker image`, and `Deploy to Control Plane` all pass. 5. Open the review-app URL from the PR comment and verify it returns HTTP 200. diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index 993e87a1..1d06defc 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -66,10 +66,10 @@ only by the protected `production` Environment after approval. Advanced optional settings are documented upstream in the [`control-plane-flow` CI automation guide](https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md). -Current workflow wrappers are pinned to upstream `control-plane-flow` PR #318 at -`19ca93eadcba81e7438f26ab55a0f2ca7ace82b0` for downstream testing. After 5.0.1 -ships, repin the wrappers to `v5.0.1`; do not treat the temporary SHA as the -steady-state master configuration. +Current workflow wrappers are pinned to merged upstream `control-plane-flow` +commit `c0ca750c4838dacf6a1d81d52cb151301516cb77` for downstream testing. After +5.0.1 ships, repin the wrappers to `v5.0.1`; do not treat the temporary SHA as +the steady-state master configuration. If staging moves off `master`, update both `STAGING_APP_BRANCH` and the branch filter in `.github/workflows/cpflow-deploy-staging.yml`. diff --git a/.github/cpflow-help.md b/.github/cpflow-help.md index 8f64e933..99484add 100644 --- a/.github/cpflow-help.md +++ b/.github/cpflow-help.md @@ -2,7 +2,7 @@ These commands are generated by [cpflow](https://github.com/shakacode/control-plane-flow). For full setup, version-pinning, and troubleshooting details, see the upstream -[CI automation guide](https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md). +[CI automation guide](https://github.com/shakacode/control-plane-flow/blob/c0ca750c4838dacf6a1d81d52cb151301516cb77/docs/ci-automation.md). ## Pull Request Commands @@ -55,14 +55,18 @@ the environment approval gate passes. ## Version Locking -Current wrappers pin both the reusable workflow `uses:` ref and -`control_plane_flow_ref` to `19ca93eadcba81e7438f26ab55a0f2ca7ace82b0` only for -upstream PR testing. Do not leave production automation on this temporary SHA: -after `cpflow` 5.0.1 ships, switch both refs to `v5.0.1`. +Generated wrappers pin Control Plane Flow once with the reusable workflow +`uses:` ref, for example `@c0ca750c4838dacf6a1d81d52cb151301516cb77`. For stable releases, +this ref should be a release tag. The upstream reusable workflow automatically +loads its matching shared actions from GitHub's workflow context, so downstream +wrappers should not pass a duplicate Control Plane Flow ref input. If your +generated wrappers still include a `with:` block whose only purpose is to repeat +the same ref, regenerate them with a newer `cpflow`. + Leave `CPFLOW_VERSION` unset so the workflow builds cpflow from the same checked-out upstream source. If you set `CPFLOW_VERSION`, it must match the -release tag, for example `CPFLOW_VERSION=5.0.1` with -`control_plane_flow_ref: v5.0.1`. +release tag, for example `CPFLOW_VERSION=5.0.1` with a wrapper pinned to +`uses: ...@v5.0.1`. Do not leave downstream apps pinned to a moving branch such as `main`. For a short-lived test of an unreleased upstream PR, pin to a full 40-character commit diff --git a/.github/testing-github-actions.md b/.github/testing-github-actions.md index 0366c641..d6b65770 100644 --- a/.github/testing-github-actions.md +++ b/.github/testing-github-actions.md @@ -25,8 +25,9 @@ guide: 4. For comment-triggered review-app commands, test a real `+review-app-deploy` after the trusted default-branch wrapper points at the code under test. 5. When testing unreleased upstream `control-plane-flow` changes downstream, pin - both the reusable workflow `uses:` ref and `control_plane_flow_ref` to the - same upstream commit SHA. + the generated reusable workflow `uses:` refs to the same upstream commit SHA. + Newer upstream workflows load their matching shared actions automatically; do + not add a duplicate ref input to downstream wrappers. Avoid testing production automation against moving branch refs such as `main` or a feature branch. Use release tags for normal operation and full commit SHAs for diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index 5ededc8b..3b62c12f 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -10,10 +10,8 @@ permissions: jobs: cleanup: - # Cleanup targets qa-react-webpack-rails-tutorial-. Manually delete any - # legacy qa-react-webpack-rails-tutorial-pr- apps during migration. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 - with: - control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 + # Cleanup targets the current inferred review-app prefix. If you changed + # naming conventions, manually delete review apps under the old prefix. + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index 73dbca68..f15fd4b9 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -19,6 +19,9 @@ permissions: jobs: delete-review-app: + # pull_request_target is intentional: fork PR-close events need access to + # staging secrets to delete review apps and update PR comments. The upstream + # reusable workflow checks out trusted base-branch action code, not fork code. if: | (github.event_name == 'issue_comment' && github.event.issue.pull_request && @@ -26,8 +29,8 @@ jobs: contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || (github.event_name == 'pull_request_target' && github.event.action == 'closed') || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 - with: - control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 + # This `if:` mirrors the upstream job guard to avoid a billable workflow_call + # when the event does not match. Keep both conditions in sync. + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index ed256892..1440e7fd 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,9 +30,7 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 - with: - control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index bd139c65..5527c540 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -8,7 +8,7 @@ on: # deploy branches unless `cpflow generate-github-actions --staging-branch BRANCH` # was used. If STAGING_APP_BRANCH is later changed in repository variables, keep # this list in sync so pushes to that branch actually trigger the workflow. - branches: ["main", "master"] + branches: ["master"] workflow_dispatch: permissions: @@ -16,10 +16,9 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 with: - control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 - staging_app_branch_default: "" + staging_app_branch_default: "master" secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index 1c54801e..84939d0f 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,6 +23,4 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 - with: - control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index 644f1fc3..2f3f44ab 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -16,11 +16,8 @@ permissions: jobs: promote-to-production: if: github.event.inputs.confirm_promotion == 'promote' - # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the - # first selects the reusable workflow, the second selects its shared actions. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 with: - control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. The caller passes the environment name, the upstream # reusable workflow runs its production job in that environment, and diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index b52d0305..5dc2f325 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -17,6 +17,4 @@ jobs: # This is intentionally unconditional: adding this wrapper opts the repo in # to PR-open help. Remove the wrapper or add a repo-specific `if:` guard if # review apps should not be advertised before Control Plane is configured. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 - with: - control_plane_flow_ref: 19ca93eadcba81e7438f26ab55a0f2ca7ace82b0 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 diff --git a/bin/pin-cpflow-github-ref b/bin/pin-cpflow-github-ref index 41f643f3..da3cb4a1 100755 --- a/bin/pin-cpflow-github-ref +++ b/bin/pin-cpflow-github-ref @@ -8,6 +8,9 @@ USAGE = <<~USAGE Use a release tag for normal operation, e.g. v5.0.0. Use a full 40-character commit SHA for temporary unreleased upstream testing. + This only updates generated reusable-workflow `uses:` refs. The called + workflows load their own matching shared actions from that same workflow + commit automatically. Regenerate from the cpflow gem when templates changed. Use --allow-moving-ref only for short-lived local branch/ref experiments. USAGE @@ -32,7 +35,7 @@ end ref = positional.first allow_moving_ref = options.include?("--allow-moving-ref") -unless ref.match?(/\A[0-9A-Za-z._\/-]+\z/) +unless ref.match?(%r{\A[0-9A-Za-z._/-]+\z}) warn "Ref contains unsupported characters: #{ref.inspect}" exit 1 end @@ -44,7 +47,7 @@ unless ref.match?(FULL_COMMIT_SHA) || ref.match?(RELEASE_TAG) || allow_moving_re end root = Pathname.new(__dir__).join("..").expand_path -workflow_paths = Dir[root.join(".github/workflows/cpflow-*.yml")].sort +workflow_paths = Dir[root.join(".github/workflows/cpflow-*.yml")] if workflow_paths.empty? warn "No cpflow workflow wrappers found." @@ -55,8 +58,7 @@ changed = [] workflow_paths.each do |path| text = File.read(path) updated = text - .gsub(%r{(uses:\s+shakacode/control-plane-flow/\.github/workflows/[^@\s]+@)[^\s]+}, "\\1#{ref}") - .gsub(/(\bcontrol_plane_flow_ref:\s*)\S+/, "\\1#{ref}") + .gsub(%r{(uses:\s+shakacode/control-plane-flow/\.github/workflows/[^@\s]+@)[^\s]+}, "\\1#{ref}") next if updated == text diff --git a/bin/test-cpflow-github-flow b/bin/test-cpflow-github-flow index 6294e5f9..e7eafa18 100755 --- a/bin/test-cpflow-github-flow +++ b/bin/test-cpflow-github-flow @@ -10,10 +10,10 @@ if [[ $# -gt 0 ]]; then fi echo "==> cpflow github-flow-readiness" -bin/conductor-exec "${cpflow_cmd[@]}" github-flow-readiness +"${cpflow_cmd[@]}" github-flow-readiness echo "==> parse generated GitHub Actions YAML" -bin/conductor-exec ruby <<'RUBY' +ruby <<'RUBY' require "yaml" Dir[".github/actions/**/action.yml", ".github/workflows/*.yml"].sort.each do |path| @@ -23,7 +23,7 @@ end RUBY echo "==> check composite action input descriptions" -bin/conductor-exec ruby <<'RUBY' +ruby <<'RUBY' require "yaml" bad = [] @@ -39,7 +39,7 @@ puts "no action metadata descriptions contain GitHub expressions" RUBY echo "==> check cpflow reusable workflow refs" -bin/conductor-exec ruby <<'RUBY' +ruby <<'RUBY' require "yaml" CONTROL_PLANE_FLOW_WORKFLOW = %r{\Ashakacode/control-plane-flow/\.github/workflows/[^@\s]+@([^\s]+)\z} @@ -60,9 +60,11 @@ Dir[".github/workflows/cpflow-*.yml"].sort.each do |path| abort "#{path}:#{job_name} uses secrets: inherit; pass only the named secrets the upstream workflow needs" end - unless uses_match - abort "#{path}:#{job_name} has control_plane_flow_ref but no control-plane-flow reusable workflow" if input_ref + if input_ref + abort "#{path}:#{job_name} passes obsolete control_plane_flow_ref. Remove it; the upstream reusable workflow checks out its own source from GitHub's job.workflow_* context." + end + unless uses_match next end @@ -80,12 +82,6 @@ Dir[".github/workflows/cpflow-*.yml"].sort.each do |path| end end - if input_ref - refs[input_ref] << "#{path}:#{job_name}" - abort "#{path}:#{job_name} mismatched cpflow refs: #{uses_ref}, #{input_ref}" if uses_ref != input_ref - elsif job.key?("secrets") - abort "#{path}:#{job_name} passes secrets but is missing control_plane_flow_ref for #{uses_ref}" - end end end @@ -102,4 +98,7 @@ end RUBY echo "==> actionlint" -actionlint -ignore "SC2129" .github/workflows/cpflow-*.yml +actionlint \ + -ignore "SC2129" \ + -ignore 'property "workflow_(ref|sha|repository|file_path)" is not defined in object type' \ + .github/workflows/cpflow-*.yml From c659043f004c15121010761150c49807deb3acc5 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 21:14:27 -1000 Subject: [PATCH 17/18] Regenerate cpflow wrappers from release polish --- .controlplane/shakacode-team.md | 2 +- .github/cpflow-help.md | 11 +++++++---- .../workflows/cpflow-cleanup-stale-review-apps.yml | 2 +- .github/workflows/cpflow-delete-review-app.yml | 2 +- .github/workflows/cpflow-deploy-review-app.yml | 2 +- .github/workflows/cpflow-deploy-staging.yml | 2 +- .github/workflows/cpflow-help-command.yml | 2 +- .../cpflow-promote-staging-to-production.yml | 2 +- .github/workflows/cpflow-review-app-help.yml | 9 +++++---- 9 files changed, 19 insertions(+), 15 deletions(-) diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index 1d06defc..f1f44ab8 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -67,7 +67,7 @@ Advanced optional settings are documented upstream in the [`control-plane-flow` CI automation guide](https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md). Current workflow wrappers are pinned to merged upstream `control-plane-flow` -commit `c0ca750c4838dacf6a1d81d52cb151301516cb77` for downstream testing. After +commit `54a6b8a066ac0a59c49e95a866e21b5633e2b1f9` for downstream testing. After 5.0.1 ships, repin the wrappers to `v5.0.1`; do not treat the temporary SHA as the steady-state master configuration. diff --git a/.github/cpflow-help.md b/.github/cpflow-help.md index 99484add..9e694710 100644 --- a/.github/cpflow-help.md +++ b/.github/cpflow-help.md @@ -2,7 +2,7 @@ These commands are generated by [cpflow](https://github.com/shakacode/control-plane-flow). For full setup, version-pinning, and troubleshooting details, see the upstream -[CI automation guide](https://github.com/shakacode/control-plane-flow/blob/c0ca750c4838dacf6a1d81d52cb151301516cb77/docs/ci-automation.md). +[CI automation guide](https://github.com/shakacode/control-plane-flow/blob/54a6b8a066ac0a59c49e95a866e21b5633e2b1f9/docs/ci-automation.md). ## Pull Request Commands @@ -56,7 +56,7 @@ the environment approval gate passes. ## Version Locking Generated wrappers pin Control Plane Flow once with the reusable workflow -`uses:` ref, for example `@c0ca750c4838dacf6a1d81d52cb151301516cb77`. For stable releases, +`uses:` ref, for example `@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9`. For stable releases, this ref should be a release tag. The upstream reusable workflow automatically loads its matching shared actions from GitHub's workflow context, so downstream wrappers should not pass a duplicate Control Plane Flow ref input. If your @@ -91,5 +91,8 @@ Most apps do not need these: | `CPLN_CLI_VERSION` | Pin a specific `@controlplane/cli` version; normally leave unset. | The PR-open help workflow posts a short command reference whenever the generated -wrapper exists. Remove `.github/workflows/cpflow-review-app-help.yml` or add a -wrapper-level `if:` guard if a repo should not advertise review apps. +wrapper exists. That is intentional for configured demo repos. Forks or clones +that copy the workflow before configuring Control Plane can remove +`.github/workflows/cpflow-review-app-help.yml` or uncomment and adapt the +wrapper-level `if:` guard shown in that file, for example +`vars.REVIEW_APP_PREFIX != '' || vars.CPLN_ORG_STAGING != ''`. diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index 3b62c12f..abf413af 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -12,6 +12,6 @@ jobs: cleanup: # Cleanup targets the current inferred review-app prefix. If you changed # naming conventions, manually delete review apps under the old prefix. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index f15fd4b9..9aed28e9 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -31,6 +31,6 @@ jobs: github.event_name == 'workflow_dispatch' # This `if:` mirrors the upstream job guard to avoid a billable workflow_call # when the event does not match. Keep both conditions in sync. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index 1440e7fd..1c3a414c 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,7 +30,7 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index 5527c540..c2c16a0c 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,7 +16,7 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 with: staging_app_branch_default: "master" secrets: diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index 84939d0f..a2c62404 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,4 +23,4 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index 2f3f44ab..fc1fb2da 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -16,7 +16,7 @@ permissions: jobs: promote-to-production: if: github.event.inputs.confirm_promotion == 'promote' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 with: # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. The caller passes the environment name, the upstream diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 5dc2f325..888c11f6 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -14,7 +14,8 @@ permissions: jobs: show-help: - # This is intentionally unconditional: adding this wrapper opts the repo in - # to PR-open help. Remove the wrapper or add a repo-specific `if:` guard if - # review apps should not be advertised before Control Plane is configured. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@c0ca750c4838dacf6a1d81d52cb151301516cb77 + # This is intentionally unconditional: committing this wrapper opts the repo in + # to PR-open help. Remove it, or uncomment and adapt this guard, if forks or + # clones should stay quiet until Control Plane is configured: + # if: vars.REVIEW_APP_PREFIX != '' || vars.CPLN_ORG_STAGING != '' + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 From 3319280f07c3c003b4bbdf611d54c42adc1a30f6 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 24 May 2026 22:32:15 -1000 Subject: [PATCH 18/18] Pin cpflow workflows to v5.0.1 --- .controlplane/shakacode-team.md | 7 +++---- .github/cpflow-help.md | 6 +++--- .github/workflows/cpflow-cleanup-stale-review-apps.yml | 2 +- .github/workflows/cpflow-delete-review-app.yml | 2 +- .github/workflows/cpflow-deploy-review-app.yml | 2 +- .github/workflows/cpflow-deploy-staging.yml | 2 +- .github/workflows/cpflow-help-command.yml | 2 +- .github/workflows/cpflow-promote-staging-to-production.yml | 2 +- .github/workflows/cpflow-review-app-help.yml | 2 +- 9 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.controlplane/shakacode-team.md b/.controlplane/shakacode-team.md index f1f44ab8..da70250a 100644 --- a/.controlplane/shakacode-team.md +++ b/.controlplane/shakacode-team.md @@ -66,10 +66,9 @@ only by the protected `production` Environment after approval. Advanced optional settings are documented upstream in the [`control-plane-flow` CI automation guide](https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md). -Current workflow wrappers are pinned to merged upstream `control-plane-flow` -commit `54a6b8a066ac0a59c49e95a866e21b5633e2b1f9` for downstream testing. After -5.0.1 ships, repin the wrappers to `v5.0.1`; do not treat the temporary SHA as -the steady-state master configuration. +Current workflow wrappers are pinned to the upstream `control-plane-flow` +release tag `v5.0.1`. Keep release tags as the steady-state configuration; use +a full commit SHA only for short-lived upstream PR testing. If staging moves off `master`, update both `STAGING_APP_BRANCH` and the branch filter in `.github/workflows/cpflow-deploy-staging.yml`. diff --git a/.github/cpflow-help.md b/.github/cpflow-help.md index 9e694710..c87e0200 100644 --- a/.github/cpflow-help.md +++ b/.github/cpflow-help.md @@ -2,7 +2,7 @@ These commands are generated by [cpflow](https://github.com/shakacode/control-plane-flow). For full setup, version-pinning, and troubleshooting details, see the upstream -[CI automation guide](https://github.com/shakacode/control-plane-flow/blob/54a6b8a066ac0a59c49e95a866e21b5633e2b1f9/docs/ci-automation.md). +[CI automation guide](https://github.com/shakacode/control-plane-flow/blob/v5.0.1/docs/ci-automation.md). ## Pull Request Commands @@ -56,8 +56,8 @@ the environment approval gate passes. ## Version Locking Generated wrappers pin Control Plane Flow once with the reusable workflow -`uses:` ref, for example `@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9`. For stable releases, -this ref should be a release tag. The upstream reusable workflow automatically +`uses:` ref, for example `@v5.0.1`. For stable releases, this ref should be a +release tag. The upstream reusable workflow automatically loads its matching shared actions from GitHub's workflow context, so downstream wrappers should not pass a duplicate Control Plane Flow ref input. If your generated wrappers still include a `with:` block whose only purpose is to repeat diff --git a/.github/workflows/cpflow-cleanup-stale-review-apps.yml b/.github/workflows/cpflow-cleanup-stale-review-apps.yml index abf413af..e2ca5915 100644 --- a/.github/workflows/cpflow-cleanup-stale-review-apps.yml +++ b/.github/workflows/cpflow-cleanup-stale-review-apps.yml @@ -12,6 +12,6 @@ jobs: cleanup: # Cleanup targets the current inferred review-app prefix. If you changed # naming conventions, manually delete review apps under the old prefix. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@v5.0.1 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-delete-review-app.yml b/.github/workflows/cpflow-delete-review-app.yml index 9aed28e9..e0989e81 100644 --- a/.github/workflows/cpflow-delete-review-app.yml +++ b/.github/workflows/cpflow-delete-review-app.yml @@ -31,6 +31,6 @@ jobs: github.event_name == 'workflow_dispatch' # This `if:` mirrors the upstream job guard to avoid a billable workflow_call # when the event does not match. Keep both conditions in sync. - uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@v5.0.1 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} diff --git a/.github/workflows/cpflow-deploy-review-app.yml b/.github/workflows/cpflow-deploy-review-app.yml index 1c3a414c..15800591 100644 --- a/.github/workflows/cpflow-deploy-review-app.yml +++ b/.github/workflows/cpflow-deploy-review-app.yml @@ -30,7 +30,7 @@ jobs: github.event.issue.pull_request && contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@v5.0.1 secrets: CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }} DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }} diff --git a/.github/workflows/cpflow-deploy-staging.yml b/.github/workflows/cpflow-deploy-staging.yml index c2c16a0c..ba603b18 100644 --- a/.github/workflows/cpflow-deploy-staging.yml +++ b/.github/workflows/cpflow-deploy-staging.yml @@ -16,7 +16,7 @@ permissions: jobs: deploy-staging: - uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@v5.0.1 with: staging_app_branch_default: "master" secrets: diff --git a/.github/workflows/cpflow-help-command.yml b/.github/workflows/cpflow-help-command.yml index a2c62404..e1b19676 100644 --- a/.github/workflows/cpflow-help-command.yml +++ b/.github/workflows/cpflow-help-command.yml @@ -23,4 +23,4 @@ jobs: contains(fromJson('["+review-app-help","+review-app-help\n","+review-app-help\r\n"]'), github.event.comment.body) && contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) || github.event_name == 'workflow_dispatch' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-help-command.yml@v5.0.1 diff --git a/.github/workflows/cpflow-promote-staging-to-production.yml b/.github/workflows/cpflow-promote-staging-to-production.yml index fc1fb2da..83ad4dcf 100644 --- a/.github/workflows/cpflow-promote-staging-to-production.yml +++ b/.github/workflows/cpflow-promote-staging-to-production.yml @@ -16,7 +16,7 @@ permissions: jobs: promote-to-production: if: github.event.inputs.confirm_promotion == 'promote' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@v5.0.1 with: # Keep CPLN_TOKEN_PRODUCTION as a secret on this protected GitHub # Environment. The caller passes the environment name, the upstream diff --git a/.github/workflows/cpflow-review-app-help.yml b/.github/workflows/cpflow-review-app-help.yml index 888c11f6..15fb171a 100644 --- a/.github/workflows/cpflow-review-app-help.yml +++ b/.github/workflows/cpflow-review-app-help.yml @@ -18,4 +18,4 @@ jobs: # to PR-open help. Remove it, or uncomment and adapt this guard, if forks or # clones should stay quiet until Control Plane is configured: # if: vars.REVIEW_APP_PREFIX != '' || vars.CPLN_ORG_STAGING != '' - uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@54a6b8a066ac0a59c49e95a866e21b5633e2b1f9 + uses: shakacode/control-plane-flow/.github/workflows/cpflow-review-app-help.yml@v5.0.1