cpflow 5.0.2 → 5.0.3
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/rspec.yml +10 -0
- data/.github/workflows/rubocop.yml +10 -0
- data/.github/workflows/trigger-docs-site.yml +2 -2
- data/CHANGELOG.md +12 -1
- data/Gemfile.lock +1 -1
- data/cpflow.gemspec +17 -0
- data/docs/ai-github-flow-prompt.md +1 -1
- data/docs/ci-automation.md +37 -0
- data/docs/commands.md +28 -0
- data/docs/releasing.md +2 -0
- data/lib/command/ai_github_flow_prompt.rb +1 -1
- data/lib/command/base.rb +11 -0
- data/lib/command/generate_github_actions.rb +15 -35
- data/lib/command/staging_branch_validation.rb +41 -0
- data/lib/command/update_github_actions.rb +109 -0
- data/lib/cpflow/version.rb +1 -1
- data/lib/github_flow_templates/.github/cpflow-help.md +15 -0
- data/rakelib/create_release.rake +17 -0
- metadata +19 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4ae89eb9d37d93ce9d6e22fa42d4210cce3183505a00ee91cee172e47cb9750b
|
|
4
|
+
data.tar.gz: ba697c7e72c80ae792feb52b47f42db38180c63af42ab7959650ce1b757d0201
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d8e9ed791d7c2401b598b2ca8aefd33b363d609baca9193b678c828eaf4ba286ce3b1177a9cbe4dee5d3c6bdaf03648d77d1d8ecc2c86b384bd123625cf38038
|
|
7
|
+
data.tar.gz: 972e3a9a8db47d3875363ab969efd14038181147b2e31ab3ad80de6d062b00d0674d4be76d53f7f470763f663b4a762568c174399c0d482c787fe4e8a1b6f79e
|
data/.github/workflows/rspec.yml
CHANGED
|
@@ -4,7 +4,17 @@ on:
|
|
|
4
4
|
push:
|
|
5
5
|
branches:
|
|
6
6
|
- main
|
|
7
|
+
paths-ignore:
|
|
8
|
+
- '**.md'
|
|
9
|
+
- 'docs/**'
|
|
10
|
+
- 'LICENSE'
|
|
11
|
+
- 'COMM-LICENSE.txt'
|
|
7
12
|
pull_request:
|
|
13
|
+
paths-ignore:
|
|
14
|
+
- '**.md'
|
|
15
|
+
- 'docs/**'
|
|
16
|
+
- 'LICENSE'
|
|
17
|
+
- 'COMM-LICENSE.txt'
|
|
8
18
|
workflow_dispatch:
|
|
9
19
|
|
|
10
20
|
jobs:
|
|
@@ -34,7 +34,7 @@ jobs:
|
|
|
34
34
|
timeout-minutes: 5
|
|
35
35
|
steps:
|
|
36
36
|
- name: Generate GitHub App token
|
|
37
|
-
uses: actions/create-github-app-token@
|
|
37
|
+
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
|
38
38
|
id: app-token
|
|
39
39
|
with:
|
|
40
40
|
app-id: ${{ secrets.DOCS_DISPATCH_APP_ID }}
|
|
@@ -44,7 +44,7 @@ jobs:
|
|
|
44
44
|
|
|
45
45
|
- name: Dispatch docs-updated event
|
|
46
46
|
id: dispatch
|
|
47
|
-
uses: peter-evans/repository-dispatch@
|
|
47
|
+
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4.0.1
|
|
48
48
|
with:
|
|
49
49
|
token: ${{ steps.app-token.outputs.token }}
|
|
50
50
|
repository: shakacode/controlplaneflow-com
|
data/CHANGELOG.md
CHANGED
|
@@ -12,6 +12,16 @@ In addition to the standard keepachangelog.com categories, this project uses a l
|
|
|
12
12
|
|
|
13
13
|
## [Unreleased]
|
|
14
14
|
|
|
15
|
+
## [5.0.3] - 2026-05-26
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- **Changed `cpflow update-github-actions`, `cpflow generate-github-actions --force`, RubyGems install guidance, and release-task follow-up output so downstream repositories remember to update checked-in generated GitHub Actions wrappers when the `cpflow` gem version changes.** [PR 328](https://github.com/shakacode/control-plane-flow/pull/328) by [Justin Gordon](https://github.com/justin808).
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **Fixed `cpflow update-github-actions` so reordered default staging branches remain defaults, empty or non-hash staging workflow YAML no longer crashes, and fresh repos fail with a clear `generate-github-actions` instruction instead of silently creating wrappers.** [PR 331](https://github.com/shakacode/control-plane-flow/pull/331) by [Justin Gordon](https://github.com/justin808). Clarified the RubyGems post-install message so first-time installers see the `generate-github-actions` path.
|
|
24
|
+
|
|
15
25
|
## [5.0.2] - 2026-05-25
|
|
16
26
|
|
|
17
27
|
### Fixed
|
|
@@ -382,7 +392,8 @@ Deprecated `cpl` gem. New gem is `cpflow`.
|
|
|
382
392
|
|
|
383
393
|
First release.
|
|
384
394
|
|
|
385
|
-
[Unreleased]: https://github.com/shakacode/control-plane-flow/compare/v5.0.
|
|
395
|
+
[Unreleased]: https://github.com/shakacode/control-plane-flow/compare/v5.0.3...HEAD
|
|
396
|
+
[5.0.3]: https://github.com/shakacode/control-plane-flow/compare/v5.0.2...v5.0.3
|
|
386
397
|
[5.0.2]: https://github.com/shakacode/control-plane-flow/compare/v5.0.1...v5.0.2
|
|
387
398
|
[5.0.1]: https://github.com/shakacode/control-plane-flow/compare/v5.0.0...v5.0.1
|
|
388
399
|
[5.0.0]: https://github.com/shakacode/control-plane-flow/compare/v5.0.0.rc.3...v5.0.0
|
data/Gemfile.lock
CHANGED
data/cpflow.gemspec
CHANGED
|
@@ -12,6 +12,23 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.description = "CLI for providing Heroku-like platform-as-a-service on Control Plane"
|
|
13
13
|
spec.homepage = "https://github.com/shakacode/control-plane-flow"
|
|
14
14
|
spec.license = "MIT"
|
|
15
|
+
spec.post_install_message = <<~MESSAGE
|
|
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).
|
|
31
|
+
MESSAGE
|
|
15
32
|
|
|
16
33
|
spec.required_ruby_version = ">= 3.0.0"
|
|
17
34
|
|
|
@@ -20,7 +20,7 @@ prompt tells the agent to stop on.
|
|
|
20
20
|
```text
|
|
21
21
|
Set up Control Plane GitHub Flow for this repo. Start with `cpflow github-flow-readiness` and stop on any reported blockers. The repo must be deployable from a clean clone: published package versions, complete runtime scaffold, and a production Dockerfile that can build the app. If any package version is unpublished, inaccessible from CI, or requires credentials that are not already modeled in the repo or GitHub settings, stop and report the blocker instead of generating workflow files. If the repo is a legacy sample pinned to an obsolete Ruby or Bundler toolchain, if it does not even have a production Dockerfile yet, or if it is a monorepo without an already-decided single app boundary for this flow, stop and report that as a prerequisite instead of forcing the rollout.
|
|
22
22
|
|
|
23
|
-
If `.controlplane/` is missing, run `cpflow generate`. Treat the generated app names as the repo-name default and rename them only if the project needs a different prefix. Then run `cpflow generate-github-actions` (or `cpflow generate-github-actions --staging-branch BRANCH` when staging should deploy from a branch other than `main`/`master`), keep review apps opt-in via `+review-app-deploy`, make sure any `STAGING_APP_BRANCH` repository variable is also present in the generated staging workflow's `on.push.branches` filter, and list the GitHub secrets and variables that must be configured. Do not hand-edit duplicated upstream refs into the generated wrappers: the only downstream Control Plane Flow pin should be the reusable workflow `uses: ...@vX.Y.Z` value generated from the installed `cpflow` gem version, and upstream workflows load their matching shared actions automatically. Keep the standard path simple: review apps require only `CPLN_TOKEN_STAGING` when the generated review app config can be inferred. 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.
|
|
23
|
+
If `.controlplane/` is missing, run `cpflow generate`. Treat the generated app names as the repo-name default and rename them only if the project needs a different prefix. Then run `cpflow generate-github-actions` (or `cpflow generate-github-actions --staging-branch BRANCH` when staging should deploy from a branch other than `main`/`master`), keep review apps opt-in via `+review-app-deploy`, make sure any `STAGING_APP_BRANCH` repository variable is also present in the generated staging workflow's `on.push.branches` filter, and list the GitHub secrets and variables that must be configured. Do not hand-edit duplicated upstream refs into the generated wrappers: the only downstream Control Plane Flow pin should be the reusable workflow `uses: ...@vX.Y.Z` value generated from the installed `cpflow` gem version, and upstream workflows load their matching shared actions automatically. When bumping the `cpflow` gem in a downstream repo, run `cpflow update-github-actions` (or `bundle exec cpflow update-github-actions`) and validate with `bin/test-cpflow-github-flow` in the same PR so the checked-in wrappers move to the matching release tag. Keep the standard path simple: review apps require only `CPLN_TOKEN_STAGING` when the generated review app config can be inferred. Document the one-time Control Plane bootstrap command for persistent staging and production apps with `cpflow setup-app --skip-post-creation-hook`; for existing apps or later template updates, document `cpflow apply-template` and the need for the app identity to have `reveal` on the app secret policy. Do not imply the staging deploy or promotion workflows create those persistent GVCs. For production promotion, document a protected `production` GitHub Environment with required reviewers, prevent self-review, and `CPLN_TOKEN_PRODUCTION` stored as an environment secret, not as a repository or organization secret.
|
|
24
24
|
|
|
25
25
|
Keep Node available in the final image if asset compilation or SSR depends on ExecJS, Yarn, `pnpm`, or npm after the main install layer. Make sure the generated Dockerfile uses a Ruby base image compatible with the app's declared Ruby requirement. Preserve repo-defined frontend build hooks: if `config/shakapacker.yml` defines a `precompile_hook`, or React on Rails enables `config.auto_load_bundle = true`, confirm the generated Dockerfile runs that codegen step before `rails assets:precompile`. If `config/database.yml` shows SQLite in production, confirm that the generated scaffold uses persistent `db` and `storage` volumes plus a release script that runs `rails db:prepare`; otherwise keep the default Postgres workload. If the public workload is not named `rails`, set `PRIMARY_WORKLOAD` or adjust the generated workflows. Inspect the Dockerfile and package sources for private GitHub dependencies or `RUN --mount=type=ssh`; if present, wire `DOCKER_BUILD_SSH_KEY`, optionally set `DOCKER_BUILD_SSH_KNOWN_HOSTS` for non-GitHub SSH hosts, and keep `DOCKER_BUILD_EXTRA_ARGS` to newline-delimited single tokens such as `--build-arg=FOO=bar`.
|
|
26
26
|
|
data/docs/ci-automation.md
CHANGED
|
@@ -455,6 +455,43 @@ That release tag should point to the same source that produced the RubyGems
|
|
|
455
455
|
release. Downstream production automation should use release tags, not `main` or
|
|
456
456
|
feature-branch refs.
|
|
457
457
|
|
|
458
|
+
## Updating Generated GitHub Actions After Gem Updates
|
|
459
|
+
|
|
460
|
+
Whenever a downstream repo updates the `cpflow` gem, update the checked-in
|
|
461
|
+
GitHub Actions wrappers in the same PR. The gem version does not make GitHub load
|
|
462
|
+
new reusable workflow YAML by itself; GitHub loads the `uses:` ref committed in
|
|
463
|
+
`.github/workflows/cpflow-*.yml`.
|
|
464
|
+
|
|
465
|
+
Use the installed gem to refresh the generated wrappers:
|
|
466
|
+
|
|
467
|
+
```sh
|
|
468
|
+
cpflow update-github-actions
|
|
469
|
+
bin/test-cpflow-github-flow
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
If the app runs `cpflow` through Bundler, use:
|
|
473
|
+
|
|
474
|
+
```sh
|
|
475
|
+
bundle exec cpflow update-github-actions
|
|
476
|
+
bin/test-cpflow-github-flow bundle exec cpflow
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
`cpflow update-github-actions` regenerates the generated wrapper and helper
|
|
480
|
+
files from the installed gem, pins the wrapper `uses:` refs to `v<gem-version>`,
|
|
481
|
+
and preserves a single custom staging branch from the existing generated staging
|
|
482
|
+
workflow. Pass `--staging-branch BRANCH` when changing or restoring a custom
|
|
483
|
+
staging branch explicitly.
|
|
484
|
+
|
|
485
|
+
When keeping `cpflow` in an app Gemfile, leave a comment next to the gem entry
|
|
486
|
+
so future dependency bumps include the wrapper update:
|
|
487
|
+
|
|
488
|
+
```ruby
|
|
489
|
+
# After bumping cpflow, run:
|
|
490
|
+
# bundle exec cpflow update-github-actions
|
|
491
|
+
# bin/test-cpflow-github-flow bundle exec cpflow
|
|
492
|
+
gem "cpflow", "5.0.1"
|
|
493
|
+
```
|
|
494
|
+
|
|
458
495
|
`CPFLOW_VERSION` is a runtime override. If a downstream repository sets the
|
|
459
496
|
`CPFLOW_VERSION` variable, the setup action runs `gem install cpflow -v
|
|
460
497
|
<version>`. If it is unset, the setup action builds `cpflow` from the checked-out
|
data/docs/commands.md
CHANGED
|
@@ -218,6 +218,9 @@ Creates GitHub Actions templates for a Heroku Flow style Control Plane pipeline:
|
|
|
218
218
|
Pass `--staging-branch BRANCH` when staging should auto-deploy from a branch
|
|
219
219
|
other than `main` or `master`; the generator will bake that branch into the
|
|
220
220
|
GitHub Actions push trigger and use it as the default STAGING_APP_BRANCH.
|
|
221
|
+
Pass `--force` to overwrite existing generated files. Prefer
|
|
222
|
+
`cpflow update-github-actions` after bumping the cpflow gem in a downstream
|
|
223
|
+
repo.
|
|
221
224
|
|
|
222
225
|
```sh
|
|
223
226
|
# Creates thin .github/workflows wrappers for the Control Plane flow
|
|
@@ -225,6 +228,9 @@ cpflow generate-github-actions
|
|
|
225
228
|
|
|
226
229
|
# Creates the flow with staging deploys triggered from develop
|
|
227
230
|
cpflow generate-github-actions --staging-branch develop
|
|
231
|
+
|
|
232
|
+
# Overwrites existing generated wrappers from the installed cpflow gem
|
|
233
|
+
cpflow generate-github-actions --force
|
|
228
234
|
```
|
|
229
235
|
|
|
230
236
|
### `github-flow-readiness`
|
|
@@ -533,6 +539,28 @@ cpflow terraform generate
|
|
|
533
539
|
cpflow terraform import
|
|
534
540
|
```
|
|
535
541
|
|
|
542
|
+
### `update-github-actions`
|
|
543
|
+
|
|
544
|
+
Regenerates the generated cpflow GitHub Actions wrappers and helper files
|
|
545
|
+
from the currently installed cpflow gem. Use this after updating the
|
|
546
|
+
cpflow gem so checked-in workflow wrappers move to the matching upstream
|
|
547
|
+
release tag, for example `v5.0.2`.
|
|
548
|
+
|
|
549
|
+
If the existing generated staging workflow uses a custom single staging
|
|
550
|
+
branch, the command preserves it. Pass `--staging-branch BRANCH` to set or
|
|
551
|
+
replace the generated staging branch explicitly.
|
|
552
|
+
|
|
553
|
+
```sh
|
|
554
|
+
# After updating the cpflow gem, refresh generated GitHub Actions wrappers
|
|
555
|
+
cpflow update-github-actions
|
|
556
|
+
|
|
557
|
+
# When running cpflow through Bundler
|
|
558
|
+
bundle exec cpflow update-github-actions
|
|
559
|
+
|
|
560
|
+
# Preserve or set a custom staging branch
|
|
561
|
+
cpflow update-github-actions --staging-branch develop
|
|
562
|
+
```
|
|
563
|
+
|
|
536
564
|
### `version`
|
|
537
565
|
|
|
538
566
|
- Displays the current version of the CLI
|
data/docs/releasing.md
CHANGED
|
@@ -99,6 +99,8 @@ GEM_RELEASE_MAX_RETRIES=<n>
|
|
|
99
99
|
9. Commits the version bump, tags `vX.Y.Z`, and pushes the commit and tags.
|
|
100
100
|
10. Publishes the `cpflow` gem to RubyGems.org.
|
|
101
101
|
11. Creates or updates the GitHub release from the matching changelog section.
|
|
102
|
+
12. Prints the downstream GitHub Actions follow-up command:
|
|
103
|
+
`cpflow update-github-actions`.
|
|
102
104
|
|
|
103
105
|
The older `bundle exec rake "create_release[4.2.0,false]"` task name remains as
|
|
104
106
|
a compatibility alias, but new releases should use `bundle exec rake release`.
|
|
@@ -32,7 +32,7 @@ module Command
|
|
|
32
32
|
<<~PROMPT
|
|
33
33
|
Set up Control Plane GitHub Flow for this repo. Start with `cpflow github-flow-readiness` and stop on any reported blockers. The repo must be deployable from a clean clone: published package versions, complete runtime scaffold, and a production Dockerfile that can build the app. If any package version is unpublished, inaccessible from CI, or requires credentials that are not already modeled in the repo or GitHub settings, stop and report the blocker instead of generating workflow files. If the repo is a legacy sample pinned to an obsolete Ruby or Bundler toolchain, if it does not even have a production Dockerfile yet, or if it is a monorepo without an already-decided single app boundary for this flow, stop and report that as a prerequisite instead of forcing the rollout.
|
|
34
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: the only downstream Control Plane Flow pin should be the reusable workflow `uses: ...@vX.Y.Z` value generated from the installed `cpflow` gem version, and upstream workflows load their matching shared actions automatically. Keep the standard path simple: review apps require only `CPLN_TOKEN_STAGING` when the generated review app config can be inferred. 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.
|
|
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: the only downstream Control Plane Flow pin should be the reusable workflow `uses: ...@vX.Y.Z` value generated from the installed `cpflow` gem version, and upstream workflows load their matching shared actions automatically. When bumping the `cpflow` gem in a downstream repo, run `cpflow update-github-actions` (or `bundle exec cpflow update-github-actions`) and validate with `bin/test-cpflow-github-flow` in the same PR so the checked-in wrappers move to the matching release tag. Keep the standard path simple: review apps require only `CPLN_TOKEN_STAGING` when the generated review app config can be inferred. 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
36
|
|
|
37
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 uses persistent `db` and `storage` volumes plus a release script that runs `rails db:prepare`; otherwise keep the default Postgres workload. If the public workload is not named `rails`, set `PRIMARY_WORKLOAD` or adjust the generated workflows. Inspect the Dockerfile and package sources for private GitHub dependencies or `RUN --mount=type=ssh`; if present, wire `DOCKER_BUILD_SSH_KEY`, optionally set `DOCKER_BUILD_SSH_KNOWN_HOSTS` for non-GitHub SSH hosts, and keep `DOCKER_BUILD_EXTRA_ARGS` to newline-delimited single tokens such as `--build-arg=FOO=bar`.
|
|
38
38
|
|
data/lib/command/base.rb
CHANGED
|
@@ -330,6 +330,17 @@ module Command
|
|
|
330
330
|
}
|
|
331
331
|
end
|
|
332
332
|
|
|
333
|
+
def self.force_option(required: false)
|
|
334
|
+
{
|
|
335
|
+
name: :force,
|
|
336
|
+
params: {
|
|
337
|
+
desc: "Overwrite existing generated files",
|
|
338
|
+
type: :boolean,
|
|
339
|
+
required: required
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
end
|
|
343
|
+
|
|
333
344
|
def self.logs_limit_option(required: false)
|
|
334
345
|
{
|
|
335
346
|
name: :limit,
|
|
@@ -4,6 +4,7 @@ require "json"
|
|
|
4
4
|
require "pathname"
|
|
5
5
|
|
|
6
6
|
require_relative "generator_helpers"
|
|
7
|
+
require_relative "staging_branch_validation"
|
|
7
8
|
|
|
8
9
|
module Command
|
|
9
10
|
class GithubActionsGenerator < Thor::Group
|
|
@@ -80,8 +81,10 @@ module Command
|
|
|
80
81
|
end
|
|
81
82
|
|
|
82
83
|
class GenerateGithubActions < Base
|
|
84
|
+
include StagingBranchValidation
|
|
85
|
+
|
|
83
86
|
NAME = "generate-github-actions"
|
|
84
|
-
OPTIONS = [staging_branch_option].freeze
|
|
87
|
+
OPTIONS = [staging_branch_option, force_option].freeze
|
|
85
88
|
DESCRIPTION = "Creates GitHub Actions templates for review apps, staging deploys, and production promotion"
|
|
86
89
|
LONG_DESCRIPTION = <<~DESC
|
|
87
90
|
Creates GitHub Actions templates for a Heroku Flow style Control Plane pipeline:
|
|
@@ -93,6 +96,9 @@ module Command
|
|
|
93
96
|
Pass `--staging-branch BRANCH` when staging should auto-deploy from a branch
|
|
94
97
|
other than `main` or `master`; the generator will bake that branch into the
|
|
95
98
|
GitHub Actions push trigger and use it as the default STAGING_APP_BRANCH.
|
|
99
|
+
Pass `--force` to overwrite existing generated files. Prefer
|
|
100
|
+
`cpflow update-github-actions` after bumping the cpflow gem in a downstream
|
|
101
|
+
repo.
|
|
96
102
|
DESC
|
|
97
103
|
EXAMPLES = <<~EX
|
|
98
104
|
```sh
|
|
@@ -101,6 +107,9 @@ module Command
|
|
|
101
107
|
|
|
102
108
|
# Creates the flow with staging deploys triggered from develop
|
|
103
109
|
cpflow generate-github-actions --staging-branch develop
|
|
110
|
+
|
|
111
|
+
# Overwrites existing generated wrappers from the installed cpflow gem
|
|
112
|
+
cpflow generate-github-actions --force
|
|
104
113
|
```
|
|
105
114
|
EX
|
|
106
115
|
WITH_INFO_HEADER = false
|
|
@@ -129,10 +138,11 @@ module Command
|
|
|
129
138
|
self.class.ensure_template_root!
|
|
130
139
|
branch = staging_branch
|
|
131
140
|
|
|
132
|
-
if (existing = existing_files).any?
|
|
141
|
+
if (existing = existing_files).any? && !force?
|
|
133
142
|
files = existing.map { |path| "- #{path}" }.join("\n")
|
|
134
143
|
Shell.warn("The following files already exist:\n#{files}\n\n" \
|
|
135
|
-
"Remove or rename them before running `cpflow #{NAME}` again
|
|
144
|
+
"Remove or rename them before running `cpflow #{NAME}` again, " \
|
|
145
|
+
"or run `cpflow update-github-actions` after updating the cpflow gem.")
|
|
136
146
|
return
|
|
137
147
|
end
|
|
138
148
|
|
|
@@ -145,38 +155,8 @@ module Command
|
|
|
145
155
|
@existing_files ||= self.class.generated_files.select { |path| File.exist?(path) }
|
|
146
156
|
end
|
|
147
157
|
|
|
148
|
-
def
|
|
149
|
-
|
|
150
|
-
return nil if branch.empty?
|
|
151
|
-
|
|
152
|
-
unless valid_staging_branch?(branch)
|
|
153
|
-
Shell.abort(
|
|
154
|
-
"Invalid --staging-branch value: #{branch.inspect}. " \
|
|
155
|
-
"Use a valid git branch name containing only alphanumerics, dots, slashes, underscores, hyphens, and @."
|
|
156
|
-
)
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
branch
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def valid_staging_branch?(branch)
|
|
163
|
-
return false unless branch.match?(%r{\A[a-zA-Z0-9._/@-]+\z})
|
|
164
|
-
|
|
165
|
-
valid_git_branch_shape?(branch) && valid_git_branch_components?(branch)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def valid_git_branch_shape?(branch)
|
|
169
|
-
return false if branch.start_with?("-", "/", ".")
|
|
170
|
-
return false if branch.end_with?("/", ".")
|
|
171
|
-
return false if branch.include?("@{")
|
|
172
|
-
|
|
173
|
-
!branch.include?("..")
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def valid_git_branch_components?(branch)
|
|
177
|
-
branch.split("/").none? do |component|
|
|
178
|
-
component.empty? || component.start_with?(".") || component.end_with?(".lock")
|
|
179
|
-
end
|
|
158
|
+
def force?
|
|
159
|
+
config.options[:force]
|
|
180
160
|
end
|
|
181
161
|
end
|
|
182
162
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Command
|
|
4
|
+
module StagingBranchValidation
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def staging_branch
|
|
8
|
+
branch = config.options[:staging_branch].to_s.strip
|
|
9
|
+
return nil if branch.empty?
|
|
10
|
+
|
|
11
|
+
unless valid_staging_branch?(branch)
|
|
12
|
+
Shell.abort(
|
|
13
|
+
"Invalid --staging-branch value: #{branch.inspect}. " \
|
|
14
|
+
"Use a valid git branch name containing only alphanumerics, dots, slashes, underscores, hyphens, and @."
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
branch
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def valid_staging_branch?(branch)
|
|
22
|
+
return false unless branch.match?(%r{\A[a-zA-Z0-9._/@-]+\z})
|
|
23
|
+
|
|
24
|
+
valid_git_branch_shape?(branch) && valid_git_branch_components?(branch)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def valid_git_branch_shape?(branch)
|
|
28
|
+
return false if branch.start_with?("-", "/", ".")
|
|
29
|
+
return false if branch.end_with?("/", ".")
|
|
30
|
+
return false if branch.include?("@{")
|
|
31
|
+
|
|
32
|
+
!branch.include?("..")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def valid_git_branch_components?(branch)
|
|
36
|
+
branch.split("/").none? do |component|
|
|
37
|
+
component.empty? || component.start_with?(".") || component.end_with?(".lock")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
require_relative "staging_branch_validation"
|
|
6
|
+
|
|
7
|
+
module Command
|
|
8
|
+
class UpdateGithubActions < Base
|
|
9
|
+
include StagingBranchValidation
|
|
10
|
+
|
|
11
|
+
NAME = "update-github-actions"
|
|
12
|
+
OPTIONS = [staging_branch_option].freeze
|
|
13
|
+
DESCRIPTION = "Regenerates generated GitHub Actions wrappers for the installed cpflow version"
|
|
14
|
+
LONG_DESCRIPTION = <<~DESC.freeze
|
|
15
|
+
Regenerates the generated cpflow GitHub Actions wrappers and helper files
|
|
16
|
+
from the currently installed cpflow gem. Use this after updating the
|
|
17
|
+
cpflow gem so checked-in workflow wrappers move to the matching upstream
|
|
18
|
+
release tag, for example `v#{Cpflow::VERSION}`.
|
|
19
|
+
|
|
20
|
+
If the existing generated staging workflow uses a custom single staging
|
|
21
|
+
branch, the command preserves it. Pass `--staging-branch BRANCH` to set or
|
|
22
|
+
replace the generated staging branch explicitly.
|
|
23
|
+
DESC
|
|
24
|
+
EXAMPLES = <<~EX
|
|
25
|
+
```sh
|
|
26
|
+
# After updating the cpflow gem, refresh generated GitHub Actions wrappers
|
|
27
|
+
cpflow update-github-actions
|
|
28
|
+
|
|
29
|
+
# When running cpflow through Bundler
|
|
30
|
+
bundle exec cpflow update-github-actions
|
|
31
|
+
|
|
32
|
+
# Preserve or set a custom staging branch
|
|
33
|
+
cpflow update-github-actions --staging-branch develop
|
|
34
|
+
```
|
|
35
|
+
EX
|
|
36
|
+
WITH_INFO_HEADER = false
|
|
37
|
+
VALIDATIONS = [].freeze
|
|
38
|
+
REQUIRES_STARTUP_CHECKS = false
|
|
39
|
+
|
|
40
|
+
DEFAULT_STAGING_BRANCHES = %w[main master].freeze
|
|
41
|
+
STAGING_WORKFLOW_PATH = Pathname.new(".github/workflows/cpflow-deploy-staging.yml")
|
|
42
|
+
|
|
43
|
+
def call
|
|
44
|
+
GenerateGithubActions.ensure_template_root!
|
|
45
|
+
abort_if_no_generated_files!
|
|
46
|
+
|
|
47
|
+
branch = staging_branch || inferred_staging_branch
|
|
48
|
+
GithubActionsGenerator.start([branch].compact)
|
|
49
|
+
|
|
50
|
+
print_post_update_message
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def abort_if_no_generated_files!
|
|
56
|
+
return if GenerateGithubActions.generated_files.any? { |path| File.exist?(path) }
|
|
57
|
+
|
|
58
|
+
Shell.abort(
|
|
59
|
+
"No generated cpflow GitHub Actions files found in this repository. " \
|
|
60
|
+
"Run `cpflow generate-github-actions` first to create the wrappers, " \
|
|
61
|
+
"then use `cpflow update-github-actions` after future gem upgrades."
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def print_post_update_message
|
|
66
|
+
Shell.info("")
|
|
67
|
+
Shell.info("Updated cpflow GitHub Actions wrappers for cpflow #{Cpflow::VERSION}.")
|
|
68
|
+
Shell.info("Next: review the diff and run `bin/test-cpflow-github-flow`.")
|
|
69
|
+
Shell.info("If you run cpflow through Bundler, use `bin/test-cpflow-github-flow bundle exec cpflow`.")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def inferred_staging_branch
|
|
73
|
+
branches = existing_staging_branches
|
|
74
|
+
return if branches.empty? || branches.sort == DEFAULT_STAGING_BRANCHES.sort
|
|
75
|
+
return branches.first if branches.length == 1
|
|
76
|
+
|
|
77
|
+
Shell.warn(
|
|
78
|
+
"Existing staging workflow has multiple custom push branches: #{branches.join(', ')}. " \
|
|
79
|
+
"Run with --staging-branch BRANCH if only one branch should auto-deploy staging."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def existing_staging_branches
|
|
86
|
+
Array(parsed_staging_workflow_on.dig("push", "branches")).map(&:to_s)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def parsed_staging_workflow_on
|
|
90
|
+
return {} unless STAGING_WORKFLOW_PATH.file?
|
|
91
|
+
|
|
92
|
+
workflow = YAML.load_file(STAGING_WORKFLOW_PATH, aliases: true)
|
|
93
|
+
return {} unless workflow.is_a?(Hash)
|
|
94
|
+
|
|
95
|
+
workflow_on = workflow["on"] || workflow[true]
|
|
96
|
+
workflow_on.is_a?(Hash) ? workflow_on : {}
|
|
97
|
+
rescue Psych::SyntaxError => e
|
|
98
|
+
warn_unparseable_staging_workflow(e)
|
|
99
|
+
{}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def warn_unparseable_staging_workflow(error)
|
|
103
|
+
Shell.warn(
|
|
104
|
+
"Could not parse #{STAGING_WORKFLOW_PATH}: #{error.message}. " \
|
|
105
|
+
"Run with --staging-branch BRANCH if staging should use a custom branch."
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
data/lib/cpflow/version.rb
CHANGED
|
@@ -88,6 +88,21 @@ checked-out upstream source. If you set `CPFLOW_VERSION`, it must match the
|
|
|
88
88
|
release tag, for example `CPFLOW_VERSION=5.0.1` with a wrapper pinned to
|
|
89
89
|
`uses: ...@v5.0.1`.
|
|
90
90
|
|
|
91
|
+
After updating the `cpflow` gem in this repo, update the generated wrappers in
|
|
92
|
+
the same PR:
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
cpflow update-github-actions
|
|
96
|
+
bin/test-cpflow-github-flow
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
If `cpflow` is bundled by the app, use:
|
|
100
|
+
|
|
101
|
+
```sh
|
|
102
|
+
bundle exec cpflow update-github-actions
|
|
103
|
+
bin/test-cpflow-github-flow bundle exec cpflow
|
|
104
|
+
```
|
|
105
|
+
|
|
91
106
|
Do not leave downstream apps pinned to a moving branch such as `main`. For a
|
|
92
107
|
short-lived test of an unreleased upstream PR, pin to a full 40-character commit
|
|
93
108
|
SHA and leave `CPFLOW_VERSION` unset:
|
data/rakelib/create_release.rake
CHANGED
|
@@ -101,6 +101,8 @@ task :release, %i[version dry_run override_version_policy] do |_t, args|
|
|
|
101
101
|
puts "RELEASE COMPLETE"
|
|
102
102
|
puts "Published cpflow #{released_gem_version} to RubyGems.org."
|
|
103
103
|
end
|
|
104
|
+
|
|
105
|
+
Release.print_github_actions_update_reminder(released_gem_version)
|
|
104
106
|
end
|
|
105
107
|
|
|
106
108
|
desc("Compatibility alias for the old release task. Prefer `bundle exec rake release`.")
|
|
@@ -452,6 +454,21 @@ module Release
|
|
|
452
454
|
abort "gem-release is required. Run `bundle install`.\n\n#{output}" unless status.success?
|
|
453
455
|
end
|
|
454
456
|
|
|
457
|
+
def print_github_actions_update_reminder(version)
|
|
458
|
+
puts ""
|
|
459
|
+
puts "GitHub Actions follow-up:"
|
|
460
|
+
puts " Downstream repos using generated cpflow GitHub Actions must update their checked-in wrappers."
|
|
461
|
+
puts " Run this in each downstream repo after installing cpflow #{version}:"
|
|
462
|
+
puts ""
|
|
463
|
+
puts " cpflow update-github-actions"
|
|
464
|
+
puts " bin/test-cpflow-github-flow"
|
|
465
|
+
puts ""
|
|
466
|
+
puts " If cpflow is bundled in the app:"
|
|
467
|
+
puts ""
|
|
468
|
+
puts " bundle exec cpflow update-github-actions"
|
|
469
|
+
puts " bin/test-cpflow-github-flow bundle exec cpflow"
|
|
470
|
+
end
|
|
471
|
+
|
|
455
472
|
def github_repo_slug(gem_root)
|
|
456
473
|
origin_url, status = Open3.capture2e("git", "-C", gem_root, "remote", "get-url", "origin")
|
|
457
474
|
abort "Unable to determine git origin URL.\n\n#{origin_url}" unless status.success?
|
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: 5.0.
|
|
4
|
+
version: 5.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Gordon
|
|
@@ -177,10 +177,12 @@ files:
|
|
|
177
177
|
- lib/command/ps_wait.rb
|
|
178
178
|
- lib/command/run.rb
|
|
179
179
|
- lib/command/setup_app.rb
|
|
180
|
+
- lib/command/staging_branch_validation.rb
|
|
180
181
|
- lib/command/terraform/base.rb
|
|
181
182
|
- lib/command/terraform/generate.rb
|
|
182
183
|
- lib/command/terraform/import.rb
|
|
183
184
|
- lib/command/test.rb
|
|
185
|
+
- lib/command/update_github_actions.rb
|
|
184
186
|
- lib/command/version.rb
|
|
185
187
|
- lib/constants/exit_code.rb
|
|
186
188
|
- lib/core/config.rb
|
|
@@ -268,6 +270,22 @@ licenses:
|
|
|
268
270
|
- MIT
|
|
269
271
|
metadata:
|
|
270
272
|
rubygems_mfa_required: 'true'
|
|
273
|
+
post_install_message: |
|
|
274
|
+
cpflow 5.0.3 installed.
|
|
275
|
+
|
|
276
|
+
If this repository already uses generated cpflow GitHub Actions, update the
|
|
277
|
+
checked-in wrappers so GitHub loads the matching control-plane-flow release tag:
|
|
278
|
+
|
|
279
|
+
cpflow update-github-actions
|
|
280
|
+
bin/test-cpflow-github-flow
|
|
281
|
+
|
|
282
|
+
If you run cpflow through Bundler:
|
|
283
|
+
|
|
284
|
+
bundle exec cpflow update-github-actions
|
|
285
|
+
bin/test-cpflow-github-flow bundle exec cpflow
|
|
286
|
+
|
|
287
|
+
New repository? Run `cpflow generate-github-actions` first to create the
|
|
288
|
+
wrappers (and the `bin/test-cpflow-github-flow` script referenced above).
|
|
271
289
|
rdoc_options: []
|
|
272
290
|
require_paths:
|
|
273
291
|
- lib
|