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 +4 -4
- data/.github/workflows/cpflow-cleanup-stale-review-apps.yml +3 -10
- data/.github/workflows/cpflow-delete-review-app.yml +4 -11
- data/.github/workflows/cpflow-deploy-review-app.yml +6 -13
- data/.github/workflows/cpflow-deploy-staging.yml +5 -11
- data/.github/workflows/cpflow-promote-staging-to-production.yml +6 -6
- data/CHANGELOG.md +9 -1
- data/CONTRIBUTING.md +3 -3
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/docs/ai-github-flow-prompt.md +104 -8
- data/docs/ci-automation.md +85 -51
- data/docs/commands.md +9 -6
- data/lib/command/ai_github_flow_prompt.rb +10 -13
- data/lib/command/ai_github_flow_prompt.txt +103 -0
- data/lib/command/generate_github_actions.rb +8 -16
- data/lib/command/update_github_actions.rb +28 -2
- data/lib/cpflow/version.rb +1 -1
- data/lib/github_flow_templates/.github/cpflow-help.md +2 -2
- data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +6 -6
- data/lib/github_flow_templates/bin/test-cpflow-github-flow +26 -4
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5339d75233bef2f62028093b5037a44155a56d2c4f39741efbc4861c47848e86
|
|
4
|
+
data.tar.gz: bd30c3c9da8a468b6e5cc9479ed6be3912066b70e03f021533a815498756f162
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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.
|
|
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/`
|
|
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.
|
|
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
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`
|
|
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.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
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
|
data/docs/ci-automation.md
CHANGED
|
@@ -23,7 +23,7 @@ End-to-end rollout in one view:
|
|
|
23
23
|
|
|
24
24
|
1. `cpflow github-flow-readiness` — exits non-zero if the repo is not ready to deploy.
|
|
25
25
|
2. `cpflow generate` — creates `.controlplane/` if missing.
|
|
26
|
-
3. `cpflow generate-github-actions` — adds `cpflow-*` workflow wrappers
|
|
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
|
|
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
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
|
583
|
-
|
|
584
|
-
the commit GitHub recorded for the triggering event (`GITHUB_SHA`, the base-branch tip under
|
|
585
|
-
|
|
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
|
|
799
|
-
composite actions live in this repository
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
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
|
|
832
|
-
checkout that GitHub runs.
|
|
833
|
-
- The installed Ruby gem supplies the generated
|
|
834
|
-
|
|
835
|
-
|
|
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
|
|
840
|
-
|
|
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
|
|
846
|
-
|
|
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,
|
|
856
|
-
|
|
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
|
|
867
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1049
|
-
|
|
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
|
|
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
|
|
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,
|
|
1082
|
-
|
|
1083
|
-
|
|
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
|
-
|
|
238
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
109
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
167
|
-
Dir.glob(root.join(
|
|
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
|
|
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
|
|
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}.")
|
data/lib/cpflow/version.rb
CHANGED
|
@@ -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
|
|
156
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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: []
|