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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/agent-workflow.yml +15 -0
  3. data/.agents/bin/README.md +19 -0
  4. data/.agents/bin/docs +5 -0
  5. data/.agents/bin/lint +5 -0
  6. data/.agents/bin/setup +5 -0
  7. data/.agents/bin/test +5 -0
  8. data/.agents/bin/validate +5 -0
  9. data/.agents/trusted-github-actors.yml +32 -0
  10. data/.agents/workflows/ai-rollout-e2e-test.md +166 -0
  11. data/.github/actions/cpflow-wait-for-health/action.yml +11 -4
  12. data/.github/workflows/claude-code-review.yml +2 -0
  13. data/.github/workflows/claude.yml +2 -0
  14. data/.github/workflows/cpflow-deploy-review-app.yml +16 -1
  15. data/.github/workflows/cpflow-promote-staging-to-production.yml +224 -37
  16. data/.github/workflows/rspec-shared.yml +15 -1
  17. data/.github/workflows/rspec-specific.yml +1 -0
  18. data/.github/workflows/rspec.yml +58 -1
  19. data/AGENTS.md +57 -0
  20. data/CHANGELOG.md +36 -1
  21. data/CLAUDE.md +3 -0
  22. data/CONTRIBUTING.md +6 -2
  23. data/Gemfile.lock +1 -1
  24. data/README.md +25 -7
  25. data/docs/ai-github-flow-prompt.md +18 -16
  26. data/docs/assets/logo/favicon.ico +0 -0
  27. data/docs/assets/logo/icon-1024.png +0 -0
  28. data/docs/assets/logo/icon-128.png +0 -0
  29. data/docs/assets/logo/icon-16.png +0 -0
  30. data/docs/assets/logo/icon-192.png +0 -0
  31. data/docs/assets/logo/icon-24.png +0 -0
  32. data/docs/assets/logo/icon-32.png +0 -0
  33. data/docs/assets/logo/icon-48.png +0 -0
  34. data/docs/assets/logo/icon-512.png +0 -0
  35. data/docs/assets/logo/icon-64.png +0 -0
  36. data/docs/assets/logo/icon-tile.svg +17 -0
  37. data/docs/assets/logo/mark-transparent.svg +16 -0
  38. data/docs/ci-automation.md +203 -15
  39. data/docs/commands.md +16 -1
  40. data/docs/grafana-opentelemetry.md +699 -0
  41. data/docs/secrets-and-env-values.md +29 -2
  42. data/docs/sidebars.ts +70 -0
  43. data/docs/telemetry/application-instrumentation.md +161 -0
  44. data/docs/telemetry/collector.md +297 -0
  45. data/docs/telemetry/index.md +152 -0
  46. data/docs/telemetry/pipelines.md +98 -0
  47. data/docs/telemetry/review-apps.md +55 -0
  48. data/docs/telemetry/troubleshooting.md +92 -0
  49. data/docs/terraform/example/.controlplane/controlplane.yml +0 -1
  50. data/docs/terraform/overview.md +11 -0
  51. data/docs/tips.md +458 -29
  52. data/examples/controlplane.yml +2 -0
  53. data/lib/command/ai_github_flow_prompt.rb +2 -2
  54. data/lib/command/base.rb +17 -2
  55. data/lib/command/deploy_image.rb +77 -5
  56. data/lib/command/maintenance_off.rb +1 -0
  57. data/lib/command/maintenance_on.rb +1 -0
  58. data/lib/command/promote_app_from_upstream.rb +1 -0
  59. data/lib/command/ps_wait.rb +2 -10
  60. data/lib/command/run.rb +25 -5
  61. data/lib/core/config.rb +94 -0
  62. data/lib/core/doctor_service.rb +44 -3
  63. data/lib/core/maintenance_mode.rb +93 -6
  64. data/lib/core/template_parser.rb +43 -9
  65. data/lib/cpflow/version.rb +1 -1
  66. data/lib/generator_templates/controlplane.yml +1 -2
  67. data/lib/github_flow_templates/.github/cpflow-help.md +23 -1
  68. data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +224 -39
  69. 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, see the [AI rollout prompt](./docs/ai-github-flow-prompt.md) or run `cpflow ai-github-flow-prompt` inside the target repo to print a copy-paste prompt with that repo's default app prefix filled in.
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. [Examples](#examples)
65
- 14. [Migrating Postgres Database from Heroku Infrastructure](https://www.shakacode.com/control-plane-flow/docs/postgres/)
66
- 15. [Migrating Redis Database from Heroku Infrastructure](https://www.shakacode.com/control-plane-flow/docs/redis/)
67
- 16. [Tips](https://www.shakacode.com/control-plane-flow/docs/tips/)
68
- 17. [Thruster HTTP/2 Proxy on Control Plane](https://www.shakacode.com/control-plane-flow/docs/thruster/)
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, start with the [AI rollout prompt](./docs/ai-github-flow-prompt.md) or run `cpflow ai-github-flow-prompt` in the target repo rather than giving a vague "set up CI" request.
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
- Use this file when you want an AI agent to add the reusable `cpflow` review-app,
4
- staging, and production-promotion flow to a repository.
5
-
6
- If `cpflow` is already installed in the target repo, you can print the current
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 standard path simple: review apps require only `CPLN_TOKEN_STAGING` when the generated review app config can be inferred. 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.
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>
@@ -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: bundle exec rails db:drop
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 deletes it automatically when the pull request closes.
405
- - Accepts `+review-app-delete` only from trusted commenters (`OWNER`, `MEMBER`, or `COLLABORATOR`).
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, start with
683
- `cpflow github-flow-readiness`, then use the standalone
684
- [AI rollout prompt](./ai-github-flow-prompt.md). It captures the exact wording,
685
- hard stop conditions, and definition of done for this workflow. You can also
686
- run `cpflow ai-github-flow-prompt` from inside the target repo to print the
687
- current prompt with that repo's default app prefix already filled in.
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.0.4`.
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