cpflow 5.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/commands/update-changelog.md +88 -23
  3. data/.github/actions/cpflow-resolve-review-config/action.yml +137 -0
  4. data/.github/actions/cpflow-setup-environment/action.yml +118 -0
  5. data/.github/workflows/cpflow-cleanup-stale-review-apps.yml +26 -21
  6. data/.github/workflows/cpflow-delete-review-app.yml +21 -18
  7. data/.github/workflows/cpflow-deploy-review-app.yml +23 -19
  8. data/.github/workflows/cpflow-deploy-staging.yml +15 -11
  9. data/.github/workflows/cpflow-help-command.yml +0 -6
  10. data/.github/workflows/cpflow-promote-staging-to-production.yml +30 -5
  11. data/.github/workflows/cpflow-review-app-help.yml +1 -10
  12. data/CHANGELOG.md +23 -1
  13. data/Gemfile.lock +1 -1
  14. data/docs/ai-github-flow-prompt.md +1 -1
  15. data/docs/ci-automation.md +165 -29
  16. data/lib/command/ai_github_flow_prompt.rb +1 -1
  17. data/lib/cpflow/version.rb +1 -1
  18. data/lib/generator_templates/Dockerfile +1 -0
  19. data/lib/generator_templates/entrypoint.sh +42 -2
  20. data/lib/github_flow_templates/.github/cpflow-help.md +79 -83
  21. data/lib/github_flow_templates/.github/workflows/cpflow-cleanup-stale-review-apps.yml +4 -9
  22. data/lib/github_flow_templates/.github/workflows/cpflow-delete-review-app.yml +2 -9
  23. data/lib/github_flow_templates/.github/workflows/cpflow-deploy-review-app.yml +3 -9
  24. data/lib/github_flow_templates/.github/workflows/cpflow-deploy-staging.yml +3 -8
  25. data/lib/github_flow_templates/.github/workflows/cpflow-help-command.yml +0 -9
  26. data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +10 -8
  27. data/lib/github_flow_templates/.github/workflows/cpflow-review-app-help.yml +4 -10
  28. data/lib/github_flow_templates/bin/pin-cpflow-github-ref +3 -1
  29. data/lib/github_flow_templates/bin/test-cpflow-github-flow +23 -8
  30. metadata +2 -1
@@ -150,29 +150,98 @@ apps:
150
150
 
151
151
  Important points:
152
152
 
153
- - `REVIEW_APP_PREFIX` in GitHub Actions must match the review config key prefix, for example `my-app-review`.
154
153
  - `match_if_app_name_starts_with: true` is what allows a single config entry to back `my-app-review-123`, `my-app-review-456`, and cleanup commands like `cpflow cleanup-stale-apps -a my-app-review`.
154
+ - Review-app deploy, delete, and cleanup workflows infer the review app prefix from the single app entry with `match_if_app_name_starts_with: true`.
155
+ - Review-app workflows infer the staging Control Plane org from that review app entry's `cpln_org`.
155
156
  - `upstream: my-app-staging` is what lets the production promotion workflow copy the exact staging artifact.
156
157
  - If your main web workload is not named `rails`, set the optional `PRIMARY_WORKLOAD` repository variable described below.
157
158
 
158
159
  ## Required GitHub Repository Settings
159
160
 
160
- Configure these repository secrets:
161
+ For a normal generated review-app setup, configure one repository secret:
161
162
 
162
163
  - `CPLN_TOKEN_STAGING`: token for the staging Control Plane org
163
- - `CPLN_TOKEN_PRODUCTION`: token for the production Control Plane org
164
164
 
165
- Configure these repository variables:
165
+ No GitHub repository variables are required for review apps when `.controlplane/controlplane.yml`
166
+ has exactly one review app entry with `match_if_app_name_starts_with: true` and
167
+ that entry has a `cpln_org`. The inferred values come from that config file:
168
+ the review-app prefix is the app key with `match_if_app_name_starts_with: true`,
169
+ and the staging org is that app's `cpln_org` value. Set these variables only
170
+ when you need to test a fork or clone against a different Control Plane org,
171
+ choose a different review-app prefix, expose a different public workload, or
172
+ disambiguate generated review-app config:
173
+
174
+ - `CPLN_ORG_STAGING`: override the staging/review org inferred from `cpln_org`, for example `company-staging`
175
+ - `REVIEW_APP_PREFIX`: override the inferred review-app prefix; required only when multiple review app prefixes exist in `controlplane.yml`
176
+ - `PRIMARY_WORKLOAD`: override the public workload used to discover the public endpoint and do production health checks; defaults to `rails`
177
+
178
+ If `controlplane.yml` defines more than one app with
179
+ `match_if_app_name_starts_with: true`, inference intentionally fails. Set
180
+ `CPLN_ORG_STAGING` and `REVIEW_APP_PREFIX` to tell the workflow which review-app
181
+ family to manage.
182
+
183
+ For staging deploys, also configure:
166
184
 
167
185
  - `CPLN_ORG_STAGING`: staging org name, for example `company-staging`
168
- - `CPLN_ORG_PRODUCTION`: production org name, for example `company-production`
169
186
  - `STAGING_APP_NAME`: staging GVC name, for example `my-app-staging`
170
- - `PRODUCTION_APP_NAME`: production GVC name, for example `my-app-production`
171
- - `REVIEW_APP_PREFIX`: review-app prefix, for example `my-app-review`
172
187
  - `STAGING_APP_BRANCH`: optional branch that auto-deploys staging. If you use a custom branch, either pass it to `cpflow generate-github-actions --staging-branch BRANCH` during generation or edit `cpflow-deploy-staging.yml` so its `on.push.branches` list includes the same branch.
173
- - `PRIMARY_WORKLOAD`: optional workload name used to discover the public endpoint and do production health checks; defaults to `rails`
174
- - `DOCKER_BUILD_EXTRA_ARGS`: optional newline-delimited single `docker build` tokens passed through to `cpflow build-image`, for example `--build-arg=FOO=bar` or `--secret=id=npmrc,src=.npmrc`
175
- - `DOCKER_BUILD_SSH_KNOWN_HOSTS`: optional multi-line `known_hosts` content used with `DOCKER_BUILD_SSH_KEY` when the build needs SSH access to hosts other than GitHub.com
188
+
189
+ For production promotion, also configure:
190
+
191
+ - a GitHub Environment named `production`
192
+ - required reviewers on that environment, limited to the people or team allowed to promote production
193
+ - "Prevent self-review" on that environment, so the person who starts the promotion cannot approve it
194
+ - optionally disable administrator bypass and restrict deployment branches/tags to your protected release branch
195
+ - `CPLN_TOKEN_PRODUCTION` as an environment secret on `production`, not as a repository or organization secret
196
+ - `CPLN_ORG_PRODUCTION` as a production environment variable, for example `company-production`
197
+ - `PRODUCTION_APP_NAME` as a production environment variable, for example `my-app-production`
198
+
199
+ Do not put `CPLN_TOKEN_PRODUCTION` in repository or organization secrets for
200
+ sensitive production systems. The generated promotion reusable workflow declares
201
+ `environment: production`; the generated caller passes that environment name
202
+ through `production_environment`. GitHub waits for the `production`
203
+ environment's protection rules before injecting `CPLN_TOKEN_PRODUCTION` into
204
+ the upstream production job.
205
+
206
+ GitHub's reusable-workflow syntax still requires the upstream workflow to
207
+ declare `CPLN_TOKEN_PRODUCTION` as an optional `workflow_call` secret so static
208
+ validation accepts `secrets.CPLN_TOKEN_PRODUCTION`, but the generated caller
209
+ must not pass it. GitHub uses the secret from the reusable workflow job's
210
+ `production` environment when that environment is configured.
211
+
212
+ Generated caller workflows pass only the named secrets each reusable workflow
213
+ needs. They do not use `secrets: inherit`; the production token is supplied by
214
+ the protected `production` Environment after approval, not forwarded from a
215
+ repository secret.
216
+
217
+ ### Production Promotion Safety
218
+
219
+ `CPLN_TOKEN_PRODUCTION` can change live production workloads, images, releases,
220
+ and rollback state. Treat it differently from review-app and staging credentials.
221
+ The standard path is:
222
+
223
+ 1. Create the `production` GitHub Environment before setting the production token.
224
+ 2. Add a small required-reviewer list or team with production authority.
225
+ 3. Enable prevent self-review.
226
+ 4. Disable administrator bypass if your org policy requires two-person control.
227
+ 5. Restrict deployable branches or tags to the protected release branch.
228
+ 6. Store `CPLN_TOKEN_PRODUCTION` only as a `production` environment secret.
229
+ 7. Store `CPLN_ORG_PRODUCTION` and `PRODUCTION_APP_NAME` as `production`
230
+ environment variables, or as repository variables only when those names are
231
+ intentionally non-sensitive.
232
+
233
+ GitHub only exposes environment secrets to jobs that reference the environment
234
+ after configured protection rules pass. GitHub also does not allow a caller job
235
+ that directly invokes a reusable workflow to set `environment`; for that reason,
236
+ the reusable promotion workflow itself declares `environment: production`. See
237
+ GitHub's docs for [managing environments](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments),
238
+ [deployment protection rules](https://docs.github.com/en/actions/reference/workflows-and-actions/deployments-and-environments),
239
+ and [reusable workflow limitations](https://docs.github.com/en/actions/reference/reusable-workflows-reference#supported-keywords-for-jobs-that-call-a-reusable-workflow).
240
+
241
+ Application runtime secrets such as `SECRET_KEY_BASE`, API keys, or private
242
+ license keys belong in Control Plane secret dictionaries referenced by
243
+ `controlplane.yml`. They are not GitHub repository variables unless your
244
+ Docker build itself needs them.
176
245
 
177
246
  Recommended org layout:
178
247
 
@@ -183,6 +252,17 @@ Optional repository secret for private dependency builds:
183
252
 
184
253
  - `DOCKER_BUILD_SSH_KEY`: private SSH key used when the Dockerfile needs `RUN --mount=type=ssh` to fetch private GitHub dependencies during image build
185
254
 
255
+ Optional repository variables for private dependency builds:
256
+
257
+ - `DOCKER_BUILD_EXTRA_ARGS`: optional newline-delimited single `docker build` tokens passed through to `cpflow build-image`, for example `--build-arg=FOO=bar` or `--secret=id=npmrc,src=.npmrc`
258
+ - `DOCKER_BUILD_SSH_KNOWN_HOSTS`: optional multi-line `known_hosts` content used with `DOCKER_BUILD_SSH_KEY` when the build needs SSH access to hosts other than GitHub.com
259
+
260
+ Advanced optional repository variables:
261
+
262
+ - `REVIEW_APP_DEPLOYING_ICON_URL`: custom image URL for the animated icon in review-app PR comments. Ignore this for the standard setup; it is cosmetic only.
263
+ - `CPLN_CLI_VERSION`: pin only when Control Plane CLI compatibility requires it.
264
+ - `CPFLOW_VERSION`: pin a published RubyGems version only when intentionally overriding the default build-from-ref behavior.
265
+
186
266
  ## Docker Builds with Private Dependencies
187
267
 
188
268
  Some apps need extra Docker build configuration before the generated workflows are turnkey. Common examples are:
@@ -212,6 +292,9 @@ The action will start an SSH agent, add the key, write `known_hosts`, and pass `
212
292
  `cpflow-review-app-help.yml`
213
293
 
214
294
  - Posts a quick reference when a pull request opens, including on fork-based PRs.
295
+ - This is an onboarding comment only; it does not checkout PR code or receive
296
+ Control Plane secrets. Remove this wrapper if a repo does not want automatic
297
+ review-app command help on every new PR.
215
298
 
216
299
  `cpflow-help-command.yml`
217
300
 
@@ -242,6 +325,7 @@ The action will start an SSH agent, add the key, write `known_hosts`, and pass `
242
325
  `cpflow-promote-staging-to-production.yml`
243
326
 
244
327
  - Manually promotes the staging artifact to production with a confirmation input.
328
+ - Runs the production job in the `production` GitHub Environment, so configured reviewers approve the job before production environment secrets are available.
245
329
  - Verifies that production has the env var names staging expects.
246
330
  - Runs a health check against `PRIMARY_WORKLOAD`.
247
331
  - Attempts a rollback of every configured application workload if the new production image does not come up healthy.
@@ -252,6 +336,25 @@ The action will start an SSH agent, add the key, write `known_hosts`, and pass `
252
336
  - Runs nightly and on demand.
253
337
  - Deletes stale review apps using `cpflow cleanup-stale-apps`.
254
338
 
339
+ Generated review app names use `<review-app-prefix>-<PR number>`, for example
340
+ `my-app-review-123`. If an existing repository is migrating from older local
341
+ workflow glue that created names like `<review-app-prefix>-pr-123`, delete those
342
+ old review apps manually after merging the generated flow; the cleanup workflow
343
+ only targets the current prefix convention.
344
+
345
+ To inventory old-prefix review apps before cleanup, run:
346
+
347
+ ```sh
348
+ cpln gvc query --org <staging-org> -o yaml --prop name~<review-app-prefix>-pr-
349
+ ```
350
+
351
+ The PR-open help workflow posts the short command reference whenever the
352
+ generated wrapper exists. That is intentional for configured demo repos. Forks
353
+ or clones that copy the workflow before configuring Control Plane can remove
354
+ `.github/workflows/cpflow-review-app-help.yml` or uncomment and adapt the
355
+ wrapper-level `if:` guard shown in that file, for example
356
+ `vars.REVIEW_APP_PREFIX != '' || vars.CPLN_ORG_STAGING != ''`.
357
+
255
358
  ## Upstream Reusable Workflows
256
359
 
257
360
  The generated workflows are intentionally small wrappers. The deployment logic,
@@ -271,18 +374,26 @@ GitHub ref:
271
374
 
272
375
  ```yaml
273
376
  uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@<ref>
274
- with:
275
- control_plane_flow_ref: <ref>
276
377
  ```
277
378
 
278
- Those two pins must stay in sync:
379
+ That single `uses:` ref is the downstream lock. GitHub exposes the reusable
380
+ workflow's own repository, ref, and SHA to the called job, so the upstream
381
+ workflow checks out the matching `control-plane-flow` source automatically from
382
+ that context. Downstream wrappers should not pass `control_plane_flow_ref`; if
383
+ you see that input in generated wrappers, regenerate with a newer `cpflow`.
384
+
385
+ There are two locks, and they protect different things:
279
386
 
280
- - `uses: ...@<ref>` chooses the upstream reusable workflow file.
281
- - `control_plane_flow_ref: <ref>` tells that reusable workflow which
282
- `control-plane-flow` checkout to use for shared composite actions and, when
283
- `CPFLOW_VERSION` is empty, for building and installing the `cpflow` gem.
387
+ - The GitHub ref locks the reusable workflow and composite action code that
388
+ GitHub runs.
389
+ - The RubyGems version locks the `cpflow` CLI/runtime code only when you install
390
+ or run that gem. It does not make GitHub load reusable workflow YAML from the
391
+ gem.
284
392
 
285
- The stable release path is still gem-driven:
393
+ That means a downstream app cannot rely on the gem alone for GitHub Actions
394
+ behavior. The safe stable path is still gem-driven for generation, but
395
+ developers must commit generated wrappers that reference the matching upstream
396
+ release tag:
286
397
 
287
398
  1. Publish a `cpflow` gem.
288
399
  2. Install or bundle that released gem in the downstream project.
@@ -297,9 +408,26 @@ feature-branch refs.
297
408
  `CPFLOW_VERSION` is a runtime override. If a downstream repository sets the
298
409
  `CPFLOW_VERSION` variable, the setup action runs `gem install cpflow -v
299
410
  <version>`. If it is unset, the setup action builds `cpflow` from the checked-out
300
- `control-plane-flow` ref. For normal releases, leave `CPFLOW_VERSION` unset while
301
- pinning the wrappers to the matching `v<version>` tag, or set
302
- `CPFLOW_VERSION` to that same released gem version without the leading `v`.
411
+ `control-plane-flow` source selected by the reusable workflow's own SHA. For
412
+ normal releases, leave `CPFLOW_VERSION` unset while pinning the wrappers to the
413
+ matching `v<version>` tag, or set `CPFLOW_VERSION` to that same released gem
414
+ version without the leading `v`.
415
+ When setting `CPFLOW_VERSION`, use RubyGems version syntax, for example
416
+ `5.0.0` or `5.0.0.rc.1`; do not use `v5.0.0` or dash-separated prereleases
417
+ because the value is passed directly to `gem install cpflow -v`.
418
+
419
+ The setup action fails early when `CPFLOW_VERSION` and the reusable workflow tag
420
+ are out of sync. `CPFLOW_VERSION=5.0.0` is accepted only when the wrapper uses a
421
+ release tag such as `@v5.0.0` (or GitHub resolves it to `refs/tags/v5.0.0`).
422
+ Release tags may use dot- or dash-separated prerelease suffixes, such as
423
+ `v5.0.0.rc.1` or `v5.0.0-rc.1`; the gem version should still use dots. The
424
+ action also checks the remote `control-plane-flow` tag and the checked-out
425
+ action commit, so a moving branch named like `v5.0.0` cannot be used with
426
+ `CPFLOW_VERSION=5.0.0`. That tag check uses outbound HTTPS to GitHub; restricted
427
+ runners that cannot reach GitHub should leave `CPFLOW_VERSION` unset and build
428
+ `cpflow` from the checked-out ref instead. When testing an unreleased upstream
429
+ commit SHA, leave `CPFLOW_VERSION` unset so the workflow builds `cpflow` from the
430
+ same source that supplies the reusable workflow and composite actions.
303
431
 
304
432
  ## Testing Unreleased Upstream Changes Downstream
305
433
 
@@ -319,7 +447,10 @@ releasing it. Use an immutable commit SHA from the upstream PR branch:
319
447
  local experiments that should not be committed.
320
448
 
321
449
  3. Keep `CPFLOW_VERSION` unset so the workflow builds `cpflow` from the same
322
- upstream SHA that supplies the reusable workflow and composite actions.
450
+ upstream SHA that supplies the reusable workflow and composite actions. If
451
+ `CPFLOW_VERSION` is set while the wrapper is pinned to a SHA, the setup
452
+ action fails before deployment because the gem and action code cannot be
453
+ proven to match.
323
454
  4. Run:
324
455
 
325
456
  ```sh
@@ -343,8 +474,10 @@ releasing it. Use an immutable commit SHA from the upstream PR branch:
343
474
  6. Verify the deploy logs show the expected upstream commit SHA, the setup step
344
475
  prints the expected `cpflow` source/version, and the review app URL returns
345
476
  HTTP 200.
346
- 7. After the upstream PR merges and a gem is released, regenerate or repin the
347
- downstream wrappers to the release tag.
477
+ 7. After the upstream PR merges and a gem is released, regenerate the downstream
478
+ wrappers from that released gem and commit the release tag. Use
479
+ `bin/pin-cpflow-github-ref vX.Y.Z` only for a ref-only update when the
480
+ generated templates are already current.
348
481
 
349
482
  This tests the real reusable workflow, shared composite actions, and source-built
350
483
  `cpflow` gem from one immutable upstream commit. It avoids merging upstream blind
@@ -360,9 +493,12 @@ bin/test-cpflow-github-flow
360
493
 
361
494
  The helper runs `cpflow github-flow-readiness`, parses generated workflow YAML,
362
495
  checks composite action metadata for literal GitHub expressions in descriptions,
363
- checks that all generated wrappers use one upstream ref consistently, requires
364
- secret-inheriting reusable workflows to pass `control_plane_flow_ref`, and runs
365
- `actionlint -ignore "SC2129" .github/workflows/cpflow-*.yml`.
496
+ checks that all generated wrappers use one upstream ref consistently, rejects
497
+ broad `secrets: inherit` usage in generated cpflow wrappers, rejects obsolete
498
+ `control_plane_flow_ref` wrapper inputs, and runs
499
+ `actionlint` against `.github/workflows/cpflow-*.yml`. Its `actionlint` command
500
+ keeps the existing shellcheck ignore and also ignores stale local `actionlint`
501
+ false positives for GitHub's newer reusable-workflow `job.workflow_*` fields.
366
502
 
367
503
  ## Applying This to React on Rails Demo Apps
368
504
 
@@ -397,7 +533,7 @@ In practice, porting the flow into a demo app usually follows five phases.
397
533
 
398
534
  **Wire up GitHub secrets, variables, and private builds:**
399
535
 
400
- 11. Make sure the repo variables and secrets line up with the configured app names.
536
+ 11. Make sure the repo variables and secrets line up with the configured app names. For production promotion, store `CPLN_TOKEN_PRODUCTION` only on a protected `production` GitHub Environment with required reviewers.
401
537
  12. If the Dockerfile pulls private dependencies over SSH, configure `DOCKER_BUILD_SSH_KEY`, add `DOCKER_BUILD_SSH_KNOWN_HOSTS` when the host is not GitHub.com, and validate that the image can build with `RUN --mount=type=ssh`.
402
538
 
403
539
  **Validate and push:**
@@ -417,7 +553,7 @@ current prompt with that repo's default app prefix already filled in.
417
553
  Short version:
418
554
 
419
555
  ```text
420
- Set up Control Plane GitHub Flow for this repo. Start with `cpflow github-flow-readiness` and stop on any reported blockers. The repo must be deployable from a clean clone, with published package versions and a production Dockerfile that can really build the app. Stop and report blockers for unpublished packages, inaccessible private dependencies, legacy toolchains, or missing production build paths instead of generating workflows blindly. Then run `cpflow generate` if `.controlplane/` is missing, run `cpflow generate-github-actions`, adapt the generated scaffold to the real workloads, document the required GitHub secrets and variables, validate the real build path locally, push the branch, and check the GitHub Actions results.
556
+ Set up Control Plane GitHub Flow for this repo. Start with `cpflow github-flow-readiness` and stop on any reported blockers. The repo must be deployable from a clean clone, with published package versions and a production Dockerfile that can really build the app. Stop and report blockers for unpublished packages, inaccessible private dependencies, legacy toolchains, or missing production build paths instead of generating workflows blindly. Then run `cpflow generate` if `.controlplane/` is missing, run `cpflow generate-github-actions`, adapt the generated scaffold to the real workloads, document the required GitHub secrets and variables, validate the real build path locally, push the branch, and check the GitHub Actions results. Keep production promotion safe by documenting `CPLN_TOKEN_PRODUCTION` as a protected `production` GitHub Environment secret, not a repository or organization secret.
421
557
  ```
422
558
 
423
559
  Expand that prompt with app-specific requirements before editing files:
@@ -32,7 +32,7 @@ module Command
32
32
  <<~PROMPT
33
33
  Set up Control Plane GitHub Flow for this repo. Start with `cpflow github-flow-readiness` and stop on any reported blockers. The repo must be deployable from a clean clone: published package versions, complete runtime scaffold, and a production Dockerfile that can build the app. If any package version is unpublished, inaccessible from CI, or requires credentials that are not already modeled in the repo or GitHub settings, stop and report the blocker instead of generating workflow files. If the repo is a legacy sample pinned to an obsolete Ruby or Bundler toolchain, if it does not even have a production Dockerfile yet, or if it is a monorepo without an already-decided single app boundary for this flow, stop and report that as a prerequisite instead of forcing the rollout.
34
34
 
35
- If `.controlplane/` is missing, run `cpflow generate`. Treat the generated app names as the repo-name default (`#{inferred_app_prefix}`) and rename them only if the project needs a different prefix. Then run `cpflow generate-github-actions` (or `cpflow generate-github-actions --staging-branch BRANCH` when staging should deploy from a branch other than `main`/`master`), keep review apps opt-in via `+review-app-deploy`, make sure any `STAGING_APP_BRANCH` repository variable is also present in the generated staging workflow's `on.push.branches` filter, and list the GitHub secrets and variables that must be configured.
35
+ If `.controlplane/` is missing, run `cpflow generate`. Treat the generated app names as the repo-name default (`#{inferred_app_prefix}`) and rename them only if the project needs a different prefix. Then run `cpflow generate-github-actions` (or `cpflow generate-github-actions --staging-branch BRANCH` when staging should deploy from a branch other than `main`/`master`), keep review apps opt-in via `+review-app-deploy`, make sure any `STAGING_APP_BRANCH` repository variable is also present in the generated staging workflow's `on.push.branches` filter, and list the GitHub secrets and variables that must be configured. Do not hand-edit duplicated upstream refs into the generated wrappers: the only downstream Control Plane Flow pin should be the reusable workflow `uses: ...@vX.Y.Z` value generated from the installed `cpflow` gem version, and upstream workflows load their matching shared actions automatically. Keep the standard path simple: review apps require only `CPLN_TOKEN_STAGING` when the generated review app config can be inferred. For production promotion, document a protected `production` GitHub Environment with required reviewers, prevent self-review, and `CPLN_TOKEN_PRODUCTION` stored as an environment secret, not as a repository or organization secret.
36
36
 
37
37
  Keep Node available in the final image if asset compilation or SSR depends on ExecJS, Yarn, `pnpm`, or npm after the main install layer. Make sure the generated Dockerfile uses a Ruby base image compatible with the app's declared Ruby requirement. Preserve repo-defined frontend build hooks: if `config/shakapacker.yml` defines a `precompile_hook`, or React on Rails enables `config.auto_load_bundle = true`, confirm the generated Dockerfile runs that codegen step before `rails assets:precompile`. If `config/database.yml` shows SQLite in production, confirm that the generated scaffold uses persistent `db` and `storage` volumes plus a release script that runs `rails db:prepare`; otherwise keep the default Postgres workload. If the public workload is not named `rails`, set `PRIMARY_WORKLOAD` or adjust the generated workflows. Inspect the Dockerfile and package sources for private GitHub dependencies or `RUN --mount=type=ssh`; if present, wire `DOCKER_BUILD_SSH_KEY`, optionally set `DOCKER_BUILD_SSH_KNOWN_HOSTS` for non-GitHub SSH hosts, and keep `DOCKER_BUILD_EXTRA_ARGS` to newline-delimited single tokens such as `--build-arg=FOO=bar`.
38
38
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cpflow
4
- VERSION = "5.0.0"
4
+ VERSION = "5.0.1"
5
5
  MIN_CPLN_VERSION = "3.1.0"
6
6
  end
@@ -78,6 +78,7 @@ RUN rails assets:precompile
78
78
 
79
79
  # add entrypoint
80
80
  COPY .controlplane/entrypoint.sh ./
81
+ RUN chmod +x /app/entrypoint.sh
81
82
  ENTRYPOINT ["/app/entrypoint.sh"]
82
83
 
83
84
  CMD ["rails", "s"]
@@ -1,8 +1,48 @@
1
1
  #!/bin/sh
2
+ set -e
2
3
  # Runs before the main command
3
4
 
4
- echo " -- Preparing database"
5
- rails db:prepare
5
+ is_rails_server_command() {
6
+ if [ "${1:-}" = "env" ]; then
7
+ shift
8
+ while [ "$#" -gt 0 ]; do
9
+ case "${1}" in
10
+ *=*) shift ;;
11
+ --) shift; break ;;
12
+ -*) return 1 ;;
13
+ *) break ;;
14
+ esac
15
+ done
16
+ fi
17
+
18
+ if [ "${1:-}" = "bundle" ] && [ "${2:-}" = "exec" ]; then
19
+ shift 2
20
+ fi
21
+
22
+ # Matches generated, optionally env-prefixed, flag-free Thruster invocations.
23
+ # Hand-edited commands with env flags or Thruster flags before rails skip
24
+ # generated DB prep.
25
+ if [ "${1:-}" = "thrust" ] || [ "${1:-}" = "bin/thrust" ] || [ "${1:-}" = "./bin/thrust" ]; then
26
+ shift
27
+ fi
28
+
29
+ # Thruster may be wrapped with its own `bundle exec`, and the Rails command
30
+ # it proxies may also be wrapped with `bundle exec`.
31
+ if [ "${1:-}" = "bundle" ] && [ "${2:-}" = "exec" ]; then
32
+ shift 2
33
+ fi
34
+
35
+ { [ "${1:-}" = "rails" ] || [ "${1:-}" = "bin/rails" ] || [ "${1:-}" = "./bin/rails" ]; } &&
36
+ { [ "${2:-}" = "server" ] || [ "${2:-}" = "s" ]; }
37
+ }
38
+
39
+ # Match generated Rails server commands; workers and renderers skip DB prep.
40
+ # Generated Dockerfiles use WORKDIR /app; adjust this path if your hand-edited
41
+ # image runs the entrypoint from a different working directory.
42
+ if is_rails_server_command "$@"; then
43
+ echo " -- Preparing database"
44
+ ./bin/rails db:prepare
45
+ fi
6
46
 
7
47
  echo " -- Finishing entrypoint.sh, executing command"
8
48
  exec "$@"
@@ -1,102 +1,98 @@
1
- # Review app help
1
+ # Review App Commands
2
2
 
3
- You asked for review app help. These commands are generated by [cpflow](https://github.com/shakacode/control-plane-flow).
3
+ These commands are generated by [cpflow](https://github.com/shakacode/control-plane-flow).
4
+ For full setup, version-pinning, and troubleshooting details, see the upstream
5
+ [CI automation guide](https://github.com/shakacode/control-plane-flow/blob/__CPFLOW_GITHUB_ACTIONS_REF__/docs/ci-automation.md).
4
6
 
5
- ## PR commands
7
+ ## Pull Request Commands
6
8
 
7
- `+review-app-deploy`
8
- - Creates the review app if it does not exist
9
- - Builds the PR commit image
10
- - Deploys the image and comments with the review URL
11
- - 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.
9
+ Comment with exactly one command, with no surrounding text or trailing spaces.
10
+ A single trailing newline from GitHub's comment editor is accepted.
12
11
 
13
- `+review-app-delete`
14
- - Deletes the review app when the PR is done
15
- - This also runs automatically when the PR closes
16
- - 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`.
12
+ | Command | What it does |
13
+ | --- | --- |
14
+ | `+review-app-deploy` | Builds the PR image, creates the review app if needed, deploys, and comments with the review URL. |
15
+ | `+review-app-delete` | Deletes the review app. This also runs automatically when the PR closes. |
16
+ | `+review-app-help` | Posts this help message on the PR. |
17
17
 
18
- `+review-app-help`
19
- - Posts this message on the PR.
20
- - 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`.
18
+ ## Standard Setup
21
19
 
22
- ## Workflow behavior
20
+ For the normal generated review-app path, GitHub needs one repository secret:
23
21
 
24
- - Review apps are opt-in and created with `+review-app-deploy`
25
- - New commits redeploy existing review apps automatically
26
- - Pushes to the staging branch deploy staging automatically
27
- - Promotion to production is manual via the Actions tab
28
- - A nightly workflow removes stale review apps
29
-
30
- <details>
31
- <summary>Advanced: GitHub Actions secrets and variables</summary>
32
-
33
- ### GitHub Actions secrets
34
-
35
- | Name | Required | Notes |
22
+ | Name | Where | Notes |
36
23
  | --- | --- | --- |
37
- | `CPLN_TOKEN_STAGING` | yes | Service-account token scoped to the staging Control Plane org on controlplane.com. |
38
- | `CPLN_TOKEN_PRODUCTION` | yes (for promote) | Service-account token scoped to the production Control Plane org on controlplane.com. |
39
- | `DOCKER_BUILD_SSH_KEY` | optional | Private SSH key used when Docker builds fetch private deps via `RUN --mount=type=ssh`. |
24
+ | `CPLN_TOKEN_STAGING` | Repository secret | Control Plane service-account token for the staging/review org. |
40
25
 
41
- ### GitHub Actions variables
26
+ No repository variables are required for the standard review-app path when
27
+ `.controlplane/controlplane.yml` has exactly one review app entry with
28
+ `match_if_app_name_starts_with: true`. cpflow infers the review-app prefix and
29
+ staging org from that config.
42
30
 
43
- | Name | Required | Notes |
44
- | --- | --- | --- |
45
- | `CPLN_ORG_STAGING` | yes | Control Plane org on controlplane.com for staging and review apps. |
46
- | `CPLN_ORG_PRODUCTION` | yes (for promote) | Control Plane org on controlplane.com for production. |
47
- | `STAGING_APP_NAME` | yes | App name in `controlplane.yml` used as the staging deploy target. |
48
- | `PRODUCTION_APP_NAME` | yes (for promote) | App name in `controlplane.yml` used as the production deploy target. |
49
- | `REVIEW_APP_PREFIX` | yes | Prefix for per-PR review app names (e.g. `review-app`). |
50
- | `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. |
51
- | `STAGING_APP_BRANCH` | optional | Custom staging branch. Custom branches must also appear in `cpflow-deploy-staging.yml`'s push filter. |
52
- | `PRIMARY_WORKLOAD` | optional | Workload polled for health and rollback (defaults to `rails`). |
53
- | `DOCKER_BUILD_EXTRA_ARGS` | optional | Newline-delimited extra docker build tokens (e.g. `--build-arg=FOO=bar`). |
54
- | `DOCKER_BUILD_SSH_KNOWN_HOSTS` | optional | SSH known_hosts entries when SSH build hosts are not GitHub.com. |
55
- | `HEALTH_CHECK_ACCEPTED_STATUSES` | optional | Space-separated HTTP statuses considered healthy on promote (default `200 301 302`). |
56
- | `CPLN_CLI_VERSION` | optional | Pin a specific `@controlplane/cli` version; falls back to the action default when unset. |
57
- | `CPFLOW_VERSION` | optional | Pin a published RubyGems version such as `5.0.0`. 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. |
58
-
59
- </details>
60
-
61
- <details>
62
- <summary>Advanced: testing unreleased control-plane-flow changes</summary>
63
-
64
- Generated workflow wrappers have two related pins:
65
-
66
- - The `uses: shakacode/control-plane-flow/...@<ref>` GitHub ref selects the reusable workflow code.
67
- - The `control_plane_flow_ref: <ref>` input tells the setup action which `control-plane-flow` source to check out and build when `CPFLOW_VERSION` is empty.
68
-
69
- For normal releases, point both pins at a release tag such as `v5.0.0`.
70
- You may leave `CPFLOW_VERSION` unset, or set it to the matching RubyGems version
71
- without the leading `v`, such as `5.0.0`.
72
-
73
- For temporary downstream testing of an upstream PR before a gem is released, pin
74
- both values to the exact 40-character commit SHA and leave `CPFLOW_VERSION`
75
- unset:
31
+ Optional overrides exist for forks, clones, and unusual apps:
76
32
 
77
- ```sh
78
- bin/pin-cpflow-github-ref <40-character-control-plane-flow-commit-sha>
79
- bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow
80
- ```
33
+ | Name | Notes |
34
+ | --- | --- |
35
+ | `CPLN_ORG_STAGING` | Override the staging/review Control Plane org inferred from `controlplane.yml`. |
36
+ | `REVIEW_APP_PREFIX` | Override the review-app prefix inferred from `controlplane.yml`. |
37
+ | `PRIMARY_WORKLOAD` | Public workload used for review URLs and health checks; defaults to `rails`. |
81
38
 
82
- Do not leave downstream apps pinned to a moving branch such as `main`. A branch
83
- can pick up beta or work-in-progress changes without a downstream PR changing.
84
- Use commit SHAs for short-lived PR testing, then switch to the release tag once
85
- the gem and tag exist.
39
+ ## Staging And Production
86
40
 
87
- </details>
41
+ Staging deploys use the same `CPLN_TOKEN_STAGING` secret plus `STAGING_APP_NAME`.
88
42
 
89
- <details>
90
- <summary>Advanced: testing changes to generated workflows</summary>
43
+ Production promotion is part of the generated flow, but keep it protected:
91
44
 
92
- 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`:
45
+ | Name | Where | Notes |
46
+ | --- | --- | --- |
47
+ | `CPLN_TOKEN_PRODUCTION` | `production` GitHub Environment secret | Do not store this as a repository or organization secret. |
48
+ | `CPLN_ORG_PRODUCTION` | Prefer `production` Environment variable | Production Control Plane org. |
49
+ | `PRODUCTION_APP_NAME` | Prefer `production` Environment variable | Production app name from `controlplane.yml`. |
50
+
51
+ Configure the `production` GitHub Environment with required reviewers and
52
+ prevent self-review. The generated promotion wrapper passes only the staging
53
+ token from repository secrets; GitHub injects `CPLN_TOKEN_PRODUCTION` only after
54
+ the environment approval gate passes.
55
+
56
+ ## Version Locking
57
+
58
+ Generated wrappers pin Control Plane Flow once with the reusable workflow
59
+ `uses:` ref, for example `@__CPFLOW_GITHUB_ACTIONS_REF__`. For stable releases,
60
+ this ref should be a release tag. The upstream reusable workflow automatically
61
+ loads its matching shared actions from GitHub's workflow context, so downstream
62
+ wrappers should not pass a duplicate Control Plane Flow ref input. If your
63
+ generated wrappers still include a `with:` block whose only purpose is to repeat
64
+ the same ref, regenerate them with a newer `cpflow`.
65
+
66
+ Leave `CPFLOW_VERSION` unset so the workflow builds cpflow from the same
67
+ checked-out upstream source. If you set `CPFLOW_VERSION`, it must match the
68
+ release tag, for example `CPFLOW_VERSION=5.0.1` with a wrapper pinned to
69
+ `uses: ...@v5.0.1`.
70
+
71
+ Do not leave downstream apps pinned to a moving branch such as `main`. For a
72
+ short-lived test of an unreleased upstream PR, pin to a full 40-character commit
73
+ SHA and leave `CPFLOW_VERSION` unset:
93
74
 
94
75
  ```sh
95
- gh workflow run cpflow-deploy-review-app.yml --ref <your-pr-branch> -f pr_number=<pr-number>
96
- gh workflow run cpflow-delete-review-app.yml --ref <your-pr-branch> -f pr_number=<pr-number>
97
- gh workflow run cpflow-help-command.yml --ref <your-pr-branch> -f pr_number=<pr-number>
76
+ bin/pin-cpflow-github-ref <40-character-control-plane-flow-commit-sha>
77
+ bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow
98
78
  ```
99
79
 
100
- `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.
101
-
102
- </details>
80
+ ## Advanced Variables
81
+
82
+ Most apps do not need these:
83
+
84
+ | Name | Notes |
85
+ | --- | --- |
86
+ | `DOCKER_BUILD_EXTRA_ARGS` | Newline-delimited extra Docker build tokens. |
87
+ | `DOCKER_BUILD_SSH_KEY` | Private SSH key for Docker builds that fetch private dependencies. |
88
+ | `DOCKER_BUILD_SSH_KNOWN_HOSTS` | SSH known_hosts entries when SSH build hosts are not GitHub.com. |
89
+ | `REVIEW_APP_DEPLOYING_ICON_URL` | Cosmetic custom image URL for the animated deploying icon. Set to `none` to use the text fallback icon. |
90
+ | `STAGING_APP_BRANCH` | Custom staging branch. The branch must also appear in `cpflow-deploy-staging.yml`'s push filter. |
91
+ | `CPLN_CLI_VERSION` | Pin a specific `@controlplane/cli` version; normally leave unset. |
92
+
93
+ The PR-open help workflow posts a short command reference whenever the generated
94
+ wrapper exists. That is intentional for configured demo repos. Forks or clones
95
+ that copy the workflow before configuring Control Plane can remove
96
+ `.github/workflows/cpflow-review-app-help.yml` or uncomment and adapt the
97
+ wrapper-level `if:` guard shown in that file, for example
98
+ `vars.REVIEW_APP_PREFIX != '' || vars.CPLN_ORG_STAGING != ''`.
@@ -10,13 +10,8 @@ permissions:
10
10
 
11
11
  jobs:
12
12
  cleanup:
13
- # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the
14
- # first selects the reusable workflow, the second selects its shared actions.
13
+ # Cleanup targets the current inferred review-app prefix. If you changed
14
+ # naming conventions, manually delete review apps under the old prefix.
15
15
  uses: shakacode/control-plane-flow/.github/workflows/cpflow-cleanup-stale-review-apps.yml@__CPFLOW_GITHUB_ACTIONS_REF__
16
- with:
17
- control_plane_flow_ref: __CPFLOW_GITHUB_ACTIONS_REF__
18
- # `secrets: inherit` passes all caller repository secrets to the trusted
19
- # upstream workflow. The upstream workflow only reads the named secrets it
20
- # references, but GitHub does not enforce that boundary. Strict consumers can
21
- # set CPFLOW_GITHUB_ACTIONS_REF to an immutable commit SHA.
22
- secrets: inherit
16
+ secrets:
17
+ CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
@@ -31,13 +31,6 @@ jobs:
31
31
  github.event_name == 'workflow_dispatch'
32
32
  # This `if:` mirrors the upstream job guard to avoid a billable workflow_call
33
33
  # when the event does not match. Keep both conditions in sync.
34
- # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the
35
- # first selects the reusable workflow, the second selects its shared actions.
36
34
  uses: shakacode/control-plane-flow/.github/workflows/cpflow-delete-review-app.yml@__CPFLOW_GITHUB_ACTIONS_REF__
37
- with:
38
- control_plane_flow_ref: __CPFLOW_GITHUB_ACTIONS_REF__
39
- # `secrets: inherit` passes all caller repository secrets to the trusted
40
- # upstream workflow. The upstream workflow only reads the named secrets it
41
- # references, but GitHub does not enforce that boundary. Strict consumers can
42
- # set CPFLOW_GITHUB_ACTIONS_REF to an immutable commit SHA.
43
- secrets: inherit
35
+ secrets:
36
+ CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
@@ -30,13 +30,7 @@ jobs:
30
30
  github.event.issue.pull_request &&
31
31
  contains(fromJson('["+review-app-deploy","+review-app-deploy\n","+review-app-deploy\r\n"]'), github.event.comment.body) &&
32
32
  contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association))
33
- # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the
34
- # first selects the reusable workflow, the second selects its shared actions.
35
33
  uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-review-app.yml@__CPFLOW_GITHUB_ACTIONS_REF__
36
- with:
37
- control_plane_flow_ref: __CPFLOW_GITHUB_ACTIONS_REF__
38
- # `secrets: inherit` passes all caller repository secrets to the trusted
39
- # upstream workflow. The upstream workflow only reads the named secrets it
40
- # references, but GitHub does not enforce that boundary. Strict consumers can
41
- # set CPFLOW_GITHUB_ACTIONS_REF to an immutable commit SHA.
42
- secrets: inherit
34
+ secrets:
35
+ CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
36
+ DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }}
@@ -16,14 +16,9 @@ permissions:
16
16
 
17
17
  jobs:
18
18
  deploy-staging:
19
- # Keep the @ref in `uses:` and `control_plane_flow_ref` below in sync: the
20
- # first selects the reusable workflow, the second selects its shared actions.
21
19
  uses: shakacode/control-plane-flow/.github/workflows/cpflow-deploy-staging.yml@__CPFLOW_GITHUB_ACTIONS_REF__
22
20
  with:
23
- control_plane_flow_ref: __CPFLOW_GITHUB_ACTIONS_REF__
24
21
  staging_app_branch_default: "__STAGING_BRANCH_DEFAULT__"
25
- # `secrets: inherit` passes all caller repository secrets to the trusted
26
- # upstream workflow. The upstream workflow only reads the named secrets it
27
- # references, but GitHub does not enforce that boundary. Strict consumers can
28
- # set CPFLOW_GITHUB_ACTIONS_REF to an immutable commit SHA.
29
- secrets: inherit
22
+ secrets:
23
+ CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
24
+ DOCKER_BUILD_SSH_KEY: ${{ secrets.DOCKER_BUILD_SSH_KEY }}