carson 2.27.0 → 2.29.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/RELEASE.md +33 -0
- data/VERSION +1 -1
- data/lib/carson/cli.rb +26 -2
- data/lib/carson/runtime/audit.rb +22 -15
- data/lib/carson/runtime/setup.rb +6 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4db94cc8153a2add26aa8e34ee4d1232d2211bf0ecc81ed0a806b1b5608b0550
|
|
4
|
+
data.tar.gz: da51fe2ad074433bd52d83c22c06ed95187ac96745ffe66152a2f01120fdd8ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6a1aeea9070859c0781734ed69e7c984054f40dc6c3572b063614c81b10ed75c933989e99c65de749a7d5bcbe0b16e65702020d30fb69d3d8c8d8d9ecbcbab3a
|
|
7
|
+
data.tar.gz: e2e54a61d3e4d8acf3267f192482d0cc0358b1476f06037933e298a3150282a5c112c7de2ab14c057c930dfe20e05416eececc53824ee54f96872eafdbeead39
|
data/RELEASE.md
CHANGED
|
@@ -5,6 +5,39 @@ Release-note scope rule:
|
|
|
5
5
|
- `RELEASE.md` records only version deltas, breaking changes, and migration actions.
|
|
6
6
|
- Operational usage guides live in `MANUAL.md` and `API.md`.
|
|
7
7
|
|
|
8
|
+
## 2.29.0 — Audit Concise Output UX
|
|
9
|
+
|
|
10
|
+
### What changed
|
|
11
|
+
|
|
12
|
+
- **"Checks: skipped" and "Baseline: skipped" messages are no longer shown in concise audit output.** If checks cannot run (no PR, no required checks, `gh` unavailable), the audit stays silent instead of surfacing a misleading "skipped" line. Verbose mode still logs the skip reason for debugging.
|
|
13
|
+
- **Audit state no longer bumps to "attention" for skipped checks.** A skip is a non-event, not a concern.
|
|
14
|
+
- **Failing and pending check names are now included in concise messages.** Before: `Baseline (main): 1 failing — fix before merge.` After: `Baseline (main): 1 failing (lint) — fix before merge.` Same treatment applies to PR required checks and advisory baseline messages.
|
|
15
|
+
|
|
16
|
+
### Migration
|
|
17
|
+
|
|
18
|
+
- No action required. This is a display-only change to concise audit output.
|
|
19
|
+
|
|
20
|
+
## 2.28.0 — Non-Interactive CLI Flags for Setup
|
|
21
|
+
|
|
22
|
+
### What changed
|
|
23
|
+
|
|
24
|
+
- **`carson setup` now accepts optional CLI flags** to configure settings without interactive prompts:
|
|
25
|
+
- `--remote NAME` sets `git.remote`
|
|
26
|
+
- `--main-branch NAME` sets `git.main_branch`
|
|
27
|
+
- `--workflow STYLE` sets `workflow.style` (branch or trunk)
|
|
28
|
+
- `--merge METHOD` sets `govern.merge.method` (squash, rebase, or merge)
|
|
29
|
+
- `--canonical PATH` sets `template.canonical`
|
|
30
|
+
- When any flag is present, interactive prompts are skipped and only the specified values are written.
|
|
31
|
+
- When no flags are present, existing behaviour is preserved (interactive in TTY, silent detection in non-TTY).
|
|
32
|
+
|
|
33
|
+
### Why
|
|
34
|
+
|
|
35
|
+
Running `carson setup` in non-TTY environments (CI pipelines, agent scripts) previously required manually editing `config.json`. CLI flags allow fully automated configuration without file manipulation.
|
|
36
|
+
|
|
37
|
+
### Migration
|
|
38
|
+
|
|
39
|
+
No action required. Existing behaviour is unchanged when no flags are provided.
|
|
40
|
+
|
|
8
41
|
## 2.27.0 — Absorbed Branch Pruning
|
|
9
42
|
|
|
10
43
|
### What changed
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.29.0
|
data/lib/carson/cli.rb
CHANGED
|
@@ -53,7 +53,7 @@ module Carson
|
|
|
53
53
|
|
|
54
54
|
def self.build_parser
|
|
55
55
|
OptionParser.new do |opts|
|
|
56
|
-
opts.banner = "Usage: carson [setup|audit|sync|prune|onboard [repo_path]|refresh [--all|repo_path]|offboard [repo_path]|template check|template apply|review gate|review sweep|govern [--dry-run] [--json] [--loop SECONDS]|version]"
|
|
56
|
+
opts.banner = "Usage: carson [setup [--remote NAME] [--main-branch NAME] [--workflow STYLE] [--merge METHOD] [--canonical PATH]|audit|sync|prune|onboard [repo_path]|refresh [--all|repo_path]|offboard [repo_path]|template check|template apply|review gate|review sweep|govern [--dry-run] [--json] [--loop SECONDS]|version]"
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -74,6 +74,8 @@ module Carson
|
|
|
74
74
|
when "version"
|
|
75
75
|
parser.parse!( argv )
|
|
76
76
|
{ command: "version" }
|
|
77
|
+
when "setup"
|
|
78
|
+
parse_setup_command( argv: argv, parser: parser, err: err )
|
|
77
79
|
when "onboard", "offboard"
|
|
78
80
|
parse_repo_path_command( command: command, argv: argv, parser: parser, err: err )
|
|
79
81
|
when "refresh"
|
|
@@ -90,6 +92,28 @@ module Carson
|
|
|
90
92
|
end
|
|
91
93
|
end
|
|
92
94
|
|
|
95
|
+
def self.parse_setup_command( argv:, parser:, err: )
|
|
96
|
+
options = {}
|
|
97
|
+
setup_parser = OptionParser.new do |opts|
|
|
98
|
+
opts.banner = "Usage: carson setup [--remote NAME] [--main-branch NAME] [--workflow STYLE] [--merge METHOD] [--canonical PATH]"
|
|
99
|
+
opts.on( "--remote NAME", "Git remote name" ) { |v| options[ "git.remote" ] = v }
|
|
100
|
+
opts.on( "--main-branch NAME", "Main branch name" ) { |v| options[ "git.main_branch" ] = v }
|
|
101
|
+
opts.on( "--workflow STYLE", "Workflow style (branch or trunk)" ) { |v| options[ "workflow.style" ] = v }
|
|
102
|
+
opts.on( "--merge METHOD", "Merge method (squash, rebase, or merge)" ) { |v| options[ "govern.merge.method" ] = v }
|
|
103
|
+
opts.on( "--canonical PATH", "Canonical template directory path" ) { |v| options[ "template.canonical" ] = v }
|
|
104
|
+
end
|
|
105
|
+
setup_parser.parse!( argv )
|
|
106
|
+
unless argv.empty?
|
|
107
|
+
err.puts "#{BADGE} Unexpected arguments for setup: #{argv.join( ' ' )}"
|
|
108
|
+
err.puts setup_parser
|
|
109
|
+
return { command: :invalid }
|
|
110
|
+
end
|
|
111
|
+
{ command: "setup", cli_choices: options }
|
|
112
|
+
rescue OptionParser::ParseError => e
|
|
113
|
+
err.puts "#{BADGE} #{e.message}"
|
|
114
|
+
{ command: :invalid }
|
|
115
|
+
end
|
|
116
|
+
|
|
93
117
|
def self.parse_repo_path_command( command:, argv:, parser:, err: )
|
|
94
118
|
parser.parse!( argv )
|
|
95
119
|
if argv.length > 1
|
|
@@ -209,7 +233,7 @@ module Carson
|
|
|
209
233
|
|
|
210
234
|
case command
|
|
211
235
|
when "setup"
|
|
212
|
-
runtime.setup!
|
|
236
|
+
runtime.setup!( cli_choices: parsed.fetch( :cli_choices, {} ) )
|
|
213
237
|
when "audit"
|
|
214
238
|
runtime.audit!
|
|
215
239
|
when "sync"
|
data/lib/carson/runtime/audit.rb
CHANGED
|
@@ -54,42 +54,49 @@ module Carson
|
|
|
54
54
|
puts_verbose ""
|
|
55
55
|
puts_verbose "[PR and Required Checks (gh)]"
|
|
56
56
|
monitor_report = pr_and_check_report
|
|
57
|
-
audit_state = "attention" if audit_state == "ok" && monitor_report.fetch( :status )
|
|
58
|
-
if monitor_report.fetch( :status ) == "
|
|
59
|
-
audit_concise_problems << "Checks: skipped (#{monitor_report.fetch( :skip_reason )})."
|
|
60
|
-
elsif monitor_report.fetch( :status ) == "attention"
|
|
57
|
+
audit_state = "attention" if audit_state == "ok" && !%w[ok skipped].include?( monitor_report.fetch( :status ) )
|
|
58
|
+
if monitor_report.fetch( :status ) == "attention"
|
|
61
59
|
checks = monitor_report.fetch( :checks )
|
|
62
60
|
fail_n = checks.fetch( :failing_count )
|
|
63
61
|
pend_n = checks.fetch( :pending_count )
|
|
64
62
|
total = checks.fetch( :required_total )
|
|
63
|
+
fail_names = checks.fetch( :failing ).map { |e| e.fetch( :name ) }.join( ", " )
|
|
65
64
|
if fail_n.positive? && pend_n.positive?
|
|
66
|
-
audit_concise_problems << "Checks: #{fail_n} failing, #{pend_n} pending of #{total} required."
|
|
65
|
+
audit_concise_problems << "Checks: #{fail_n} failing (#{fail_names}), #{pend_n} pending of #{total} required."
|
|
67
66
|
elsif fail_n.positive?
|
|
68
|
-
audit_concise_problems << "Checks: #{fail_n} of #{total} failing."
|
|
67
|
+
audit_concise_problems << "Checks: #{fail_n} of #{total} failing (#{fail_names})."
|
|
69
68
|
elsif pend_n.positive?
|
|
70
69
|
audit_concise_problems << "Checks: pending (#{total - pend_n} of #{total} complete)."
|
|
71
|
-
elsif checks.fetch( :status ) == "skipped"
|
|
72
|
-
audit_concise_problems << "Checks: skipped (#{checks.fetch( :skip_reason )})."
|
|
73
70
|
end
|
|
74
71
|
end
|
|
75
72
|
puts_verbose ""
|
|
76
73
|
puts_verbose "[Default Branch CI Baseline (gh)]"
|
|
77
74
|
default_branch_baseline = default_branch_ci_baseline_report
|
|
78
|
-
audit_state = "attention" if audit_state == "ok" && default_branch_baseline.fetch( :status )
|
|
75
|
+
audit_state = "attention" if audit_state == "ok" && !%w[ok skipped].include?( default_branch_baseline.fetch( :status ) )
|
|
79
76
|
baseline_st = default_branch_baseline.fetch( :status )
|
|
80
77
|
if baseline_st == "block"
|
|
81
78
|
parts = []
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
if default_branch_baseline.fetch( :failing_count ).positive?
|
|
80
|
+
names = default_branch_baseline.fetch( :failing ).map { |e| e.fetch( :name ) }.join( ", " )
|
|
81
|
+
parts << "#{default_branch_baseline.fetch( :failing_count )} failing (#{names})"
|
|
82
|
+
end
|
|
83
|
+
if default_branch_baseline.fetch( :pending_count ).positive?
|
|
84
|
+
names = default_branch_baseline.fetch( :pending ).map { |e| e.fetch( :name ) }.join( ", " )
|
|
85
|
+
parts << "#{default_branch_baseline.fetch( :pending_count )} pending (#{names})"
|
|
86
|
+
end
|
|
84
87
|
parts << "no check-runs for active workflows" if default_branch_baseline.fetch( :no_check_evidence )
|
|
85
88
|
audit_concise_problems << "Baseline (#{default_branch_baseline.fetch( :default_branch, config.main_branch )}): #{parts.join( ', ' )} — fix before merge."
|
|
86
89
|
elsif baseline_st == "attention"
|
|
87
90
|
parts = []
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
if default_branch_baseline.fetch( :advisory_failing_count ).positive?
|
|
92
|
+
names = default_branch_baseline.fetch( :advisory_failing ).map { |e| e.fetch( :name ) }.join( ", " )
|
|
93
|
+
parts << "#{default_branch_baseline.fetch( :advisory_failing_count )} advisory failing (#{names})"
|
|
94
|
+
end
|
|
95
|
+
if default_branch_baseline.fetch( :advisory_pending_count ).positive?
|
|
96
|
+
names = default_branch_baseline.fetch( :advisory_pending ).map { |e| e.fetch( :name ) }.join( ", " )
|
|
97
|
+
parts << "#{default_branch_baseline.fetch( :advisory_pending_count )} advisory pending (#{names})"
|
|
98
|
+
end
|
|
90
99
|
audit_concise_problems << "Baseline (#{default_branch_baseline.fetch( :default_branch, config.main_branch )}): #{parts.join( ', ' )}."
|
|
91
|
-
elsif baseline_st == "skipped"
|
|
92
|
-
audit_concise_problems << "Baseline: skipped (#{default_branch_baseline.fetch( :skip_reason )})."
|
|
93
100
|
end
|
|
94
101
|
if config.template_canonical.nil? || config.template_canonical.to_s.empty?
|
|
95
102
|
puts_verbose ""
|
data/lib/carson/runtime/setup.rb
CHANGED
|
@@ -6,13 +6,17 @@ module Carson
|
|
|
6
6
|
module Setup
|
|
7
7
|
WELL_KNOWN_REMOTES = %w[origin github upstream].freeze
|
|
8
8
|
|
|
9
|
-
def setup!
|
|
9
|
+
def setup!( cli_choices: {} )
|
|
10
10
|
puts_verbose ""
|
|
11
11
|
puts_verbose "[Setup]"
|
|
12
12
|
|
|
13
13
|
unless inside_git_work_tree?
|
|
14
14
|
puts_line "WARN: not a git repository. Skipping remote and branch detection."
|
|
15
|
-
return write_setup_config( choices:
|
|
15
|
+
return write_setup_config( choices: cli_choices )
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
unless cli_choices.empty?
|
|
19
|
+
return write_setup_config( choices: cli_choices )
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
if self.in.respond_to?( :tty? ) && self.in.tty?
|