cpflow 5.1.0 → 5.2.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 +15 -0
- data/.agents/bin/README.md +19 -0
- data/.agents/bin/docs +5 -0
- data/.agents/bin/lint +5 -0
- data/.agents/bin/setup +5 -0
- data/.agents/bin/test +5 -0
- data/.agents/bin/validate +5 -0
- data/.agents/trusted-github-actors.yml +32 -0
- data/.agents/workflows/ai-rollout-e2e-test.md +166 -0
- data/.github/actions/cpflow-wait-for-health/action.yml +11 -4
- data/.github/workflows/claude-code-review.yml +2 -0
- data/.github/workflows/claude.yml +2 -0
- data/.github/workflows/cpflow-deploy-review-app.yml +16 -1
- data/.github/workflows/cpflow-promote-staging-to-production.yml +224 -37
- data/.github/workflows/rspec-shared.yml +15 -1
- data/.github/workflows/rspec-specific.yml +1 -0
- data/.github/workflows/rspec.yml +58 -1
- data/AGENTS.md +57 -0
- data/CHANGELOG.md +36 -1
- data/CLAUDE.md +3 -0
- data/CONTRIBUTING.md +6 -2
- data/Gemfile.lock +1 -1
- data/README.md +25 -7
- data/docs/ai-github-flow-prompt.md +18 -16
- data/docs/assets/logo/favicon.ico +0 -0
- data/docs/assets/logo/icon-1024.png +0 -0
- data/docs/assets/logo/icon-128.png +0 -0
- data/docs/assets/logo/icon-16.png +0 -0
- data/docs/assets/logo/icon-192.png +0 -0
- data/docs/assets/logo/icon-24.png +0 -0
- data/docs/assets/logo/icon-32.png +0 -0
- data/docs/assets/logo/icon-48.png +0 -0
- data/docs/assets/logo/icon-512.png +0 -0
- data/docs/assets/logo/icon-64.png +0 -0
- data/docs/assets/logo/icon-tile.svg +17 -0
- data/docs/assets/logo/mark-transparent.svg +16 -0
- data/docs/ci-automation.md +203 -15
- data/docs/commands.md +16 -1
- data/docs/grafana-opentelemetry.md +699 -0
- data/docs/secrets-and-env-values.md +29 -2
- data/docs/sidebars.ts +70 -0
- data/docs/telemetry/application-instrumentation.md +161 -0
- data/docs/telemetry/collector.md +297 -0
- data/docs/telemetry/index.md +152 -0
- data/docs/telemetry/pipelines.md +98 -0
- data/docs/telemetry/review-apps.md +55 -0
- data/docs/telemetry/troubleshooting.md +92 -0
- data/docs/terraform/example/.controlplane/controlplane.yml +0 -1
- data/docs/terraform/overview.md +11 -0
- data/docs/tips.md +458 -29
- data/examples/controlplane.yml +2 -0
- data/lib/command/ai_github_flow_prompt.rb +2 -2
- data/lib/command/base.rb +17 -2
- data/lib/command/deploy_image.rb +77 -5
- data/lib/command/maintenance_off.rb +1 -0
- data/lib/command/maintenance_on.rb +1 -0
- data/lib/command/promote_app_from_upstream.rb +1 -0
- data/lib/command/ps_wait.rb +2 -10
- data/lib/command/run.rb +25 -5
- data/lib/core/config.rb +94 -0
- data/lib/core/doctor_service.rb +44 -3
- data/lib/core/maintenance_mode.rb +93 -6
- data/lib/core/template_parser.rb +43 -9
- data/lib/cpflow/version.rb +1 -1
- data/lib/generator_templates/controlplane.yml +1 -2
- data/lib/github_flow_templates/.github/cpflow-help.md +23 -1
- data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +224 -39
- metadata +33 -2
data/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="./docs/assets/logo/icon-tile.svg" alt="Control Plane Flow (cpflow) logo" width="160" height="160" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
1
5
|
# The power of Kubernetes with the ease of Heroku!
|
|
2
6
|
|
|
3
7
|
<meta name="author" content="Justin Gordon and Sergey Tarasov" />
|
|
@@ -30,7 +34,7 @@ To bootstrap a new project, run three commands from the repo root:
|
|
|
30
34
|
|
|
31
35
|
The generated scaffold is a starting point. After generation, adapt `.controlplane/` for app-specific workloads (Sidekiq, Node renderer), wire any private-dependency Docker build settings (SSH key, optional known-host overrides), and verify that the production Docker build succeeds.
|
|
32
36
|
|
|
33
|
-
See [CI automation](./docs/ci-automation.md) for the full setup and required GitHub secrets and variables. For an AI agent rollout,
|
|
37
|
+
See [CI automation](./docs/ci-automation.md) for the full setup and required GitHub secrets and variables. For an AI agent rollout, copy the [AI rollout prompt](./docs/ai-github-flow-prompt.md). If `cpflow` is already available in the target repo, `cpflow ai-github-flow-prompt` prints the same prompt with that repo's default app prefix filled in.
|
|
34
38
|
|
|
35
39
|
For a live reference, see the [demo app](https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/.controlplane) and its [GitHub Actions flow](https://github.com/shakacode/react-webpack-rails-tutorial/tree/master/.github).
|
|
36
40
|
Here is a brief [video overview](https://www.youtube.com/watch?v=llaQoAV_6Iw).
|
|
@@ -61,11 +65,13 @@ Additionally, the documentation includes numerous examples and practical tips fo
|
|
|
61
65
|
10. [Scheduled Jobs](#scheduled-jobs)
|
|
62
66
|
11. [CLI Commands Reference](#cli-commands-reference)
|
|
63
67
|
12. [Mapping of Heroku Commands to `cpflow` and `cpln`](#mapping-of-heroku-commands-to-cpflow-and-cpln)
|
|
64
|
-
13. [
|
|
65
|
-
14. [
|
|
66
|
-
15. [Migrating
|
|
67
|
-
16. [
|
|
68
|
-
17. [
|
|
68
|
+
13. [Telemetry](https://www.shakacode.com/control-plane-flow/docs/telemetry/)
|
|
69
|
+
14. [Examples](#examples)
|
|
70
|
+
15. [Migrating Postgres Database from Heroku Infrastructure](https://www.shakacode.com/control-plane-flow/docs/postgres/)
|
|
71
|
+
16. [Migrating Redis Database from Heroku Infrastructure](https://www.shakacode.com/control-plane-flow/docs/redis/)
|
|
72
|
+
17. [Tips](https://www.shakacode.com/control-plane-flow/docs/tips/)
|
|
73
|
+
18. [Thruster HTTP/2 Proxy on Control Plane](https://www.shakacode.com/control-plane-flow/docs/thruster/)
|
|
74
|
+
19. [Grafana and OpenTelemetry on Control Plane](https://www.shakacode.com/control-plane-flow/docs/grafana-opentelemetry/)
|
|
69
75
|
|
|
70
76
|
## Key Features
|
|
71
77
|
|
|
@@ -262,9 +268,19 @@ aliases:
|
|
|
262
268
|
# On the other hand, if you have a workload for Redis, that would NOT use the application Docker image
|
|
263
269
|
# and not be listed here.
|
|
264
270
|
app_workloads:
|
|
271
|
+
- node-renderer
|
|
265
272
|
- rails
|
|
266
273
|
- sidekiq
|
|
267
274
|
|
|
275
|
+
# Optional ordered deploy groups for `cpflow deploy-image` and
|
|
276
|
+
# `cpflow promote-app-from-upstream`. Each group is deployed and waited on
|
|
277
|
+
# before the next group starts. Any app workloads omitted here deploy last
|
|
278
|
+
# as an implicit final group. Explicit `deploy-image -w/--workload` options
|
|
279
|
+
# override this ordering for one-off deploys.
|
|
280
|
+
deploy_order:
|
|
281
|
+
- [node-renderer]
|
|
282
|
+
- [rails, sidekiq]
|
|
283
|
+
|
|
268
284
|
# Additional "service type" workloads, using non-application Docker images.
|
|
269
285
|
# These are only used by the `info` and `ps:` commands in order to get all of the defined workloads.
|
|
270
286
|
additional_workloads:
|
|
@@ -322,6 +338,8 @@ apps:
|
|
|
322
338
|
post_creation: bundle exec rake db:prepare
|
|
323
339
|
|
|
324
340
|
# Used by the command `cpflow delete` to run a hook before deleting the app.
|
|
341
|
+
# For a shared database, prefer admin-side cleanup instead: `cpflow delete` runs this hook before removing the
|
|
342
|
+
# workloads, so live connections can block the drop. See docs/tips.md ("Share One Control Plane Postgres").
|
|
325
343
|
pre_deletion: bundle exec rake db:drop
|
|
326
344
|
|
|
327
345
|
my-app-production:
|
|
@@ -375,7 +393,7 @@ cpflow generate-github-actions
|
|
|
375
393
|
bin/test-cpflow-github-flow
|
|
376
394
|
```
|
|
377
395
|
|
|
378
|
-
`cpflow github-flow-readiness` exits non-zero when it finds blockers such as unpublished exact-pinned packages or a missing production Dockerfile, so use it as the gate before generation. Then review the generated `.controlplane/controlplane.yml` entries, adjust any app-specific workloads, and configure the GitHub repository variables and secrets described in [CI automation](./docs/ci-automation.md), including the optional Docker build settings for private GitHub dependencies and custom SSH known hosts. `cpflow generate-github-actions` also writes `bin/test-cpflow-github-flow` for local validation and `bin/pin-cpflow-github-ref` for temporarily pinning downstream wrappers to an upstream commit SHA during pre-release testing. `cpflow generate` already switches to persistent `db` and `storage` volumes when `config/database.yml` shows SQLite in production and preserves detected frontend precompile hooks, but you should still confirm that the generated Dockerfile picked a Ruby base image compatible with the app's declared Ruby requirement and that the emitted workload set matches the real app. If you want an AI agent to do this end to end,
|
|
396
|
+
`cpflow github-flow-readiness` exits non-zero when it finds blockers such as unpublished exact-pinned packages or a missing production Dockerfile, so use it as the gate before generation. Then review the generated `.controlplane/controlplane.yml` entries, adjust any app-specific workloads, and configure the GitHub repository variables and secrets described in [CI automation](./docs/ci-automation.md), including the optional Docker build settings for private GitHub dependencies and custom SSH known hosts. `cpflow generate-github-actions` also writes `bin/test-cpflow-github-flow` for local validation and `bin/pin-cpflow-github-ref` for temporarily pinning downstream wrappers to an upstream commit SHA during pre-release testing. `cpflow generate` already switches to persistent `db` and `storage` volumes when `config/database.yml` shows SQLite in production and preserves detected frontend precompile hooks, but you should still confirm that the generated Dockerfile picked a Ruby base image compatible with the app's declared Ruby requirement and that the emitted workload set matches the real app. If you want an AI agent to do this end to end, give it the [AI rollout prompt](./docs/ai-github-flow-prompt.md) rather than a vague "set up CI" request.
|
|
379
397
|
|
|
380
398
|
For a live example, see the [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/readme.md) repository.
|
|
381
399
|
|
|
@@ -1,32 +1,34 @@
|
|
|
1
1
|
# AI Rollout Prompt for Control Plane GitHub Flow
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
staging, and production-promotion flow to a repository.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
copy-paste version of this prompt with:
|
|
8
|
-
|
|
9
|
-
```sh
|
|
10
|
-
cpflow ai-github-flow-prompt
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
That local-only command works even before `cpln` is installed and fills in the
|
|
14
|
-
repo-name default app prefix for the current checkout. You can also run
|
|
15
|
-
`cpflow github-flow-readiness` first to check the same blocker categories the
|
|
16
|
-
prompt tells the agent to stop on.
|
|
3
|
+
Copy the recommended prompt below when you want an AI agent to add the reusable
|
|
4
|
+
`cpflow` review-app, staging, and production-promotion flow to a repository.
|
|
5
|
+
It works whether `cpflow` is already installed or the agent needs to install it
|
|
6
|
+
first.
|
|
17
7
|
|
|
18
8
|
## Recommended Prompt
|
|
19
9
|
|
|
20
10
|
```text
|
|
21
|
-
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.
|
|
11
|
+
Set up Control Plane GitHub Flow for this repo. First make sure the `cpflow` CLI is available: use the repo's existing `bundle exec cpflow` if present, otherwise install the published `cpflow` Ruby gem with `gem install cpflow`; if neither is possible, stop and report that blocker. Use the same `cpflow` invocation for the rest of the rollout. 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.
|
|
22
12
|
|
|
23
|
-
If `.controlplane/` is missing, run `cpflow generate`. Treat the generated app names as the repo-name default 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. When bumping the `cpflow` gem in a downstream repo, run `cpflow update-github-actions` (or `bundle exec cpflow update-github-actions`) and validate with `bin/test-cpflow-github-flow` in the same PR so the checked-in wrappers move to the matching release tag. Keep the
|
|
13
|
+
If `.controlplane/` is missing, run `cpflow generate`. Treat the generated app names as the repo-name default 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. When bumping the `cpflow` gem in a downstream repo, run `cpflow update-github-actions` (or `bundle exec cpflow update-github-actions`) and validate with `bin/test-cpflow-github-flow` in the same PR so the checked-in wrappers move to the matching release tag. Keep the normal generated review-app setup simple: review apps require only `CPLN_TOKEN_STAGING` when the generated review app config can be inferred. For public demos, starter staging apps, and long-lived review apps, keep the app workload `type: standard` with one warm replica, set its autoscaling metric to `disabled`, and enable `capacityAI: true` so Control Plane can right-size CPU and memory allocation at that fixed replica count. Shared Postgres and other stateful workloads are the usual exceptions and should stay manually sized; Capacity AI is for supported stateless app/service workloads. If true idle scale-to-zero is explicitly required, create a separate `serverless` workload before first deploy or plan a delete/recreate migration because Control Plane will not change an existing `standard` workload to `serverless` in place. For shared review-app resources such as one staging database, use `shared_secret_grants` and `{{SHARED_SECRET_DATABASE}}` placeholders instead of hardcoding the base app secret name; this keeps review-app policy binding and cleanup automatic while avoiding per-PR database cost. Document the one-time Control Plane bootstrap command for persistent staging and production apps with `cpflow setup-app --skip-post-creation-hook`; for existing apps or later template updates, document `cpflow apply-template` and the need for the app identity to have `reveal` on the app secret policy. Do not imply the staging deploy or promotion workflows create those persistent GVCs. 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.
|
|
24
14
|
|
|
25
15
|
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`.
|
|
26
16
|
|
|
27
17
|
Run the real local validations you can: Docker build if feasible, repo tests or smoke checks, YAML validation, and any CI-equivalent build steps. Push the branch and check the GitHub Actions results. Only stop early for a real external blocker or a product decision that changes scope.
|
|
28
18
|
```
|
|
29
19
|
|
|
20
|
+
## Local Shortcut
|
|
21
|
+
|
|
22
|
+
If `cpflow` is already available in the target repo, this local-only command
|
|
23
|
+
prints the same recommended prompt with the repo-name default app prefix filled
|
|
24
|
+
in:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
cpflow ai-github-flow-prompt
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
That command works even before `cpln` is installed.
|
|
31
|
+
|
|
30
32
|
## Hard Stop Conditions
|
|
31
33
|
|
|
32
34
|
Stop and report the blocker instead of generating `cpflow-*` workflow files when:
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-label="Control Plane Flow icon">
|
|
2
|
+
<title>Control Plane Flow icon</title>
|
|
3
|
+
<rect x="64" y="64" width="896" height="896" rx="192" fill="#0B1118" stroke="#26313D" stroke-width="12"/>
|
|
4
|
+
<g fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
5
|
+
<path d="M290 650 C330 500 435 430 512 512 C600 606 704 560 746 390" stroke="#2BD7FF" stroke-width="68"/>
|
|
6
|
+
<path d="M746 390 L688 397" stroke="#2BD7FF" stroke-width="68"/>
|
|
7
|
+
<path d="M746 390 L731 446" stroke="#2BD7FF" stroke-width="68"/>
|
|
8
|
+
</g>
|
|
9
|
+
<circle cx="290" cy="650" r="92" fill="#8973FF"/>
|
|
10
|
+
<circle cx="512" cy="512" r="92" fill="#70D187"/>
|
|
11
|
+
<circle cx="746" cy="390" r="92" fill="#FFB000"/>
|
|
12
|
+
<circle cx="290" cy="650" r="32" fill="#0B1118" opacity="0.88"/>
|
|
13
|
+
<circle cx="512" cy="512" r="32" fill="#0B1118" opacity="0.88"/>
|
|
14
|
+
<circle cx="746" cy="390" r="32" fill="#0B1118" opacity="0.88"/>
|
|
15
|
+
<path d="M390 730 H612" stroke="#2BD7FF" stroke-width="40" stroke-linecap="round" opacity="0.86"/>
|
|
16
|
+
<path d="M612 730 L555 692 M612 730 L555 768" stroke="#2BD7FF" stroke-width="40" stroke-linecap="round" stroke-linejoin="round" opacity="0.86"/>
|
|
17
|
+
</svg>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-label="Control Plane Flow icon transparent mark">
|
|
2
|
+
<title>Control Plane Flow icon transparent mark</title>
|
|
3
|
+
<g fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
4
|
+
<path d="M290 650 C330 500 435 430 512 512 C600 606 704 560 746 390" stroke="#2BD7FF" stroke-width="68"/>
|
|
5
|
+
<path d="M746 390 L688 397" stroke="#2BD7FF" stroke-width="68"/>
|
|
6
|
+
<path d="M746 390 L731 446" stroke="#2BD7FF" stroke-width="68"/>
|
|
7
|
+
</g>
|
|
8
|
+
<circle cx="290" cy="650" r="92" fill="#8973FF"/>
|
|
9
|
+
<circle cx="512" cy="512" r="92" fill="#70D187"/>
|
|
10
|
+
<circle cx="746" cy="390" r="92" fill="#FFB000"/>
|
|
11
|
+
<circle cx="290" cy="650" r="32" fill="#0B1118" opacity="0.88"/>
|
|
12
|
+
<circle cx="512" cy="512" r="32" fill="#0B1118" opacity="0.88"/>
|
|
13
|
+
<circle cx="746" cy="390" r="32" fill="#0B1118" opacity="0.88"/>
|
|
14
|
+
<path d="M390 730 H612" stroke="#2BD7FF" stroke-width="40" stroke-linecap="round" opacity="0.86"/>
|
|
15
|
+
<path d="M612 730 L555 692 M612 730 L555 768" stroke="#2BD7FF" stroke-width="40" stroke-linecap="round" stroke-linejoin="round" opacity="0.86"/>
|
|
16
|
+
</svg>
|
data/docs/ci-automation.md
CHANGED
|
@@ -10,6 +10,13 @@ The goal is to bring the Heroku Flow model into any `cpflow` project:
|
|
|
10
10
|
4. Promote the already-built staging artifact to production from the Actions tab.
|
|
11
11
|
5. Let a nightly workflow clean up stale review apps.
|
|
12
12
|
|
|
13
|
+
For public repositories, review apps are intentionally limited to branches in the base repository. Fork pull requests can
|
|
14
|
+
receive help comments, but generated deploy workflows skip fork heads because review-app deployment builds Docker images
|
|
15
|
+
with repository secrets. The skip is enforced by complementary guards on different trigger axes; see
|
|
16
|
+
[Review app security for repositories with external contributors](#review-app-security-for-repositories-with-external-contributors)
|
|
17
|
+
before customizing these workflows. If a forked change needs a review app, a maintainer should first review the code and
|
|
18
|
+
move the change into a trusted branch in the base repository.
|
|
19
|
+
|
|
13
20
|
## Quick Start
|
|
14
21
|
|
|
15
22
|
End-to-end rollout in one view:
|
|
@@ -20,6 +27,8 @@ End-to-end rollout in one view:
|
|
|
20
27
|
4. Configure the GitHub [repository secrets and variables](#required-github-repository-settings) the workflows expect.
|
|
21
28
|
5. Push the branch, then comment `+review-app-deploy` on a PR to spin up a review environment.
|
|
22
29
|
|
|
30
|
+
AI rollout: copy the [AI rollout prompt](./ai-github-flow-prompt.md) when you want an agent to run this setup. The prompt works whether `cpflow` is already installed or the agent needs to install it first. If `cpflow` is already available in the target repo, `cpflow ai-github-flow-prompt` prints the same prompt with the default app prefix filled in.
|
|
31
|
+
|
|
23
32
|
See [Bootstrap a Project](#bootstrap-a-project) for command details, [Repo Readiness Checklist](#repo-readiness-checklist) for what "ready" means, and [AI Playbook](#ai-playbook) to run the rollout through an agent.
|
|
24
33
|
|
|
25
34
|
## Bootstrap a Project
|
|
@@ -137,7 +146,8 @@ apps:
|
|
|
137
146
|
match_if_app_name_starts_with: true
|
|
138
147
|
hooks:
|
|
139
148
|
post_creation: bundle exec rails db:prepare
|
|
140
|
-
pre_deletion:
|
|
149
|
+
# pre_deletion intentionally omitted for shared databases: `cpflow delete` runs it before removing the workloads,
|
|
150
|
+
# so live connections can block the drop. Prefer admin-side cleanup. See docs/tips.md ("Share One Control Plane Postgres").
|
|
141
151
|
|
|
142
152
|
my-app-production:
|
|
143
153
|
<<: *common
|
|
@@ -155,12 +165,18 @@ Important points:
|
|
|
155
165
|
- Review-app workflows infer the staging Control Plane org from that review app entry's `cpln_org`.
|
|
156
166
|
- `upstream: my-app-staging` is what lets the production promotion workflow copy the exact staging artifact.
|
|
157
167
|
- If your main web workload is not named `rails`, set the optional `PRIMARY_WORKLOAD` repository variable described below.
|
|
168
|
+
- For public demos, starter staging apps, and long-lived review apps, prefer `capacityAI: true` with one warm replica
|
|
169
|
+
and the workload's autoscaling metric disabled, so Control Plane can right-size CPU and memory allocation at that
|
|
170
|
+
fixed replica count. See
|
|
171
|
+
[Enable Capacity AI for Demo and Starter Staging Apps](tips.md#enable-capacity-ai-for-demo-and-starter-staging-apps).
|
|
158
172
|
|
|
159
173
|
## Required GitHub Repository Settings
|
|
160
174
|
|
|
161
175
|
For a normal generated review-app setup, configure one repository secret:
|
|
162
176
|
|
|
163
|
-
- `CPLN_TOKEN_STAGING`: token for the staging Control Plane org
|
|
177
|
+
- `CPLN_TOKEN_STAGING`: token for the staging/review Control Plane org. Generate it from a Control Plane service account
|
|
178
|
+
whose policies only allow review/staging CI operations; it must not read production secrets or manage production
|
|
179
|
+
workloads.
|
|
164
180
|
|
|
165
181
|
No GitHub repository variables are required for review apps when `.controlplane/controlplane.yml`
|
|
166
182
|
has exactly one review app entry with `match_if_app_name_starts_with: true` and
|
|
@@ -173,7 +189,11 @@ disambiguate generated review-app config:
|
|
|
173
189
|
|
|
174
190
|
- `CPLN_ORG_STAGING`: override the staging/review org inferred from `cpln_org`, for example `company-staging`
|
|
175
191
|
- `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`
|
|
192
|
+
- `PRIMARY_WORKLOAD`: override the public workload used to discover the public endpoint and do review/production health checks; defaults to `rails`
|
|
193
|
+
- `REVIEW_APP_HEALTH_CHECK_RETRIES`: override review-app health polling attempts; defaults to `24`
|
|
194
|
+
- `REVIEW_APP_HEALTH_CHECK_INTERVAL`: override seconds between review-app health attempts; defaults to `15`
|
|
195
|
+
- `REVIEW_APP_HEALTH_CHECK_ACCEPTED_STATUSES`: override space-separated healthy HTTP statuses for review apps; defaults to `200 301 302`
|
|
196
|
+
- `REVIEW_APP_HEALTH_CHECK_CURL_MAX_TIME`: override per-request review-app curl timeout in seconds; defaults to `10`
|
|
177
197
|
|
|
178
198
|
If `controlplane.yml` defines more than one app with
|
|
179
199
|
`match_if_app_name_starts_with: true`, inference intentionally fails. Set
|
|
@@ -196,6 +216,30 @@ For production promotion, also configure:
|
|
|
196
216
|
- `CPLN_ORG_PRODUCTION` as a production environment variable, for example `company-production`
|
|
197
217
|
- `PRODUCTION_APP_NAME` as a production environment variable, for example `my-app-production`
|
|
198
218
|
|
|
219
|
+
Enter GitHub variables such as `CPLN_ORG_STAGING`,
|
|
220
|
+
`CPLN_ORG_PRODUCTION`, `STAGING_APP_NAME`, and `PRODUCTION_APP_NAME`
|
|
221
|
+
as plain single-line values. The generated production promotion workflow trims
|
|
222
|
+
accidental leading/trailing whitespace and line endings from Control Plane org
|
|
223
|
+
names before building registry URLs, but embedded line breaks are rejected
|
|
224
|
+
because they could change the target org name after normalization.
|
|
225
|
+
|
|
226
|
+
Production promotion copies the exact image currently deployed on the selected
|
|
227
|
+
staging workload. If that staging image is digest-pinned, the digest is used for
|
|
228
|
+
the source copy while the production tag is derived from the tag portion. Tags
|
|
229
|
+
with a `_<commit>` suffix keep that suffix in production; plain numeric tags are
|
|
230
|
+
also valid and promote to the next plain production tag. The copy step uses
|
|
231
|
+
`docker buildx imagetools create --prefer-index=false --tag` with isolated
|
|
232
|
+
Docker credentials, which preserves multi-architecture manifests, preserves
|
|
233
|
+
single-platform manifest format when supported, and avoids pulling image layers
|
|
234
|
+
onto the GitHub Actions runner.
|
|
235
|
+
|
|
236
|
+
Before copying the image, production promotion compares the environment variable
|
|
237
|
+
names exposed by staging and production at both the GVC level and each configured
|
|
238
|
+
app workload's container level. Variables present in staging are treated as
|
|
239
|
+
required for production, while production-only variables emit warnings. A missing
|
|
240
|
+
production workload variable such as a renderer password or runtime secret fails
|
|
241
|
+
the promotion before the image copy starts.
|
|
242
|
+
|
|
199
243
|
Do not put `CPLN_TOKEN_PRODUCTION` in repository or organization secrets for
|
|
200
244
|
sensitive production systems. Production promotion intentionally runs as a
|
|
201
245
|
normal caller-repo workflow job with `environment: production`, then checks out
|
|
@@ -271,6 +315,12 @@ cpflow setup-app -a my-app-production --org my-org-production --skip-post-creati
|
|
|
271
315
|
Use production-only runtime secrets and values for the production app. The
|
|
272
316
|
protected GitHub Environment controls who can run the promotion workflow, but
|
|
273
317
|
the production app resources still need to exist before the first promotion.
|
|
318
|
+
After bootstrap, populate the production app secret dictionary with the values
|
|
319
|
+
referenced by `.controlplane/templates`, then run `cpflow apply-template` against
|
|
320
|
+
production when templates change so the workload env references remain persisted.
|
|
321
|
+
Production promotion checks for missing GVC and workload container env names
|
|
322
|
+
before copying the staging image, so a staging-only runtime variable will stop
|
|
323
|
+
the run early instead of deploying an image that cannot boot.
|
|
274
324
|
|
|
275
325
|
Review apps are different: the generated `+review-app-deploy` workflow creates
|
|
276
326
|
temporary PR apps as needed, including the identity and secret policy binding.
|
|
@@ -316,6 +366,17 @@ The standard path is:
|
|
|
316
366
|
7. Store `CPLN_ORG_PRODUCTION` and `PRODUCTION_APP_NAME` as `production`
|
|
317
367
|
environment variables, or as repository variables only when those names are
|
|
318
368
|
intentionally non-sensitive.
|
|
369
|
+
8. Keep GitHub variable values single-line; a pasted trailing newline is trimmed
|
|
370
|
+
for Control Plane org names, but embedded line breaks are rejected before
|
|
371
|
+
deployment, copy, health-check, or rollback steps run.
|
|
372
|
+
9. Bootstrap or re-apply the persistent production app templates before first
|
|
373
|
+
promotion so app workload container env references and Control Plane secret
|
|
374
|
+
dictionaries exist in production.
|
|
375
|
+
10. Expect promotion to preserve the selected staging image reference. Digest
|
|
376
|
+
references are copied by digest, commit-suffixed tags keep the commit suffix,
|
|
377
|
+
and plain numeric tags remain valid.
|
|
378
|
+
11. Expect production health and rollback readiness polling to require Control
|
|
379
|
+
Plane `status.ready` and `status.readyLatest` before checking the endpoint.
|
|
319
380
|
|
|
320
381
|
GitHub only exposes environment secrets to jobs that reference the environment
|
|
321
382
|
after configured protection rules pass. GitHub does not allow a caller job that
|
|
@@ -336,6 +397,9 @@ Recommended org layout:
|
|
|
336
397
|
|
|
337
398
|
- keep review apps and staging in a staging org that developers can access
|
|
338
399
|
- keep production in a separate org with tighter access controls
|
|
400
|
+
- for public repositories, use a staging/review token generated from a service account whose policies only allow
|
|
401
|
+
review/staging CI operations; use a dedicated review-app org only if you also customize the generated
|
|
402
|
+
workflow/configuration to target that org separately
|
|
339
403
|
|
|
340
404
|
Optional repository secret for private dependency builds:
|
|
341
405
|
|
|
@@ -352,6 +416,105 @@ Advanced optional repository variables:
|
|
|
352
416
|
- `CPLN_CLI_VERSION`: pin only when Control Plane CLI compatibility requires it.
|
|
353
417
|
- `CPFLOW_VERSION`: pin a published RubyGems version only when intentionally overriding the default build-from-ref behavior.
|
|
354
418
|
|
|
419
|
+
## Review App Security for Repositories with External Contributors
|
|
420
|
+
|
|
421
|
+
Review-app deployment and teardown can execute pull request code. Even when the workflow itself is trusted, the
|
|
422
|
+
Dockerfile, package scripts, Rails initializers, server-rendering code, application runtime, any `release_script` or
|
|
423
|
+
`hooks.post_creation` defined in the PR's `.controlplane/controlplane.yml`, and `.controlplane/templates/*.yaml` identity
|
|
424
|
+
and policy templates applied by `cpflow setup-app` at first deploy with `CPLN_TOKEN_STAGING` can all be changed by the
|
|
425
|
+
pull request being deployed. Teardown can also run a `hooks.pre_deletion` command through the latest PR-built image, even
|
|
426
|
+
when the hook command comes from the base-branch config. A PR author can embed malicious code in that image; at runtime
|
|
427
|
+
the code executes inside the review app workload and can read any secrets mounted into the workload environment.
|
|
428
|
+
The generated setup action also exports `CPLN_TOKEN_STAGING` as `CPLN_TOKEN` via `GITHUB_ENV`, making it available in the
|
|
429
|
+
process environment of every subsequent runner step for the rest of the job, so keep any custom runner steps after setup
|
|
430
|
+
trusted. PR-controlled `release_script` and hook commands normally run through `cpflow run` in remote Control Plane
|
|
431
|
+
containers from the latest image, not as runner shell steps, so they do not read that runner token directly unless a
|
|
432
|
+
custom workflow passes a local token through. Inside the Control Plane workload, a separate runtime `CPLN_TOKEN` is
|
|
433
|
+
available only when the workload spec has an `identityLink`; `skip_secrets_setup` skips automatic identity creation and
|
|
434
|
+
binding, but templates can still attach an identity. Because `cpflow setup-app` reads `controlplane.yml` from the PR
|
|
435
|
+
checkout, a PR can also set `skip_secrets_setup: false` or omit it to re-enable automatic identity binding unless the
|
|
436
|
+
trusted workflow passes `--skip-secrets-setup` as a CLI flag. Do not treat `skip_secrets_setup` in `controlplane.yml` as
|
|
437
|
+
a token-removal control or reliable security boundary. This is why workload-mounted secrets and the staging
|
|
438
|
+
service-account token must remain disposable and scoped to minimum permissions.
|
|
439
|
+
|
|
440
|
+
The generated flow uses these defaults:
|
|
441
|
+
|
|
442
|
+
- same-repository pull requests can update existing review apps automatically on each push; creating the first review app
|
|
443
|
+
requires either a `+review-app-deploy` comment from a trusted commenter (`OWNER`, `MEMBER`, or `COLLABORATOR`
|
|
444
|
+
association, regardless of permission level) or a manual workflow dispatch by a repository collaborator with write
|
|
445
|
+
access. The trusted-commenter gate applies to every `+review-app-deploy` comment, whether or not a review app already
|
|
446
|
+
exists. Later pushes to a base-repository branch PR redeploy automatically without another approval because the
|
|
447
|
+
auto-push path (`pull_request` event) has no trusted-commenter check; only the `issue_comment` path does;
|
|
448
|
+
- fork pull requests cannot deploy via the generated `pull_request` path because the caller workflow's job-level `if:`
|
|
449
|
+
condition explicitly skips fork-originated runs. For `issue_comment` events, the caller `if:` restricts commands to
|
|
450
|
+
trusted author associations (`OWNER`, `MEMBER`, `COLLABORATOR`) but does not check fork status; the fork check for
|
|
451
|
+
comment events is enforced inside the reusable workflow's source-validation step. These complementary guards cover
|
|
452
|
+
different axes, so preserve both. A trusted commenter can comment on a fork PR and pass the caller's
|
|
453
|
+
`author_association` check; the reusable workflow's source-validation step is what blocks that fork head from being
|
|
454
|
+
deployed. Removing the source-validation guard opens a path to deploy untrusted code with repository-secret access,
|
|
455
|
+
because `issue_comment` events execute with base-repository secret access. Removing the `pull_request` guard still
|
|
456
|
+
lets untrusted fork code into the staging environment even though GitHub withholds repository secrets from fork
|
|
457
|
+
`pull_request` runs. Keep both workflow guards in place because the workflow builds Docker images with repository
|
|
458
|
+
secrets;
|
|
459
|
+
- review apps are also deleted automatically when the pull request closes; that PR-close path uses `pull_request_target`
|
|
460
|
+
so it runs in the base-repository context and has repository-secret access for teardown. That is also why you must
|
|
461
|
+
never check out PR or fork code in this job; see the customization guidance below. The PR-close path does not require a
|
|
462
|
+
trusted commenter. The generated `cpflow-delete-review-app.yml` pins `GITHUB_TOKEN` permissions to the minimum it needs
|
|
463
|
+
(`contents: read`, `issues: write`, `pull-requests: write`); if you customize this workflow, preserve that
|
|
464
|
+
`permissions:` block because omitting it can fall back to broader repository defaults;
|
|
465
|
+
- manual workflow dispatch by a repository collaborator can also delete a review app without a `+review-app-delete`
|
|
466
|
+
comment, and does not require a trusted commenter;
|
|
467
|
+
- trusted comments on fork PRs still do not deploy the fork head; the workflow posts no PR comment in this case. The
|
|
468
|
+
commenter receives a rocket reaction and the skip appears only in the Actions step summary. Review the fork code
|
|
469
|
+
carefully, then move the change to a branch in the base repository if it needs a generated review app. That build will
|
|
470
|
+
run with repository-secret access;
|
|
471
|
+
- production promotion is manual and uses production environment secrets separately from review and staging.
|
|
472
|
+
|
|
473
|
+
The generated review-app workflow targets the staging/review Control Plane org inferred from `cpln_org` in
|
|
474
|
+
`controlplane.yml`, or overridden by `CPLN_ORG_STAGING`, and uses the token from `CPLN_TOKEN_STAGING`. A fully separate
|
|
475
|
+
review-app org or token requires workflow/configuration customization; otherwise, keep review apps and staging in the
|
|
476
|
+
generated staging/review org and make that token disposable and unable to access production resources.
|
|
477
|
+
|
|
478
|
+
The PR-close teardown workflow runs trusted base-branch workflow code with repository secret access so it can delete fork
|
|
479
|
+
PR review apps. The generated `cpflow-delete-control-plane-app` composite action script refuses to call `cpflow delete`
|
|
480
|
+
on any app whose name does not match the review-app prefix. This shell-level guard is effective because the generated
|
|
481
|
+
delete workflow checks out base/default-branch code, not PR or fork code: its repository checkout has no `ref:` override,
|
|
482
|
+
so `pull_request_target` runs use the base branch. If you customize this workflow, never check out PR or fork code in the
|
|
483
|
+
same job as the delete step; doing so could let a PR replace the guard script itself and would also make
|
|
484
|
+
`hooks.pre_deletion` come from the PR's `controlplane.yml`. This is still not a token policy, so use a scoped staging
|
|
485
|
+
service account limited to review/staging operations. A configured `hooks.pre_deletion` command still runs through the
|
|
486
|
+
latest PR-built image on all delete paths: PR-close teardown, `+review-app-delete` comments, manual dispatch, and
|
|
487
|
+
scheduled cleanup. Review-app credentials must remain disposable even during deletion.
|
|
488
|
+
|
|
489
|
+
If you customize the generated `pull_request_target` workflow, never pass `github.event.pull_request.head.sha` or another
|
|
490
|
+
fork-controlled ref to `actions/checkout`, `git fetch`, `git merge`, `git cherry-pick`, or any other step that fetches,
|
|
491
|
+
materializes, or executes the ref. Those operations run untrusted fork code with repository secret access. Referencing the
|
|
492
|
+
SHA as an opaque identifier for deployment status updates, comments, or API calls is safe because SHA values are hex-only
|
|
493
|
+
and cannot contain shell metacharacters. Do not apply that reasoning to other attacker-controlled PR event fields such as
|
|
494
|
+
`head.ref`, `head.label`, `title`, `body`, `head.repo.full_name`, or repository descriptions; pass user-controlled strings
|
|
495
|
+
through environment variables instead of interpolating them directly into `run:` steps.
|
|
496
|
+
|
|
497
|
+
These defaults protect repository secrets from direct fork PR execution, but they do not make deployed PR code harmless.
|
|
498
|
+
For repositories with external contributors, keep review-app credentials and runtime values disposable:
|
|
499
|
+
|
|
500
|
+
- do not mount production secrets, staging customer data, package-publishing tokens, payment keys, or monitoring tokens
|
|
501
|
+
into review apps;
|
|
502
|
+
- do not use `DOCKER_BUILD_SSH_KEY` with a long-lived personal key or broad deploy key; see
|
|
503
|
+
[Docker Builds with Private Dependencies](#docker-builds-with-private-dependencies) for the minimum-access deploy-key
|
|
504
|
+
requirements;
|
|
505
|
+
- do not use broad Control Plane `superusers` tokens for review-app CI; use a review/staging service account scoped only to
|
|
506
|
+
review/staging CI operations;
|
|
507
|
+
- keep review databases, Redis instances, object stores, and renderer credentials separate from staging and production;
|
|
508
|
+
- rotate any credential that may have been exposed to a malicious review-app build;
|
|
509
|
+
- use scheduled cleanup so stale review apps stop consuming compute and secrets access. Cleanup deletes apps through
|
|
510
|
+
`cpflow delete`, so any configured `hooks.pre_deletion` still runs through the latest PR-built image; review-app
|
|
511
|
+
credentials must remain disposable for this path too.
|
|
512
|
+
|
|
513
|
+
Secret indirection such as `cpln://secret/...` protects values in Control Plane configuration. It does not protect a
|
|
514
|
+
value after that secret is mounted into a workload that runs pull request code. See
|
|
515
|
+
[Secrets and ENV Values - Review app secrets](./secrets-and-env-values.md#review-app-secrets) for review-app secret
|
|
516
|
+
handling guidance.
|
|
517
|
+
|
|
355
518
|
## Docker Builds with Private Dependencies
|
|
356
519
|
|
|
357
520
|
Some apps need extra Docker build configuration before the generated workflows are turnkey. Common examples are:
|
|
@@ -376,6 +539,10 @@ DOCKER_BUILD_EXTRA_ARGS=--build-arg=BUNDLE_WITHOUT=development:test
|
|
|
376
539
|
|
|
377
540
|
The action will start an SSH agent, add the key, write `known_hosts`, and pass `--ssh=default` to `cpflow build-image`. When `DOCKER_BUILD_SSH_KNOWN_HOSTS` is unset, the generated action uses pinned GitHub.com host keys by default. If your Dockerfile relies on `RUN --mount=type=ssh`, validate the build locally with `cpflow build-image -a <app> --ssh=default` before relying on CI.
|
|
378
541
|
|
|
542
|
+
Do not configure `DOCKER_BUILD_SSH_KEY` unless the Dockerfile truly needs it. When configured, it is available to all
|
|
543
|
+
review-app and staging Docker builds; follow the review-app security guidance above by using a read-only, revocable
|
|
544
|
+
deploy key scoped to the minimum private dependency access, and never use a personal SSH key.
|
|
545
|
+
|
|
379
546
|
## Generated Workflow Behavior
|
|
380
547
|
|
|
381
548
|
`cpflow-review-app-help.yml`
|
|
@@ -391,18 +558,31 @@ The action will start an SSH agent, add the key, write `known_hosts`, and pass `
|
|
|
391
558
|
|
|
392
559
|
`cpflow-deploy-review-app.yml`
|
|
393
560
|
|
|
394
|
-
- Creates a review app when someone comments `+review-app-deploy
|
|
561
|
+
- Creates a review app when someone comments `+review-app-deploy` or via manual workflow dispatch by a repository
|
|
562
|
+
collaborator.
|
|
563
|
+
- For manual dispatch, provide the PR number; the workflow rejects fork PRs at runtime because it builds Docker images
|
|
564
|
+
with repository secrets.
|
|
395
565
|
- Redeploys an existing review app automatically on later PR pushes.
|
|
396
566
|
- Creates a GitHub deployment and comments with the review URL and logs.
|
|
397
567
|
- Leaves PR pushes alone until the first review app is explicitly requested, which keeps demo-app costs down.
|
|
568
|
+
- Supports cost-conscious review apps when paired with one warm replica, Capacity AI, and a disabled autoscaling
|
|
569
|
+
metric for public demos, starter staging apps, and long-lived review apps; see
|
|
570
|
+
[Enable Capacity AI for Demo and Starter Staging Apps](tips.md#enable-capacity-ai-for-demo-and-starter-staging-apps).
|
|
398
571
|
- Accepts `+review-app-deploy` only from trusted commenters (`OWNER`, `MEMBER`, or `COLLABORATOR`).
|
|
399
|
-
- Skips fork-based PR deploys because the workflow builds Docker images with repository secrets.
|
|
572
|
+
- Skips fork-based PR deploys because the workflow builds Docker images with repository secrets. A trusted comment on a
|
|
573
|
+
fork PR still does not deploy the fork head, and manual dispatch must use a base-repository PR number; dispatching with
|
|
574
|
+
a fork PR number causes a hard workflow failure. To give a fork PR a review app, review the code carefully first, then
|
|
575
|
+
move the change to a branch in the base repository. The build will then run with repository-secret access.
|
|
400
576
|
|
|
401
577
|
`cpflow-delete-review-app.yml`
|
|
402
578
|
|
|
403
579
|
- Deletes the review app on `+review-app-delete`.
|
|
404
|
-
- Also
|
|
405
|
-
-
|
|
580
|
+
- Also supports manual workflow dispatch by a repository collaborator without requiring a trusted-commenter command.
|
|
581
|
+
- Also deletes it automatically when the pull request closes through a `pull_request_target` event, so repository secrets
|
|
582
|
+
are available for teardown; `hooks.pre_deletion` still executes through the latest PR-built image on this path, so
|
|
583
|
+
review-app credentials must remain disposable.
|
|
584
|
+
- Accepts `+review-app-delete` only from trusted commenters (`OWNER`, `MEMBER`, or `COLLABORATOR`); manual dispatch and
|
|
585
|
+
automatic PR-close teardown do not require a trusted commenter.
|
|
406
586
|
|
|
407
587
|
`cpflow-deploy-staging.yml`
|
|
408
588
|
|
|
@@ -415,11 +595,17 @@ The action will start an SSH agent, add the key, write `known_hosts`, and pass `
|
|
|
415
595
|
|
|
416
596
|
- Manually promotes the staging artifact to production with a confirmation input.
|
|
417
597
|
- Runs the production job in the `production` GitHub Environment, so configured reviewers approve the job before production environment secrets are available.
|
|
418
|
-
- Verifies that production has the env var names staging expects.
|
|
419
|
-
- Runs a health check against `PRIMARY_WORKLOAD
|
|
598
|
+
- Verifies that production has the GVC and app workload container env var names staging expects.
|
|
599
|
+
- Runs a health check against `PRIMARY_WORKLOAD` only after Control Plane reports the latest workload version ready.
|
|
420
600
|
- Attempts a rollback of every configured application workload if the new production image does not come up healthy.
|
|
421
601
|
- Creates a GitHub release after a successful promotion.
|
|
422
602
|
|
|
603
|
+
`cpflow-deploy-review-app.yml`
|
|
604
|
+
|
|
605
|
+
- Builds and deploys the pull request image after the review app exists or is created.
|
|
606
|
+
- Waits for `PRIMARY_WORKLOAD` to report `readyLatest=true` and return an accepted HTTP status before marking the GitHub deployment successful.
|
|
607
|
+
- Uses `REVIEW_APP_HEALTH_CHECK_*` repository variables to tune review-app health checks independently from production promotion.
|
|
608
|
+
|
|
423
609
|
`cpflow-cleanup-stale-review-apps.yml`
|
|
424
610
|
|
|
425
611
|
- Runs nightly and on demand.
|
|
@@ -676,15 +862,17 @@ In practice, porting the flow into a demo app usually follows five phases.
|
|
|
676
862
|
|
|
677
863
|
13. Validate the real production Docker build before relying on the workflows, especially if asset compilation or SSR requires Node, extra system packages, multiple processes, extra Docker build flags, or persistent writable paths.
|
|
678
864
|
14. Expect review app deploys to run only for branches in the base repository; fork PRs still get help comments, but deploys are skipped because the workflow uses repository secrets.
|
|
865
|
+
15. For public repositories, confirm that review-app secrets are disposable and that `CPLN_TOKEN_STAGING` cannot access production resources.
|
|
679
866
|
|
|
680
867
|
## AI Playbook
|
|
681
868
|
|
|
682
|
-
If you want an AI agent to apply this flow to another project,
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
869
|
+
If you want an AI agent to apply this flow to another project, use the
|
|
870
|
+
standalone [AI rollout prompt](./ai-github-flow-prompt.md). It captures the
|
|
871
|
+
exact wording, hard stop conditions, and definition of done for this workflow,
|
|
872
|
+
and it works whether `cpflow` is already installed or the agent needs to install
|
|
873
|
+
it first. When `cpflow` is already available in the target repo,
|
|
874
|
+
`cpflow ai-github-flow-prompt` prints the same prompt with that repo's default
|
|
875
|
+
app prefix already filled in.
|
|
688
876
|
|
|
689
877
|
Short version:
|
|
690
878
|
|
data/docs/commands.md
CHANGED
|
@@ -149,6 +149,9 @@ cpflow delete -a $APP_NAME -w $WORKLOAD_NAME
|
|
|
149
149
|
### `deploy-image`
|
|
150
150
|
|
|
151
151
|
- Deploys the latest image to app workloads
|
|
152
|
+
- Use `--workload`/`-w` one or more times to deploy only selected app workloads
|
|
153
|
+
- If `deploy_order` is configured and no `--workload` is provided, deploys ordered workload groups one at a time and waits for each group to be ready before continuing
|
|
154
|
+
- Workloads listed in `app_workloads` but omitted from `deploy_order` deploy last as an implicit final group
|
|
152
155
|
- Runs a release script before deploying if `release_script` is specified in the `.controlplane/controlplane.yml` file and `--run-release-phase` is provided
|
|
153
156
|
- The release script is run in the context of `cpflow run` with the latest image
|
|
154
157
|
- If the release script exits with a non-zero code, the command will stop executing and also exit with a non-zero code
|
|
@@ -156,7 +159,14 @@ cpflow delete -a $APP_NAME -w $WORKLOAD_NAME
|
|
|
156
159
|
- Repairs missing `shared_secret_grants` policy bindings before running a release phase or updating workloads
|
|
157
160
|
|
|
158
161
|
```sh
|
|
162
|
+
# Deploys the latest image to all app workloads.
|
|
159
163
|
cpflow deploy-image -a $APP_NAME
|
|
164
|
+
|
|
165
|
+
# Deploys only one app workload.
|
|
166
|
+
cpflow deploy-image -a $APP_NAME -w node-renderer
|
|
167
|
+
|
|
168
|
+
# Deploys only selected app workloads.
|
|
169
|
+
cpflow deploy-image -a $APP_NAME -w node-renderer -w sidekiq
|
|
160
170
|
```
|
|
161
171
|
|
|
162
172
|
### `doctor`
|
|
@@ -315,6 +325,7 @@ cpflow maintenance -a $APP_NAME
|
|
|
315
325
|
### `maintenance:off`
|
|
316
326
|
|
|
317
327
|
- Disables maintenance mode for an app
|
|
328
|
+
- Safe to re-run: if a previous run timed out after switching the domain but before stopping the maintenance workload, re-running while maintenance mode is already disabled stops the maintenance workload to finish it (so it is not a pure no-op)
|
|
318
329
|
- Specify the one-off workload through `one_off_workload` in the `.controlplane/controlplane.yml` file
|
|
319
330
|
- Optionally specify the maintenance workload through `maintenance_workload` in the `.controlplane/controlplane.yml` file (defaults to 'maintenance')
|
|
320
331
|
- Maintenance mode is only supported for domains that use path based routing mode and have a route configured for the prefix '/' on either port 80 or 443
|
|
@@ -326,6 +337,7 @@ cpflow maintenance:off -a $APP_NAME
|
|
|
326
337
|
### `maintenance:on`
|
|
327
338
|
|
|
328
339
|
- Enables maintenance mode for an app
|
|
340
|
+
- Safe to re-run: if a previous run timed out after switching the domain but before stopping the app workloads, re-running while maintenance mode is already enabled stops the app workloads to finish it (so it is not a pure no-op)
|
|
329
341
|
- Specify the one-off workload through `one_off_workload` in the `.controlplane/controlplane.yml` file
|
|
330
342
|
- Optionally specify the maintenance workload through `maintenance_workload` in the `.controlplane/controlplane.yml` file (defaults to 'maintenance')
|
|
331
343
|
- Maintenance mode is only supported for domains that use path based routing mode and have a route configured for the prefix '/' on either port 80 or 443
|
|
@@ -372,6 +384,7 @@ cpflow open-console -a $APP_NAME
|
|
|
372
384
|
- It performs the following steps:
|
|
373
385
|
- Runs `cpflow copy-image-from-upstream` to copy the latest image from upstream
|
|
374
386
|
- Runs `cpflow deploy-image` to deploy the image
|
|
387
|
+
- Honors `deploy_order` from `.controlplane/controlplane.yml` through `cpflow deploy-image`
|
|
375
388
|
- If `.controlplane/controlplane.yml` includes the `release_script`, `cpflow deploy-image` will use the `--run-release-phase` option
|
|
376
389
|
- If the release script exits with a non-zero code, the command will stop executing and also exit with a non-zero code
|
|
377
390
|
- If `use_digest_image_ref` is `true` in the `.controlplane/controlplane.yml` file or `--use-digest-image-ref` option is provided, deployed image's reference will include its digest
|
|
@@ -466,6 +479,8 @@ timeout 300 cpflow ps:wait -a $APP_NAME
|
|
|
466
479
|
and also overridden per job through `--cpu` and `--memory`)
|
|
467
480
|
- By default, the job is stopped if it takes longer than 6 hours to finish
|
|
468
481
|
(can be configured though `runner_job_timeout` in `controlplane.yml`)
|
|
482
|
+
- Non-interactive jobs return the Control Plane cron job status even when the job finishes before
|
|
483
|
+
Control Plane exposes a runner replica to attach logs to
|
|
469
484
|
|
|
470
485
|
```sh
|
|
471
486
|
# Opens shell (bash by default).
|
|
@@ -550,7 +565,7 @@ cpflow terraform import
|
|
|
550
565
|
Regenerates the generated cpflow GitHub Actions wrappers and helper files
|
|
551
566
|
from the currently installed cpflow gem. Use this after updating the
|
|
552
567
|
cpflow gem so checked-in workflow wrappers move to the matching upstream
|
|
553
|
-
release tag, for example `v5.
|
|
568
|
+
release tag, for example `v5.1.1`.
|
|
554
569
|
|
|
555
570
|
If the existing generated staging workflow uses a custom single staging
|
|
556
571
|
branch, the command preserves it. Pass `--staging-branch BRANCH` to set or
|