cpflow 5.3.0 → 6.0.0.rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/agent-workflow.yml +13 -0
  3. data/.agents/bin/README.md +12 -1
  4. data/.agents/bin/docs +1 -1
  5. data/.agents/bin/lint +1 -1
  6. data/.agents/bin/setup +1 -1
  7. data/.agents/bin/test +1 -1
  8. data/.agents/bin/validate +2 -1
  9. data/.coderabbit.yaml +13 -0
  10. data/.github/actions/cpflow-delete-control-plane-app/action.yml +2 -1
  11. data/.github/actions/cpflow-setup-environment/action.yml +8 -6
  12. data/.github/dependabot.yml +10 -0
  13. data/.github/workflows/check_cpln_links.yml +6 -1
  14. data/.github/workflows/claude-code-review.yml +5 -2
  15. data/.github/workflows/claude.yml +6 -4
  16. data/.github/workflows/coderabbit-lifecycle-review.yml +29 -0
  17. data/.github/workflows/command_docs.yml +7 -2
  18. data/.github/workflows/cpflow-cleanup-stale-review-apps.yml +12 -5
  19. data/.github/workflows/cpflow-delete-review-app.yml +18 -11
  20. data/.github/workflows/cpflow-deploy-review-app.yml +25 -16
  21. data/.github/workflows/cpflow-deploy-staging.yml +16 -10
  22. data/.github/workflows/cpflow-help-command.yml +3 -3
  23. data/.github/workflows/cpflow-promote-staging-to-production.yml +7 -7
  24. data/.github/workflows/cpflow-review-app-help.yml +1 -1
  25. data/.github/workflows/rspec-shared.yml +34 -26
  26. data/.github/workflows/rspec-specific.yml +10 -2
  27. data/.github/workflows/rspec.yml +71 -4
  28. data/.github/workflows/rubocop.yml +9 -2
  29. data/.github/workflows/trigger-docs-site.yml +2 -0
  30. data/.rubocop.yml +6 -1
  31. data/CHANGELOG.md +24 -1
  32. data/CONTRIBUTING.md +39 -2
  33. data/Gemfile.lock +1 -1
  34. data/README.md +6 -1
  35. data/cpflow.gemspec +3 -15
  36. data/docs/ai-github-flow-prompt.md +2 -2
  37. data/docs/ci-automation.md +112 -60
  38. data/docs/commands.md +18 -8
  39. data/docs/rds-private-networking.md +12 -12
  40. data/docs/releasing.md +15 -4
  41. data/examples/controlplane.yml +5 -0
  42. data/lib/command/ai_github_flow_prompt.rb +1 -1
  43. data/lib/command/cleanup_stale_apps.rb +28 -4
  44. data/lib/command/copy_image_from_upstream.rb +3 -0
  45. data/lib/command/deploy_image.rb +38 -1
  46. data/lib/command/generate_github_actions.rb +36 -12
  47. data/lib/command/run.rb +224 -34
  48. data/lib/command/update_github_actions.rb +7 -6
  49. data/lib/core/controlplane.rb +145 -78
  50. data/lib/core/shell.rb +35 -10
  51. data/lib/core/timed_command.rb +111 -0
  52. data/lib/cpflow/version.rb +1 -1
  53. data/lib/cpflow.rb +1 -1
  54. data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +7 -7
  55. data/lib/github_flow_templates/bin/test-cpflow-github-flow +63 -3
  56. data/lib/patches/hash.rb +2 -2
  57. data/rakelib/create_release.rake +14 -14
  58. data/script/check_shell_scripts +66 -0
  59. metadata +11 -18
@@ -60,9 +60,15 @@ jobs:
60
60
  echo "is_deployable=false" >> "$GITHUB_OUTPUT"
61
61
  fi
62
62
 
63
+ - name: Checkout generated cpflow actions
64
+ if: steps.check-branch.outputs.is_deployable == 'true'
65
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
66
+ with:
67
+ persist-credentials: false
68
+
63
69
  - name: Checkout control-plane-flow actions
64
70
  if: steps.check-branch.outputs.is_deployable == 'true'
65
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
71
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
66
72
  with:
67
73
  repository: ${{ job.workflow_repository }}
68
74
  ref: ${{ job.workflow_sha }}
@@ -71,7 +77,7 @@ jobs:
71
77
 
72
78
  - name: Validate required secrets and variables
73
79
  if: steps.check-branch.outputs.is_deployable == 'true'
74
- uses: ./.cpflow/.github/actions/cpflow-validate-config
80
+ uses: ./.github/actions/cpflow-validate-config
75
81
  env:
76
82
  CPLN_TOKEN_STAGING: ${{ secrets.CPLN_TOKEN_STAGING }}
77
83
  CPLN_ORG_STAGING: ${{ vars.CPLN_ORG_STAGING }}
@@ -89,12 +95,12 @@ jobs:
89
95
  timeout-minutes: 30
90
96
  steps:
91
97
  - name: Checkout repository
92
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
98
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
93
99
  with:
94
100
  persist-credentials: false
95
101
 
96
102
  - name: Checkout control-plane-flow actions
97
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
103
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
98
104
  with:
99
105
  repository: ${{ job.workflow_repository }}
100
106
  ref: ${{ job.workflow_sha }}
@@ -102,7 +108,7 @@ jobs:
102
108
  persist-credentials: false
103
109
 
104
110
  - name: Setup environment
105
- uses: ./.cpflow/.github/actions/cpflow-setup-environment
111
+ uses: ./.github/actions/cpflow-setup-environment
106
112
  with:
107
113
  token: ${{ secrets.CPLN_TOKEN_STAGING }}
108
114
  org: ${{ vars.CPLN_ORG_STAGING }}
@@ -112,7 +118,7 @@ jobs:
112
118
  control_plane_flow_ref: ${{ job.workflow_ref }}
113
119
 
114
120
  - name: Build Docker image
115
- uses: ./.cpflow/.github/actions/cpflow-build-docker-image
121
+ uses: ./.github/actions/cpflow-build-docker-image
116
122
  with:
117
123
  app_name: ${{ env.APP_NAME }}
118
124
  org: ${{ vars.CPLN_ORG_STAGING }}
@@ -128,12 +134,12 @@ jobs:
128
134
  timeout-minutes: 30
129
135
  steps:
130
136
  - name: Checkout repository
131
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
137
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
132
138
  with:
133
139
  persist-credentials: false
134
140
 
135
141
  - name: Checkout control-plane-flow actions
136
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
142
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
137
143
  with:
138
144
  repository: ${{ job.workflow_repository }}
139
145
  ref: ${{ job.workflow_sha }}
@@ -141,7 +147,7 @@ jobs:
141
147
  persist-credentials: false
142
148
 
143
149
  - name: Setup environment
144
- uses: ./.cpflow/.github/actions/cpflow-setup-environment
150
+ uses: ./.github/actions/cpflow-setup-environment
145
151
  with:
146
152
  token: ${{ secrets.CPLN_TOKEN_STAGING }}
147
153
  org: ${{ vars.CPLN_ORG_STAGING }}
@@ -152,7 +158,7 @@ jobs:
152
158
 
153
159
  - name: Detect release phase support
154
160
  id: release-phase
155
- uses: ./.cpflow/.github/actions/cpflow-detect-release-phase
161
+ uses: ./.github/actions/cpflow-detect-release-phase
156
162
  with:
157
163
  app_name: ${{ env.APP_NAME }}
158
164
 
@@ -28,7 +28,7 @@ jobs:
28
28
  - name: React to help command
29
29
  if: github.event_name == 'issue_comment'
30
30
  continue-on-error: true
31
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
31
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
32
32
  with:
33
33
  script: |
34
34
  try {
@@ -47,14 +47,14 @@ jobs:
47
47
  }
48
48
 
49
49
  - name: Checkout repository
50
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
50
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
51
51
  with:
52
52
  # Help only reads `.github/cpflow-help.md`; no git push happens, so drop the
53
53
  # GITHUB_TOKEN credential helper to keep the token out of .git/config.
54
54
  persist-credentials: false
55
55
 
56
56
  - name: Post help message
57
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
57
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
58
58
  with:
59
59
  script: |
60
60
  const fs = require("fs");
@@ -65,12 +65,12 @@ jobs:
65
65
 
66
66
  steps:
67
67
  - name: Checkout repository
68
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
68
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
69
69
  with:
70
70
  persist-credentials: false
71
71
 
72
72
  - name: Checkout control-plane-flow actions
73
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
73
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
74
74
  with:
75
75
  repository: ${{ job.workflow_repository }}
76
76
  ref: ${{ job.workflow_sha }}
@@ -91,7 +91,7 @@ jobs:
91
91
  fi
92
92
 
93
93
  - name: Validate required secrets and variables
94
- uses: ./.cpflow/.github/actions/cpflow-validate-config
94
+ uses: ./.github/actions/cpflow-validate-config
95
95
  # Pass secrets via env so the composite action checks indirect shell
96
96
  # variables instead of interpolating secret values into a run script.
97
97
  env:
@@ -158,7 +158,7 @@ jobs:
158
158
  } >> "$GITHUB_OUTPUT"
159
159
 
160
160
  - name: Setup production environment
161
- uses: ./.cpflow/.github/actions/cpflow-setup-environment
161
+ uses: ./.github/actions/cpflow-setup-environment
162
162
  with:
163
163
  token: ${{ secrets.CPLN_TOKEN_PRODUCTION }}
164
164
  org: ${{ steps.cpln-orgs.outputs.production }}
@@ -220,7 +220,7 @@ jobs:
220
220
 
221
221
  - name: Detect release phase support
222
222
  id: release-phase
223
- uses: ./.cpflow/.github/actions/cpflow-detect-release-phase
223
+ uses: ./.github/actions/cpflow-detect-release-phase
224
224
  with:
225
225
  app_name: ${{ vars.PRODUCTION_APP_NAME }}
226
226
 
@@ -422,7 +422,7 @@ jobs:
422
422
  echo "image=${staging_image}" >> "$GITHUB_OUTPUT"
423
423
 
424
424
  - name: Set up Docker Buildx
425
- uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5
425
+ uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
426
426
 
427
427
  - name: Copy image from staging
428
428
  id: copy-image
@@ -570,7 +570,7 @@ jobs:
570
570
 
571
571
  - name: Wait for deployment health
572
572
  id: health-check
573
- uses: ./.cpflow/.github/actions/cpflow-wait-for-health
573
+ uses: ./.github/actions/cpflow-wait-for-health
574
574
  with:
575
575
  workload_name: ${{ steps.workloads.outputs.primary }}
576
576
  app_name: ${{ vars.PRODUCTION_APP_NAME }}
@@ -13,7 +13,7 @@ jobs:
13
13
  timeout-minutes: 5
14
14
  steps:
15
15
  - name: Post quick reference
16
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
16
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
17
17
  with:
18
18
  script: |
19
19
  const body = [
@@ -15,38 +15,27 @@ on:
15
15
  spec_paths:
16
16
  required: false
17
17
  type: string
18
- uses_shared_org:
18
+ secrets:
19
+ CPLN_TOKEN:
20
+ description: Control Plane token used by integration specs
19
21
  required: false
20
- default: false
21
- type: boolean
22
+
23
+ permissions:
24
+ contents: read
22
25
 
23
26
  jobs:
24
27
  rspec:
25
28
  runs-on: ${{ inputs.os_version }}
26
- # Scope the live Control Plane org queue per PR (or ref) instead of globally:
27
- # each run uses its own random app suffix (SecureRandom.hex(2)) on a fresh
28
- # runner, so concurrent PRs don't collide on app names or CLI profiles. PRs
29
- # run only the fast (~slow) suite, which doesn't switch the shared domain's
30
- # route; domain-mutating specs are :slow and dispatched manually, keyed by
31
- # github.ref so same-ref fast dispatches still serialize. Callers that may
32
- # touch the shared domain use one ref-independent queue, covering scheduled,
33
- # manual slow, and manual specific runs. Fall back to the repository rather
34
- # than a unique run ID so missing CPLN_ORG cannot bypass serialization.
35
- # cancel-in-progress is false, so queued runs wait rather than cancel.
36
- concurrency:
37
- group: cpln-shared-org-${{ vars.CPLN_ORG || github.repository }}-${{ inputs.uses_shared_org && 'shared-org' || github.event.pull_request.number || github.ref }}
38
- cancel-in-progress: false
39
29
  env:
40
30
  RAILS_ENV: test
41
- # We have to add "_CI" to the end, otherwise it messes with tests where we switch profiles,
42
- # as Control Plane will try to use this token's profile instead.
43
- CPLN_TOKEN_CI: ${{ secrets.CPLN_TOKEN }}
44
31
  CPLN_ORG: ${{ vars.CPLN_ORG }}
45
32
  steps:
46
33
  - name: Checkout code
47
- uses: actions/checkout@v3
34
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35
+ with:
36
+ persist-credentials: false
48
37
  - name: Set up Ruby
49
- uses: ruby/setup-ruby@v1
38
+ uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
50
39
  with:
51
40
  ruby-version: ${{ inputs.ruby_version }}
52
41
  bundler-cache: true
@@ -54,22 +43,41 @@ jobs:
54
43
  run: bundle install
55
44
  - name: Install Control Plane tools
56
45
  run: |
57
- sudo npm install -g @controlplane/cli
46
+ sudo npm install -g @controlplane/cli@3.11.0
58
47
  cpln --version
59
48
  - name: Setup Control Plane tools
49
+ env:
50
+ # Keep the _CI suffix so tests that switch profiles do not use this token's profile.
51
+ CPLN_TOKEN_CI: ${{ secrets.CPLN_TOKEN }}
60
52
  run: |
61
- cpln profile create default --token $CPLN_TOKEN_CI --org $CPLN_ORG
53
+ cpln profile create default --token "$CPLN_TOKEN_CI" --org "$CPLN_ORG"
62
54
  cpln image docker-login
63
55
  - name: Run tests
64
- run: bundle exec rspec --format documentation ${{ inputs.test_tag && format('--tag {0}', inputs.test_tag) }} ${{ inputs.spec_paths }}
56
+ env:
57
+ CPLN_TOKEN_CI: ${{ secrets.CPLN_TOKEN }}
58
+ RSPEC_SPEC_PATHS: ${{ inputs.spec_paths }}
59
+ RSPEC_TEST_TAG: ${{ inputs.test_tag }}
60
+ run: |
61
+ set -euo pipefail
62
+
63
+ rspec_args=(--format documentation)
64
+ if [[ -n "${RSPEC_TEST_TAG}" ]]; then
65
+ rspec_args+=(--tag "${RSPEC_TEST_TAG}")
66
+ fi
67
+ if [[ -n "${RSPEC_SPEC_PATHS}" ]]; then
68
+ read -r -a requested_paths <<< "${RSPEC_SPEC_PATHS}"
69
+ rspec_args+=("${requested_paths[@]}")
70
+ fi
71
+
72
+ bundle exec rspec "${rspec_args[@]}"
65
73
  - name: Upload spec log
66
- uses: actions/upload-artifact@master
74
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
67
75
  if: always()
68
76
  with:
69
77
  name: spec-${{ inputs.test_tag }}-${{ github.run_id }}-${{ inputs.os_version }}-${{ inputs.ruby_version }}.log
70
78
  path: spec.log
71
79
  - name: Upload coverage results
72
- uses: actions/upload-artifact@master
80
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
73
81
  if: always()
74
82
  with:
75
83
  name: coverage-report-${{ inputs.test_tag }}-${{ github.run_id }}-${{ inputs.os_version }}-${{ inputs.ruby_version }}
@@ -7,13 +7,21 @@ on:
7
7
  description: "Test files or directories that should be run"
8
8
  required: true
9
9
 
10
+ permissions:
11
+ contents: read
12
+
10
13
  jobs:
11
14
  rspec-specific:
12
15
  name: RSpec (Specific)
13
16
  uses: ./.github/workflows/rspec-shared.yml
17
+ # Share the no-drop queue with scheduled and manually dispatched Slow runs.
18
+ concurrency:
19
+ group: cpln-shared-org-${{ vars.CPLN_ORG || github.repository }}-shared-org
20
+ cancel-in-progress: false
21
+ queue: max
14
22
  with:
15
23
  os_version: ubuntu-latest
16
24
  ruby_version: "3.2"
17
25
  spec_paths: ${{ inputs.spec_paths }}
18
- uses_shared_org: true
19
- secrets: inherit
26
+ secrets:
27
+ CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }}
@@ -19,27 +19,94 @@ on:
19
19
  schedule:
20
20
  - cron: "0 2 * * *"
21
21
 
22
+ permissions:
23
+ contents: read
24
+
22
25
  jobs:
23
26
  rspec-fast:
24
27
  name: RSpec (Fast)
25
28
  uses: ./.github/workflows/rspec-shared.yml
26
29
  if: github.event_name != 'schedule'
30
+ # Fast runs use isolated random app suffixes, so only serialize the same PR
31
+ # or ref. Keep the default single pending slot so obsolete heads are
32
+ # replaced instead of accumulating behind the current run.
33
+ concurrency:
34
+ group: cpln-shared-org-${{ vars.CPLN_ORG || github.repository }}-${{ github.event.pull_request.number || github.ref }}
35
+ cancel-in-progress: false
27
36
  with:
28
37
  os_version: ubuntu-latest
29
38
  ruby_version: "3.2"
30
39
  test_tag: ~slow
31
- secrets: inherit
40
+ secrets:
41
+ CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }}
42
+
43
+ ruby-compatibility:
44
+ name: Ruby ${{ matrix.ruby-version }} compatibility
45
+ if: github.event_name != 'schedule'
46
+ runs-on: ubuntu-latest
47
+ permissions:
48
+ contents: read
49
+ strategy:
50
+ fail-fast: false
51
+ matrix:
52
+ ruby-version:
53
+ - "3.3"
54
+ - "3.4"
55
+ steps:
56
+ - name: Checkout code
57
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
58
+ with:
59
+ persist-credentials: false
60
+ - name: Set up Ruby
61
+ uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
62
+ with:
63
+ ruby-version: ${{ matrix.ruby-version }}
64
+ bundler-cache: true
65
+ - name: Run credential-free offline suite
66
+ env:
67
+ CPLN_ORG: ""
68
+ run: |
69
+ bundle exec rspec \
70
+ spec/cpflow_spec.rb \
71
+ spec/github_actions_dependency_policy_spec.rb \
72
+ spec/github_workflows_spec.rb \
73
+ spec/patches \
74
+ spec/support_specs \
75
+ spec/core/controlplane_api_direct_spec.rb \
76
+ spec/core/controlplane_api_spec.rb \
77
+ spec/core/doctor_service_spec.rb \
78
+ spec/core/github_flow_readiness/checks_spec.rb \
79
+ spec/core/github_flow_readiness_service_spec.rb \
80
+ spec/core/helpers_spec.rb \
81
+ spec/core/repo_introspection_spec.rb \
82
+ spec/core/shell_spec.rb \
83
+ spec/rakelib/create_release_spec.rb \
84
+ spec/command/no_command_spec.rb \
85
+ spec/command/staging_branch_validation_spec.rb \
86
+ spec/command/test_spec.rb \
87
+ spec/command/update_github_actions_spec.rb \
88
+ spec/command/version_spec.rb \
89
+ spec/command/deploy_image_unit_spec.rb \
90
+ spec/command/promote_app_from_upstream_unit_spec.rb
91
+ - name: Analyze code
92
+ run: bundle exec rubocop
32
93
 
33
94
  rspec-slow:
34
95
  name: RSpec (Slow)
35
96
  uses: ./.github/workflows/rspec-shared.yml
36
97
  if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'
98
+ # Slow and Specific runs can mutate the shared domain. Retain every waiter
99
+ # in one repository-wide queue instead of replacing an earlier pending run.
100
+ concurrency:
101
+ group: cpln-shared-org-${{ vars.CPLN_ORG || github.repository }}-shared-org
102
+ cancel-in-progress: false
103
+ queue: max
37
104
  with:
38
105
  os_version: ubuntu-latest
39
106
  ruby_version: "3.2"
40
107
  test_tag: slow
41
- uses_shared_org: true
42
- secrets: inherit
108
+ secrets:
109
+ CPLN_TOKEN: ${{ secrets.CPLN_TOKEN }}
43
110
 
44
111
  report-scheduled-slow-suite-failure:
45
112
  name: Report scheduled slow suite failure
@@ -50,7 +117,7 @@ jobs:
50
117
  issues: write
51
118
  steps:
52
119
  - name: Create or update the scheduled slow suite failure issue
53
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
120
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
54
121
  with:
55
122
  script: |
56
123
  const title = "Scheduled slow suite failing";
@@ -16,15 +16,22 @@ on:
16
16
  - 'LICENSE'
17
17
  - 'COMM-LICENSE.txt'
18
18
 
19
+ permissions:
20
+ contents: read
21
+
19
22
  jobs:
20
23
  rubocop:
21
24
  runs-on: ubuntu-latest
22
25
  name: Rubocop
23
26
  steps:
24
27
  - name: Checkout code
25
- uses: actions/checkout@v3
28
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
29
+ with:
30
+ persist-credentials: false
31
+ - name: Analyze shell scripts
32
+ run: script/check_shell_scripts
26
33
  - name: Set up Ruby
27
- uses: ruby/setup-ruby@v1
34
+ uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
28
35
  with:
29
36
  ruby-version: "3.2"
30
37
  bundler-cache: true
@@ -18,6 +18,8 @@ on:
18
18
  # Non-main manual dispatches are skipped by the job guard below.
19
19
  # Only use this for forcing a rebuild from main.
20
20
 
21
+ permissions: {}
22
+
21
23
  concurrency:
22
24
  # Include the ref so accidental non-main manual runs cannot cancel a main dispatch
23
25
  # before the job guard skips them.
data/.rubocop.yml CHANGED
@@ -3,7 +3,7 @@ plugins:
3
3
  - rubocop-rspec
4
4
 
5
5
  AllCops:
6
- TargetRubyVersion: 3.0
6
+ TargetRubyVersion: 3.2
7
7
  NewCops: enable
8
8
 
9
9
  Metrics/AbcSize:
@@ -12,6 +12,11 @@ Metrics/AbcSize:
12
12
  Style/Documentation:
13
13
  Enabled: false
14
14
 
15
+ # These value objects intentionally reject positional construction. Retaining
16
+ # keyword-only initialization is part of their API contract, not redundant.
17
+ Style/RedundantStructKeywordInit:
18
+ Enabled: false
19
+
15
20
  Style/StringLiterals:
16
21
  EnforcedStyle: double_quotes
17
22
 
data/CHANGELOG.md CHANGED
@@ -12,6 +12,28 @@ In addition to the standard keepachangelog.com categories, this project uses a l
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [6.0.0.rc.0] - 2026-09-06
16
+
17
+ ### Breaking Changes
18
+
19
+ - BREAKING CHANGE: Raised the minimum supported Ruby version from 3.0 to 3.2. Users on Ruby 3.0 or 3.1 must upgrade Ruby before installing the next major cpflow release. CI now tests each supported Ruby minor from 3.2 through 3.4. [PR 464](https://github.com/shakacode/control-plane-flow/pull/464) by [Justin Gordon](https://github.com/justin808).
20
+
21
+ ### Changed
22
+
23
+ - **Bumped the pinned GitHub Actions in the generated production-promotion workflow and this repository's reusable workflows to `actions/checkout` 7.0.1, `actions/github-script` 9.0.0, and `docker/setup-buildx-action` 4.3.0.** Downstream repositories pick up the template change with `cpflow update-github-actions`. [PR 460](https://github.com/shakacode/control-plane-flow/pull/460) by [Justin Gordon](https://github.com/justin808). Fixes [issue 459](https://github.com/shakacode/control-plane-flow/issues/459).
24
+ - **Changed generated GitHub Actions to check in cpflow's composite actions under `.github/actions/cpflow-*` and refresh them with `cpflow update-github-actions`.** Reusable workflows now load those local actions from the caller repository's trusted event revision, while the separately pinned checkout at `.cpflow` supplies the cpflow runtime source. Downstream repositories must commit generated workflows and local actions together when upgrading. [PR 451](https://github.com/shakacode/control-plane-flow/pull/451) by [Justin Gordon](https://github.com/justin808). Part of [issue 375](https://github.com/shakacode/control-plane-flow/issues/375).
25
+ - **Trimmed the RubyGems post-install message to a three-line generated-workflow reminder with a link to the full update and validation instructions.** [PR 447](https://github.com/shakacode/control-plane-flow/pull/447) by [Justin Gordon](https://github.com/justin808). Fixes [issue 377](https://github.com/shakacode/control-plane-flow/issues/377).
26
+
27
+ ### Fixed
28
+
29
+ - **Kept no-argument releases pinned to the latest changelog version during retries, and blocked implicit prerelease-to-stable promotion.** If an interrupted release already bumped the gem version or created its tag, rerunning `bundle exec rake release` no longer falls through to a stable release that is absent from the changelog. [PR 472](https://github.com/shakacode/control-plane-flow/pull/472) by [Justin Gordon](https://github.com/justin808).
30
+ - **Loaded the caller's generated `.github/actions/cpflow-*` through `actions/checkout`'s trusted default in the reusable review-app deploy, delete, and stale-cleanup workflows.** Dropping the explicit `ref:` still resolves to the revision GitHub already recorded for the triggering event (`GITHUB_SHA`, the base-branch tip under `pull_request_target`), and a default checkout is never inspected by the fork-PR checkout guard added in `actions/checkout` 7.0.0. The previous explicit `github.event.pull_request.base.sha` pin in the delete workflow could trip that guard, and fail teardown, when a fork PR was closed by a manual fast-forward that left the recorded base SHA equal to the PR head. Review-app teardown consequently runs the base-tip copy of the generated actions rather than the pull request's recorded pre-merge base, which matters only if the merge itself changed those actions. Downstream repositories pick up the change when `cpflow update-github-actions` bumps the reusable-workflow ref. [PR 467](https://github.com/shakacode/control-plane-flow/pull/467) by [Justin Gordon](https://github.com/justin808). Fixes [issue 463](https://github.com/shakacode/control-plane-flow/issues/463).
31
+ - **Gave `pull_request_target` an explicit branch in the reusable review-app deploy source validator.** The reusable workflow's event allowlist admits `pull_request_target`, but the deploy source validator handled only `pull_request` and `issue_comment`. It now treats `pull_request_target` exactly like `pull_request`, skipping fork pull requests with the documented deploy-skipped summary, as defence-in-depth for callers that rewire the deploy trigger. The shipped caller templates never send that event, so no existing deployment behavior changes and same-repository pull requests are unaffected. [PR 467](https://github.com/shakacode/control-plane-flow/pull/467) by [Justin Gordon](https://github.com/justin808). Fixes [issue 462](https://github.com/shakacode/control-plane-flow/issues/462).
32
+ - **Prevented shell interpretation of dynamic Control Plane CLI and Docker arguments.** Resource names, image references, container names, locations, and other dynamic values now remain literal argv elements; output suppression and stderr capture use process redirection options without rebuilding a shell command. Fixes [issue 452](https://github.com/shakacode/control-plane-flow/issues/452). [PR 458](https://github.com/shakacode/control-plane-flow/pull/458) by [Justin Gordon](https://github.com/justin808).
33
+ - **Fixed scheduled slow-suite regressions in stale-app workload suspension, invalid upstream-token handling, transient workload image deployment, and delayed one-off job output.** `cleanup-stale-apps --mode=stop` now skips configured workloads absent from a stale app, upstream authorization failures cleanly remove their temporary profile and stderr capture, workload image updates retry for a bounded window before failing, and non-interactive `cpflow run` commands drain logs for a bounded post-terminal window so delayed ingestion does not drop completed job output. Slow-suite command logs and failure artifacts now redact token options and explicitly supplied sensitive values. [PR 413](https://github.com/shakacode/control-plane-flow/pull/413) by [Justin Gordon](https://github.com/justin808). Addresses [issue 409](https://github.com/shakacode/control-plane-flow/issues/409).
34
+ - **Queued every pending shared-org Slow and Specific RSpec run instead of letting GitHub replace an older waiter.** Fast runs keep their per-PR or per-ref queue, while the domain-mutating suites use GitHub's bounded `queue: max` behavior in one repository-wide concurrency group. [PR 457](https://github.com/shakacode/control-plane-flow/pull/457) by [Justin Gordon](https://github.com/justin808). Fixes [issue 403](https://github.com/shakacode/control-plane-flow/issues/403).
35
+ - **Bounded `cpflow run` status reconciliation after a non-interactive command finishes.** When Control Plane keeps reporting a cron job as active or pending after the command completion marker, `cpflow run` now waits up to a configurable 20-minute grace period and then exits nonzero with the job, replica, and last observed status instead of polling forever. Addresses the bounded-reconciliation portion of [HiChee issue 10375](https://github.com/shakacode/hichee/issues/10375). [PR 453](https://github.com/shakacode/control-plane-flow/pull/453) by [Justin Gordon](https://github.com/justin808).
36
+
15
37
  ## [5.3.0] - 2026-09-02
16
38
 
17
39
  ### Added
@@ -474,7 +496,8 @@ Deprecated `cpl` gem. New gem is `cpflow`.
474
496
 
475
497
  First release.
476
498
 
477
- [Unreleased]: https://github.com/shakacode/control-plane-flow/compare/v5.3.0...main
499
+ [Unreleased]: https://github.com/shakacode/control-plane-flow/compare/v6.0.0.rc.0...main
500
+ [6.0.0.rc.0]: https://github.com/shakacode/control-plane-flow/compare/v5.3.0...v6.0.0.rc.0
478
501
  [5.3.0]: https://github.com/shakacode/control-plane-flow/compare/v5.2.0...v5.3.0
479
502
  [5.2.0]: https://github.com/shakacode/control-plane-flow/compare/v5.1.1...v5.2.0
480
503
  [5.1.1]: https://github.com/shakacode/control-plane-flow/compare/v5.1.0...v5.1.1
data/CONTRIBUTING.md CHANGED
@@ -35,6 +35,43 @@ gem install overcommit
35
35
  overcommit --install
36
36
  ```
37
37
 
38
+ ## GitHub Actions Dependencies
39
+
40
+ Every repository-based external `uses:` entry under `.github/workflows/` or `.github/actions/` must use an immutable,
41
+ lowercase 40-character commit SHA followed by the exact release tag in a same-line comment. Do not use moving
42
+ major-version tags or branch names. New action repositories also require explicit review before they are added to
43
+ `trusted_actions` in `.agents/agent-workflow.yml`. Docker actions must use an exact `sha256:` image digest; manually
44
+ review the registry, image, and digest because Docker references are outside the repository allowlist.
45
+ Generated downstream calls to Control Plane Flow's cross-repository reusable workflows are the deliberate exception:
46
+ they use an exact `vX.Y.Z` release tag so the workflow source stays aligned with the released `cpflow` gem.
47
+
48
+ Dependabot checks GitHub Actions dependencies weekly. For each proposed update, verify that the release tag resolves to
49
+ the proposed commit, review the upstream release and diff, and keep the version comment synchronized with the pin.
50
+ Dependabot-authored runs receive no repository secrets, so `RSpec (Fast)` fails at `Setup Control Plane tools`. The
51
+ credential-free `Ruby 3.3 compatibility` and `Ruby 3.4 compatibility` jobs do run
52
+ `spec/github_actions_dependency_policy_spec.rb`, so a bump that leaves the generated templates or the
53
+ `EXPECTED_CPFLOW_CHECKOUT_ACTION` constant behind still fails there, even on a Dependabot-authored pull request that
54
+ cannot run the credentialed suite. A Dependabot bump is therefore never merged directly: a maintainer opens a pull
55
+ request that supersedes it and lands the same pin. That maintainer pull request must update the pin in all four places,
56
+ or CI fails:
57
+
58
+ - `.github/workflows/**` and `.github/actions/**` — the workflows that actually run in this repository.
59
+ - `lib/github_flow_templates/.github/workflows/**` — the workflows generated into downstream repositories.
60
+ - `lib/github_flow_templates/bin/test-cpflow-github-flow` — the `EXPECTED_CPFLOW_CHECKOUT_ACTION` constant.
61
+ - `spec/command/generate_github_actions_spec.rb` and `spec/github_workflows_spec.rb` — the assertions that hard-code
62
+ the expected SHA.
63
+
64
+ `spec/github_actions_dependency_policy_spec.rb` guards the first three. It fails when a template occurrence's commit
65
+ SHA or version comment drifts from the repository pin for the same action, when a template `uses:` entry is left on a
66
+ mutable ref such as `@v7` or `@main`, or when a template pins an external action that the repository workflows do not
67
+ use. Only script constants such as `EXPECTED_CPFLOW_CHECKOUT_ACTION` and `docker://` image references may omit the
68
+ version comment, because neither is a workflow step with somewhere to hang a same-line release tag. Calls to cpflow's
69
+ own reusable workflows under `.github/workflows/` are the one exception to the commit-pin rule, and only for the
70
+ `@__CPFLOW_GITHUB_ACTIONS_REF__` generator placeholder or an exact release tag. `@main` and `@v5` fail, and so does any
71
+ other `shakacode/control-plane-flow` reference, such as `shakacode/control-plane-flow/some-action@v6.0.0` or the bare
72
+ repository, which must be commit-pinned like any external action. The literal `vX.Y.Z` is accepted only in the verifier
73
+ script's `EXPECTED_PROMOTE_WORKFLOW_REF_FORMAT` constant, never in a workflow step.
74
+
38
75
  ## Docs Site Dispatch
39
76
 
40
77
  The `trigger-docs-site.yml` workflow notifies `shakacode/controlplaneflow-com` when docs-related files change on `main`.
@@ -113,9 +150,9 @@ cpflow test
113
150
 
114
151
  ## Developing the GitHub flow generator
115
152
 
116
- `cpflow generate-github-actions` copies templates from `lib/github_flow_templates/` 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/` and canonical composite actions in `.github/actions/cpflow-*` into a target repo's `.github/` directory. To work on this feature:
117
154
 
118
- - **Edit the templates 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. Make changes there, not in a generated copy.
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.
119
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.
120
157
  - **Run the generator spec on every change:**
121
158
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cpflow (5.3.0)
4
+ cpflow (6.0.0.rc.0)
5
5
  dotenv (~> 3.1)
6
6
  jwt (~> 3.1)
7
7
  psych (~> 5.2)
data/README.md CHANGED
@@ -307,6 +307,11 @@ aliases:
307
307
  # If not specified, defaults to 21600 (6 hours).
308
308
  runner_job_timeout: 1000
309
309
 
310
+ # Sets how long `cpflow run` waits for Control Plane to reconcile a cron job status
311
+ # after the non-interactive command prints its completion marker.
312
+ # If not specified, defaults to 1200 (20 minutes).
313
+ runner_job_status_reconciliation_timeout: 1200
314
+
310
315
  # Apps with a deployed image, or an image-less GVC, created before this amount of days
311
316
  # will be listed for deletion when running the command `cpflow cleanup-stale-apps`.
312
317
  stale_app_image_deployed_days: 5
@@ -393,7 +398,7 @@ cpflow generate-github-actions
393
398
  bin/test-cpflow-github-flow
394
399
  ```
395
400
 
396
- `cpflow github-flow-readiness` exits non-zero when it finds blockers such as unpublished exact-pinned packages or a missing production Dockerfile, so use it as the gate before generation. Then review the generated `.controlplane/controlplane.yml` entries, adjust any app-specific workloads, and configure the GitHub repository variables and secrets described in [CI automation](./docs/ci-automation.md), including the optional Docker build settings for private GitHub dependencies and custom SSH known hosts. `cpflow generate-github-actions` also writes `bin/test-cpflow-github-flow` for local validation and `bin/pin-cpflow-github-ref` for temporarily pinning downstream wrappers to an upstream commit SHA during pre-release testing. `cpflow generate` already switches to persistent `db` and `storage` volumes when `config/database.yml` shows SQLite in production and preserves detected frontend precompile hooks, but you should still confirm that the generated Dockerfile picked a Ruby base image compatible with the app's declared Ruby requirement and that the emitted workload set matches the real app. If you want an AI agent to do this end to end, give it the [AI rollout prompt](./docs/ai-github-flow-prompt.md) rather than a vague "set up CI" request.
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 `db` and `storage` volumes when `config/database.yml` shows SQLite in production and preserves detected frontend precompile hooks, but you should still confirm that the generated Dockerfile picked a Ruby base image compatible with the app's declared Ruby requirement and that the emitted workload set matches the real app. If you want an AI agent to do this end to end, give it the [AI rollout prompt](./docs/ai-github-flow-prompt.md) rather than a vague "set up CI" request.
397
402
 
398
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.
399
404
 
data/cpflow.gemspec CHANGED
@@ -14,23 +14,11 @@ Gem::Specification.new do |spec|
14
14
  spec.license = "MIT"
15
15
  spec.post_install_message = <<~MESSAGE
16
16
  cpflow #{Cpflow::VERSION} installed.
17
-
18
- If this repository already uses generated cpflow GitHub Actions, update the
19
- checked-in wrappers so GitHub loads the matching control-plane-flow release tag:
20
-
21
- cpflow update-github-actions
22
- bin/test-cpflow-github-flow
23
-
24
- If you run cpflow through Bundler:
25
-
26
- bundle exec cpflow update-github-actions
27
- bin/test-cpflow-github-flow bundle exec cpflow
28
-
29
- New repository? Run `cpflow generate-github-actions` first to create the
30
- wrappers (and the `bin/test-cpflow-github-flow` script referenced above).
17
+ Generated GitHub Actions users: run `cpflow update-github-actions` after upgrading.
18
+ Docs: https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md#updating-generated-github-actions-after-gem-updates
31
19
  MESSAGE
32
20
 
33
- spec.required_ruby_version = ">= 3.0.0"
21
+ spec.required_ruby_version = ">= 3.2"
34
22
 
35
23
  spec.add_dependency "dotenv", "~> 3.1"
36
24
  spec.add_dependency "jwt", "~> 3.1"