cpflow 6.0.0 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2abf58d5c1099a9f57164ebe03843c014180098e23551d73822419b05191070a
4
- data.tar.gz: ab7adb1a83dbea1215c5686748e1438006a3a58ce419d1797a53172a670ce86e
3
+ metadata.gz: 5339d75233bef2f62028093b5037a44155a56d2c4f39741efbc4861c47848e86
4
+ data.tar.gz: bd30c3c9da8a468b6e5cc9479ed6be3912066b70e03f021533a815498756f162
5
5
  SHA512:
6
- metadata.gz: 4fcb5b12cc9b4ce2edec7afc583bdd37a62812a40e5ea478e1efc09a29e82d3d0bfe47386001b3978a11bcc3deb342044596c54d52eecc605e5a04077fa322be
7
- data.tar.gz: e5f33d39830fffc5fcb66e9f22aeb5d519c1ed18b51ae1ec77fe7a65c9e26b42426bac48aeaa5e62bff5c218c6cc2f5e9f131fdfd211cd2343a9c0671afa7880
6
+ metadata.gz: 417be2b206c02fda8ff5f2374ba3874ebc61c05ac00af7a8ff163306f359a9f323c3af268fad415b3422603b59bb7d1572335af4e5de9b88a0a5a7e6659f485b
7
+ data.tar.gz: 72f2e06d1325fc09028ad8454454fda8dfd0b8fa1a0a075050b5fe997b7ed1653d1abfcb5ccc35382caa1737325b40a52b4575de11d7f4a3707ccfc8d4e9803f
@@ -23,13 +23,6 @@ jobs:
23
23
  runs-on: ubuntu-latest
24
24
  timeout-minutes: 30
25
25
  steps:
26
- # No explicit ref: checkout's default resolves to this event's recorded commit
27
- # (GITHUB_SHA), which is trusted base-repository code and is exempt from checkout v7's fork-PR guard.
28
- - name: Checkout generated cpflow actions
29
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
30
- with:
31
- persist-credentials: false
32
-
33
26
  - name: Checkout control-plane-flow actions
34
27
  uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35
28
  with:
@@ -39,7 +32,7 @@ jobs:
39
32
  persist-credentials: false
40
33
 
41
34
  - name: Validate required secrets and variables
42
- uses: ./.github/actions/cpflow-validate-config
35
+ uses: ./.cpflow/.github/actions/cpflow-validate-config
43
36
  env:
44
37
  CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
45
38
  with:
@@ -54,14 +47,14 @@ jobs:
54
47
 
55
48
  - name: Resolve review app config
56
49
  id: review-config
57
- uses: ./.github/actions/cpflow-resolve-review-config
50
+ uses: ./.cpflow/.github/actions/cpflow-resolve-review-config
58
51
  with:
59
52
  working_directory: app
60
53
  configured_cpln_org_staging: ${{ vars.CPLN_ORG_STAGING }}
61
54
  configured_review_app_prefix: ${{ vars.REVIEW_APP_PREFIX }}
62
55
 
63
56
  - name: Setup environment
64
- uses: ./.github/actions/cpflow-setup-environment
57
+ uses: ./.cpflow/.github/actions/cpflow-setup-environment
65
58
  with:
66
59
  token: ${{ secrets.CPLN_TOKEN_STAGING }}
67
60
  org: ${{ steps.review-config.outputs.cpln_org }}
@@ -563,13 +563,6 @@ jobs:
563
563
  }
564
564
  }
565
565
 
566
- # No explicit ref: checkout's default resolves to this event's recorded commit
567
- # (GITHUB_SHA; the base-branch tip under pull_request_target) and is exempt from checkout v7's fork-PR guard.
568
- - name: Checkout generated cpflow actions
569
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
570
- with:
571
- persist-credentials: false
572
-
573
566
  - name: Checkout control-plane-flow actions
574
567
  uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
575
568
  with:
@@ -580,7 +573,7 @@ jobs:
580
573
 
581
574
  - name: Validate required secrets and variables
582
575
  id: config
583
- uses: ./.github/actions/cpflow-validate-config
576
+ uses: ./.cpflow/.github/actions/cpflow-validate-config
584
577
  env:
585
578
  CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
586
579
  with:
@@ -598,7 +591,7 @@ jobs:
598
591
  - name: Resolve review app config
599
592
  if: steps.config.outputs.ready == 'true'
600
593
  id: review-config
601
- uses: ./.github/actions/cpflow-resolve-review-config
594
+ uses: ./.cpflow/.github/actions/cpflow-resolve-review-config
602
595
  with:
603
596
  working_directory: app
604
597
  configured_cpln_org_staging: ${{ vars.CPLN_ORG_STAGING }}
@@ -607,7 +600,7 @@ jobs:
607
600
 
608
601
  - name: Setup environment
609
602
  if: steps.config.outputs.ready == 'true'
610
- uses: ./.github/actions/cpflow-setup-environment
603
+ uses: ./.cpflow/.github/actions/cpflow-setup-environment
611
604
  with:
612
605
  token: ${{ secrets.CPLN_TOKEN_STAGING }}
613
606
  org: ${{ steps.review-config.outputs.cpln_org }}
@@ -651,7 +644,7 @@ jobs:
651
644
  - name: Delete review app
652
645
  id: delete-app
653
646
  if: steps.config.outputs.ready == 'true'
654
- uses: ./.github/actions/cpflow-delete-control-plane-app
647
+ uses: ./.cpflow/.github/actions/cpflow-delete-control-plane-app
655
648
  with:
656
649
  app_name: ${{ steps.review-config.outputs.app_name }}
657
650
  cpln_org: ${{ steps.review-config.outputs.cpln_org }}
@@ -617,13 +617,6 @@ jobs:
617
617
  }
618
618
  }
619
619
 
620
- # No explicit ref: checkout's default resolves to this event's recorded commit
621
- # (GITHUB_SHA), which is trusted base-repository code and is exempt from checkout v7's fork-PR guard.
622
- - name: Checkout generated cpflow actions
623
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
624
- with:
625
- persist-credentials: false
626
-
627
620
  - name: Checkout control-plane-flow actions
628
621
  uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
629
622
  with:
@@ -634,7 +627,7 @@ jobs:
634
627
 
635
628
  - name: Validate required secrets and variables
636
629
  id: config
637
- uses: ./.github/actions/cpflow-validate-config
630
+ uses: ./.cpflow/.github/actions/cpflow-validate-config
638
631
  env:
639
632
  CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
640
633
  with:
@@ -748,7 +741,7 @@ jobs:
748
741
  - name: Resolve review app config
749
742
  if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true'
750
743
  id: review-config
751
- uses: ./.github/actions/cpflow-resolve-review-config
744
+ uses: ./.cpflow/.github/actions/cpflow-resolve-review-config
752
745
  with:
753
746
  working_directory: app
754
747
  configured_cpln_org_staging: ${{ vars.CPLN_ORG_STAGING }}
@@ -757,7 +750,7 @@ jobs:
757
750
 
758
751
  - name: Setup environment
759
752
  if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true'
760
- uses: ./.github/actions/cpflow-setup-environment
753
+ uses: ./.cpflow/.github/actions/cpflow-setup-environment
761
754
  with:
762
755
  token: ${{ secrets.CPLN_TOKEN_STAGING }}
763
756
  org: ${{ steps.review-config.outputs.cpln_org }}
@@ -769,7 +762,7 @@ jobs:
769
762
  - name: Detect release phase support
770
763
  if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true'
771
764
  id: release-phase
772
- uses: ./.github/actions/cpflow-detect-release-phase
765
+ uses: ./.cpflow/.github/actions/cpflow-detect-release-phase
773
766
  with:
774
767
  app_name: ${{ steps.review-config.outputs.app_name }}
775
768
  working_directory: app
@@ -931,7 +924,7 @@ jobs:
931
924
  - name: Build Docker image
932
925
  id: build-image
933
926
  if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true' && (steps.check-app.outputs.exists == 'true' || steps.setup-review-app.outcome == 'success')
934
- uses: ./.github/actions/cpflow-build-docker-image
927
+ uses: ./.cpflow/.github/actions/cpflow-build-docker-image
935
928
  with:
936
929
  app_name: ${{ steps.review-config.outputs.app_name }}
937
930
  org: ${{ steps.review-config.outputs.cpln_org }}
@@ -1019,7 +1012,7 @@ jobs:
1019
1012
  - name: Wait for deployment health
1020
1013
  if: steps.config.outputs.ready == 'true' && steps.source.outputs.allowed == 'true' && (steps.check-app.outputs.exists == 'true' || steps.setup-review-app.outcome == 'success')
1021
1014
  id: health-check
1022
- uses: ./.github/actions/cpflow-wait-for-health
1015
+ uses: ./.cpflow/.github/actions/cpflow-wait-for-health
1023
1016
  with:
1024
1017
  workload_name: ${{ env.PRIMARY_WORKLOAD || 'rails' }}
1025
1018
  app_name: ${{ steps.review-config.outputs.app_name }}
@@ -68,12 +68,6 @@ jobs:
68
68
  echo "is_deployable=false" >> "$GITHUB_OUTPUT"
69
69
  fi
70
70
 
71
- - name: Checkout generated cpflow actions
72
- if: steps.check-branch.outputs.is_deployable == 'true'
73
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
74
- with:
75
- persist-credentials: false
76
-
77
71
  - name: Checkout control-plane-flow actions
78
72
  if: steps.check-branch.outputs.is_deployable == 'true'
79
73
  uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -85,7 +79,7 @@ jobs:
85
79
 
86
80
  - name: Validate required secrets and variables
87
81
  if: steps.check-branch.outputs.is_deployable == 'true'
88
- uses: ./.github/actions/cpflow-validate-config
82
+ uses: ./.cpflow/.github/actions/cpflow-validate-config
89
83
  env:
90
84
  CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
91
85
  CPLN_ORG_STAGING: ${{ vars.CPLN_ORG_STAGING }}
@@ -116,7 +110,7 @@ jobs:
116
110
  persist-credentials: false
117
111
 
118
112
  - name: Setup environment
119
- uses: ./.github/actions/cpflow-setup-environment
113
+ uses: ./.cpflow/.github/actions/cpflow-setup-environment
120
114
  with:
121
115
  token: ${{ secrets.CPLN_TOKEN_STAGING }}
122
116
  org: ${{ vars.CPLN_ORG_STAGING }}
@@ -126,7 +120,7 @@ jobs:
126
120
  control_plane_flow_ref: ${{ job.workflow_ref }}
127
121
 
128
122
  - name: Build Docker image
129
- uses: ./.github/actions/cpflow-build-docker-image
123
+ uses: ./.cpflow/.github/actions/cpflow-build-docker-image
130
124
  with:
131
125
  app_name: ${{ env.APP_NAME }}
132
126
  org: ${{ vars.CPLN_ORG_STAGING }}
@@ -158,7 +152,7 @@ jobs:
158
152
  persist-credentials: false
159
153
 
160
154
  - name: Setup environment
161
- uses: ./.github/actions/cpflow-setup-environment
155
+ uses: ./.cpflow/.github/actions/cpflow-setup-environment
162
156
  with:
163
157
  token: ${{ secrets.CPLN_TOKEN_STAGING }}
164
158
  org: ${{ vars.CPLN_ORG_STAGING }}
@@ -169,7 +163,7 @@ jobs:
169
163
 
170
164
  - name: Detect release phase support
171
165
  id: release-phase
172
- uses: ./.github/actions/cpflow-detect-release-phase
166
+ uses: ./.cpflow/.github/actions/cpflow-detect-release-phase
173
167
  with:
174
168
  app_name: ${{ env.APP_NAME }}
175
169
 
@@ -93,7 +93,7 @@ jobs:
93
93
  echo "org=$org" >> "$GITHUB_OUTPUT"
94
94
 
95
95
  - name: Setup staging environment
96
- uses: ./.github/actions/cpflow-setup-environment
96
+ uses: ./.cpflow/.github/actions/cpflow-setup-environment
97
97
  with:
98
98
  token: ${{ secrets.CPLN_TOKEN_STAGING }}
99
99
  org: ${{ steps.staging-org.outputs.org }}
@@ -104,7 +104,7 @@ jobs:
104
104
 
105
105
  - name: Preview commits before production approval
106
106
  id: preview
107
- uses: ./.github/actions/cpflow-preview-promotion
107
+ uses: ./.cpflow/.github/actions/cpflow-preview-promotion
108
108
  env:
109
109
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
110
110
  GH_REPO: ${{ github.repository }}
@@ -157,7 +157,7 @@ jobs:
157
157
  fi
158
158
 
159
159
  - name: Validate required secrets and variables
160
- uses: ./.github/actions/cpflow-validate-config
160
+ uses: ./.cpflow/.github/actions/cpflow-validate-config
161
161
  # Pass secrets via env so the composite action checks indirect shell
162
162
  # variables instead of interpolating secret values into a run script.
163
163
  env:
@@ -224,7 +224,7 @@ jobs:
224
224
  } >> "$GITHUB_OUTPUT"
225
225
 
226
226
  - name: Setup production environment
227
- uses: ./.github/actions/cpflow-setup-environment
227
+ uses: ./.cpflow/.github/actions/cpflow-setup-environment
228
228
  with:
229
229
  token: ${{ secrets.CPLN_TOKEN_PRODUCTION }}
230
230
  org: ${{ steps.cpln-orgs.outputs.production }}
@@ -286,7 +286,7 @@ jobs:
286
286
 
287
287
  - name: Detect release phase support
288
288
  id: release-phase
289
- uses: ./.github/actions/cpflow-detect-release-phase
289
+ uses: ./.cpflow/.github/actions/cpflow-detect-release-phase
290
290
  with:
291
291
  app_name: ${{ vars.PRODUCTION_APP_NAME }}
292
292
 
@@ -679,7 +679,7 @@ jobs:
679
679
 
680
680
  - name: Wait for deployment health
681
681
  id: health-check
682
- uses: ./.github/actions/cpflow-wait-for-health
682
+ uses: ./.cpflow/.github/actions/cpflow-wait-for-health
683
683
  with:
684
684
  workload_name: ${{ steps.workloads.outputs.primary }}
685
685
  app_name: ${{ vars.PRODUCTION_APP_NAME }}
data/CHANGELOG.md CHANGED
@@ -12,6 +12,13 @@ In addition to the standard keepachangelog.com categories, this project uses a l
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [6.0.1] - 2026-10-10
16
+
17
+ ### Changed
18
+
19
+ - **`cpflow ai-github-flow-prompt` prints its rollout prompt as titled sections with one instruction per bullet instead of four long paragraphs.** The instructions are unchanged. [PR 512](https://github.com/shakacode/control-plane-flow/pull/512) by [Justin Gordon](https://github.com/justin808).
20
+ - **Generated GitHub Actions workflows load cpflow's composite actions from the pinned `shakacode/control-plane-flow` ref again, so downstream repositories no longer check in `.github/actions/cpflow-*`.** A repository carries the wrappers and one pinned ref, and action fixes arrive with the next ref bump. The actions that receive `CPLN_TOKEN_STAGING` come from that pinned commit, not from the triggering pull request's revision. `cpflow generate-github-actions` stops writing the copies. Migrating from 6.0.0: replace the wrappers and the production promotion workflow with `cpflow update-github-actions --workflows FILE...`, then delete the copies yourself; the command lists them and never deletes them, because a wrapper still pinned to 6.0.0 runs them. See [Removing cpflow 6.0.0 action copies](docs/ci-automation.md#removing-cpflow-600-action-copies). [PR 511](https://github.com/shakacode/control-plane-flow/pull/511) by [Justin Gordon](https://github.com/justin808). Fixes [issue 510](https://github.com/shakacode/control-plane-flow/issues/510).
21
+
15
22
  ## [6.0.0] - 2026-10-08
16
23
 
17
24
  ### Breaking Changes
@@ -511,7 +518,8 @@ Deprecated `cpl` gem. New gem is `cpflow`.
511
518
 
512
519
  First release.
513
520
 
514
- [Unreleased]: https://github.com/shakacode/control-plane-flow/compare/v6.0.0...HEAD
521
+ [Unreleased]: https://github.com/shakacode/control-plane-flow/compare/v6.0.1...HEAD
522
+ [6.0.1]: https://github.com/shakacode/control-plane-flow/compare/v6.0.0...v6.0.1
515
523
  [6.0.0]: https://github.com/shakacode/control-plane-flow/compare/v5.3.0...v6.0.0
516
524
  [5.3.0]: https://github.com/shakacode/control-plane-flow/compare/v5.2.0...v5.3.0
517
525
  [5.2.0]: https://github.com/shakacode/control-plane-flow/compare/v5.1.1...v5.2.0
data/CONTRIBUTING.md CHANGED
@@ -150,10 +150,10 @@ cpflow test
150
150
 
151
151
  ## Developing the GitHub flow generator
152
152
 
153
- `cpflow generate-github-actions` copies workflow templates in `lib/github_flow_templates/` and canonical composite actions in `.github/actions/cpflow-*` into a target repo's `.github/` directory. To work on this feature:
153
+ `cpflow generate-github-actions` copies workflow templates in `lib/github_flow_templates/` into a target repo's `.github/` directory. The composite actions those workflows call stay in this repository's `.github/actions/cpflow-*`; workflows load them from the pinned `.cpflow` checkout as `./.cpflow/.github/actions/cpflow-*`. To work on this feature:
154
154
 
155
- - **Edit each canonical source in place.** The generator does no string-mangling beyond a small set of substitutions handled in `lib/command/generate_github_actions.rb`; what you put in `lib/github_flow_templates/.github/` is (almost) exactly what ships into a generated repo. Edit composite actions in the root `.github/actions/cpflow-*` directories. Make changes in those canonical sources, not in a generated copy.
156
- - **Surface area to keep consistent.** A change to a PR command (e.g. `+review-app-deploy`) usually touches three places: the trigger workflow (`lib/github_flow_templates/.github/workflows/cpflow-deploy-review-app.yml`), the PR-open quick reference (`cpflow-review-app-help.yml`), and the long-form help (`lib/github_flow_templates/.github/cpflow-help.md`). The AI flow prompt (`lib/command/ai_github_flow_prompt.rb`) also names commands and should be kept in sync.
155
+ - **Edit each canonical source in place.** The generator does no string-mangling beyond a small set of substitutions handled in `lib/command/generate_github_actions.rb`; what you put in `lib/github_flow_templates/.github/` is (almost) exactly what ships into a generated repo. Edit composite actions in the root `.github/actions/cpflow-*` directories; they are not copied downstream. Make changes in those canonical sources, not in a generated copy.
156
+ - **Surface area to keep consistent.** A change to a PR command (e.g. `+review-app-deploy`) usually touches three places: the trigger workflow (`lib/github_flow_templates/.github/workflows/cpflow-deploy-review-app.yml`), the PR-open quick reference (`cpflow-review-app-help.yml`), and the long-form help (`lib/github_flow_templates/.github/cpflow-help.md`). The AI flow prompt (`lib/command/ai_github_flow_prompt.txt`, mirrored in `docs/ai-github-flow-prompt.md`) also names commands and should be kept in sync.
157
157
  - **Run the generator spec on every change:**
158
158
 
159
159
  ```sh
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cpflow (6.0.0)
4
+ cpflow (6.0.1)
5
5
  dotenv (~> 3.1)
6
6
  jwt (~> 3.1)
7
7
  psych (~> 5.2)
data/README.md CHANGED
@@ -398,7 +398,7 @@ cpflow generate-github-actions
398
398
  bin/test-cpflow-github-flow
399
399
  ```
400
400
 
401
- `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` checks in the local `.github/actions/cpflow-*` implementations alongside the workflows, and 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 `/app/data` and `/app/storage` volumes when `config/database.yml` shows SQLite in production, redirects every detected production SQLite database into persistent storage, keeps migrations visible under `/app/db`, and preserves detected frontend precompile hooks. 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.
401
+ `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` writes the workflows, which load cpflow's composite actions from the pinned upstream ref, and 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 `/app/data` and `/app/storage` volumes when `config/database.yml` shows SQLite in production, redirects every detected production SQLite database into persistent storage, keeps migrations visible under `/app/db`, and preserves detected frontend precompile hooks. 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.
402
402
 
403
403
  For a live example, see the [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/blob/master/.controlplane/readme.md) repository.
404
404
 
@@ -8,13 +8,109 @@ first.
8
8
  ## Recommended Prompt
9
9
 
10
10
  ```text
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.
12
-
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. Keep every generated Control Plane Flow source pin synchronized at the same `vX.Y.Z`: each reusable-workflow `uses: ...@vX.Y.Z` ref, the production promotion `.cpflow` checkout ref, and its `control_plane_flow_ref` setup input. By default, `cpflow generate-github-actions` derives these values from the installed `cpflow` gem version; temporary explicit ref overrides are for unreleased testing and must keep all three values synchronized. Keep the generated `.github/actions/cpflow-*` files from that same gem unchanged; upstream reusable workflows invoke those checked-in local actions and separately check out the matching runtime source at `.cpflow`. When bumping the `cpflow` gem in a downstream repo, `cpflow update-github-actions` refreshes local actions and helpers while preserving all top-level workflows, including their refs and triggers. Use `cpflow update-github-actions --workflows FILE...` to explicitly select wrappers for replacement, then review the diff and reapply downstream customizations. A differing existing validator blocks all writes: preserve its downstream checks in executable `bin/test-cpflow-github-flow-custom`, then remove or rename the old generated validator before retrying. The generated validator invokes that preserved extension and propagates its failures. Follow `docs/ci-automation.md` for the migration procedure, keep source pins compatible with the updated actions, and run `bin/test-cpflow-github-flow` in the same PR (or `bin/test-cpflow-github-flow bundle exec cpflow` when using Bundler). 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.
14
-
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 redirects every production database file into persistent `/app/data` (or keeps it under persistent `/app/storage`) without mounting over the image's `/app/db` migrations, stores Active Storage under `/app/storage`, and 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`.
16
-
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.
11
+ Set up Control Plane GitHub Flow for this repo.
12
+
13
+ Before generating anything:
14
+ - First make sure the `cpflow` CLI is available: use the repo's existing `bundle exec cpflow` if
15
+ present, otherwise install the published `cpflow` Ruby gem with `gem install cpflow`; if neither
16
+ is possible, stop and report that blocker.
17
+ - Use the same `cpflow` invocation for the rest of the rollout.
18
+ - Start with `cpflow github-flow-readiness` and stop on any reported blockers.
19
+ - The repo must be deployable from a clean clone: published package versions, complete runtime
20
+ scaffold, and a production Dockerfile that can build the app.
21
+ - If any package version is unpublished, inaccessible from CI, or requires credentials that are not
22
+ already modeled in the repo or GitHub settings, stop and report the blocker instead of generating
23
+ workflow files.
24
+ - If the repo is a legacy sample pinned to an obsolete Ruby or Bundler toolchain, if it does not
25
+ even have a production Dockerfile yet, or if it is a monorepo without an already-decided single
26
+ app boundary for this flow, stop and report that as a prerequisite instead of forcing the rollout.
27
+
28
+ Generate the flow:
29
+ - If `.controlplane/` is missing, run `cpflow generate`.
30
+ - Treat the generated app names as the repo-name default and rename them only if the
31
+ project needs a different prefix.
32
+ - Then run `cpflow generate-github-actions` (or `cpflow generate-github-actions --staging-branch
33
+ BRANCH` when staging should deploy from a branch other than `main`/`master`).
34
+ - Keep review apps opt-in via `+review-app-deploy`.
35
+ - Make sure any `STAGING_APP_BRANCH` repository variable is also present in the generated staging
36
+ workflow's `on.push.branches` filter.
37
+ - List the GitHub secrets and variables that must be configured.
38
+
39
+ Keep the Control Plane Flow source pins together:
40
+ - Do not hand-edit duplicated upstream refs into the generated wrappers.
41
+ - Keep every generated Control Plane Flow source pin synchronized at the same `vX.Y.Z`: each
42
+ reusable-workflow `uses: ...@vX.Y.Z` ref, the production promotion `.cpflow` checkout ref, and its
43
+ `control_plane_flow_ref` setup input.
44
+ - By default, `cpflow generate-github-actions` derives these values from the installed `cpflow` gem
45
+ version; temporary explicit ref overrides are for unreleased testing and must keep all three
46
+ values synchronized.
47
+ - Do not add `.github/actions/cpflow-*` copies; the workflows load cpflow's composite actions and
48
+ runtime source from the pinned `.cpflow` checkout.
49
+
50
+ When bumping the `cpflow` gem in a downstream repo:
51
+ - `cpflow update-github-actions` refreshes helpers while preserving all top-level workflows,
52
+ including their refs and triggers.
53
+ - It reports `.github/actions/cpflow-*` copies left by cpflow 6.0.0; delete them once no workflow is
54
+ pinned to 6.0.0 or names one.
55
+ - Use `cpflow update-github-actions --workflows FILE...` to explicitly select wrappers for
56
+ replacement, then review the diff and reapply downstream customizations.
57
+ - A differing existing validator blocks all writes: preserve its downstream checks in executable
58
+ `bin/test-cpflow-github-flow-custom`, then remove or rename the old generated validator before
59
+ retrying.
60
+ - The generated validator invokes that preserved extension and propagates its failures.
61
+ - Follow `docs/ci-automation.md` for the migration procedure, keep source pins compatible with the
62
+ updated actions, and run `bin/test-cpflow-github-flow` in the same PR (or
63
+ `bin/test-cpflow-github-flow bundle exec cpflow` when using Bundler).
64
+
65
+ Review apps and workload sizing:
66
+ - Keep the normal generated review-app setup simple: review apps require only `CPLN_TOKEN_STAGING`
67
+ when the generated review app config can be inferred.
68
+ - For public demos, starter staging apps, and long-lived review apps, keep the app workload `type:
69
+ standard` with one warm replica, set its autoscaling metric to `disabled`, and enable `capacityAI:
70
+ true` so Control Plane can right-size CPU and memory allocation at that fixed replica count.
71
+ - Shared Postgres and other stateful workloads are the usual exceptions and should stay manually
72
+ sized; Capacity AI is for supported stateless app/service workloads.
73
+ - If true idle scale-to-zero is explicitly required, create a separate `serverless` workload before
74
+ first deploy or plan a delete/recreate migration because Control Plane will not change an existing
75
+ `standard` workload to `serverless` in place.
76
+ - For shared review-app resources such as one staging database, use `shared_secret_grants` and
77
+ `{{SHARED_SECRET_DATABASE}}` placeholders instead of hardcoding the base app secret name; this
78
+ keeps review-app policy binding and cleanup automatic while avoiding per-PR database cost.
79
+
80
+ Staging and production:
81
+ - Document the one-time Control Plane bootstrap command for persistent staging and production apps
82
+ with `cpflow setup-app --skip-post-creation-hook`; for existing apps or later template updates,
83
+ document `cpflow apply-template` and the need for the app identity to have `reveal` on the app
84
+ secret policy.
85
+ - Do not imply the staging deploy or promotion workflows create those persistent GVCs.
86
+ - For production promotion, document a protected `production` GitHub Environment with required
87
+ reviewers, prevent self-review, and `CPLN_TOKEN_PRODUCTION` stored as an environment secret, not
88
+ as a repository or organization secret.
89
+
90
+ Docker image and runtime:
91
+ - Keep Node available in the final image if asset compilation or SSR depends on ExecJS, Yarn,
92
+ `pnpm`, or npm after the main install layer.
93
+ - Make sure the generated Dockerfile uses a Ruby base image compatible with the app's declared Ruby
94
+ requirement.
95
+ - Preserve repo-defined frontend build hooks: if `config/shakapacker.yml` defines a
96
+ `precompile_hook`, or React on Rails enables `config.auto_load_bundle = true`, confirm the
97
+ generated Dockerfile runs that codegen step before `rails assets:precompile`.
98
+ - If `config/database.yml` shows SQLite in production, confirm that the generated scaffold redirects
99
+ every production database file into persistent `/app/data` (or keeps it under persistent
100
+ `/app/storage`) without mounting over the image's `/app/db` migrations, stores Active Storage
101
+ under `/app/storage`, and runs `rails db:prepare`; otherwise keep the default Postgres workload.
102
+ - If the public workload is not named `rails`, set `PRIMARY_WORKLOAD` or adjust the generated
103
+ workflows.
104
+ - Inspect the Dockerfile and package sources for private GitHub dependencies or `RUN
105
+ --mount=type=ssh`; if present, wire `DOCKER_BUILD_SSH_KEY`, optionally set
106
+ `DOCKER_BUILD_SSH_KNOWN_HOSTS` for non-GitHub SSH hosts, and keep `DOCKER_BUILD_EXTRA_ARGS` to
107
+ newline-delimited single tokens such as `--build-arg=FOO=bar`.
108
+
109
+ Validate and finish:
110
+ - Run the real local validations you can: Docker build if feasible, repo tests or smoke checks, YAML
111
+ validation, and any CI-equivalent build steps.
112
+ - Push the branch and check the GitHub Actions results.
113
+ - Only stop early for a real external blocker or a product decision that changes scope.
18
114
  ```
19
115
 
20
116
  ## Local Shortcut
@@ -46,7 +142,7 @@ Stop and report the blocker instead of generating `cpflow-*` workflow files when
46
142
  The rollout is done when all of the following are true:
47
143
 
48
144
  - `.controlplane/` exists and matches the actual app shape
49
- - `.github/cpflow-help.md`, `.github/workflows/cpflow-*`, and `.github/actions/cpflow-*` are in place
145
+ - `.github/cpflow-help.md` and `.github/workflows/cpflow-*` are in place, with no `.github/actions/cpflow-*` copies
50
146
  - review apps are opt-in, staging auto-deploys from one branch, and production promotion is manual
51
147
  - required GitHub secrets and variables are documented for the repo
52
148
  - the production image build path is validated for the real app
@@ -23,7 +23,7 @@ End-to-end rollout in one view:
23
23
 
24
24
  1. `cpflow github-flow-readiness` — exits non-zero if the repo is not ready to deploy.
25
25
  2. `cpflow generate` — creates `.controlplane/` if missing.
26
- 3. `cpflow generate-github-actions` — adds `cpflow-*` workflow wrappers and `.github/actions/cpflow-*` composite actions. Review-app, staging, cleanup, and helper workflows call upstream reusable workflows; production promotion is a normal caller-repo job so it can own the protected production Environment.
26
+ 3. `cpflow generate-github-actions` — adds `cpflow-*` workflow wrappers. Review-app, staging, cleanup, and helper workflows call upstream reusable workflows; production promotion is a normal caller-repo job so it can own the protected production Environment.
27
27
  4. Configure the [GitHub Actions secrets and variables](#github-actions-secrets-and-variables) the workflows expect.
28
28
  5. Push the branch, then comment `+review-app-deploy` on a PR to spin up a review environment.
29
29
 
@@ -69,7 +69,6 @@ The second command writes namespaced files so they can coexist with an app's exi
69
69
  - `.github/workflows/cpflow-deploy-staging.yml`
70
70
  - `.github/workflows/cpflow-promote-staging-to-production.yml`
71
71
  - `.github/workflows/cpflow-cleanup-stale-review-apps.yml`
72
- - `.github/actions/cpflow-*` (the local composite actions used by those workflows)
73
72
  - `bin/pin-cpflow-github-ref`
74
73
  - `bin/test-cpflow-github-flow`
75
74
 
@@ -268,8 +267,7 @@ summary does not claim to know the production-to-staging diff.
268
267
  Existing repositories adopt this with
269
268
  `cpflow update-github-actions --workflows cpflow-promote-staging-to-production.yml`.
270
269
  Review and reapply downstream workflow customizations before committing the
271
- replacement and the refreshed local composite actions. The bare update command
272
- preserves existing top-level workflows.
270
+ replacement. The bare update command preserves existing top-level workflows.
273
271
 
274
272
  ### Recover a missing or stale production release record
275
273
 
@@ -538,12 +536,11 @@ The generated flow uses these defaults:
538
536
  makes the newest intent fail closed instead of falling back to an older operation. This permission gate applies to every
539
537
  `+review-app-deploy` comment, whether or not a review app already exists. Later pushes to a base-repository branch PR
540
538
  redeploy automatically without another approval because the auto-push path (`pull_request` event) does not use the
541
- comment permission gate. The reusable deploy workflow loads generated local actions through `actions/checkout`'s
542
- default with no `ref:` override, which resolves to the commit GitHub recorded for the triggering event: the
543
- pull-request merge revision for automatic same-repository deploys, the selected ref for manual dispatch, or the
544
- default-branch revision for comment triggers. This keeps the wrapper and generated actions synchronized during an
545
- upgrade or first-installation PR. Preserve the same-repository caller guard because pull-request workflow and action
546
- code run with staging/review credentials;
539
+ comment permission gate. The reusable deploy workflow loads its composite actions from `shakacode/control-plane-flow`
540
+ at the commit the wrapper pins, never from the pull request's revision; see
541
+ [Where the actions run from](#where-the-actions-run-from). Preserve the same-repository caller guard because a
542
+ same-repository pull request can still change the wrapper and the application code that run with staging/review
543
+ credentials;
547
544
  - fork pull requests cannot deploy via the generated `pull_request` path because the caller workflow's job-level `if:`
548
545
  condition explicitly skips fork-originated runs. For `issue_comment` events, the caller `if:` restricts invocation to
549
546
  the exact command shape; the reusable workflow resolves repository permission before its deploy job runs, then its
@@ -579,11 +576,10 @@ generated staging/review org and make that token disposable and unable to access
579
576
 
580
577
  The PR-close teardown workflow runs trusted base-branch workflow code with repository secret access so it can delete fork
581
578
  PR review apps. The generated `cpflow-delete-control-plane-app` composite action script refuses to call `cpflow delete`
582
- on any app whose name does not match the review-app prefix. This shell-level guard is effective because the generated
583
- delete workflow loads generated actions through `actions/checkout`'s default with no `ref:` override, which resolves to
584
- the commit GitHub recorded for the triggering event (`GITHUB_SHA`, the base-branch tip under `pull_request_target`). Its
585
- separate app checkout has no `ref:` override either, so `pull_request_target` teardown uses base-branch code for both
586
- paths rather than PR or fork code. If you customize this
579
+ on any app whose name does not match the review-app prefix. This shell-level guard is effective because the delete
580
+ workflow loads that action from the wrapper-pinned `.cpflow` commit. Its separate app checkout has no `ref:` override,
581
+ which resolves to the commit GitHub recorded for the triggering event (`GITHUB_SHA`, the base-branch tip under
582
+ `pull_request_target`), so teardown reads base-branch configuration rather than PR or fork code. If you customize this
587
583
  workflow, never check out PR or fork code in the same job as the delete step; doing so could let a PR replace the guard
588
584
  script itself and would also make `hooks.pre_deletion` come from the PR's `controlplane.yml`. This is still not a token
589
585
  policy, so use a scoped staging service account limited to review/staging operations. A configured
@@ -795,18 +791,36 @@ wrapper-level `if:` guard shown in that file, for example
795
791
  ## Upstream Workflows And Actions
796
792
 
797
793
  Most generated workflows are intentionally small wrappers. The deployment
798
- logic and comment formatting live in upstream reusable workflows. The canonical
799
- composite actions live in this repository and the gem copies them into each
800
- downstream repository at `.github/actions/cpflow-*`; the reusable workflows use
801
- those regular local action paths. They separately check out the matching
802
- upstream ref at `.cpflow` for the `cpflow` runtime source. Production promotion
803
- is expanded into the caller repository so it can own `environment: production`,
804
- but follows the same local-action and `.cpflow` source split.
794
+ logic and comment formatting live in upstream reusable workflows, and the
795
+ composite actions they call live in this repository. Each job checks out
796
+ `shakacode/control-plane-flow` at the pinned ref into `.cpflow`, then runs the
797
+ actions as `./.cpflow/.github/actions/cpflow-*` and builds the `cpflow` runtime
798
+ from the same checkout. A downstream repository carries the wrappers and one
799
+ pinned ref, with no copies of the actions. Production promotion is expanded
800
+ into the caller repository so it can own `environment: production`; it pins the
801
+ same source in its own `.cpflow` checkout.
805
802
 
806
803
  - `cpflow-setup-environment`: installs Ruby, the Control Plane CLI, and `cpflow`, then logs into the target org. By default it builds `cpflow` from the checked-out upstream `control-plane-flow` ref; set the `CPFLOW_VERSION` repository variable only when you want to force a published RubyGems release.
807
804
  - `cpflow-build-docker-image`: builds and pushes the app image with the desired commit SHA
808
805
  - `cpflow-delete-control-plane-app`: safely deletes temporary apps and refuses to touch names outside the configured review-app prefix
809
806
 
807
+ ### Where the actions run from
808
+
809
+ The actions that receive `CPLN_TOKEN_STAGING` come from
810
+ `shakacode/control-plane-flow` at the commit the calling wrapper pins. For a
811
+ reusable workflow that is `job.workflow_sha`; for production promotion it is the
812
+ `ref:` of the `Checkout control-plane-flow actions` step.
813
+
814
+ A same-repository pull request can change the wrapper, including its pinned
815
+ ref, and the application code and `.controlplane/` configuration that the
816
+ deployment builds. It cannot change the action code without changing that pin,
817
+ so reviewing the pin reviews the actions. The review-app jobs check out caller
818
+ files only under `app/`, and never at the workspace root.
819
+
820
+ cpflow 6.0.0 instead ran copies of the actions from the caller repository at
821
+ the triggering revision. See
822
+ [Removing cpflow 6.0.0 action copies](#removing-cpflow-600-action-copies).
823
+
810
824
  ## Version Pins: GitHub Ref vs RubyGems
811
825
 
812
826
  Generated `cpflow-*` workflow files pin `shakacode/control-plane-flow` from
@@ -828,22 +842,22 @@ that input outside the production promotion setup step, regenerate with a newer
828
842
 
829
843
  There are two coordinated inputs, and they protect different things:
830
844
 
831
- - The GitHub ref locks the reusable workflow and the `.cpflow` runtime source
832
- checkout that GitHub runs.
833
- - The installed Ruby gem supplies the generated local composite actions and
834
- wrappers. `CPFLOW_VERSION`, when configured, separately locks the published
835
- `cpflow` runtime installed by the setup action.
845
+ - The GitHub ref locks the reusable workflow, its composite actions, and the
846
+ `.cpflow` runtime source checkout that GitHub runs.
847
+ - The installed Ruby gem supplies the generated wrappers. `CPFLOW_VERSION`,
848
+ when configured, separately locks the published `cpflow` runtime installed by
849
+ the setup action.
836
850
 
837
851
  That means a downstream app cannot rely on the gem alone for GitHub Actions
838
852
  behavior. The safe stable path is still gem-driven for generation, but
839
- developers must commit the generated wrappers and local actions from the gem
840
- that matches the upstream release tag:
853
+ developers must commit the generated wrappers from the gem that matches the
854
+ upstream release tag:
841
855
 
842
856
  1. Publish a `cpflow` gem.
843
857
  2. Install or bundle that released gem in the downstream project.
844
858
  3. Run `cpflow generate-github-actions`.
845
- 4. Commit the generated wrappers and `.github/actions/cpflow-*` files; the
846
- wrappers should point to the matching upstream release tag such as `v5.0.0`.
859
+ 4. Commit the generated wrappers; they should point to the matching upstream
860
+ release tag such as `v5.0.0`.
847
861
 
848
862
  That release tag should point to the same source that produced the RubyGems
849
863
  release. Downstream production automation should use release tags, not `main` or
@@ -852,8 +866,8 @@ feature-branch refs.
852
866
  Generated cross-repository reusable-workflow calls deliberately use an exact
853
867
  release tag such as `v5.0.0`. This is the intentional downstream exception to
854
868
  the repository's full-SHA external-action policy: it keeps the installed gem,
855
- generated wrappers, generated local actions, and upstream workflow on one
856
- reviewed release update contract. It does not extend to external actions inside
869
+ generated wrappers, and upstream workflow on one reviewed release update
870
+ contract. It does not extend to external actions inside
857
871
  those workflows, which remain pinned to full commit SHAs with readable release
858
872
  comments. Unreleased downstream validation temporarily replaces the exact tag
859
873
  with the upstream PR's full commit SHA as described below; moving version
@@ -863,14 +877,14 @@ aliases and branches remain forbidden.
863
877
 
864
878
  Whenever a downstream repo updates the `cpflow` gem, update all checked-in
865
879
  GitHub Actions files in the same PR. The gem version does not make GitHub load
866
- new reusable workflow YAML or local action files by itself; GitHub uses the
867
- workflow ref and `.github/actions/cpflow-*` files committed in the repository.
880
+ new reusable workflow YAML by itself; GitHub uses the workflow ref committed in
881
+ the repository.
868
882
 
869
883
  For a new repository that does not have generated wrappers yet, run
870
884
  `cpflow generate-github-actions` first. Use the update command below for later
871
885
  gem upgrades.
872
886
 
873
- Use the installed gem to refresh the generated workflows and actions:
887
+ Use the installed gem to refresh the generated files:
874
888
 
875
889
  ```sh
876
890
  cpflow update-github-actions
@@ -884,7 +898,7 @@ bundle exec cpflow update-github-actions
884
898
  bin/test-cpflow-github-flow bundle exec cpflow
885
899
  ```
886
900
 
887
- `cpflow update-github-actions` refreshes local composite actions and helpers.
901
+ `cpflow update-github-actions` refreshes the generated helpers.
888
902
  It preserves every top-level workflow by default, including refs, triggers,
889
903
  permissions, and downstream deployment ownership. It does not create staging
890
904
  or production deployment workflows when another CI system owns those deploys.
@@ -901,13 +915,36 @@ bundle exec cpflow update-github-actions --workflows \
901
915
  Each selected wrapper is replaced in full with the installed gem's template
902
916
  and release ref. Review the diff and reapply downstream changes before
903
917
  committing. Unselected wrappers keep their refs: preservation is not evidence
904
- that old wrappers are compatible with new local actions. Migrate wrapper
918
+ that old wrappers are compatible with the new release. Migrate wrapper
905
919
  contracts and source pins together, then run the validator in the same PR.
906
920
  For a selected staging wrapper, a single existing push branch or the default
907
921
  main/master pair can be inferred. Missing, malformed, or ambiguous branch
908
922
  configuration requires an explicit `--staging-branch BRANCH`; that option also
909
923
  requires selecting `cpflow-deploy-staging.yml`.
910
924
 
925
+ ### Removing cpflow 6.0.0 action copies
926
+
927
+ cpflow 6.0.0 generated `.github/actions/cpflow-*` and ran those copies. Later
928
+ releases load the actions from the pinned ref, so the copies are unused once
929
+ every workflow has moved off 6.0.0:
930
+
931
+ 1. Update the gem, then replace the wrappers and the production promotion
932
+ workflow with `cpflow update-github-actions --workflows FILE...`. Reapply
933
+ downstream customizations.
934
+ 2. Confirm that no workflow is still pinned to cpflow 6.0.0 and that none has a
935
+ `uses: ./.github/actions/cpflow-*` line. In a workflow you maintain by hand,
936
+ change each such line to `uses: ./.cpflow/.github/actions/cpflow-*`.
937
+ 3. Delete the copies with `git rm -r .github/actions/cpflow-*`, delete any
938
+ downstream check that compares them with the gem, and run
939
+ `bin/test-cpflow-github-flow`.
940
+
941
+ `cpflow update-github-actions` lists the copies it finds and never deletes
942
+ them. A wrapper pinned to 6.0.0 runs them without naming them, and a full-SHA
943
+ pin does not say which release it is, so the command cannot prove a copy is
944
+ unused. Until step 3 the copies are harmless: a wrapper pinned to a later
945
+ release never reads them. The validator fails when a workflow names a copy
946
+ that is not checked in.
947
+
911
948
  ### Preserving downstream validation
912
949
 
913
950
  The generated `bin/test-cpflow-github-flow` runs baseline checks followed by
@@ -949,7 +986,7 @@ trusted_actions:
949
986
  ```
950
987
 
951
988
  Merge the needed entries into your existing policy rather than replacing it.
952
- `ruby/setup-ruby` is used by the generated local setup action, including in
989
+ `ruby/setup-ruby` is used by cpflow's setup action, including in
953
990
  review-app-only installations. `actions/checkout` and `docker/setup-buildx-action`
954
991
  are used directly by the generated production wrapper;
955
992
  `shakacode/control-plane-flow` supplies the reusable wrappers. Other existing
@@ -999,8 +1036,7 @@ action commit, so a moving branch named like `v5.0.0` cannot be used with
999
1036
  runners that cannot reach GitHub should leave `CPFLOW_VERSION` unset and build
1000
1037
  `cpflow` from the checked-out ref instead. When testing an unreleased upstream
1001
1038
  commit SHA, leave `CPFLOW_VERSION` unset so the workflow builds `cpflow` from the
1002
- same source that supplies the reusable workflow. Refresh the generated local
1003
- actions from that checkout before testing as described below.
1039
+ same source that supplies the reusable workflow and its actions.
1004
1040
 
1005
1041
  ## Testing Unreleased Upstream Changes Downstream
1006
1042
 
@@ -1016,8 +1052,7 @@ releasing it. Use an immutable commit SHA from the upstream PR branch:
1016
1052
  bin/pin-cpflow-github-ref --version <reviewed-version-tag> <upstream-pr-sha>
1017
1053
  ```
1018
1054
 
1019
- The update command copies the PR checkout's canonical composite actions into
1020
- `.github/actions/cpflow-*`. The pin helper then updates every generated
1055
+ The pin helper updates every generated
1021
1056
  reusable-workflow `uses:` ref plus the production workflow's pinned
1022
1057
  `control-plane-flow` checkout and setup validation ref. It accepts release
1023
1058
  tags and full commit SHAs (with a reviewed `--version` label), rejects branch names such as `main` or
@@ -1045,19 +1080,18 @@ releasing it. Use an immutable commit SHA from the upstream PR branch:
1045
1080
  matches the repository's installation state.
1046
1081
 
1047
1082
  For an existing installation, where `cpflow-deploy-review-app.yml` is already
1048
- present on the default branch, dispatch the workflow definition and generated
1049
- local actions from the unmerged downstream test branch against a suitable open
1050
- base-repository PR:
1083
+ present on the default branch, dispatch the workflow definition from the
1084
+ unmerged downstream test branch against a suitable open base-repository PR:
1051
1085
 
1052
1086
  ```sh
1053
1087
  gh workflow run cpflow-deploy-review-app.yml --ref <downstream-test-branch> -f pr_number=<pr-number>
1054
1088
  ```
1055
1089
 
1056
- The `--ref` selects the downstream wrapper and local-action commit under test;
1090
+ The `--ref` selects the downstream wrapper commit under test;
1057
1091
  `pr_number` selects the application source to deploy. Do not use a
1058
1092
  `+review-app-deploy` comment for this pre-merge validation: `issue_comment`
1059
1093
  always loads the workflow definition from the default branch, so it cannot
1060
- validate unmerged wrappers or local actions.
1094
+ validate unmerged wrappers.
1061
1095
 
1062
1096
  For a first installation, GitHub will not dispatch a workflow that does not
1063
1097
  yet exist on the default branch, even when `--ref` names the installation
@@ -1078,9 +1112,9 @@ releasing it. Use an immutable commit SHA from the upstream PR branch:
1078
1112
  `bin/pin-cpflow-github-ref vX.Y.Z` only for a ref-only update when the
1079
1113
  generated templates are already current.
1080
1114
 
1081
- For an existing installation, this tests the real reusable workflows, the
1082
- unmerged regenerated local composite actions, and the source-built `cpflow` gem
1083
- against one immutable upstream commit. A first installation gets the same remote
1115
+ For an existing installation, this tests the real reusable workflows, their
1116
+ composite actions, and the source-built `cpflow` gem against one immutable
1117
+ upstream commit. A first installation gets the same remote
1084
1118
  proof immediately after its locally validated files reach the default branch.
1085
1119
  Both paths avoid testing against a moving upstream branch.
1086
1120
 
data/docs/commands.md CHANGED
@@ -234,8 +234,8 @@ Creates GitHub Actions templates for a Heroku Flow style Control Plane pipeline:
234
234
  - manual promotion from staging to production
235
235
  - nightly cleanup and PR help workflows
236
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.
237
+ The workflows load cpflow's composite actions from this gem's repository at
238
+ the ref each workflow pins, so no `.github/actions/cpflow-*` copies are generated.
239
239
 
240
240
  Pass `--staging-branch BRANCH` when staging should auto-deploy from a branch
241
241
  other than `main` or `master`; the generator will bake that branch into the
@@ -245,7 +245,7 @@ Pass `--force` to overwrite existing generated files. Prefer
245
245
  repo.
246
246
 
247
247
  ```sh
248
- # Creates workflow wrappers, local composite actions, and validation helpers
248
+ # Creates workflow wrappers and validation helpers
249
249
  cpflow generate-github-actions
250
250
 
251
251
  # Creates the flow with staging deploys triggered from develop
@@ -591,11 +591,14 @@ cpflow terraform import
591
591
 
592
592
  ### `update-github-actions`
593
593
 
594
- Refreshes local composite actions and helper files from the installed gem.
594
+ Refreshes helper files from the installed gem. Workflows load cpflow's
595
+ composite actions from the pinned cpflow ref, so the command reports any
596
+ `.github/actions/cpflow-*` copies left by cpflow 6.0.0 for you to delete
597
+ once no workflow is pinned to 6.0.0. It never deletes them itself.
595
598
  All top-level workflows are preserved by default, including their refs,
596
599
  triggers, permissions, and deployment ownership. Use --workflows FILE...
597
600
  to explicitly add or replace named generated workflows. Replacement resets
598
- each selected workflow to its template and matching v6.0.0 ref;
601
+ each selected workflow to its template and matching v6.0.1 ref;
599
602
  review and reapply any downstream customizations in the same PR.
600
603
 
601
604
  Selecting cpflow-deploy-staging.yml preserves a single existing push branch
@@ -609,7 +612,7 @@ invokes that extension after its baseline checks; the extension is never
609
612
  generated or overwritten. See docs/ci-automation.md for migration guidance.
610
613
 
611
614
  ```sh
612
- # Refresh actions/helpers while preserving downstream workflows
615
+ # Refresh helpers while preserving downstream workflows
613
616
  cpflow update-github-actions
614
617
 
615
618
  # When running cpflow through Bundler
@@ -22,24 +22,21 @@ module Command
22
22
  VALIDATIONS = [].freeze
23
23
  REQUIRES_STARTUP_CHECKS = false
24
24
 
25
+ # The prompt lives in its own file with one instruction per bullet, so a person can read it and
26
+ # a change shows as a small diff. docs/ai-github-flow-prompt.md carries the same text with no app
27
+ # prefix; a spec keeps the two equal.
28
+ PROMPT_PATH = File.expand_path("ai_github_flow_prompt.txt", __dir__)
29
+
30
+ def self.text(app_prefix: nil)
31
+ format(File.read(PROMPT_PATH), prefix_note: app_prefix ? " (`#{app_prefix}`)" : "")
32
+ end
33
+
25
34
  def call
26
- puts prompt
35
+ puts self.class.text(app_prefix: inferred_app_prefix)
27
36
  end
28
37
 
29
38
  private
30
39
 
31
- def prompt
32
- <<~PROMPT
33
- 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.
34
-
35
- If `.controlplane/` is missing, run `cpflow generate`. Treat the generated app names as the repo-name default (`#{inferred_app_prefix}`) and rename them only if the project needs a different prefix. Then run `cpflow generate-github-actions` (or `cpflow generate-github-actions --staging-branch BRANCH` when staging should deploy from a branch other than `main`/`master`), keep review apps opt-in via `+review-app-deploy`, make sure any `STAGING_APP_BRANCH` repository variable is also present in the generated staging workflow's `on.push.branches` filter, and list the GitHub secrets and variables that must be configured. Do not hand-edit duplicated upstream refs into the generated wrappers. Keep every generated Control Plane Flow source pin synchronized at the same `vX.Y.Z`: each reusable-workflow `uses: ...@vX.Y.Z` ref, the production promotion `.cpflow` checkout ref, and its `control_plane_flow_ref` setup input. By default, `cpflow generate-github-actions` derives these values from the installed `cpflow` gem version; temporary explicit ref overrides are for unreleased testing and must keep all three values synchronized. Keep the generated `.github/actions/cpflow-*` files from that same gem unchanged; upstream reusable workflows invoke those checked-in local actions and separately check out the matching runtime source at `.cpflow`. When bumping the `cpflow` gem in a downstream repo, `cpflow update-github-actions` refreshes local actions and helpers while preserving all top-level workflows, including their refs and triggers. Use `cpflow update-github-actions --workflows FILE...` to explicitly select wrappers for replacement, then review the diff and reapply downstream customizations. A differing existing validator blocks all writes: preserve its downstream checks in executable `bin/test-cpflow-github-flow-custom`, then remove or rename the old generated validator before retrying. The generated validator invokes that preserved extension and propagates its failures. Follow `docs/ci-automation.md` for the migration procedure, keep source pins compatible with the updated actions, and run `bin/test-cpflow-github-flow` in the same PR (or `bin/test-cpflow-github-flow bundle exec cpflow` when using Bundler). 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.
36
-
37
- Keep Node available in the final image if asset compilation or SSR depends on ExecJS, Yarn, `pnpm`, or npm after the main install layer. Make sure the generated Dockerfile uses a Ruby base image compatible with the app's declared Ruby requirement. Preserve repo-defined frontend build hooks: if `config/shakapacker.yml` defines a `precompile_hook`, or React on Rails enables `config.auto_load_bundle = true`, confirm the generated Dockerfile runs that codegen step before `rails assets:precompile`. If `config/database.yml` shows SQLite in production, confirm that the generated scaffold redirects every production database file into persistent `/app/data` (or keeps it under persistent `/app/storage`) without mounting over the image's `/app/db` migrations, stores Active Storage under `/app/storage`, and runs `rails db:prepare`; otherwise keep the default Postgres workload. If the public workload is not named `rails`, set `PRIMARY_WORKLOAD` or adjust the generated workflows. Inspect the Dockerfile and package sources for private GitHub dependencies or `RUN --mount=type=ssh`; if present, wire `DOCKER_BUILD_SSH_KEY`, optionally set `DOCKER_BUILD_SSH_KNOWN_HOSTS` for non-GitHub SSH hosts, and keep `DOCKER_BUILD_EXTRA_ARGS` to newline-delimited single tokens such as `--build-arg=FOO=bar`.
38
-
39
- 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.
40
- PROMPT
41
- end
42
-
43
40
  def inferred_app_prefix
44
41
  RepoIntrospection.inferred_app_prefix(Dir.pwd)
45
42
  end
@@ -0,0 +1,103 @@
1
+ Set up Control Plane GitHub Flow for this repo.
2
+
3
+ Before generating anything:
4
+ - First make sure the `cpflow` CLI is available: use the repo's existing `bundle exec cpflow` if
5
+ present, otherwise install the published `cpflow` Ruby gem with `gem install cpflow`; if neither
6
+ is possible, stop and report that blocker.
7
+ - Use the same `cpflow` invocation for the rest of the rollout.
8
+ - Start with `cpflow github-flow-readiness` and stop on any reported blockers.
9
+ - The repo must be deployable from a clean clone: published package versions, complete runtime
10
+ scaffold, and a production Dockerfile that can build the app.
11
+ - If any package version is unpublished, inaccessible from CI, or requires credentials that are not
12
+ already modeled in the repo or GitHub settings, stop and report the blocker instead of generating
13
+ workflow files.
14
+ - If the repo is a legacy sample pinned to an obsolete Ruby or Bundler toolchain, if it does not
15
+ even have a production Dockerfile yet, or if it is a monorepo without an already-decided single
16
+ app boundary for this flow, stop and report that as a prerequisite instead of forcing the rollout.
17
+
18
+ Generate the flow:
19
+ - If `.controlplane/` is missing, run `cpflow generate`.
20
+ - Treat the generated app names as the repo-name default%<prefix_note>s and rename them only if the
21
+ project needs a different prefix.
22
+ - Then run `cpflow generate-github-actions` (or `cpflow generate-github-actions --staging-branch
23
+ BRANCH` when staging should deploy from a branch other than `main`/`master`).
24
+ - Keep review apps opt-in via `+review-app-deploy`.
25
+ - Make sure any `STAGING_APP_BRANCH` repository variable is also present in the generated staging
26
+ workflow's `on.push.branches` filter.
27
+ - List the GitHub secrets and variables that must be configured.
28
+
29
+ Keep the Control Plane Flow source pins together:
30
+ - Do not hand-edit duplicated upstream refs into the generated wrappers.
31
+ - Keep every generated Control Plane Flow source pin synchronized at the same `vX.Y.Z`: each
32
+ reusable-workflow `uses: ...@vX.Y.Z` ref, the production promotion `.cpflow` checkout ref, and its
33
+ `control_plane_flow_ref` setup input.
34
+ - By default, `cpflow generate-github-actions` derives these values from the installed `cpflow` gem
35
+ version; temporary explicit ref overrides are for unreleased testing and must keep all three
36
+ values synchronized.
37
+ - Do not add `.github/actions/cpflow-*` copies; the workflows load cpflow's composite actions and
38
+ runtime source from the pinned `.cpflow` checkout.
39
+
40
+ When bumping the `cpflow` gem in a downstream repo:
41
+ - `cpflow update-github-actions` refreshes helpers while preserving all top-level workflows,
42
+ including their refs and triggers.
43
+ - It reports `.github/actions/cpflow-*` copies left by cpflow 6.0.0; delete them once no workflow is
44
+ pinned to 6.0.0 or names one.
45
+ - Use `cpflow update-github-actions --workflows FILE...` to explicitly select wrappers for
46
+ replacement, then review the diff and reapply downstream customizations.
47
+ - A differing existing validator blocks all writes: preserve its downstream checks in executable
48
+ `bin/test-cpflow-github-flow-custom`, then remove or rename the old generated validator before
49
+ retrying.
50
+ - The generated validator invokes that preserved extension and propagates its failures.
51
+ - Follow `docs/ci-automation.md` for the migration procedure, keep source pins compatible with the
52
+ updated actions, and run `bin/test-cpflow-github-flow` in the same PR (or
53
+ `bin/test-cpflow-github-flow bundle exec cpflow` when using Bundler).
54
+
55
+ Review apps and workload sizing:
56
+ - Keep the normal generated review-app setup simple: review apps require only `CPLN_TOKEN_STAGING`
57
+ when the generated review app config can be inferred.
58
+ - For public demos, starter staging apps, and long-lived review apps, keep the app workload `type:
59
+ standard` with one warm replica, set its autoscaling metric to `disabled`, and enable `capacityAI:
60
+ true` so Control Plane can right-size CPU and memory allocation at that fixed replica count.
61
+ - Shared Postgres and other stateful workloads are the usual exceptions and should stay manually
62
+ sized; Capacity AI is for supported stateless app/service workloads.
63
+ - If true idle scale-to-zero is explicitly required, create a separate `serverless` workload before
64
+ first deploy or plan a delete/recreate migration because Control Plane will not change an existing
65
+ `standard` workload to `serverless` in place.
66
+ - For shared review-app resources such as one staging database, use `shared_secret_grants` and
67
+ `{{SHARED_SECRET_DATABASE}}` placeholders instead of hardcoding the base app secret name; this
68
+ keeps review-app policy binding and cleanup automatic while avoiding per-PR database cost.
69
+
70
+ Staging and production:
71
+ - Document the one-time Control Plane bootstrap command for persistent staging and production apps
72
+ with `cpflow setup-app --skip-post-creation-hook`; for existing apps or later template updates,
73
+ document `cpflow apply-template` and the need for the app identity to have `reveal` on the app
74
+ secret policy.
75
+ - Do not imply the staging deploy or promotion workflows create those persistent GVCs.
76
+ - For production promotion, document a protected `production` GitHub Environment with required
77
+ reviewers, prevent self-review, and `CPLN_TOKEN_PRODUCTION` stored as an environment secret, not
78
+ as a repository or organization secret.
79
+
80
+ Docker image and runtime:
81
+ - Keep Node available in the final image if asset compilation or SSR depends on ExecJS, Yarn,
82
+ `pnpm`, or npm after the main install layer.
83
+ - Make sure the generated Dockerfile uses a Ruby base image compatible with the app's declared Ruby
84
+ requirement.
85
+ - Preserve repo-defined frontend build hooks: if `config/shakapacker.yml` defines a
86
+ `precompile_hook`, or React on Rails enables `config.auto_load_bundle = true`, confirm the
87
+ generated Dockerfile runs that codegen step before `rails assets:precompile`.
88
+ - If `config/database.yml` shows SQLite in production, confirm that the generated scaffold redirects
89
+ every production database file into persistent `/app/data` (or keeps it under persistent
90
+ `/app/storage`) without mounting over the image's `/app/db` migrations, stores Active Storage
91
+ under `/app/storage`, and runs `rails db:prepare`; otherwise keep the default Postgres workload.
92
+ - If the public workload is not named `rails`, set `PRIMARY_WORKLOAD` or adjust the generated
93
+ workflows.
94
+ - Inspect the Dockerfile and package sources for private GitHub dependencies or `RUN
95
+ --mount=type=ssh`; if present, wire `DOCKER_BUILD_SSH_KEY`, optionally set
96
+ `DOCKER_BUILD_SSH_KNOWN_HOSTS` for non-GitHub SSH hosts, and keep `DOCKER_BUILD_EXTRA_ARGS` to
97
+ newline-delimited single tokens such as `--build-arg=FOO=bar`.
98
+
99
+ Validate and finish:
100
+ - Run the real local validations you can: Docker build if feasible, repo tests or smoke checks, YAML
101
+ validation, and any CI-equivalent build steps.
102
+ - Push the branch and check the GitHub Actions results.
103
+ - Only stop early for a real external blocker or a product decision that changes scope.
@@ -105,8 +105,8 @@ module Command
105
105
  - manual promotion from staging to production
106
106
  - nightly cleanup and PR help workflows
107
107
 
108
- It also copies cpflow's composite actions into `.github/actions/cpflow-*`
109
- so every local `uses:` target is checked in and can be audited directly.
108
+ The workflows load cpflow's composite actions from this gem's repository at
109
+ the ref each workflow pins, so no `.github/actions/cpflow-*` copies are generated.
110
110
 
111
111
  Pass `--staging-branch BRANCH` when staging should auto-deploy from a branch
112
112
  other than `main` or `master`; the generator will bake that branch into the
@@ -117,7 +117,7 @@ module Command
117
117
  DESC
118
118
  EXAMPLES = <<~EX
119
119
  ```sh
120
- # Creates workflow wrappers, local composite actions, and validation helpers
120
+ # Creates workflow wrappers and validation helpers
121
121
  cpflow generate-github-actions
122
122
 
123
123
  # Creates the flow with staging deploys triggered from develop
@@ -131,23 +131,16 @@ module Command
131
131
  VALIDATIONS = [].freeze
132
132
  REQUIRES_STARTUP_CHECKS = false
133
133
 
134
- # Resolve source roots from __dir__ rather than Cpflow.root_path because this file is
134
+ # Resolve the source root from __dir__ rather than Cpflow.root_path because this file is
135
135
  # loaded before `module Cpflow` finishes defining its class methods.
136
- REPOSITORY_ROOT = Pathname.new(File.expand_path("../..", __dir__))
137
136
  TEMPLATE_ROOT = Pathname.new(File.expand_path("../github_flow_templates", __dir__))
138
- ACTIONS_ROOT = REPOSITORY_ROOT.join(".github/actions")
139
137
 
140
138
  def self.generated_file_sources
141
139
  ensure_template_root!
142
140
 
143
- template_sources = files_beneath(TEMPLATE_ROOT).to_h do |source|
141
+ files_beneath(TEMPLATE_ROOT).to_h do |source|
144
142
  [source.relative_path_from(TEMPLATE_ROOT).to_s, source]
145
- end
146
- action_sources = files_beneath(ACTIONS_ROOT, "cpflow-*/**/*").to_h do |source|
147
- [source.relative_path_from(REPOSITORY_ROOT).to_s, source]
148
- end
149
-
150
- template_sources.merge(action_sources).sort.to_h.freeze
143
+ end.sort.to_h.freeze
151
144
  end
152
145
 
153
146
  def self.generated_files
@@ -160,11 +153,10 @@ module Command
160
153
 
161
154
  def self.ensure_template_root!
162
155
  raise "cpflow template directory not found: #{TEMPLATE_ROOT}" unless TEMPLATE_ROOT.directory?
163
- raise "cpflow action directory not found: #{ACTIONS_ROOT}" unless ACTIONS_ROOT.directory?
164
156
  end
165
157
 
166
- def self.files_beneath(root, pattern = "**/*")
167
- Dir.glob(root.join(pattern).to_s, File::FNM_DOTMATCH)
158
+ def self.files_beneath(root)
159
+ Dir.glob(root.join("**/*").to_s, File::FNM_DOTMATCH)
168
160
  .select { |path| File.file?(path) }
169
161
  .map { |path| Pathname.new(path) }
170
162
  .sort
@@ -18,7 +18,10 @@ module Command
18
18
  }].freeze
19
19
  DESCRIPTION = "Regenerates GitHub Actions files for the installed cpflow version"
20
20
  LONG_DESCRIPTION = <<~DESC.freeze
21
- Refreshes local composite actions and helper files from the installed gem.
21
+ Refreshes helper files from the installed gem. Workflows load cpflow's
22
+ composite actions from the pinned cpflow ref, so the command reports any
23
+ `.github/actions/cpflow-*` copies left by cpflow 6.0.0 for you to delete
24
+ once no workflow is pinned to 6.0.0. It never deletes them itself.
22
25
  All top-level workflows are preserved by default, including their refs,
23
26
  triggers, permissions, and deployment ownership. Use --workflows FILE...
24
27
  to explicitly add or replace named generated workflows. Replacement resets
@@ -37,7 +40,7 @@ module Command
37
40
  DESC
38
41
  EXAMPLES = <<~EX
39
42
  ```sh
40
- # Refresh actions/helpers while preserving downstream workflows
43
+ # Refresh helpers while preserving downstream workflows
41
44
  cpflow update-github-actions
42
45
 
43
46
  # When running cpflow through Bundler
@@ -64,6 +67,7 @@ module Command
64
67
  branch = selected_staging_branch(workflows)
65
68
  abort_if_custom_validator!
66
69
  GithubActionsGenerator.new([branch].compact, workflows: workflows).invoke_all
70
+ report_vendored_actions
67
71
 
68
72
  print_post_update_message
69
73
  end
@@ -102,6 +106,7 @@ module Command
102
106
 
103
107
  def abort_if_no_generated_files!
104
108
  return if GenerateGithubActions.generated_files.any? { |path| File.exist?(path) }
109
+ return if vendored_action_directories.any?
105
110
 
106
111
  Shell.abort(<<~MESSAGE)
107
112
  No generated cpflow GitHub Actions files found in this repository.
@@ -110,6 +115,27 @@ module Command
110
115
  MESSAGE
111
116
  end
112
117
 
118
+ # cpflow 6.0.0 copied its composite actions here. Scan the caller's tree rather than this
119
+ # gem's action names, so a copy is still found after an action is renamed or removed.
120
+ def vendored_action_directories
121
+ Dir.glob(".github/actions/cpflow-*").select { |path| File.directory?(path) }.sort
122
+ end
123
+
124
+ # Whether a copy is still needed depends on the cpflow release each wrapper pins, which a
125
+ # full-SHA pin does not reveal, so deletion stays a reviewed human step.
126
+ def report_vendored_actions
127
+ directories = vendored_action_directories
128
+ return if directories.empty?
129
+
130
+ Shell.warn(<<~MESSAGE)
131
+ cpflow no longer generates or updates these local action copies:
132
+ #{directories.map { |path| "- #{path}" }.join("\n")}
133
+ A workflow pinned to cpflow 6.0.0 still runs them. Delete them once every cpflow workflow
134
+ in this repository is pinned to a later release and none has a
135
+ `uses: ./.github/actions/cpflow-*` line. See docs/ci-automation.md.
136
+ MESSAGE
137
+ end
138
+
113
139
  def print_post_update_message
114
140
  Shell.info("")
115
141
  Shell.info("Updated cpflow GitHub Actions files for cpflow #{Cpflow::VERSION}.")
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cpflow
4
- VERSION = "6.0.0"
4
+ VERSION = "6.0.1"
5
5
  MIN_CPLN_VERSION = "3.1.0"
6
6
  end
@@ -152,8 +152,8 @@ validator, first preserve its downstream checks in executable
152
152
  `bin/test-cpflow-github-flow-custom`, then remove or rename the old generated
153
153
  validator before retrying the update.
154
154
 
155
- After updating the `cpflow` gem in this repo, refresh the generated local
156
- actions and validation helpers in the same PR. The default command
155
+ After updating the `cpflow` gem in this repo, refresh the generated
156
+ validation helpers in the same PR. The default command
157
157
  preserves every top-level workflow, including its refs, triggers, permissions, and
158
158
  downstream customizations:
159
159
 
@@ -85,7 +85,7 @@ jobs:
85
85
  echo "org=$org" >> "$GITHUB_OUTPUT"
86
86
 
87
87
  - name: Setup staging environment
88
- uses: ./.github/actions/cpflow-setup-environment
88
+ uses: ./.cpflow/.github/actions/cpflow-setup-environment
89
89
  with:
90
90
  token: ${{ secrets.CPLN_TOKEN_STAGING }}
91
91
  org: ${{ steps.staging-org.outputs.org }}
@@ -97,7 +97,7 @@ jobs:
97
97
 
98
98
  - name: Preview commits before production approval
99
99
  id: preview
100
- uses: ./.github/actions/cpflow-preview-promotion
100
+ uses: ./.cpflow/.github/actions/cpflow-preview-promotion
101
101
  env:
102
102
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
103
103
  GH_REPO: ${{ github.repository }}
@@ -156,7 +156,7 @@ jobs:
156
156
  fi
157
157
 
158
158
  - name: Validate required secrets and variables
159
- uses: ./.github/actions/cpflow-validate-config
159
+ uses: ./.cpflow/.github/actions/cpflow-validate-config
160
160
  # Pass secrets via env so the composite action checks indirect shell
161
161
  # variables instead of interpolating secret values into a run script.
162
162
  env:
@@ -237,7 +237,7 @@ jobs:
237
237
  } >> "$GITHUB_OUTPUT"
238
238
 
239
239
  - name: Setup production environment
240
- uses: ./.github/actions/cpflow-setup-environment
240
+ uses: ./.cpflow/.github/actions/cpflow-setup-environment
241
241
  with:
242
242
  token: ${{ secrets.CPLN_TOKEN_PRODUCTION }}
243
243
  org: ${{ steps.cpln-orgs.outputs.production }}
@@ -303,7 +303,7 @@ jobs:
303
303
 
304
304
  - name: Detect release phase support
305
305
  id: release-phase
306
- uses: ./.github/actions/cpflow-detect-release-phase
306
+ uses: ./.cpflow/.github/actions/cpflow-detect-release-phase
307
307
  with:
308
308
  app_name: ${{ vars.PRODUCTION_APP_NAME }}
309
309
 
@@ -698,7 +698,7 @@ jobs:
698
698
 
699
699
  - name: Wait for deployment health
700
700
  id: health-check
701
- uses: ./.github/actions/cpflow-wait-for-health
701
+ uses: ./.cpflow/.github/actions/cpflow-wait-for-health
702
702
  with:
703
703
  workload_name: ${{ steps.workloads.outputs.primary }}
704
704
  app_name: ${{ vars.PRODUCTION_APP_NAME }}
@@ -29,7 +29,11 @@ documents.each_key do |path|
29
29
  puts "parsed #{path}"
30
30
  end
31
31
 
32
- generated_local_action_pattern = %r{\A\./(?<directory>\.github/actions/cpflow-[a-z0-9]+(?:-[a-z0-9]+)*)\z}
32
+ # Current workflows load cpflow actions from the pinned `.cpflow` checkout, which exists
33
+ # only at run time. Caller-owned `.github/actions/cpflow-*` copies are the cpflow 6.0.0
34
+ # layout; a workflow that still references one needs that copy checked in.
35
+ generated_local_action_pattern =
36
+ %r{\A\./(?<checkout>\.cpflow/)?(?<directory>\.github/actions/cpflow-[a-z0-9]+(?:-[a-z0-9]+)*)\z}
33
37
  local_action_references = []
34
38
  invalid_local_action_references = []
35
39
  visited_container_ids = {}
@@ -43,10 +47,10 @@ walk = lambda do |node, source_path|
43
47
  case node
44
48
  when Hash
45
49
  uses = node["uses"]
46
- if uses.is_a?(String) && uses.start_with?("./.github/actions/cpflow-")
50
+ if uses.is_a?(String) && uses.start_with?("./.github/actions/cpflow-", "./.cpflow/")
47
51
  match = uses.match(generated_local_action_pattern)
48
52
  if match
49
- local_action_references << [source_path, match[:directory]]
53
+ local_action_references << [source_path, match[:directory]] unless match[:checkout]
50
54
  else
51
55
  invalid_local_action_references <<
52
56
  "#{source_path} has invalid generated local action reference #{uses}"
@@ -61,6 +65,24 @@ documents.each { |path, document| walk.call(document, path) }
61
65
 
62
66
  abort invalid_local_action_references.uniq.join("\n") unless invalid_local_action_references.empty?
63
67
 
68
+ # `.cpflow` exists only after a job checks it out, so each job must do that before its first cpflow action.
69
+ missing_checkouts = documents.flat_map do |path, document|
70
+ jobs = document.is_a?(Hash) && document["jobs"].is_a?(Hash) ? document["jobs"] : {}
71
+ jobs.filter_map do |job_name, job|
72
+ steps = job.is_a?(Hash) && job["steps"].is_a?(Array) ? job["steps"].grep(Hash) : []
73
+ first_action = steps.index { |step| step["uses"].to_s.start_with?("./.cpflow/") }
74
+ next unless first_action
75
+
76
+ checked_out = steps.take(first_action).any? do |step|
77
+ step["uses"].to_s.start_with?("actions/checkout@") &&
78
+ step["with"].is_a?(Hash) && step["with"]["path"] == ".cpflow" && !step["with"]["ref"].to_s.strip.empty?
79
+ end
80
+ "#{path}:#{job_name} must check out a pinned ref into .cpflow before its first cpflow action" unless checked_out
81
+ end
82
+ end
83
+
84
+ abort missing_checkouts.join("\n") unless missing_checkouts.empty?
85
+
64
86
  missing_actions = local_action_references.uniq.filter_map do |source_path, action_directory|
65
87
  directory_is_local = begin
66
88
  File.lstat(action_directory).directory?
@@ -79,7 +101,7 @@ missing_actions = local_action_references.uniq.filter_map do |source_path, actio
79
101
  end
80
102
 
81
103
  abort missing_actions.join("\n") unless missing_actions.empty?
82
- puts "all referenced local actions have checked-in descriptors"
104
+ puts "all cpflow action references use the pinned checkout or a checked-in descriptor"
83
105
  RUBY
84
106
 
85
107
  echo "==> check composite action input descriptions"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cpflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0
4
+ version: 6.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Gordon
@@ -184,6 +184,7 @@ files:
184
184
  - examples/circleci.yml
185
185
  - examples/controlplane.yml
186
186
  - lib/command/ai_github_flow_prompt.rb
187
+ - lib/command/ai_github_flow_prompt.txt
187
188
  - lib/command/apply_template.rb
188
189
  - lib/command/base.rb
189
190
  - lib/command/base_sub_command.rb
@@ -317,7 +318,7 @@ licenses:
317
318
  metadata:
318
319
  rubygems_mfa_required: 'true'
319
320
  post_install_message: |
320
- cpflow 6.0.0 installed.
321
+ cpflow 6.0.1 installed.
321
322
  Generated GitHub Actions users: run `cpflow update-github-actions` after upgrading.
322
323
  Docs: https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md#updating-generated-github-actions-after-gem-updates
323
324
  rdoc_options: []