cpflow 5.0.4 → 5.1.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.github/actions/cpflow-wait-for-health/action.yml +11 -4
  3. data/.github/workflows/cpflow-promote-staging-to-production.yml +269 -43
  4. data/.github/workflows/rspec-shared.yml +8 -1
  5. data/CHANGELOG.md +28 -1
  6. data/Gemfile.lock +1 -1
  7. data/README.md +36 -11
  8. data/docs/ai-github-flow-prompt.md +1 -1
  9. data/docs/assets/logo/favicon.ico +0 -0
  10. data/docs/assets/logo/icon-1024.png +0 -0
  11. data/docs/assets/logo/icon-128.png +0 -0
  12. data/docs/assets/logo/icon-16.png +0 -0
  13. data/docs/assets/logo/icon-192.png +0 -0
  14. data/docs/assets/logo/icon-24.png +0 -0
  15. data/docs/assets/logo/icon-32.png +0 -0
  16. data/docs/assets/logo/icon-48.png +0 -0
  17. data/docs/assets/logo/icon-512.png +0 -0
  18. data/docs/assets/logo/icon-64.png +0 -0
  19. data/docs/assets/logo/icon-tile.svg +17 -0
  20. data/docs/assets/logo/mark-transparent.svg +16 -0
  21. data/docs/ci-automation.md +137 -47
  22. data/docs/commands.md +13 -3
  23. data/docs/postgres.md +6 -0
  24. data/docs/rds-private-networking.md +649 -0
  25. data/docs/secrets-and-env-values.md +49 -0
  26. data/docs/tips.md +256 -10
  27. data/examples/controlplane.yml +8 -0
  28. data/lib/command/ai_github_flow_prompt.rb +1 -1
  29. data/lib/command/apply_template.rb +3 -0
  30. data/lib/command/base.rb +69 -0
  31. data/lib/command/cleanup_stale_apps.rb +1 -1
  32. data/lib/command/delete.rb +85 -10
  33. data/lib/command/deploy_image.rb +30 -8
  34. data/lib/command/generate_github_actions.rb +6 -0
  35. data/lib/command/maintenance_off.rb +1 -0
  36. data/lib/command/maintenance_on.rb +1 -0
  37. data/lib/command/run.rb +25 -5
  38. data/lib/command/setup_app.rb +11 -2
  39. data/lib/core/config.rb +81 -0
  40. data/lib/core/controlplane.rb +15 -5
  41. data/lib/core/maintenance_mode.rb +93 -6
  42. data/lib/core/template_parser.rb +4 -0
  43. data/lib/cpflow/version.rb +1 -1
  44. data/lib/generator_templates/controlplane.yml +7 -0
  45. data/lib/generator_templates_sqlite/controlplane.yml +7 -0
  46. data/lib/github_flow_templates/.github/cpflow-help.md +48 -13
  47. data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +768 -15
  48. data/lib/github_flow_templates/bin/pin-cpflow-github-ref +17 -3
  49. data/lib/github_flow_templates/bin/test-cpflow-github-flow +61 -9
  50. metadata +15 -2
@@ -8,15 +8,25 @@ USAGE = <<~USAGE
8
8
 
9
9
  Use a release tag for normal operation, e.g. v5.0.0.
10
10
  Use a full 40-character commit SHA for temporary unreleased upstream testing.
11
- This only updates generated reusable-workflow `uses:` refs. The called
12
- workflows load their own matching shared actions from that same workflow
13
- commit automatically. Regenerate from the cpflow gem when templates changed.
11
+ This updates generated reusable-workflow `uses:` refs plus the production
12
+ workflow's pinned control-plane-flow checkout and setup validation ref.
13
+ Regenerate from the cpflow gem when templates changed.
14
14
  Use --allow-moving-ref only for short-lived local branch/ref experiments.
15
15
  USAGE
16
16
 
17
17
  ALLOWED_OPTIONS = ["--allow-moving-ref"].freeze
18
18
  FULL_COMMIT_SHA = /\A[0-9a-f]{40}\z/i
19
19
  RELEASE_TAG = /\Av\d+\.\d+\.\d+(?:[-.][0-9A-Za-z][0-9A-Za-z.-]*)?\z/
20
+ PRODUCTION_WORKFLOW_REF = "shakacode/control-plane-flow/.github/workflows/" \
21
+ "cpflow-promote-staging-to-production.yml"
22
+ CPFLOW_CHECKOUT_REF_PATTERN = %r{
23
+ (^\s*-\s+name:\s+Checkout\ control-plane-flow\ actions\s*\n
24
+ (?:(?!^\s*-\s+name:).)*?
25
+ ^\s+repository:\s+shakacode/control-plane-flow\s*\n
26
+ (?:(?!^\s*-\s+name:).)*?
27
+ ^\s+ref:\s+)
28
+ [^\s]+
29
+ }mx
20
30
 
21
31
  options, positional = ARGV.partition { |arg| arg.start_with?("--") }
22
32
  unknown_options = options - ALLOWED_OPTIONS
@@ -57,8 +67,12 @@ end
57
67
  changed = []
58
68
  workflow_paths.each do |path|
59
69
  text = File.read(path)
70
+ production_setup_ref_pattern =
71
+ /(control_plane_flow_ref:\s+#{Regexp.escape(PRODUCTION_WORKFLOW_REF)}@)[^\s]+/
60
72
  updated = text
61
73
  .gsub(%r{(uses:\s+shakacode/control-plane-flow/\.github/workflows/[^@\s]+@)[^\s]+}, "\\1#{ref}")
74
+ .gsub(production_setup_ref_pattern, "\\1#{ref}")
75
+ .gsub(CPFLOW_CHECKOUT_REF_PATTERN, "\\1#{ref}")
62
76
 
63
77
  next if updated == text
64
78
 
@@ -43,7 +43,10 @@ ruby <<'RUBY'
43
43
  require "yaml"
44
44
 
45
45
  CONTROL_PLANE_FLOW_WORKFLOW = %r{\Ashakacode/control-plane-flow/\.github/workflows/[^@\s]+@([^\s]+)\z}
46
- PROMOTE_WORKFLOW = %r{\Ashakacode/control-plane-flow/\.github/workflows/cpflow-promote-staging-to-production\.yml@[^\s]+\z}
46
+ PROMOTE_WORKFLOW = %r{\Ashakacode/control-plane-flow/\.github/workflows/cpflow-promote-staging-to-production\.yml@([^\s]+)\z}
47
+ EXPECTED_PROMOTE_WORKFLOW_REF_FORMAT = "shakacode/control-plane-flow/.github/workflows/cpflow-promote-staging-to-production.yml@vX.Y.Z"
48
+ EXPECTED_CPFLOW_CHECKOUT_ACTION = "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd"
49
+ EXPECTED_CPFLOW_CHECKOUT_REPOSITORY = "shakacode/control-plane-flow"
47
50
 
48
51
  refs = Hash.new { |hash, key| hash[key] = [] }
49
52
 
@@ -72,19 +75,68 @@ Dir[".github/workflows/cpflow-*.yml"].sort.each do |path|
72
75
  refs[uses_ref] << "#{path}:#{job_name}"
73
76
 
74
77
  if job["uses"].to_s.match?(PROMOTE_WORKFLOW)
75
- if with["production_environment"].to_s.strip.empty?
76
- abort "#{path}:#{job_name} must set production_environment so GitHub can expose production environment secrets after approval"
77
- end
78
-
79
- secrets = job["secrets"].is_a?(Hash) ? job["secrets"] : {}
80
- if secrets.key?("CPLN_TOKEN_PRODUCTION")
81
- abort "#{path}:#{job_name} must not pass CPLN_TOKEN_PRODUCTION as a caller secret; store it on the protected production environment"
82
- end
78
+ abort "#{path}:#{job_name} must not call the cross-repo production reusable workflow; use a normal caller-repo job with environment: production"
83
79
  end
84
80
 
85
81
  end
86
82
  end
87
83
 
84
+ promote_path = ".github/workflows/cpflow-promote-staging-to-production.yml"
85
+ promote_doc = YAML.load_file(promote_path, aliases: true)
86
+ promote_job = promote_doc.fetch("jobs", {})["promote-to-production"]
87
+
88
+ unless promote_job
89
+ abort "#{promote_path}:promote-to-production job is missing"
90
+ end
91
+
92
+ if promote_job.key?("uses")
93
+ abort "#{promote_path}:promote-to-production must run as a normal caller-repo job, not a reusable workflow, so GitHub can expose production environment secrets"
94
+ end
95
+
96
+ unless promote_job["environment"].to_s == "production"
97
+ abort "#{promote_path}:promote-to-production must declare environment: production"
98
+ end
99
+
100
+ checkout_step = Array(promote_job["steps"]).find { |step| step["name"] == "Checkout control-plane-flow actions" }
101
+
102
+ unless checkout_step
103
+ abort "#{promote_path}:promote-to-production must include a Checkout control-plane-flow actions step"
104
+ end
105
+
106
+ unless checkout_step["uses"] == EXPECTED_CPFLOW_CHECKOUT_ACTION
107
+ abort "#{promote_path}:promote-to-production must use #{EXPECTED_CPFLOW_CHECKOUT_ACTION} for the Checkout control-plane-flow actions step"
108
+ end
109
+
110
+ checkout_with = checkout_step.fetch("with", {})
111
+ checkout_repository = checkout_with["repository"]
112
+ checkout_ref = checkout_with["ref"]
113
+
114
+ unless checkout_repository == EXPECTED_CPFLOW_CHECKOUT_REPOSITORY
115
+ abort "#{promote_path}:promote-to-production must check out #{EXPECTED_CPFLOW_CHECKOUT_REPOSITORY}"
116
+ end
117
+
118
+ if checkout_ref.to_s.strip.empty?
119
+ abort "#{promote_path}:promote-to-production must pin the Checkout control-plane-flow actions step"
120
+ end
121
+
122
+ refs[checkout_ref] << "#{promote_path}:promote-to-production"
123
+
124
+ setup_step = Array(promote_job["steps"]).find { |step| step["name"] == "Setup production environment" }
125
+
126
+ unless setup_step
127
+ abort "#{promote_path}:promote-to-production must include a Setup production environment step"
128
+ end
129
+
130
+ setup_ref = setup_step.fetch("with", {})["control_plane_flow_ref"]
131
+ setup_match = setup_ref.to_s.match(PROMOTE_WORKFLOW)
132
+
133
+ unless setup_match
134
+ abort "#{promote_path}:promote-to-production must pass a pinned production control_plane_flow_ref to setup, " \
135
+ "for example #{EXPECTED_PROMOTE_WORKFLOW_REF_FORMAT}"
136
+ end
137
+
138
+ refs[setup_match[1]] << "#{promote_path}:promote-to-production setup"
139
+
88
140
  if refs.empty?
89
141
  puts "no upstream cpflow reusable workflow refs found"
90
142
  elsif refs.length > 1
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
4
+ version: 5.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Gordon
@@ -120,6 +120,18 @@ files:
120
120
  - docs/ai-github-flow-prompt.md
121
121
  - docs/assets/cpflow-deploying.svg
122
122
  - docs/assets/grafana-alert.png
123
+ - docs/assets/logo/favicon.ico
124
+ - docs/assets/logo/icon-1024.png
125
+ - docs/assets/logo/icon-128.png
126
+ - docs/assets/logo/icon-16.png
127
+ - docs/assets/logo/icon-192.png
128
+ - docs/assets/logo/icon-24.png
129
+ - docs/assets/logo/icon-32.png
130
+ - docs/assets/logo/icon-48.png
131
+ - docs/assets/logo/icon-512.png
132
+ - docs/assets/logo/icon-64.png
133
+ - docs/assets/logo/icon-tile.svg
134
+ - docs/assets/logo/mark-transparent.svg
123
135
  - docs/assets/memcached.png
124
136
  - docs/assets/sidekiq-pre-stop-hook.png
125
137
  - docs/ci-automation.md
@@ -127,6 +139,7 @@ files:
127
139
  - docs/dns.md
128
140
  - docs/migrating-heroku-to-control-plane.md
129
141
  - docs/postgres.md
142
+ - docs/rds-private-networking.md
130
143
  - docs/redis.md
131
144
  - docs/releasing.md
132
145
  - docs/secrets-and-env-values.md
@@ -271,7 +284,7 @@ licenses:
271
284
  metadata:
272
285
  rubygems_mfa_required: 'true'
273
286
  post_install_message: |
274
- cpflow 5.0.4 installed.
287
+ cpflow 5.1.1 installed.
275
288
 
276
289
  If this repository already uses generated cpflow GitHub Actions, update the
277
290
  checked-in wrappers so GitHub loads the matching control-plane-flow release tag: