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.
- checksums.yaml +4 -4
- data/.agents/agent-workflow.yml +13 -0
- data/.agents/bin/README.md +12 -1
- data/.agents/bin/docs +1 -1
- data/.agents/bin/lint +1 -1
- data/.agents/bin/setup +1 -1
- data/.agents/bin/test +1 -1
- data/.agents/bin/validate +2 -1
- data/.coderabbit.yaml +13 -0
- data/.github/actions/cpflow-delete-control-plane-app/action.yml +2 -1
- data/.github/actions/cpflow-setup-environment/action.yml +8 -6
- data/.github/dependabot.yml +10 -0
- data/.github/workflows/check_cpln_links.yml +6 -1
- data/.github/workflows/claude-code-review.yml +5 -2
- data/.github/workflows/claude.yml +6 -4
- data/.github/workflows/coderabbit-lifecycle-review.yml +29 -0
- data/.github/workflows/command_docs.yml +7 -2
- data/.github/workflows/cpflow-cleanup-stale-review-apps.yml +12 -5
- data/.github/workflows/cpflow-delete-review-app.yml +18 -11
- data/.github/workflows/cpflow-deploy-review-app.yml +25 -16
- data/.github/workflows/cpflow-deploy-staging.yml +16 -10
- data/.github/workflows/cpflow-help-command.yml +3 -3
- data/.github/workflows/cpflow-promote-staging-to-production.yml +7 -7
- data/.github/workflows/cpflow-review-app-help.yml +1 -1
- data/.github/workflows/rspec-shared.yml +34 -26
- data/.github/workflows/rspec-specific.yml +10 -2
- data/.github/workflows/rspec.yml +71 -4
- data/.github/workflows/rubocop.yml +9 -2
- data/.github/workflows/trigger-docs-site.yml +2 -0
- data/.rubocop.yml +6 -1
- data/CHANGELOG.md +24 -1
- data/CONTRIBUTING.md +39 -2
- data/Gemfile.lock +1 -1
- data/README.md +6 -1
- data/cpflow.gemspec +3 -15
- data/docs/ai-github-flow-prompt.md +2 -2
- data/docs/ci-automation.md +112 -60
- data/docs/commands.md +18 -8
- data/docs/rds-private-networking.md +12 -12
- data/docs/releasing.md +15 -4
- data/examples/controlplane.yml +5 -0
- data/lib/command/ai_github_flow_prompt.rb +1 -1
- data/lib/command/cleanup_stale_apps.rb +28 -4
- data/lib/command/copy_image_from_upstream.rb +3 -0
- data/lib/command/deploy_image.rb +38 -1
- data/lib/command/generate_github_actions.rb +36 -12
- data/lib/command/run.rb +224 -34
- data/lib/command/update_github_actions.rb +7 -6
- data/lib/core/controlplane.rb +145 -78
- data/lib/core/shell.rb +35 -10
- data/lib/core/timed_command.rb +111 -0
- data/lib/cpflow/version.rb +1 -1
- data/lib/cpflow.rb +1 -1
- data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +7 -7
- data/lib/github_flow_templates/bin/test-cpflow-github-flow +63 -3
- data/lib/patches/hash.rb +2 -2
- data/rakelib/create_release.rake +14 -14
- data/script/check_shell_scripts +66 -0
- metadata +11 -18
|
@@ -16,10 +16,70 @@ echo "==> parse generated GitHub Actions YAML"
|
|
|
16
16
|
ruby <<'RUBY'
|
|
17
17
|
require "yaml"
|
|
18
18
|
|
|
19
|
-
Dir[
|
|
20
|
-
|
|
19
|
+
documents = Dir[
|
|
20
|
+
".github/actions/**/action.yml",
|
|
21
|
+
".github/actions/**/action.yaml",
|
|
22
|
+
".github/workflows/*.yml",
|
|
23
|
+
".github/workflows/*.yaml"
|
|
24
|
+
].sort.to_h do |path|
|
|
25
|
+
[path, YAML.load_file(path, aliases: true)]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
documents.each_key do |path|
|
|
21
29
|
puts "parsed #{path}"
|
|
22
30
|
end
|
|
31
|
+
|
|
32
|
+
generated_local_action_pattern = %r{\A\./(?<directory>\.github/actions/cpflow-[a-z0-9]+(?:-[a-z0-9]+)*)\z}
|
|
33
|
+
local_action_references = []
|
|
34
|
+
invalid_local_action_references = []
|
|
35
|
+
visited_container_ids = {}
|
|
36
|
+
walk = lambda do |node, source_path|
|
|
37
|
+
if node.is_a?(Hash) || node.is_a?(Array)
|
|
38
|
+
next if visited_container_ids.key?(node.object_id)
|
|
39
|
+
|
|
40
|
+
visited_container_ids[node.object_id] = true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
case node
|
|
44
|
+
when Hash
|
|
45
|
+
uses = node["uses"]
|
|
46
|
+
if uses.is_a?(String) && uses.start_with?("./.github/actions/cpflow-")
|
|
47
|
+
match = uses.match(generated_local_action_pattern)
|
|
48
|
+
if match
|
|
49
|
+
local_action_references << [source_path, match[:directory]]
|
|
50
|
+
else
|
|
51
|
+
invalid_local_action_references <<
|
|
52
|
+
"#{source_path} has invalid generated local action reference #{uses}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
node.each_value { |value| walk.call(value, source_path) }
|
|
56
|
+
when Array
|
|
57
|
+
node.each { |value| walk.call(value, source_path) }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
documents.each { |path, document| walk.call(document, path) }
|
|
61
|
+
|
|
62
|
+
abort invalid_local_action_references.uniq.join("\n") unless invalid_local_action_references.empty?
|
|
63
|
+
|
|
64
|
+
missing_actions = local_action_references.uniq.filter_map do |source_path, action_directory|
|
|
65
|
+
directory_is_local = begin
|
|
66
|
+
File.lstat(action_directory).directory?
|
|
67
|
+
rescue Errno::ENOENT
|
|
68
|
+
false
|
|
69
|
+
end
|
|
70
|
+
descriptor_is_local = %w[action.yml action.yaml].any? do |filename|
|
|
71
|
+
begin
|
|
72
|
+
File.lstat(File.join(action_directory, filename)).file?
|
|
73
|
+
rescue Errno::ENOENT
|
|
74
|
+
false
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
"#{source_path} references missing local action #{action_directory}" unless directory_is_local && descriptor_is_local
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
abort missing_actions.join("\n") unless missing_actions.empty?
|
|
82
|
+
puts "all referenced local actions have checked-in descriptors"
|
|
23
83
|
RUBY
|
|
24
84
|
|
|
25
85
|
echo "==> check composite action input descriptions"
|
|
@@ -45,7 +105,7 @@ require "yaml"
|
|
|
45
105
|
CONTROL_PLANE_FLOW_WORKFLOW = %r{\Ashakacode/control-plane-flow/\.github/workflows/[^@\s]+@([^\s]+)\z}
|
|
46
106
|
PROMOTE_WORKFLOW = %r{\Ashakacode/control-plane-flow/\.github/workflows/cpflow-promote-staging-to-production\.yml@([^\s]+)\z}
|
|
47
107
|
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@
|
|
108
|
+
EXPECTED_CPFLOW_CHECKOUT_ACTION = "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1"
|
|
49
109
|
EXPECTED_CPFLOW_CHECKOUT_REPOSITORY = "shakacode/control-plane-flow"
|
|
50
110
|
|
|
51
111
|
refs = Hash.new { |hash, key| hash[key] = [] }
|
data/lib/patches/hash.rb
CHANGED
data/rakelib/create_release.rake
CHANGED
|
@@ -22,8 +22,9 @@ desc("Releases the cpflow Ruby gem.
|
|
|
22
22
|
3. Enter the RubyGems OTP when prompted.
|
|
23
23
|
|
|
24
24
|
With no version argument, the task reads the latest versioned CHANGELOG.md
|
|
25
|
-
header and uses it when it is newer than the current gem version.
|
|
26
|
-
|
|
25
|
+
header and uses it when it is newer than or equal to the current gem version.
|
|
26
|
+
It falls back to a patch bump only when the current version is stable and the
|
|
27
|
+
changelog does not name the current or a newer version.
|
|
27
28
|
|
|
28
29
|
1st argument: Version (optional). Supported values:
|
|
29
30
|
patch, minor, major, 4.2.0, or 4.2.0.rc.1
|
|
@@ -205,10 +206,15 @@ module Release
|
|
|
205
206
|
|
|
206
207
|
version = parse_gem_version_components(current_gem_version)
|
|
207
208
|
|
|
209
|
+
if version[:prerelease_type] && version_input.to_s.strip.casecmp?("patch")
|
|
210
|
+
abort <<~ERROR
|
|
211
|
+
Automatic patch bumps are not allowed from prerelease version #{current_gem_version}.
|
|
212
|
+
Pass an explicit version instead so a retry cannot accidentally promote a prerelease to stable.
|
|
213
|
+
ERROR
|
|
214
|
+
end
|
|
215
|
+
|
|
208
216
|
case version_input.to_s.strip.downcase
|
|
209
217
|
when "patch"
|
|
210
|
-
return "#{version[:major]}.#{version[:minor]}.#{version[:patch]}" if version[:prerelease_type]
|
|
211
|
-
|
|
212
218
|
"#{version[:major]}.#{version[:minor]}.#{version[:patch] + 1}"
|
|
213
219
|
when "minor"
|
|
214
220
|
"#{version[:major]}.#{version[:minor] + 1}.0"
|
|
@@ -269,10 +275,8 @@ module Release
|
|
|
269
275
|
return changelog_version
|
|
270
276
|
end
|
|
271
277
|
|
|
272
|
-
if changelog_version &&
|
|
273
|
-
|
|
274
|
-
!version_tagged?(gem_root, changelog_version)
|
|
275
|
-
puts "Found untagged CHANGELOG.md version: #{changelog_version} (current: #{current_version})"
|
|
278
|
+
if changelog_version && Gem::Version.new(changelog_version) == Gem::Version.new(current_version)
|
|
279
|
+
puts "Found current CHANGELOG.md version: #{changelog_version}"
|
|
276
280
|
return changelog_version
|
|
277
281
|
end
|
|
278
282
|
|
|
@@ -308,10 +312,6 @@ module Release
|
|
|
308
312
|
tags_output.lines.map(&:strip).filter_map { |tag| parse_release_tag_to_gem_version(tag) }.uniq
|
|
309
313
|
end
|
|
310
314
|
|
|
311
|
-
def version_tagged?(gem_root, version)
|
|
312
|
-
tagged_release_gem_versions(gem_root, fetch_tags: true).include?(version)
|
|
313
|
-
end
|
|
314
|
-
|
|
315
315
|
def version_bump_type(previous_stable_gem_version:, target_gem_version:)
|
|
316
316
|
previous = parse_gem_version_components(previous_stable_gem_version)
|
|
317
317
|
target = parse_gem_version_components(target_gem_version)
|
|
@@ -479,8 +479,8 @@ module Release
|
|
|
479
479
|
match[:repo]
|
|
480
480
|
end
|
|
481
481
|
|
|
482
|
-
def capture_gh_output(*
|
|
483
|
-
Open3.capture2e("gh", *
|
|
482
|
+
def capture_gh_output(*)
|
|
483
|
+
Open3.capture2e("gh", *)
|
|
484
484
|
rescue Errno::ENOENT
|
|
485
485
|
abort "GitHub CLI (`gh`) is not installed. Install it from https://cli.github.com/ and retry."
|
|
486
486
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
repo_root=$(CDPATH='' cd -- "$(dirname -- "$0")/.." && pwd)
|
|
5
|
+
cd "$repo_root"
|
|
6
|
+
|
|
7
|
+
if ! command -v shellcheck >/dev/null 2>&1; then
|
|
8
|
+
echo "ShellCheck is required; install it and ensure shellcheck is on PATH." >&2
|
|
9
|
+
exit 127
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
# Add, rename, or remove extensionless shell entrypoints here. See .agents/bin/README.md.
|
|
13
|
+
extensionless_shell_files=(
|
|
14
|
+
.agents/bin/docs
|
|
15
|
+
.agents/bin/lint
|
|
16
|
+
.agents/bin/setup
|
|
17
|
+
.agents/bin/test
|
|
18
|
+
.agents/bin/validate
|
|
19
|
+
lib/github_flow_templates/bin/test-cpflow-github-flow
|
|
20
|
+
script/check_command_docs
|
|
21
|
+
script/check_cpln_links
|
|
22
|
+
script/check_shell_scripts
|
|
23
|
+
script/precommit/check_command_docs
|
|
24
|
+
script/precommit/check_cpln_links
|
|
25
|
+
script/precommit/check_trailing_newlines
|
|
26
|
+
script/precommit/get_changed_files
|
|
27
|
+
script/precommit/ruby_autofix
|
|
28
|
+
script/precommit/ruby_lint
|
|
29
|
+
spec/dummy/bin/dev
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# A file preserves NUL-separated names while letting us check Git's exit status.
|
|
33
|
+
inventory_file=$(mktemp)
|
|
34
|
+
trap 'rm -f -- "$inventory_file"' EXIT
|
|
35
|
+
if ! git ls-files -z -- '*.sh' '*.bash' "${extensionless_shell_files[@]}" >"$inventory_file"; then
|
|
36
|
+
echo "Unable to read tracked shell inventory." >&2
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
shell_files=()
|
|
41
|
+
while IFS= read -r -d '' file; do
|
|
42
|
+
if [ ! -r "$file" ]; then
|
|
43
|
+
echo "Tracked shell script is not readable: $file" >&2
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
shell_files+=("$file")
|
|
47
|
+
done < "$inventory_file"
|
|
48
|
+
|
|
49
|
+
if [ "${#shell_files[@]}" -eq 0 ]; then
|
|
50
|
+
echo "No tracked shell scripts found." >&2
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
for file in "${extensionless_shell_files[@]}"; do
|
|
55
|
+
if [ ! -f "$file" ] || [ ! -r "$file" ]; then
|
|
56
|
+
echo "Declared shell entrypoint is not a readable file: $file" >&2
|
|
57
|
+
exit 1
|
|
58
|
+
fi
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
if ! git ls-files --error-unmatch -- "${extensionless_shell_files[@]}" >/dev/null; then
|
|
62
|
+
echo "Declared extensionless shell scripts must be tracked; update the inventory for renamed or removed files." >&2
|
|
63
|
+
exit 1
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
shellcheck -- "${shell_files[@]}"
|
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:
|
|
4
|
+
version: 6.0.0.rc.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Gordon
|
|
@@ -87,6 +87,7 @@ files:
|
|
|
87
87
|
- ".agents/workflows/ai-rollout-e2e-test.md"
|
|
88
88
|
- ".claude/commands/address-review.md"
|
|
89
89
|
- ".claude/commands/update-changelog.md"
|
|
90
|
+
- ".coderabbit.yaml"
|
|
90
91
|
- ".github/actions/cpflow-build-docker-image/action.yml"
|
|
91
92
|
- ".github/actions/cpflow-delete-control-plane-app/action.yml"
|
|
92
93
|
- ".github/actions/cpflow-delete-control-plane-app/delete-app.sh"
|
|
@@ -95,10 +96,12 @@ files:
|
|
|
95
96
|
- ".github/actions/cpflow-setup-environment/action.yml"
|
|
96
97
|
- ".github/actions/cpflow-validate-config/action.yml"
|
|
97
98
|
- ".github/actions/cpflow-wait-for-health/action.yml"
|
|
99
|
+
- ".github/dependabot.yml"
|
|
98
100
|
- ".github/pull_request_template.md"
|
|
99
101
|
- ".github/workflows/check_cpln_links.yml"
|
|
100
102
|
- ".github/workflows/claude-code-review.yml"
|
|
101
103
|
- ".github/workflows/claude.yml"
|
|
104
|
+
- ".github/workflows/coderabbit-lifecycle-review.yml"
|
|
102
105
|
- ".github/workflows/command_docs.yml"
|
|
103
106
|
- ".github/workflows/cpflow-cleanup-stale-review-apps.yml"
|
|
104
107
|
- ".github/workflows/cpflow-delete-review-app.yml"
|
|
@@ -247,6 +250,7 @@ files:
|
|
|
247
250
|
- lib/core/terraform_config/workload/main.tf
|
|
248
251
|
- lib/core/terraform_config/workload/required_providers.tf
|
|
249
252
|
- lib/core/terraform_config/workload/variables.tf
|
|
253
|
+
- lib/core/timed_command.rb
|
|
250
254
|
- lib/cpflow.rb
|
|
251
255
|
- lib/cpflow/version.rb
|
|
252
256
|
- lib/deprecated_commands.json
|
|
@@ -281,6 +285,7 @@ files:
|
|
|
281
285
|
- script/add_command
|
|
282
286
|
- script/check_command_docs
|
|
283
287
|
- script/check_cpln_links
|
|
288
|
+
- script/check_shell_scripts
|
|
284
289
|
- script/precommit/check_command_docs
|
|
285
290
|
- script/precommit/check_cpln_links
|
|
286
291
|
- script/precommit/check_trailing_newlines
|
|
@@ -304,21 +309,9 @@ licenses:
|
|
|
304
309
|
metadata:
|
|
305
310
|
rubygems_mfa_required: 'true'
|
|
306
311
|
post_install_message: |
|
|
307
|
-
cpflow
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
checked-in wrappers so GitHub loads the matching control-plane-flow release tag:
|
|
311
|
-
|
|
312
|
-
cpflow update-github-actions
|
|
313
|
-
bin/test-cpflow-github-flow
|
|
314
|
-
|
|
315
|
-
If you run cpflow through Bundler:
|
|
316
|
-
|
|
317
|
-
bundle exec cpflow update-github-actions
|
|
318
|
-
bin/test-cpflow-github-flow bundle exec cpflow
|
|
319
|
-
|
|
320
|
-
New repository? Run `cpflow generate-github-actions` first to create the
|
|
321
|
-
wrappers (and the `bin/test-cpflow-github-flow` script referenced above).
|
|
312
|
+
cpflow 6.0.0.rc.0 installed.
|
|
313
|
+
Generated GitHub Actions users: run `cpflow update-github-actions` after upgrading.
|
|
314
|
+
Docs: https://github.com/shakacode/control-plane-flow/blob/main/docs/ci-automation.md#updating-generated-github-actions-after-gem-updates
|
|
322
315
|
rdoc_options: []
|
|
323
316
|
require_paths:
|
|
324
317
|
- lib
|
|
@@ -326,14 +319,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
326
319
|
requirements:
|
|
327
320
|
- - ">="
|
|
328
321
|
- !ruby/object:Gem::Version
|
|
329
|
-
version: 3.
|
|
322
|
+
version: '3.2'
|
|
330
323
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
331
324
|
requirements:
|
|
332
325
|
- - ">="
|
|
333
326
|
- !ruby/object:Gem::Version
|
|
334
327
|
version: '0'
|
|
335
328
|
requirements: []
|
|
336
|
-
rubygems_version: 4.0.
|
|
329
|
+
rubygems_version: 4.0.18
|
|
337
330
|
specification_version: 4
|
|
338
331
|
summary: Control Plane Flow
|
|
339
332
|
test_files: []
|