carson 2.28.0 → 2.30.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7117079168b4f8472a4324f954bc087122e0ded0d253252b73702c41d3996283
4
- data.tar.gz: 21484878bcafe5799dfd87a18c223b5982d6b2ff1ab988c78f929de9cd642bdd
3
+ metadata.gz: 374e4a7af2ec608b1c3c9d07ca4a5ad8b028715434c1f02fb4b4034a71b153d2
4
+ data.tar.gz: a795fabcecbe2b43d03f155b0d4b730f804d0879ef4a1fe280cc4e082350c76f
5
5
  SHA512:
6
- metadata.gz: bb4b347d7ace09997a93b40fc7ec179baf4eb8aee75a620e3511d4ac7b9c46545380f7157ac442807e09cc1a679e8709a62df1a237a41618501688162f99c511
7
- data.tar.gz: 91aaf60433a82548170eb66eef14ce90c0eb730a3a56e06f7c154449d2355ca50f9a1e282d1b26e372c34b1df2946cb804587854875c14d1ffec3da5d6658bff
6
+ metadata.gz: 83b920da278fa2e457c424babaf3b06955a2317f920b5ad3681ae4da8a90fc5d5c3969d7160796b0febc49669a7cf7995c8f83ba674ce7f0865ca72eb6a48cbf
7
+ data.tar.gz: 8506fe647fe4538a27dd0ef25e0ecbeb1d9c0b78c8251d45b6023e562091cd6c861ef660707684b4e3d202a69728b0779a8a623ceb79313efc7e86eb0b482b17
data/RELEASE.md CHANGED
@@ -5,6 +5,29 @@ 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.30.0 — Prune Rebase Merge Support + Audit Noise Reduction
9
+
10
+ ### What changed
11
+
12
+ - **`carson prune` now handles rebase and cherry-pick merges.** When a stale branch fails SHA-based merged PR evidence (commit hashes change after rebase), prune falls back to content-level comparison against main. If the branch content is already absorbed into main, it is safely deleted.
13
+ - **Canonical templates hint removed from concise audit output.** The "canonical templates not configured" message no longer appears on every commit. Verbose mode retains it for debugging.
14
+
15
+ ### Migration
16
+
17
+ - No action required. Prune behaviour is strictly more capable; existing workflows are unaffected.
18
+
19
+ ## 2.29.0 — Audit Concise Output UX
20
+
21
+ ### What changed
22
+
23
+ - **"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.
24
+ - **Audit state no longer bumps to "attention" for skipped checks.** A skip is a non-event, not a concern.
25
+ - **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.
26
+
27
+ ### Migration
28
+
29
+ - No action required. This is a display-only change to concise audit output.
30
+
8
31
  ## 2.28.0 — Non-Interactive CLI Flags for Setup
9
32
 
10
33
  ### What changed
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.28.0
1
+ 2.30.0
@@ -54,48 +54,54 @@ 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 ) != "ok"
58
- if monitor_report.fetch( :status ) == "skipped"
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 ) != "ok"
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
- parts << "#{default_branch_baseline.fetch( :failing_count )} failing" if default_branch_baseline.fetch( :failing_count ).positive?
83
- parts << "#{default_branch_baseline.fetch( :pending_count )} pending" if default_branch_baseline.fetch( :pending_count ).positive?
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
- parts << "#{default_branch_baseline.fetch( :advisory_failing_count )} advisory failing" if default_branch_baseline.fetch( :advisory_failing_count ).positive?
89
- parts << "#{default_branch_baseline.fetch( :advisory_pending_count )} advisory pending" if default_branch_baseline.fetch( :advisory_pending_count ).positive?
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 ""
96
103
  puts_verbose "[Canonical Templates]"
97
104
  puts_verbose "HINT: canonical templates not configured — run carson setup to enable."
98
- audit_concise_problems << "Hint: canonical templates not configured — run carson setup to enable."
99
105
  end
100
106
  write_and_print_pr_monitor_report(
101
107
  report: monitor_report.merge(
@@ -305,6 +305,8 @@ module Carson
305
305
  end
306
306
 
307
307
  # Guarded force-delete policy for stale branches.
308
+ # Checks merged PR evidence first (exact SHA match), then falls back to
309
+ # absorbed-into-main detection (covers rebase merges where commit hashes change).
308
310
  def force_delete_evidence_for_stale_branch( branch:, delete_error_text: )
309
311
  return [ nil, "safe delete failure is not merge-related" ] unless non_merged_delete_error?( error_text: delete_error_text )
310
312
  return [ nil, "gh CLI not available; cannot verify merged PR evidence" ] unless gh_available?
@@ -318,7 +320,21 @@ module Carson
318
320
  branch_tip_sha = tip_sha_text.to_s.strip
319
321
  return [ nil, "unable to read local branch tip sha" ] if branch_tip_sha.empty?
320
322
 
321
- merged_pr_for_branch( branch: branch, branch_tip_sha: branch_tip_sha )
323
+ merged_pr, error = merged_pr_for_branch( branch: branch, branch_tip_sha: branch_tip_sha )
324
+ return [ merged_pr, error ] unless merged_pr.nil?
325
+
326
+ # Fallback: branch content is already on main (rebase/cherry-pick merges rewrite SHAs).
327
+ if branch_absorbed_into_main?( branch: branch )
328
+ absorbed_evidence = {
329
+ number: nil,
330
+ url: "absorbed into #{config.main_branch}",
331
+ merged_at: Time.now.utc.iso8601,
332
+ head_sha: branch_tip_sha
333
+ }
334
+ return [ absorbed_evidence, nil ]
335
+ end
336
+
337
+ [ nil, error ]
322
338
  end
323
339
 
324
340
  # Finds merged PR evidence for the exact local branch tip.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carson
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.28.0
4
+ version: 2.30.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hailei Wang