cpflow 5.3.0 → 6.0.0
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.
- checksums.yaml +4 -4
- data/.agents/agent-workflow.yml +16 -25
- data/.agents/bin/README.md +19 -9
- data/.agents/bin/docs +1 -1
- data/.agents/bin/lint +1 -1
- data/.agents/bin/setup +1 -1
- data/.agents/bin/test +1 -1
- data/.agents/bin/validate +2 -1
- data/.agents/legacy-workflow-policy.yml +11 -0
- data/.agents/shaka.md +20 -0
- data/.agents/trusted-actions.yml +12 -0
- data/.coderabbit.yaml +14 -0
- data/.github/actions/cpflow-build-docker-image/action.yml +153 -22
- data/.github/actions/cpflow-delete-control-plane-app/action.yml +2 -1
- data/.github/actions/cpflow-delete-control-plane-app/delete-app.sh +3 -0
- data/.github/actions/cpflow-preview-promotion/action.yml +82 -0
- data/.github/actions/cpflow-preview-promotion/preview-promotion.sh +90 -0
- data/.github/actions/cpflow-setup-environment/action.yml +8 -6
- data/.github/dependabot.yml +10 -0
- data/.github/workflows/check_cpln_links.yml +6 -1
- data/.github/workflows/claude-code-review.yml +5 -2
- data/.github/workflows/claude.yml +6 -4
- data/.github/workflows/coderabbit-lifecycle-review.yml +29 -0
- data/.github/workflows/command_docs.yml +7 -2
- data/.github/workflows/cpflow-cleanup-stale-review-apps.yml +12 -5
- data/.github/workflows/cpflow-delete-review-app.yml +18 -11
- data/.github/workflows/cpflow-deploy-review-app.yml +38 -17
- data/.github/workflows/cpflow-deploy-staging.yml +28 -11
- data/.github/workflows/cpflow-help-command.yml +3 -3
- data/.github/workflows/cpflow-promote-staging-to-production.yml +135 -16
- data/.github/workflows/cpflow-review-app-help.yml +1 -1
- data/.github/workflows/rspec-shared.yml +34 -26
- data/.github/workflows/rspec-specific.yml +10 -2
- data/.github/workflows/rspec.yml +95 -4
- data/.github/workflows/rubocop.yml +9 -2
- data/.github/workflows/trigger-docs-site.yml +2 -0
- data/.rubocop.yml +6 -1
- data/AGENTS.md +18 -3
- data/CHANGELOG.md +39 -1
- data/CONTRIBUTING.md +39 -2
- data/Gemfile.lock +1 -1
- data/README.md +10 -2
- data/cpflow.gemspec +3 -15
- data/docs/ai-github-flow-prompt.md +3 -3
- data/docs/ci-automation.md +284 -68
- data/docs/commands.md +38 -15
- data/docs/postgres-review-apps.md +90 -0
- data/docs/postgres.md +233 -375
- data/docs/rds-private-networking.md +12 -12
- data/docs/releasing.md +15 -4
- data/docs/secrets-and-env-values.md +42 -0
- data/docs/sidebars.ts +1 -0
- data/examples/controlplane.yml +5 -0
- data/lib/command/ai_github_flow_prompt.rb +2 -2
- data/lib/command/apply_template.rb +36 -4
- data/lib/command/base.rb +56 -3
- data/lib/command/cleanup_stale_apps.rb +28 -4
- data/lib/command/copy_image_from_upstream.rb +3 -0
- data/lib/command/delete.rb +174 -4
- data/lib/command/deploy_image.rb +44 -1
- data/lib/command/generate.rb +247 -3
- data/lib/command/generate_github_actions.rb +43 -13
- data/lib/command/run.rb +224 -34
- data/lib/command/setup_app.rb +92 -10
- data/lib/command/update_github_actions.rb +82 -43
- data/lib/core/config.rb +73 -0
- data/lib/core/controlplane.rb +167 -80
- data/lib/core/controlplane_api.rb +28 -2
- data/lib/core/controlplane_api_direct.rb +17 -0
- data/lib/core/disposable_postgres_helpers.rb +167 -0
- data/lib/core/doctor_service.rb +7 -0
- data/lib/core/github_flow_readiness/checks.rb +2 -1
- data/lib/core/github_flow_readiness_service.rb +4 -0
- data/lib/core/repo_introspection.rb +112 -2
- data/lib/core/shell.rb +35 -10
- data/lib/core/timed_command.rb +111 -0
- data/lib/cpflow/version.rb +1 -1
- data/lib/cpflow.rb +1 -1
- data/lib/generator_templates/.dockerignore +16 -0
- data/lib/generator_templates/Dockerfile +9 -5
- data/lib/generator_templates/entrypoint.sh +2 -0
- data/lib/generator_templates/templates/postgres.yml +9 -0
- data/lib/generator_templates_sqlite/controlplane.yml +2 -1
- data/lib/generator_templates_sqlite/release_script.sh +1 -1
- data/lib/generator_templates_sqlite/templates/rails.yml +2 -1
- data/lib/github_flow_templates/.github/cpflow-help.md +39 -12
- data/lib/github_flow_templates/.github/workflows/cpflow-deploy-review-app.yml +1 -0
- data/lib/github_flow_templates/.github/workflows/cpflow-deploy-staging.yml +1 -0
- data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +135 -16
- data/lib/github_flow_templates/bin/pin-cpflow-github-ref +37 -13
- data/lib/github_flow_templates/bin/test-cpflow-github-flow +125 -46
- data/lib/patches/hash.rb +2 -2
- data/rakelib/create_release.rake +14 -14
- data/script/check_shell_scripts +66 -0
- metadata +18 -17
data/docs/ci-automation.md
CHANGED
|
@@ -23,7 +23,7 @@ End-to-end rollout in one view:
|
|
|
23
23
|
|
|
24
24
|
1. `cpflow github-flow-readiness` — exits non-zero if the repo is not ready to deploy.
|
|
25
25
|
2. `cpflow generate` — creates `.controlplane/` if missing.
|
|
26
|
-
3. `cpflow generate-github-actions` — adds `cpflow-*` workflow wrappers. Review-app, staging, cleanup, and helper workflows call upstream reusable workflows; production promotion is a normal caller-repo job so it can own the protected production Environment.
|
|
26
|
+
3. `cpflow generate-github-actions` — adds `cpflow-*` workflow wrappers and `.github/actions/cpflow-*` composite actions. Review-app, staging, cleanup, and helper workflows call upstream reusable workflows; production promotion is a normal caller-repo job so it can own the protected production Environment.
|
|
27
27
|
4. Configure the [GitHub Actions secrets and variables](#github-actions-secrets-and-variables) the workflows expect.
|
|
28
28
|
5. Push the branch, then comment `+review-app-deploy` on a PR to spin up a review environment.
|
|
29
29
|
|
|
@@ -69,6 +69,7 @@ The second command writes namespaced files so they can coexist with an app's exi
|
|
|
69
69
|
- `.github/workflows/cpflow-deploy-staging.yml`
|
|
70
70
|
- `.github/workflows/cpflow-promote-staging-to-production.yml`
|
|
71
71
|
- `.github/workflows/cpflow-cleanup-stale-review-apps.yml`
|
|
72
|
+
- `.github/actions/cpflow-*` (the local composite actions used by those workflows)
|
|
72
73
|
- `bin/pin-cpflow-github-ref`
|
|
73
74
|
- `bin/test-cpflow-github-flow`
|
|
74
75
|
|
|
@@ -76,7 +77,7 @@ The second command writes namespaced files so they can coexist with an app's exi
|
|
|
76
77
|
Docker base Ruby version from `.ruby-version`, `.tool-versions`, or the app's
|
|
77
78
|
`Gemfile`, preserves repo-defined frontend precompile hooks such as Shakapacker
|
|
78
79
|
`precompile_hook` commands or React on Rails auto bundle generation, and
|
|
79
|
-
switches to persistent SQLite
|
|
80
|
+
switches to persistent SQLite data and Active Storage templates when
|
|
80
81
|
`config/database.yml` shows SQLite in production.
|
|
81
82
|
|
|
82
83
|
`cpflow github-flow-readiness` checks public RubyGems and npm registry metadata
|
|
@@ -214,7 +215,8 @@ For production promotion, also configure:
|
|
|
214
215
|
- a GitHub Environment named `production`
|
|
215
216
|
- required reviewers on that environment, limited to the people or team allowed to promote production
|
|
216
217
|
- "Prevent self-review" on that environment, so the person who starts the promotion cannot approve it
|
|
217
|
-
-
|
|
218
|
+
- disable administrator bypass if your organization requires two-person control
|
|
219
|
+
- restrict deployment branches/tags to your protected release branch
|
|
218
220
|
- `CPLN_TOKEN_PRODUCTION` as an environment secret on `production`, not as a repository or organization secret
|
|
219
221
|
- `CPLN_ORG_PRODUCTION` as a production environment variable, for example `company-production`
|
|
220
222
|
- `PRODUCTION_APP_NAME` as a production environment variable, for example `my-app-production`
|
|
@@ -228,14 +230,75 @@ because they could change the target org name after normalization.
|
|
|
228
230
|
|
|
229
231
|
Production promotion copies the exact image currently deployed on the selected
|
|
230
232
|
staging workload. If that staging image is digest-pinned, the digest is used for
|
|
231
|
-
the source copy while the production tag is derived from the tag portion.
|
|
232
|
-
|
|
233
|
-
|
|
233
|
+
the source copy while the production tag is derived from the tag portion. Staging tags must
|
|
234
|
+
include a full lowercase 40-character Git commit SHA suffix (`_<commit>`),
|
|
235
|
+
which is preserved in the new production tag. A staging image without that
|
|
236
|
+
provenance stops promotion before approval. The copy step uses
|
|
234
237
|
`docker buildx imagetools create --prefer-index=false --tag` with isolated
|
|
235
238
|
Docker credentials, which preserves multi-architecture manifests, preserves
|
|
236
239
|
single-platform manifest format when supported, and avoids pulling image layers
|
|
237
240
|
onto the GitHub Actions runner.
|
|
238
241
|
|
|
242
|
+
When you start production promotion, the **preview-promotion** job finishes
|
|
243
|
+
before GitHub requests approval for the protected production Environment. Open
|
|
244
|
+
the run summary’s **Commits ready for production** section before approving. It
|
|
245
|
+
compares staging’s deployed primary-workload image with the last recorded
|
|
246
|
+
successful `production-*` GitHub release. It links the full comparison, lists up
|
|
247
|
+
to 100 commit subjects, and flags diverged history or staging behind production.
|
|
248
|
+
Matching SHAs report no commits to promote. Release discovery checks the latest
|
|
249
|
+
100 releases; missing production history or GitHub API failures produce an
|
|
250
|
+
explicit availability notice. The preview uses the existing staging token and
|
|
251
|
+
GitHub `contents: read` permission; it has no production token.
|
|
252
|
+
|
|
253
|
+
The preview selects the primary workload from the staging app’s
|
|
254
|
+
`app_workloads` configuration. `PRODUCTION_APP_NAME` and
|
|
255
|
+
`CPLN_ORG_PRODUCTION` can remain production Environment variables. When staging
|
|
256
|
+
and production have different primary-workload defaults, set `PRIMARY_WORKLOAD`
|
|
257
|
+
as a repository variable shared by both jobs; the protected job rejects a
|
|
258
|
+
workload-selection mismatch. A Control Plane read failure or invalid staging
|
|
259
|
+
SHA fails the preview and prevents approval from starting.
|
|
260
|
+
|
|
261
|
+
After approval, the protected job checks that staging still uses the captured
|
|
262
|
+
image and, when a release baseline was available, that live production matches
|
|
263
|
+
that release’s commit. A mismatch stops before copying or deploying an image;
|
|
264
|
+
rerun after staging changes, or reconcile the production release record after an
|
|
265
|
+
out-of-band production deployment. If production history was unavailable, the
|
|
266
|
+
summary does not claim to know the production-to-staging diff.
|
|
267
|
+
|
|
268
|
+
Existing repositories adopt this with
|
|
269
|
+
`cpflow update-github-actions --workflows cpflow-promote-staging-to-production.yml`.
|
|
270
|
+
Review and reapply downstream workflow customizations before committing the
|
|
271
|
+
replacement and the refreshed local composite actions. The bare update command
|
|
272
|
+
preserves existing top-level workflows.
|
|
273
|
+
|
|
274
|
+
### Recover a missing or stale production release record
|
|
275
|
+
|
|
276
|
+
A successful production deployment and its GitHub release are separate jobs.
|
|
277
|
+
If deployment succeeded but **create-github-release** failed, open that original
|
|
278
|
+
Actions run and choose **Re-run failed jobs**. This retries release publication
|
|
279
|
+
at the original promoted SHA; do not rerun the whole promotion merely to repair
|
|
280
|
+
the record. Confirm the resulting `production-*` release targets the live
|
|
281
|
+
production image’s SHA, then start a fresh promotion preview.
|
|
282
|
+
|
|
283
|
+
After an out-of-band production deploy or rollback, an app maintainer should
|
|
284
|
+
first verify the healthy live primary-workload image and its full commit suffix
|
|
285
|
+
with `cpln workload get <primary-workload> --gvc <production-app> --org
|
|
286
|
+
<production-org> -o json`. Use that SHA to publish a fresh release record; do not
|
|
287
|
+
move an existing release tag. For example, after replacing `<verified-live-sha>`:
|
|
288
|
+
|
|
289
|
+
```sh
|
|
290
|
+
gh release create "production-$(date -u +%Y-%m-%d-%H%M%S)-0" \
|
|
291
|
+
--target <verified-live-sha> \
|
|
292
|
+
--title "Production state reconciliation" \
|
|
293
|
+
--notes "Records the verified live production commit after a manual deployment or rollback."
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
Run this in the app repository with the maintainer’s normal GitHub credentials.
|
|
297
|
+
Verify the new tag’s commit and the live image agree, then rerun promotion to
|
|
298
|
+
produce a fresh preview. If the live image has no commit suffix, establish its
|
|
299
|
+
provenance and use a traceable image before recording a baseline; guessing a
|
|
300
|
+
branch head would make the comparison misleading.
|
|
301
|
+
|
|
239
302
|
Before copying the image, production promotion compares the environment variable
|
|
240
303
|
names exposed by staging and production at both the GVC level and each configured
|
|
241
304
|
app workload's container level. Variables present in staging are treated as
|
|
@@ -423,9 +486,11 @@ Recommended org layout:
|
|
|
423
486
|
review/staging CI operations; use a dedicated review-app org only if you also customize the generated
|
|
424
487
|
workflow/configuration to target that org separately
|
|
425
488
|
|
|
426
|
-
Optional repository
|
|
489
|
+
Optional repository secrets for Docker builds:
|
|
427
490
|
|
|
428
491
|
- `DOCKER_BUILD_SSH_KEY`: private SSH key used when the Dockerfile needs `RUN --mount=type=ssh` to fetch private GitHub dependencies during image build
|
|
492
|
+
- `DOCKER_BUILD_SECRETS`: staging build credentials as newline-delimited `id=value` entries supplied through BuildKit secret mounts. Values must be single-line; spaces and additional `=` characters are preserved. IDs may contain letters, digits, underscores, and hyphens, and must start with a letter or digit. Empty values and duplicate IDs are rejected. Leave this secret unset when the build does not need credentials.
|
|
493
|
+
- `REVIEW_APP_DOCKER_BUILD_SECRETS`: separate credentials for review-app builds, using the same format. The generated review-app caller uses only this secret; it does not fall back to `DOCKER_BUILD_SECRETS`. Leave it unset to supply no BuildKit secrets to review apps.
|
|
429
494
|
|
|
430
495
|
Optional repository variables for private dependency builds:
|
|
431
496
|
|
|
@@ -473,7 +538,12 @@ The generated flow uses these defaults:
|
|
|
473
538
|
makes the newest intent fail closed instead of falling back to an older operation. This permission gate applies to every
|
|
474
539
|
`+review-app-deploy` comment, whether or not a review app already exists. Later pushes to a base-repository branch PR
|
|
475
540
|
redeploy automatically without another approval because the auto-push path (`pull_request` event) does not use the
|
|
476
|
-
comment permission gate
|
|
541
|
+
comment permission gate. The reusable deploy workflow loads generated local actions through `actions/checkout`'s
|
|
542
|
+
default with no `ref:` override, which resolves to the commit GitHub recorded for the triggering event: the
|
|
543
|
+
pull-request merge revision for automatic same-repository deploys, the selected ref for manual dispatch, or the
|
|
544
|
+
default-branch revision for comment triggers. This keeps the wrapper and generated actions synchronized during an
|
|
545
|
+
upgrade or first-installation PR. Preserve the same-repository caller guard because pull-request workflow and action
|
|
546
|
+
code run with staging/review credentials;
|
|
477
547
|
- fork pull requests cannot deploy via the generated `pull_request` path because the caller workflow's job-level `if:`
|
|
478
548
|
condition explicitly skips fork-originated runs. For `issue_comment` events, the caller `if:` restricts invocation to
|
|
479
549
|
the exact command shape; the reusable workflow resolves repository permission before its deploy job runs, then its
|
|
@@ -510,13 +580,16 @@ generated staging/review org and make that token disposable and unable to access
|
|
|
510
580
|
The PR-close teardown workflow runs trusted base-branch workflow code with repository secret access so it can delete fork
|
|
511
581
|
PR review apps. The generated `cpflow-delete-control-plane-app` composite action script refuses to call `cpflow delete`
|
|
512
582
|
on any app whose name does not match the review-app prefix. This shell-level guard is effective because the generated
|
|
513
|
-
delete workflow
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
583
|
+
delete workflow loads generated actions through `actions/checkout`'s default with no `ref:` override, which resolves to
|
|
584
|
+
the commit GitHub recorded for the triggering event (`GITHUB_SHA`, the base-branch tip under `pull_request_target`). Its
|
|
585
|
+
separate app checkout has no `ref:` override either, so `pull_request_target` teardown uses base-branch code for both
|
|
586
|
+
paths rather than PR or fork code. If you customize this
|
|
587
|
+
workflow, never check out PR or fork code in the same job as the delete step; doing so could let a PR replace the guard
|
|
588
|
+
script itself and would also make `hooks.pre_deletion` come from the PR's `controlplane.yml`. This is still not a token
|
|
589
|
+
policy, so use a scoped staging service account limited to review/staging operations. A configured
|
|
590
|
+
`hooks.pre_deletion` command still runs through the latest PR-built image on all delete paths: PR-close teardown,
|
|
591
|
+
`+review-app-delete` comments, manual dispatch, and scheduled cleanup. Review-app credentials must remain disposable
|
|
592
|
+
even during deletion.
|
|
520
593
|
|
|
521
594
|
If you customize the generated `pull_request_target` workflow, never pass `github.event.pull_request.head.sha` or another
|
|
522
595
|
fork-controlled ref to `actions/checkout`, `git fetch`, `git merge`, `git cherry-pick`, or any other step that fetches,
|
|
@@ -560,6 +633,30 @@ The generated `cpflow-build-docker-image` action supports this without hardcodin
|
|
|
560
633
|
- set `DOCKER_BUILD_SSH_KEY` if the Docker build needs SSH access to GitHub
|
|
561
634
|
- optionally set `DOCKER_BUILD_SSH_KNOWN_HOSTS` when the SSH build host is not GitHub.com or you need custom host entries
|
|
562
635
|
- set `DOCKER_BUILD_EXTRA_ARGS` when you need extra `docker build` flags
|
|
636
|
+
- set `DOCKER_BUILD_SECRETS` for staging builds that need credentials such as a private package token or a source-map upload token
|
|
637
|
+
- set `REVIEW_APP_DOCKER_BUILD_SECRETS` separately if review-app builds need credentials
|
|
638
|
+
|
|
639
|
+
For example, store the following as the **value** of the `DOCKER_BUILD_SECRETS` GitHub Actions secret:
|
|
640
|
+
|
|
641
|
+
```text
|
|
642
|
+
sentry_auth_token=<source-map upload token>
|
|
643
|
+
npm_token=<private package token>
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
The generated staging caller forwards `DOCKER_BUILD_SECRETS`. The review-app caller forwards `REVIEW_APP_DOCKER_BUILD_SECRETS`, the only build-secret parameter the reusable review-app workflow accepts, so review builds never receive the staging secret. The build action writes each value to a private temporary file, passes only its path through `--secret=id=...,src=...`, and removes the files on success or failure. Values are not passed as build arguments or persisted through `GITHUB_ENV`.
|
|
647
|
+
|
|
648
|
+
A Dockerfile can consume a token only for the command that needs it:
|
|
649
|
+
|
|
650
|
+
```dockerfile
|
|
651
|
+
# syntax=docker/dockerfile:1
|
|
652
|
+
ARG SENTRY_UPLOAD=false
|
|
653
|
+
RUN --mount=type=secret,id=sentry_auth_token,env=SENTRY_AUTH_TOKEN \
|
|
654
|
+
SENTRY_UPLOAD=${SENTRY_UPLOAD} npm run build
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
If the application's build requires an explicit upload opt-in, pass `docker_build_extra_args: --build-arg=SENTRY_UPLOAD=true` in the caller workflow's `with` block, or add that token to `DOCKER_BUILD_EXTRA_ARGS`. The optional workflow input is appended after the repository variable, retaining existing build arguments. Token availability alone need not enable uploads. BuildKit secret values do not invalidate build caches; force the relevant build step to run again when rotating a credential requires repeating an upload.
|
|
658
|
+
|
|
659
|
+
Every secret supplied to a review build is accessible to the PR-controlled Dockerfile, which can read and transmit it. Automatic rebuilds of an existing review app also receive these secrets; approval to create the app does not approve later commits. Configure only credentials whose access can be entrusted to anyone able to change the deployed branch, scoped to the minimum review-build permissions. Separate secret names reduce accidental sharing but do not prevent the Dockerfile from accessing supplied credentials. If a credential cannot be entrusted to branch authors, omit it from review builds or use a customized approval gate that approves each deployed commit. Apply the review-app security guidance above.
|
|
563
660
|
|
|
564
661
|
For example, a repo that installs private dependencies from GitHub during Docker build can set:
|
|
565
662
|
|
|
@@ -698,11 +795,13 @@ wrapper-level `if:` guard shown in that file, for example
|
|
|
698
795
|
## Upstream Workflows And Actions
|
|
699
796
|
|
|
700
797
|
Most generated workflows are intentionally small wrappers. The deployment
|
|
701
|
-
logic
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
ref for
|
|
798
|
+
logic and comment formatting live in upstream reusable workflows. The canonical
|
|
799
|
+
composite actions live in this repository and the gem copies them into each
|
|
800
|
+
downstream repository at `.github/actions/cpflow-*`; the reusable workflows use
|
|
801
|
+
those regular local action paths. They separately check out the matching
|
|
802
|
+
upstream ref at `.cpflow` for the `cpflow` runtime source. Production promotion
|
|
803
|
+
is expanded into the caller repository so it can own `environment: production`,
|
|
804
|
+
but follows the same local-action and `.cpflow` source split.
|
|
706
805
|
|
|
707
806
|
- `cpflow-setup-environment`: installs Ruby, the Control Plane CLI, and `cpflow`, then logs into the target org. By default it builds `cpflow` from the checked-out upstream `control-plane-flow` ref; set the `CPFLOW_VERSION` repository variable only when you want to force a published RubyGems release.
|
|
708
807
|
- `cpflow-build-docker-image`: builds and pushes the app image with the desired commit SHA
|
|
@@ -727,37 +826,51 @@ reusable-workflow wrappers should not pass `control_plane_flow_ref`; if you see
|
|
|
727
826
|
that input outside the production promotion setup step, regenerate with a newer
|
|
728
827
|
`cpflow`.
|
|
729
828
|
|
|
730
|
-
There are two
|
|
829
|
+
There are two coordinated inputs, and they protect different things:
|
|
731
830
|
|
|
732
|
-
- The GitHub ref locks the reusable workflow and
|
|
733
|
-
GitHub runs.
|
|
734
|
-
- The
|
|
735
|
-
|
|
736
|
-
|
|
831
|
+
- The GitHub ref locks the reusable workflow and the `.cpflow` runtime source
|
|
832
|
+
checkout that GitHub runs.
|
|
833
|
+
- The installed Ruby gem supplies the generated local composite actions and
|
|
834
|
+
wrappers. `CPFLOW_VERSION`, when configured, separately locks the published
|
|
835
|
+
`cpflow` runtime installed by the setup action.
|
|
737
836
|
|
|
738
837
|
That means a downstream app cannot rely on the gem alone for GitHub Actions
|
|
739
838
|
behavior. The safe stable path is still gem-driven for generation, but
|
|
740
|
-
developers must commit generated wrappers
|
|
741
|
-
release tag:
|
|
839
|
+
developers must commit the generated wrappers and local actions from the gem
|
|
840
|
+
that matches the upstream release tag:
|
|
742
841
|
|
|
743
842
|
1. Publish a `cpflow` gem.
|
|
744
843
|
2. Install or bundle that released gem in the downstream project.
|
|
745
844
|
3. Run `cpflow generate-github-actions`.
|
|
746
|
-
4. Commit the generated wrappers
|
|
747
|
-
such as `v5.0.0`.
|
|
845
|
+
4. Commit the generated wrappers and `.github/actions/cpflow-*` files; the
|
|
846
|
+
wrappers should point to the matching upstream release tag such as `v5.0.0`.
|
|
748
847
|
|
|
749
848
|
That release tag should point to the same source that produced the RubyGems
|
|
750
849
|
release. Downstream production automation should use release tags, not `main` or
|
|
751
850
|
feature-branch refs.
|
|
752
851
|
|
|
852
|
+
Generated cross-repository reusable-workflow calls deliberately use an exact
|
|
853
|
+
release tag such as `v5.0.0`. This is the intentional downstream exception to
|
|
854
|
+
the repository's full-SHA external-action policy: it keeps the installed gem,
|
|
855
|
+
generated wrappers, generated local actions, and upstream workflow on one
|
|
856
|
+
reviewed release update contract. It does not extend to external actions inside
|
|
857
|
+
those workflows, which remain pinned to full commit SHAs with readable release
|
|
858
|
+
comments. Unreleased downstream validation temporarily replaces the exact tag
|
|
859
|
+
with the upstream PR's full commit SHA as described below; moving version
|
|
860
|
+
aliases and branches remain forbidden.
|
|
861
|
+
|
|
753
862
|
## Updating Generated GitHub Actions After Gem Updates
|
|
754
863
|
|
|
755
|
-
Whenever a downstream repo updates the `cpflow` gem, update
|
|
756
|
-
GitHub Actions
|
|
757
|
-
new reusable workflow YAML by itself; GitHub
|
|
758
|
-
`.github/
|
|
864
|
+
Whenever a downstream repo updates the `cpflow` gem, update all checked-in
|
|
865
|
+
GitHub Actions files in the same PR. The gem version does not make GitHub load
|
|
866
|
+
new reusable workflow YAML or local action files by itself; GitHub uses the
|
|
867
|
+
workflow ref and `.github/actions/cpflow-*` files committed in the repository.
|
|
759
868
|
|
|
760
|
-
|
|
869
|
+
For a new repository that does not have generated wrappers yet, run
|
|
870
|
+
`cpflow generate-github-actions` first. Use the update command below for later
|
|
871
|
+
gem upgrades.
|
|
872
|
+
|
|
873
|
+
Use the installed gem to refresh the generated workflows and actions:
|
|
761
874
|
|
|
762
875
|
```sh
|
|
763
876
|
cpflow update-github-actions
|
|
@@ -771,11 +884,88 @@ bundle exec cpflow update-github-actions
|
|
|
771
884
|
bin/test-cpflow-github-flow bundle exec cpflow
|
|
772
885
|
```
|
|
773
886
|
|
|
774
|
-
`cpflow update-github-actions`
|
|
775
|
-
|
|
776
|
-
and
|
|
777
|
-
|
|
778
|
-
|
|
887
|
+
`cpflow update-github-actions` refreshes local composite actions and helpers.
|
|
888
|
+
It preserves every top-level workflow by default, including refs, triggers,
|
|
889
|
+
permissions, and downstream deployment ownership. It does not create staging
|
|
890
|
+
or production deployment workflows when another CI system owns those deploys.
|
|
891
|
+
Select the wrappers you intend to replace explicitly; see
|
|
892
|
+
`cpflow update-github-actions --help` for the current interface:
|
|
893
|
+
|
|
894
|
+
```sh
|
|
895
|
+
bundle exec cpflow update-github-actions --workflows \
|
|
896
|
+
cpflow-deploy-review-app.yml cpflow-delete-review-app.yml \
|
|
897
|
+
cpflow-cleanup-stale-review-apps.yml cpflow-help-command.yml \
|
|
898
|
+
cpflow-review-app-help.yml
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
Each selected wrapper is replaced in full with the installed gem's template
|
|
902
|
+
and release ref. Review the diff and reapply downstream changes before
|
|
903
|
+
committing. Unselected wrappers keep their refs: preservation is not evidence
|
|
904
|
+
that old wrappers are compatible with new local actions. Migrate wrapper
|
|
905
|
+
contracts and source pins together, then run the validator in the same PR.
|
|
906
|
+
For a selected staging wrapper, a single existing push branch or the default
|
|
907
|
+
main/master pair can be inferred. Missing, malformed, or ambiguous branch
|
|
908
|
+
configuration requires an explicit `--staging-branch BRANCH`; that option also
|
|
909
|
+
requires selecting `cpflow-deploy-staging.yml`.
|
|
910
|
+
|
|
911
|
+
### Preserving downstream validation
|
|
912
|
+
|
|
913
|
+
The generated `bin/test-cpflow-github-flow` runs baseline checks followed by
|
|
914
|
+
an optional executable `bin/test-cpflow-github-flow-custom`. The extension runs
|
|
915
|
+
from the repository root with the cpflow command argv forwarded unchanged
|
|
916
|
+
(for example, `bundle`, `exec`, `cpflow`), and its nonzero exit fails validation.
|
|
917
|
+
The generator never creates or overwrites the custom extension. Baseline
|
|
918
|
+
production checks apply when the generated production workflow exists;
|
|
919
|
+
downstream-owned production workflows belong in the custom checks.
|
|
920
|
+
|
|
921
|
+
An existing validator that differs from the installed template blocks the
|
|
922
|
+
entire update before any writes. This includes unmodified validators from
|
|
923
|
+
older releases: the updater cannot safely distinguish them from custom code.
|
|
924
|
+
For a legacy installation such as HiChee's 5.3-to-6.0 RC migration:
|
|
925
|
+
|
|
926
|
+
1. Save the old validator in version control and compare it with its original
|
|
927
|
+
release. Extract the downstream checks into `bin/test-cpflow-github-flow-custom`
|
|
928
|
+
and make it executable. If it is entirely downstream-specific, rename it
|
|
929
|
+
directly. Remove any call back to the generated validator to avoid recursion.
|
|
930
|
+
2. Remove or rename the old `bin/test-cpflow-github-flow` after preserving those
|
|
931
|
+
checks, then run the update command with the selected wrappers. No override
|
|
932
|
+
flag bypasses this check. Do not use `generate-github-actions --force` as a
|
|
933
|
+
migration shortcut: it intentionally replaces the full generated flow.
|
|
934
|
+
3. Run the new validator. Confirm that the custom checks execute and that a
|
|
935
|
+
failing custom check still fails the command. Repeating the updater with the
|
|
936
|
+
same selected workflows should produce no further diff.
|
|
937
|
+
|
|
938
|
+
### Migrating trusted action allowlists
|
|
939
|
+
|
|
940
|
+
Repositories using a closed `trusted_actions` policy must review these exact
|
|
941
|
+
repositories used by the complete generated output:
|
|
942
|
+
|
|
943
|
+
```yaml
|
|
944
|
+
trusted_actions:
|
|
945
|
+
- actions/checkout
|
|
946
|
+
- docker/setup-buildx-action
|
|
947
|
+
- ruby/setup-ruby
|
|
948
|
+
- shakacode/control-plane-flow
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
Merge the needed entries into your existing policy rather than replacing it.
|
|
952
|
+
`ruby/setup-ruby` is used by the generated local setup action, including in
|
|
953
|
+
review-app-only installations. `actions/checkout` and `docker/setup-buildx-action`
|
|
954
|
+
are used directly by the generated production wrapper;
|
|
955
|
+
`shakacode/control-plane-flow` supplies the reusable wrappers. Other existing
|
|
956
|
+
downstream workflows may need additional entries. Upstream reusable workflows
|
|
957
|
+
also use `actions/github-script`; reviewing that upstream implementation remains
|
|
958
|
+
part of trusting `shakacode/control-plane-flow`.
|
|
959
|
+
|
|
960
|
+
Allowlisting does not waive immutable pin requirements. For a policy requiring
|
|
961
|
+
full SHAs, resolve and independently verify the chosen upstream release tag or
|
|
962
|
+
test commit, then run `bin/pin-cpflow-github-ref --version <reviewed-version-tag>
|
|
963
|
+
<full-sha>`. The helper writes that version/tag beside all cpflow SHA pins and
|
|
964
|
+
updates obsolete same-line comments. The label is supplied by the caller; the
|
|
965
|
+
helper does not contact GitHub or certify tag-to-SHA identity. For an unreleased
|
|
966
|
+
test commit, use its source version tag as context and record the exact commit
|
|
967
|
+
in the PR. Review the generated diff and run your repository's action security
|
|
968
|
+
scanner after updating its allowlist.
|
|
779
969
|
|
|
780
970
|
When keeping `cpflow` in an app Gemfile, leave a comment next to the gem entry
|
|
781
971
|
so future dependency bumps include the wrapper update:
|
|
@@ -809,7 +999,8 @@ action commit, so a moving branch named like `v5.0.0` cannot be used with
|
|
|
809
999
|
runners that cannot reach GitHub should leave `CPFLOW_VERSION` unset and build
|
|
810
1000
|
`cpflow` from the checked-out ref instead. When testing an unreleased upstream
|
|
811
1001
|
commit SHA, leave `CPFLOW_VERSION` unset so the workflow builds `cpflow` from the
|
|
812
|
-
same source that supplies the reusable workflow
|
|
1002
|
+
same source that supplies the reusable workflow. Refresh the generated local
|
|
1003
|
+
actions from that checkout before testing as described below.
|
|
813
1004
|
|
|
814
1005
|
## Testing Unreleased Upstream Changes Downstream
|
|
815
1006
|
|
|
@@ -817,24 +1008,26 @@ You can test a `control-plane-flow` PR in a downstream app before merging or
|
|
|
817
1008
|
releasing it. Use an immutable commit SHA from the upstream PR branch:
|
|
818
1009
|
|
|
819
1010
|
1. Push the upstream PR branch and copy its full 40-character head SHA.
|
|
820
|
-
2. In a downstream test branch,
|
|
1011
|
+
2. In a downstream test branch, regenerate from the upstream checkout and then
|
|
1012
|
+
pin the wrappers to its exact SHA:
|
|
821
1013
|
|
|
822
1014
|
```sh
|
|
823
|
-
bin/
|
|
1015
|
+
ruby /path/to/control-plane-flow/bin/cpflow update-github-actions
|
|
1016
|
+
bin/pin-cpflow-github-ref --version <reviewed-version-tag> <upstream-pr-sha>
|
|
824
1017
|
```
|
|
825
1018
|
|
|
826
|
-
The
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
`--
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
`CPFLOW_VERSION` is set while the wrapper is pinned to a
|
|
836
|
-
action fails before deployment because the gem and
|
|
837
|
-
proven to match.
|
|
1019
|
+
The update command copies the PR checkout's canonical composite actions into
|
|
1020
|
+
`.github/actions/cpflow-*`. The pin helper then updates every generated
|
|
1021
|
+
reusable-workflow `uses:` ref plus the production workflow's pinned
|
|
1022
|
+
`control-plane-flow` checkout and setup validation ref. It accepts release
|
|
1023
|
+
tags and full commit SHAs (with a reviewed `--version` label), rejects branch names such as `main` or
|
|
1024
|
+
`feature/foo`, and requires `--allow-moving-ref` for short-lived local
|
|
1025
|
+
experiments that should not be committed.
|
|
1026
|
+
|
|
1027
|
+
3. Keep `CPFLOW_VERSION` unset so the workflow builds `cpflow` from the pinned
|
|
1028
|
+
upstream SHA. If `CPFLOW_VERSION` is set while the wrapper is pinned to a
|
|
1029
|
+
SHA, the setup action fails before deployment because the published gem and
|
|
1030
|
+
upstream source cannot be proven to match.
|
|
838
1031
|
4. Run:
|
|
839
1032
|
|
|
840
1033
|
```sh
|
|
@@ -848,25 +1041,48 @@ releasing it. Use an immutable commit SHA from the upstream PR branch:
|
|
|
848
1041
|
bin/test-cpflow-github-flow ruby /path/to/control-plane-flow/bin/cpflow
|
|
849
1042
|
```
|
|
850
1043
|
|
|
851
|
-
5.
|
|
852
|
-
|
|
1044
|
+
5. Push the downstream test branch, then choose the remote validation path that
|
|
1045
|
+
matches the repository's installation state.
|
|
1046
|
+
|
|
1047
|
+
For an existing installation, where `cpflow-deploy-review-app.yml` is already
|
|
1048
|
+
present on the default branch, dispatch the workflow definition and generated
|
|
1049
|
+
local actions from the unmerged downstream test branch against a suitable open
|
|
1050
|
+
base-repository PR:
|
|
1051
|
+
|
|
1052
|
+
```sh
|
|
1053
|
+
gh workflow run cpflow-deploy-review-app.yml --ref <downstream-test-branch> -f pr_number=<pr-number>
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
The `--ref` selects the downstream wrapper and local-action commit under test;
|
|
1057
|
+
`pr_number` selects the application source to deploy. Do not use a
|
|
1058
|
+
`+review-app-deploy` comment for this pre-merge validation: `issue_comment`
|
|
1059
|
+
always loads the workflow definition from the default branch, so it cannot
|
|
1060
|
+
validate unmerged wrappers or local actions.
|
|
853
1061
|
|
|
854
|
-
|
|
855
|
-
|
|
1062
|
+
For a first installation, GitHub will not dispatch a workflow that does not
|
|
1063
|
+
yet exist on the default branch, even when `--ref` names the installation
|
|
1064
|
+
branch. Treat the rollout as staged: run the local contract before merging,
|
|
1065
|
+
review the generated diff, merge it, and dispatch the merged workflow
|
|
1066
|
+
immediately afterward against a suitable open base-repository PR:
|
|
1067
|
+
|
|
1068
|
+
```sh
|
|
1069
|
+
gh workflow run cpflow-deploy-review-app.yml --ref <default-branch> -f pr_number=<pr-number>
|
|
856
1070
|
```
|
|
857
1071
|
|
|
858
|
-
6. Verify the deploy logs show the expected upstream commit SHA, the
|
|
859
|
-
prints the expected `cpflow` source/version, and the review app URL
|
|
860
|
-
HTTP 200.
|
|
1072
|
+
6. Verify the dispatched deploy logs show the expected upstream commit SHA, the
|
|
1073
|
+
setup step prints the expected `cpflow` source/version, and the review app URL
|
|
1074
|
+
returns HTTP 200. After merge, `+review-app-deploy` remains the normal
|
|
1075
|
+
maintainer-facing trigger for subsequent review apps.
|
|
861
1076
|
7. After the upstream PR merges and a gem is released, regenerate the downstream
|
|
862
1077
|
wrappers from that released gem and commit the release tag. Use
|
|
863
1078
|
`bin/pin-cpflow-github-ref vX.Y.Z` only for a ref-only update when the
|
|
864
1079
|
generated templates are already current.
|
|
865
1080
|
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
upstream commit.
|
|
869
|
-
|
|
1081
|
+
For an existing installation, this tests the real reusable workflows, the
|
|
1082
|
+
unmerged regenerated local composite actions, and the source-built `cpflow` gem
|
|
1083
|
+
against one immutable upstream commit. A first installation gets the same remote
|
|
1084
|
+
proof immediately after its locally validated files reach the default branch.
|
|
1085
|
+
Both paths avoid testing against a moving upstream branch.
|
|
870
1086
|
|
|
871
1087
|
## Local Generated-Flow Checks
|
|
872
1088
|
|
|
@@ -909,7 +1125,7 @@ In practice, porting the flow into a demo app usually follows five phases.
|
|
|
909
1125
|
|
|
910
1126
|
4. Update `.controlplane/controlplane.yml` with staging, review, and production entries.
|
|
911
1127
|
5. Confirm that the generated Dockerfile picked a Ruby base image compatible with the app's declared Ruby requirement.
|
|
912
|
-
6. For SQLite-backed apps, confirm that the generated scaffold
|
|
1128
|
+
6. For SQLite-backed apps, confirm that the generated scaffold redirects every production database file into persistent `/app/data` (or keeps it under persistent `/app/storage`) without hiding image migrations under `/app/db`, mounts Active Storage at `/app/storage`, and adds a release script that runs `rails db:prepare`.
|
|
913
1129
|
|
|
914
1130
|
**Adapt for the app's runtime:**
|
|
915
1131
|
|
|
@@ -953,7 +1169,7 @@ Expand that prompt with app-specific requirements before editing files:
|
|
|
953
1169
|
- make sure the generated Dockerfile uses a Ruby base image compatible with the app's declared Ruby requirement
|
|
954
1170
|
- preserve repo-defined frontend precompile hooks, such as Shakapacker `precompile_hook` commands or React on Rails `config.auto_load_bundle = true`
|
|
955
1171
|
- keep Node available in the final image if Rails or SSR depends on ExecJS, Yarn, or `pnpm` after the main `npm install` layer
|
|
956
|
-
- if `config/database.yml` shows SQLite in production, confirm that `cpflow generate`
|
|
1172
|
+
- if `config/database.yml` shows SQLite in production, confirm that `cpflow generate` redirected every production database path into persistent `/app/data` or `/app/storage`, emitted both volumes without mounting over `/app/db`, and added a `rails db:prepare` release script; otherwise keep the default Postgres workload
|
|
957
1173
|
- inspect the production Dockerfile and package sources for private GitHub dependencies, and wire `DOCKER_BUILD_SSH_KEY` plus `DOCKER_BUILD_SSH_KNOWN_HOSTS` when the build uses `RUN --mount=type=ssh` against non-GitHub hosts
|
|
958
1174
|
- add extra `app_workloads` and template files for any runtime sidecars, workers, or renderer processes
|
|
959
1175
|
- make sure any sidecar process exposed to sibling workloads binds to `0.0.0.0` instead of container-local `localhost`
|