cpflow 5.3.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/agent-workflow.yml +16 -25
  3. data/.agents/bin/README.md +19 -9
  4. data/.agents/bin/docs +1 -1
  5. data/.agents/bin/lint +1 -1
  6. data/.agents/bin/setup +1 -1
  7. data/.agents/bin/test +1 -1
  8. data/.agents/bin/validate +2 -1
  9. data/.agents/legacy-workflow-policy.yml +11 -0
  10. data/.agents/shaka.md +20 -0
  11. data/.agents/trusted-actions.yml +12 -0
  12. data/.coderabbit.yaml +14 -0
  13. data/.github/actions/cpflow-build-docker-image/action.yml +153 -22
  14. data/.github/actions/cpflow-delete-control-plane-app/action.yml +2 -1
  15. data/.github/actions/cpflow-delete-control-plane-app/delete-app.sh +3 -0
  16. data/.github/actions/cpflow-preview-promotion/action.yml +82 -0
  17. data/.github/actions/cpflow-preview-promotion/preview-promotion.sh +90 -0
  18. data/.github/actions/cpflow-setup-environment/action.yml +8 -6
  19. data/.github/dependabot.yml +10 -0
  20. data/.github/workflows/check_cpln_links.yml +6 -1
  21. data/.github/workflows/claude-code-review.yml +5 -2
  22. data/.github/workflows/claude.yml +6 -4
  23. data/.github/workflows/coderabbit-lifecycle-review.yml +29 -0
  24. data/.github/workflows/command_docs.yml +7 -2
  25. data/.github/workflows/cpflow-cleanup-stale-review-apps.yml +12 -5
  26. data/.github/workflows/cpflow-delete-review-app.yml +18 -11
  27. data/.github/workflows/cpflow-deploy-review-app.yml +38 -17
  28. data/.github/workflows/cpflow-deploy-staging.yml +28 -11
  29. data/.github/workflows/cpflow-help-command.yml +3 -3
  30. data/.github/workflows/cpflow-promote-staging-to-production.yml +135 -16
  31. data/.github/workflows/cpflow-review-app-help.yml +1 -1
  32. data/.github/workflows/rspec-shared.yml +34 -26
  33. data/.github/workflows/rspec-specific.yml +10 -2
  34. data/.github/workflows/rspec.yml +95 -4
  35. data/.github/workflows/rubocop.yml +9 -2
  36. data/.github/workflows/trigger-docs-site.yml +2 -0
  37. data/.rubocop.yml +6 -1
  38. data/AGENTS.md +18 -3
  39. data/CHANGELOG.md +39 -1
  40. data/CONTRIBUTING.md +39 -2
  41. data/Gemfile.lock +1 -1
  42. data/README.md +10 -2
  43. data/cpflow.gemspec +3 -15
  44. data/docs/ai-github-flow-prompt.md +3 -3
  45. data/docs/ci-automation.md +284 -68
  46. data/docs/commands.md +38 -15
  47. data/docs/postgres-review-apps.md +90 -0
  48. data/docs/postgres.md +233 -375
  49. data/docs/rds-private-networking.md +12 -12
  50. data/docs/releasing.md +15 -4
  51. data/docs/secrets-and-env-values.md +42 -0
  52. data/docs/sidebars.ts +1 -0
  53. data/examples/controlplane.yml +5 -0
  54. data/lib/command/ai_github_flow_prompt.rb +2 -2
  55. data/lib/command/apply_template.rb +36 -4
  56. data/lib/command/base.rb +56 -3
  57. data/lib/command/cleanup_stale_apps.rb +28 -4
  58. data/lib/command/copy_image_from_upstream.rb +3 -0
  59. data/lib/command/delete.rb +174 -4
  60. data/lib/command/deploy_image.rb +44 -1
  61. data/lib/command/generate.rb +247 -3
  62. data/lib/command/generate_github_actions.rb +43 -13
  63. data/lib/command/run.rb +224 -34
  64. data/lib/command/setup_app.rb +92 -10
  65. data/lib/command/update_github_actions.rb +82 -43
  66. data/lib/core/config.rb +73 -0
  67. data/lib/core/controlplane.rb +167 -80
  68. data/lib/core/controlplane_api.rb +28 -2
  69. data/lib/core/controlplane_api_direct.rb +17 -0
  70. data/lib/core/disposable_postgres_helpers.rb +167 -0
  71. data/lib/core/doctor_service.rb +7 -0
  72. data/lib/core/github_flow_readiness/checks.rb +2 -1
  73. data/lib/core/github_flow_readiness_service.rb +4 -0
  74. data/lib/core/repo_introspection.rb +112 -2
  75. data/lib/core/shell.rb +35 -10
  76. data/lib/core/timed_command.rb +111 -0
  77. data/lib/cpflow/version.rb +1 -1
  78. data/lib/cpflow.rb +1 -1
  79. data/lib/generator_templates/.dockerignore +16 -0
  80. data/lib/generator_templates/Dockerfile +9 -5
  81. data/lib/generator_templates/entrypoint.sh +2 -0
  82. data/lib/generator_templates/templates/postgres.yml +9 -0
  83. data/lib/generator_templates_sqlite/controlplane.yml +2 -1
  84. data/lib/generator_templates_sqlite/release_script.sh +1 -1
  85. data/lib/generator_templates_sqlite/templates/rails.yml +2 -1
  86. data/lib/github_flow_templates/.github/cpflow-help.md +39 -12
  87. data/lib/github_flow_templates/.github/workflows/cpflow-deploy-review-app.yml +1 -0
  88. data/lib/github_flow_templates/.github/workflows/cpflow-deploy-staging.yml +1 -0
  89. data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +135 -16
  90. data/lib/github_flow_templates/bin/pin-cpflow-github-ref +37 -13
  91. data/lib/github_flow_templates/bin/test-cpflow-github-flow +125 -46
  92. data/lib/patches/hash.rb +2 -2
  93. data/rakelib/create_release.rake +14 -14
  94. data/script/check_shell_scripts +66 -0
  95. metadata +18 -17
data/docs/commands.md CHANGED
@@ -30,6 +30,7 @@ cpflow ai-github-flow-prompt
30
30
  - Picks templates from the `.controlplane/templates` directory
31
31
  - Templates are ordinary Control Plane templates but with variable preprocessing
32
32
  - Use `--preserve-existing-runtime` to retain each workload container's configured app image, even when the workload is unready, and skip existing secret resources entirely while applying other template changes
33
+ - Use `--exclude-secret-template NAME` and `--exclude-policy-template NAME` to exclude exact named templates without changing workload image handling
33
34
  - Missing or invalid workload images use only an unambiguous app image from ready workloads; refresh fails before applying templates when no safe fallback exists
34
35
 
35
36
  **Preprocessed template variables:**
@@ -85,7 +86,7 @@ cpflow cleanup-images -a $APP_NAME
85
86
 
86
87
  - Acts on stale apps based on the creation date of the latest image, or the GVC if no images exist
87
88
  - With `--mode=delete` (default): deletes the whole app (GVC with all workloads, all volumesets and all images), and unbinds the app from the secrets policy and any configured `shared_secret_grants` policies as long as both the identity and each policy exist (and are bound)
88
- - With `--mode=stop`: suspends all workloads via `cpflow ps:stop` — no GVC, volumeset, or image is removed; resume with `cpflow ps:start`
89
+ - With `--mode=stop`: suspends configured workloads that exist in the live GVC through Control Plane — no GVC, volumeset, or image is removed; resume with `cpflow ps:start`
89
90
  - `--mode=stop` only suspends workloads listed in `app_workloads` + `additional_workloads`; workloads present in the live GVC but missing from the config are skipped silently
90
91
  - `--mode=stop` returns once each workload is marked suspended; it does not wait for the workload to reach a not-ready state
91
92
  - Specify the amount of days after an app should be considered stale through `stale_app_image_deployed_days` in the `.controlplane/controlplane.yml` file
@@ -135,6 +136,7 @@ cpflow copy-image-from-upstream -a $APP_NAME --upstream-token $UPSTREAM_TOKEN --
135
136
  - Deletes the whole app (GVC with all workloads, all volumesets and all images) or a specific workload
136
137
  - Also unbinds the app from the secrets policy and any configured `shared_secret_grants` policies, as long as both the identity and each policy exist (and are bound)
137
138
  - For the app-specific secrets policy, removes every permission held by the app identity; for `shared_secret_grants`, removes only `reveal`
139
+ - Removes a marked per-app dictionary and exact-target unbound policy for generated review credentials, including after the opt-in is removed from a dynamically matched review-app entry
138
140
  - Will ask for explicit user confirmation
139
141
  - Runs a pre-deletion hook before the app is deleted if `hooks.pre_deletion` is specified in the `.controlplane/controlplane.yml` file
140
142
  - If the hook exits with a non-zero code, the command will stop executing and also exit with a non-zero code
@@ -217,7 +219,7 @@ Creates base Control Plane config and template files for a Rails project:
217
219
  - infers the app prefix from the current directory and wires staging, review, and production entries
218
220
  - infers the Docker base Ruby version from `.ruby-version`, `.tool-versions`, or the app's `Gemfile`
219
221
  - preserves repo-defined asset precompile hooks, including React on Rails auto bundle generation
220
- - detects SQLite in `config/database.yml` and generates persistent `db` and `storage` volume templates instead of the default Postgres workload
222
+ - detects SQLite in `config/database.yml` and generates persistent `/app/data` and `/app/storage` volume templates without hiding image migrations under `/app/db`
221
223
 
222
224
  ```sh
223
225
  # Creates .controlplane directory with Control Plane config and starter templates
@@ -232,6 +234,9 @@ Creates GitHub Actions templates for a Heroku Flow style Control Plane pipeline:
232
234
  - manual promotion from staging to production
233
235
  - nightly cleanup and PR help workflows
234
236
 
237
+ It also copies cpflow's composite actions into `.github/actions/cpflow-*`
238
+ so every local `uses:` target is checked in and can be audited directly.
239
+
235
240
  Pass `--staging-branch BRANCH` when staging should auto-deploy from a branch
236
241
  other than `main` or `master`; the generator will bake that branch into the
237
242
  GitHub Actions push trigger and use it as the default STAGING_APP_BRANCH.
@@ -240,13 +245,13 @@ Pass `--force` to overwrite existing generated files. Prefer
240
245
  repo.
241
246
 
242
247
  ```sh
243
- # Creates thin .github/workflows wrappers for the Control Plane flow
248
+ # Creates workflow wrappers, local composite actions, and validation helpers
244
249
  cpflow generate-github-actions
245
250
 
246
251
  # Creates the flow with staging deploys triggered from develop
247
252
  cpflow generate-github-actions --staging-branch develop
248
253
 
249
- # Overwrites existing generated wrappers from the installed cpflow gem
254
+ # Overwrites existing generated GitHub Actions files from the installed cpflow gem
250
255
  cpflow generate-github-actions --force
251
256
  ```
252
257
 
@@ -483,6 +488,12 @@ timeout 300 cpflow ps:wait -a $APP_NAME
483
488
  (can be configured though `runner_job_timeout` in `controlplane.yml`)
484
489
  - Waiting for a runner replica is limited to the smaller of `runner_job_timeout` and 1000 seconds.
485
490
  A terminal cron status fails immediately, and reaching the observation deadline reports the last safe status
491
+ - With non-interactive log methods 2 and 3, after a command prints its completion marker, Control Plane
492
+ has up to 20 minutes to reconcile the cron job to a terminal status. This can be configured through
493
+ `runner_job_status_reconciliation_timeout` in `controlplane.yml`; timing out exits nonzero and reports
494
+ the job, replica, and last observed status
495
+ - Log method 1 does not emit a completion marker, so its job-status polling is not covered by the
496
+ post-command reconciliation timeout
486
497
  - Non-interactive jobs return the Control Plane cron job status even when the job finishes before
487
498
  Control Plane exposes a runner replica to attach logs to
488
499
  - Injects `CPFLOW_GVC_ID` and `CPFLOW_GVC_CREATED` into the job, exposing the app's immutable GVC
@@ -549,13 +560,14 @@ cpflow run -a $APP_NAME --entrypoint /app/alternative-entrypoint.sh -- rails db:
549
560
  - Configures app to have org-level secrets with default name `"{APP_PREFIX}-secrets"`
550
561
  using org-level policy with default name `"{APP_PREFIX}-secrets-policy"` (names can be customized, see docs)
551
562
  - Creates identity for secrets if it does not exist
563
+ - For dynamically named review apps with `generated_review_secret_keys`, checks an existing policy before writing credentials, creates a per-app dictionary, skips its secret template during initial setup, and fills missing disposable keys without printing or rotating values
552
564
  - Binds the app identity to any configured `shared_secret_grants` policies as part of the secrets setup flow; skipped when `--skip-secrets-setup` or `--skip-secret-access-binding` is provided, or `skip_secrets_setup` is set
553
565
  - Use `--skip-secrets-setup` to prevent the automatic setup of secrets,
554
566
  or set it through `skip_secrets_setup` in the `.controlplane/controlplane.yml` file
555
567
  - Runs a post-creation hook after the app is created if `hooks.post_creation` is specified in the `.controlplane/controlplane.yml` file
556
568
  - If the hook exits with a non-zero code, the command will stop executing and also exit with a non-zero code
557
569
  - Use `--skip-post-creation-hook` to skip the hook if specified in `controlplane.yml`
558
- - Use `--refresh-templates` to apply configured templates noninteractively to an existing app while preserving each workload's configured app image even when workloads are unready or use mixed image versions, skipping existing secret resources entirely, repairing secrets access bindings, and skipping the post-creation hook
570
+ - Use `--refresh-templates` to apply configured templates noninteractively to an existing app while preserving each workload's configured app image even when workloads are unready or use mixed image versions, skipping existing secret templates (but filling missing opt-in generated review keys), repairing secrets access bindings, and skipping the post-creation hook
559
571
 
560
572
  ```sh
561
573
  cpflow setup-app -a $APP_NAME
@@ -579,24 +591,35 @@ cpflow terraform import
579
591
 
580
592
  ### `update-github-actions`
581
593
 
582
- Regenerates the generated cpflow GitHub Actions wrappers and helper files
583
- from the currently installed cpflow gem. Use this after updating the
584
- cpflow gem so checked-in workflow wrappers move to the matching upstream
585
- release tag, for example `v5.2.0`.
594
+ Refreshes local composite actions and helper files from the installed gem.
595
+ All top-level workflows are preserved by default, including their refs,
596
+ triggers, permissions, and deployment ownership. Use --workflows FILE...
597
+ to explicitly add or replace named generated workflows. Replacement resets
598
+ each selected workflow to its template and matching v6.0.0 ref;
599
+ review and reapply any downstream customizations in the same PR.
600
+
601
+ Selecting cpflow-deploy-staging.yml preserves a single existing push branch
602
+ or the default main/master pair. Missing or ambiguous branches require
603
+ --staging-branch BRANCH. This option requires selecting the staging workflow.
586
604
 
587
- If the existing generated staging workflow uses a custom single staging
588
- branch, the command preserves it. Pass `--staging-branch BRANCH` to set or
589
- replace the generated staging branch explicitly.
605
+ A differing existing bin/test-cpflow-github-flow blocks all writes. Move
606
+ downstream checks to executable bin/test-cpflow-github-flow-custom and
607
+ remove or rename the old validator before updating. The generated validator
608
+ invokes that extension after its baseline checks; the extension is never
609
+ generated or overwritten. See docs/ci-automation.md for migration guidance.
590
610
 
591
611
  ```sh
592
- # After updating the cpflow gem, refresh generated GitHub Actions wrappers
612
+ # Refresh actions/helpers while preserving downstream workflows
593
613
  cpflow update-github-actions
594
614
 
595
615
  # When running cpflow through Bundler
596
616
  bundle exec cpflow update-github-actions
597
617
 
598
- # Preserve or set a custom staging branch
599
- cpflow update-github-actions --staging-branch develop
618
+ # Explicitly replace the review-app wrapper
619
+ cpflow update-github-actions --workflows cpflow-deploy-review-app.yml
620
+
621
+ # Explicitly add or replace staging deployment from develop
622
+ cpflow update-github-actions --workflows cpflow-deploy-staging.yml --staging-branch develop
600
623
  ```
601
624
 
602
625
  ### `version`
@@ -0,0 +1,90 @@
1
+ # PostgreSQL for disposable review apps
2
+
3
+ Use the generated `.controlplane/templates/postgres.yml` for a PostgreSQL workload
4
+ that belongs to one disposable review app. For production database migration, see
5
+ [Migrating PostgreSQL from Heroku to RDS](./postgres.md).
6
+
7
+ ## Enable cleanup for new review apps
8
+
9
+ The generated template marks three helper resources:
10
+
11
+ | Resource | Purpose |
12
+ | --- | --- |
13
+ | `<APP>-pg` | Credential dictionary |
14
+ | `<APP>-pg-script` | Initialization-script secret |
15
+ | `<APP>-pg-access` | Policy granting access to those secrets |
16
+
17
+ Each helper template carries this tag:
18
+
19
+ ```yaml
20
+ tags:
21
+ cpflow-disposable-postgres-app: "{{APP_NAME}}"
22
+ ```
23
+
24
+ `cpflow apply-template` retains the tag for dynamically named review apps and removes
25
+ it for persistent apps. In an existing project, add the tag to all three helper
26
+ **templates before creating new review apps**. Reapplying a marked template to an
27
+ existing unmarked resource is refused; it does not adopt that resource.
28
+
29
+ Before opting in, check that your deployment token can:
30
+
31
+ - Read and delete the three helper resources.
32
+ - Read each secret's effective access report (`viewAccessReport` on the org).
33
+
34
+ A missing or incomplete access report blocks cleanup before app data is deleted.
35
+
36
+ ## Delete an app
37
+
38
+ Run the normal app deletion command:
39
+
40
+ ```sh
41
+ cpflow delete -a APP_NAME
42
+ ```
43
+
44
+ Cleanup validates the helpers before deleting app data. It removes the marked
45
+ policy and secrets before deleting the GVC. Deleting a single workload with
46
+ `cpflow delete -a APP_NAME -w WORKLOAD_NAME` preserves the helpers.
47
+
48
+ The ownership checks require:
49
+
50
+ - The same app marker on every surviving helper.
51
+ - A policy targeting exactly the two helper secrets.
52
+ - Only `reveal` grants in that policy, for the app and PostgreSQL identities in the
53
+ app's GVC.
54
+ - No helper listed in `shared_secret_grants`.
55
+ - No effective secret access through another application policy or identity.
56
+
57
+ Reapplying marked helpers with `cpflow apply-template` uses the same checks.
58
+
59
+ ## Understand a cleanup refusal
60
+
61
+ Cleanup preserves resources when a marker, policy, or access report cannot establish
62
+ exclusive ownership. A refusal aborts whole-app deletion before live app data is
63
+ deleted. Fix the reported condition before retrying; do not remove a
64
+ sharing grant until you know which applications depend on it.
65
+
66
+ | Condition | Result |
67
+ | --- | --- |
68
+ | All helpers are unmarked legacy resources | App deletion proceeds; helpers are preserved |
69
+ | Marked and unmarked helpers coexist | Cleanup refuses deletion |
70
+ | A marker names another app | Cleanup refuses deletion |
71
+ | Policy targets or principals differ from the expected app scope | Cleanup refuses deletion |
72
+ | A helper is configured as shared | Cleanup refuses deletion |
73
+ | Secret access reports are unavailable or incomplete | Cleanup refuses deletion |
74
+
75
+ The access checks include permissions implied by `edit` or `manage`. Global `manage`
76
+ grants to org groups, users, or service accounts count as org administration.
77
+ Targeted or query grants, and grants to other GVC identities, block cleanup.
78
+
79
+ For shared staging credentials, use the
80
+ [shared-secret configuration guide](./secrets-and-env-values.md#shared-secrets-for-review-apps)
81
+ instead of marking them as disposable helpers.
82
+
83
+ ## Recover after interruption
84
+
85
+ Repeat `cpflow delete` with the same app name. Cleanup tolerates an already absent
86
+ GVC, policy, or secret and rechecks the surviving helpers before deleting them.
87
+
88
+ Unmarked legacy helpers need manual cleanup after you establish that no other app
89
+ uses them. Rolling back to an older cpflow version leaves the tags in place but
90
+ stops automatic helper cleanup.