carson 2.9.0 → 2.11.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/API.md +1 -0
- data/MANUAL.md +1 -0
- data/RELEASE.md +29 -3
- data/VERSION +1 -1
- data/carson.gemspec +1 -1
- data/hooks/pre-merge-commit +1 -1
- data/hooks/pre-push +1 -1
- data/hooks/prepare-commit-msg +1 -1
- data/lib/carson/config.rb +1 -1
- data/lib/carson/policy/ruby/lint.rb +0 -1
- data/lib/carson/runtime/audit.rb +2 -2
- data/lib/carson/runtime/setup.rb +54 -3
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ef5d5bfa64c8a8b7f4d2b4893dd5a3cab05ab2f154bd12ab926837ae396c2fe2
|
|
4
|
+
data.tar.gz: 106a0dc1a54c05e59ba9fe4eb0d01f647c420ac305d92a5d70cbc3c606300006
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1c1d53528fa822f8d7e4850234a039459f56053762ed8ed1982e7a27c2b1f7fd7820f6565afc2a8cda596971ce4dfab2e2288aeabf8b92e54e2cc5e9266ab213
|
|
7
|
+
data.tar.gz: 04755a262052a630f8200089d03ff1f5a7c48ab8e68f50097fcdd206a8672da988d51c5f041384752a7d97712854b054aa4fd85b2b7268989190ea248d33bf1f
|
data/API.md
CHANGED
data/MANUAL.md
CHANGED
|
@@ -212,6 +212,7 @@ Common environment overrides:
|
|
|
212
212
|
| `CARSON_REVIEW_DISPOSITION_PREFIX` | Required prefix for disposition comments. |
|
|
213
213
|
| `CARSON_REVIEW_SWEEP_WINDOW_DAYS` | Lookback window for review sweep. |
|
|
214
214
|
| `CARSON_REVIEW_SWEEP_STATES` | PR states to include in sweep. |
|
|
215
|
+
| `CARSON_WORKFLOW_STYLE` | Workflow style override (`branch` or `trunk`). |
|
|
215
216
|
| `CARSON_RUBY_INDENTATION` | Ruby indentation policy (`tabs`, `spaces`, or `either`). |
|
|
216
217
|
|
|
217
218
|
For the full configuration schema and `lint.languages` definition, see `API.md`.
|
data/RELEASE.md
CHANGED
|
@@ -5,6 +5,31 @@ 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.11.0 — Self-Diagnosing Audit and Duplicate-Remote Prevention
|
|
9
|
+
|
|
10
|
+
### What changed
|
|
11
|
+
|
|
12
|
+
- **Audit concise output now names the remote and suggests recovery actions.** "Main sync (origin): ahead by 1 — git fetch origin, or carson setup to switch remote." instead of the opaque "Main sync: ahead by 1 — reset local drift." The remote name is visible; the fix is embedded.
|
|
13
|
+
- **Setup warns when multiple remotes share the same URL.** Interactive mode annotates duplicates with `[duplicate]` and prints a warning. Silent mode logs a `duplicate_remotes:` verbose line. URL normalisation treats SSH and HTTPS variants as equal (`git@github.com:user/repo.git` matches `https://github.com/user/repo`).
|
|
14
|
+
|
|
15
|
+
### What users must do now
|
|
16
|
+
|
|
17
|
+
1. Upgrade Carson to `2.11.0`.
|
|
18
|
+
2. If you have duplicate remotes (e.g. both `origin` and `github` pointing to the same URL), remove the stale one with `git remote remove <name>`.
|
|
19
|
+
|
|
20
|
+
## 2.10.0 — Lower Ruby Requirement to 3.4
|
|
21
|
+
|
|
22
|
+
### What changed
|
|
23
|
+
|
|
24
|
+
- **Minimum Ruby version lowered from 4.0 to 3.4.** Carson uses no Ruby 4.0-specific features. Lowering to 3.4 widens compatibility to the current stable Ruby series while enabling the `it` implicit block parameter.
|
|
25
|
+
- **Removed `# frozen_string_literal: true` pragma** from the one file that had it (`lib/carson/policy/ruby/lint.rb`). Ruby 4.0 freezes strings by default; the pragma is unnecessary.
|
|
26
|
+
- **Default workflow style now actually `branch` in code.** The 2.9.0 release notes documented this change, but the hooks and config default were not updated. Now fixed: hooks fall back to `branch`, config default is `branch`, and `CARSON_WORKFLOW_STYLE` env override is documented.
|
|
27
|
+
|
|
28
|
+
### What users must do now
|
|
29
|
+
|
|
30
|
+
1. Upgrade Carson to `2.10.0`.
|
|
31
|
+
2. Ruby 3.4 or later is now sufficient — Ruby 4.0 is no longer required.
|
|
32
|
+
|
|
8
33
|
## 2.9.0 — Concise UX for All Commands
|
|
9
34
|
|
|
10
35
|
### What changed
|
|
@@ -14,16 +39,19 @@ Release-note scope rule:
|
|
|
14
39
|
- **Audit concise output.** A healthy audit prints one line (`Audit: ok`). Problems print only actionable summaries (e.g. `Hooks: mismatch — run carson prepare.`).
|
|
15
40
|
- **Refresh concise output.** Prints ~5 lines: hooks installed, templates in sync, audit result, done.
|
|
16
41
|
- **All other commands.** `prepare`, `inspect`, `offboard`, `template check/apply`, `prune`, `review gate/sweep`, `govern`, `lint setup`, `setup`, and `housekeep` all follow the same concise/verbose pattern.
|
|
42
|
+
- **Default workflow style changed from `trunk` to `branch`.** All governed repositories now enforce PR-only merges by default. Direct commits, merge commits, and pushes to protected branches (`main`/`master`) are blocked by hooks unless explicitly opted out.
|
|
17
43
|
|
|
18
44
|
### What users must do now
|
|
19
45
|
|
|
20
46
|
1. Upgrade Carson to `2.9.0`.
|
|
21
47
|
2. Use `--verbose` when you need full diagnostics (debugging, CI troubleshooting).
|
|
48
|
+
3. If you rely on direct commits to main, re-run `carson setup` and choose `trunk`, or set `CARSON_WORKFLOW_STYLE=trunk` in your environment.
|
|
22
49
|
|
|
23
50
|
### Breaking or removed behaviour
|
|
24
51
|
|
|
25
52
|
- Default output is now concise. Scripts that parse Carson's key-value diagnostic lines must add `--verbose`.
|
|
26
53
|
- Removed `@concise` internal flag (replaced by `--verbose` opt-in pattern).
|
|
54
|
+
- Default `workflow.style` changed from `trunk` to `branch`. Repositories that previously relied on the implicit `trunk` default will now block direct commits to protected branches. Escape hatches: run `carson setup` to choose `trunk`, or set `CARSON_WORKFLOW_STYLE=trunk`.
|
|
27
55
|
|
|
28
56
|
### Upgrade steps
|
|
29
57
|
|
|
@@ -41,16 +69,14 @@ carson version
|
|
|
41
69
|
- **Concise onboard output.** `carson onboard` now prints a clean 8-line summary instead of verbose internal state (hook paths, template statuses, config lines). Tells users what happened, what needs attention, and what to do next.
|
|
42
70
|
- **Graceful handling of fresh repos.** Onboard no longer fails with a fatal error on repositories with no commits yet.
|
|
43
71
|
- **Suppressed RubyGems PATH warning.** The misleading `WARNING: You don't have ... in your PATH, gem executables will not run` message from `gem install --user-install` is now suppressed during installation. Carson symlinks the executable to `~/.carson/bin`, making the gem bin directory irrelevant.
|
|
44
|
-
- **Default workflow style changed from `trunk` to `branch`.** All governed repositories now enforce PR-only merges by default. Direct commits, merge commits, and pushes to protected branches (`main`/`master`) are blocked by hooks unless explicitly opted out.
|
|
45
72
|
|
|
46
73
|
### What users must do now
|
|
47
74
|
|
|
48
75
|
1. Upgrade Carson to `2.8.1`.
|
|
49
|
-
2. If you rely on direct commits to main, re-run `carson setup` and choose `trunk`, or set `CARSON_WORKFLOW_STYLE=trunk` in your environment.
|
|
50
76
|
|
|
51
77
|
### Breaking or removed behaviour
|
|
52
78
|
|
|
53
|
-
-
|
|
79
|
+
- None.
|
|
54
80
|
|
|
55
81
|
### Upgrade steps
|
|
56
82
|
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.11.0
|
data/carson.gemspec
CHANGED
|
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
|
11
11
|
spec.description = "Carson runs outside host repositories and applies governance checks, review gates, and managed GitHub-native files."
|
|
12
12
|
spec.homepage = "https://github.com/wanghailei/carson"
|
|
13
13
|
spec.license = "MIT"
|
|
14
|
-
spec.required_ruby_version = ">= 4
|
|
14
|
+
spec.required_ruby_version = ">= 3.4"
|
|
15
15
|
spec.metadata = {
|
|
16
16
|
"source_code_uri" => "https://github.com/wanghailei/carson",
|
|
17
17
|
"changelog_uri" => "https://github.com/wanghailei/carson/blob/main/RELEASE.md",
|
data/hooks/pre-merge-commit
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
hooks_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
-
style="$(cat "$hooks_dir/workflow_style" 2>/dev/null || echo "
|
|
5
|
+
style="$(cat "$hooks_dir/workflow_style" 2>/dev/null || echo "branch")"
|
|
6
6
|
[ "$style" = "trunk" ] && exit 0
|
|
7
7
|
|
|
8
8
|
branch_name="$(git rev-parse --abbrev-ref HEAD)"
|
data/hooks/pre-push
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
hooks_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
-
style="$(cat "$hooks_dir/workflow_style" 2>/dev/null || echo "
|
|
5
|
+
style="$(cat "$hooks_dir/workflow_style" 2>/dev/null || echo "branch")"
|
|
6
6
|
[ "$style" = "trunk" ] && exit 0
|
|
7
7
|
|
|
8
8
|
remote_name="${1:-unknown}"
|
data/hooks/prepare-commit-msg
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
4
|
hooks_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
5
|
-
style="$(cat "$hooks_dir/workflow_style" 2>/dev/null || echo "
|
|
5
|
+
style="$(cat "$hooks_dir/workflow_style" 2>/dev/null || echo "branch")"
|
|
6
6
|
[ "$style" = "trunk" ] && exit 0
|
|
7
7
|
|
|
8
8
|
branch_name="$(git rev-parse --abbrev-ref HEAD)"
|
data/lib/carson/config.rb
CHANGED
data/lib/carson/runtime/audit.rb
CHANGED
|
@@ -46,13 +46,13 @@ module Carson
|
|
|
46
46
|
puts_verbose "main_vs_remote_main_behind: #{behind_count}"
|
|
47
47
|
puts_verbose "ACTION: local #{config.main_branch} is ahead of #{config.git_remote}/#{config.main_branch} by #{ahead_count} commit#{plural_suffix( count: ahead_count )}; reset local drift before commit/push workflows."
|
|
48
48
|
audit_state = "block"
|
|
49
|
-
audit_concise_problems << "Main sync: ahead by #{ahead_count} —
|
|
49
|
+
audit_concise_problems << "Main sync (#{config.git_remote}): ahead by #{ahead_count} — git fetch #{config.git_remote}, or carson setup to switch remote."
|
|
50
50
|
elsif behind_count.positive?
|
|
51
51
|
puts_verbose "main_vs_remote_main_ahead: #{ahead_count}"
|
|
52
52
|
puts_verbose "main_vs_remote_main_behind: #{behind_count}"
|
|
53
53
|
puts_verbose "ACTION: local #{config.main_branch} is behind #{config.git_remote}/#{config.main_branch} by #{behind_count} commit#{plural_suffix( count: behind_count )}; run carson sync."
|
|
54
54
|
audit_state = "attention" if audit_state == "ok"
|
|
55
|
-
audit_concise_problems << "Main sync: behind by #{behind_count} — run carson sync."
|
|
55
|
+
audit_concise_problems << "Main sync (#{config.git_remote}): behind by #{behind_count} — run carson sync."
|
|
56
56
|
else
|
|
57
57
|
puts_verbose "main_vs_remote_main_ahead: 0"
|
|
58
58
|
puts_verbose "main_vs_remote_main_behind: 0"
|
data/lib/carson/runtime/setup.rb
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
require "uri"
|
|
3
|
+
|
|
1
4
|
module Carson
|
|
2
5
|
class Runtime
|
|
3
6
|
module Setup
|
|
@@ -51,6 +54,15 @@ module Carson
|
|
|
51
54
|
puts_verbose "detected_remote: none"
|
|
52
55
|
end
|
|
53
56
|
|
|
57
|
+
remotes = list_git_remotes
|
|
58
|
+
duplicates = duplicate_remote_groups( remotes: remotes )
|
|
59
|
+
unless duplicates.empty?
|
|
60
|
+
duplicates.each_value do |group|
|
|
61
|
+
names = group.map { it.fetch( :name ) }.join( " and " )
|
|
62
|
+
puts_verbose "duplicate_remotes: #{names} share the same URL"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
54
66
|
branch = detect_main_branch
|
|
55
67
|
if branch && branch != config.main_branch
|
|
56
68
|
choices[ "git.main_branch" ] = branch
|
|
@@ -69,9 +81,18 @@ module Carson
|
|
|
69
81
|
return nil
|
|
70
82
|
end
|
|
71
83
|
|
|
84
|
+
duplicates = duplicate_remote_groups( remotes: remotes )
|
|
85
|
+
duplicate_names = duplicates.values.flatten.map { it.fetch( :name ) }.to_set
|
|
86
|
+
unless duplicates.empty?
|
|
87
|
+
duplicates.each_value do |group|
|
|
88
|
+
names = group.map { it.fetch( :name ) }.join( " and " )
|
|
89
|
+
puts_line "Remotes #{names} share the same URL. Consider removing the duplicate."
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
72
93
|
puts_line ""
|
|
73
94
|
puts_line "Git remote"
|
|
74
|
-
options = build_remote_options( remotes: remotes )
|
|
95
|
+
options = build_remote_options( remotes: remotes, duplicate_names: duplicate_names )
|
|
75
96
|
options << { label: "Other (enter name)", value: :other }
|
|
76
97
|
|
|
77
98
|
default_index = 0
|
|
@@ -151,12 +172,13 @@ module Carson
|
|
|
151
172
|
value.empty? ? nil : value
|
|
152
173
|
end
|
|
153
174
|
|
|
154
|
-
def build_remote_options( remotes: )
|
|
175
|
+
def build_remote_options( remotes:, duplicate_names: Set.new )
|
|
155
176
|
sorted = sort_remotes( remotes: remotes )
|
|
156
177
|
sorted.map do |entry|
|
|
157
178
|
name = entry.fetch( :name )
|
|
158
179
|
url = entry.fetch( :url )
|
|
159
|
-
|
|
180
|
+
tag = duplicate_names.include?( name ) ? " [duplicate]" : ""
|
|
181
|
+
{ label: "#{name} (#{url})#{tag}", value: name }
|
|
160
182
|
end
|
|
161
183
|
end
|
|
162
184
|
|
|
@@ -173,6 +195,35 @@ module Carson
|
|
|
173
195
|
well_known.sort_by { |e| WELL_KNOWN_REMOTES.index( e.fetch( :name ) ) || 999 } + others.sort_by { |e| e.fetch( :name ) }
|
|
174
196
|
end
|
|
175
197
|
|
|
198
|
+
# Normalises a remote URL so SSH and HTTPS variants of the same host/path compare equal.
|
|
199
|
+
# Strips trailing .git, lowercases, converts git@host:path to https://host/path.
|
|
200
|
+
def normalise_remote_url( url: )
|
|
201
|
+
text = url.to_s.strip
|
|
202
|
+
return "" if text.empty?
|
|
203
|
+
|
|
204
|
+
# Convert SSH shorthand (git@host:owner/repo) to HTTPS form.
|
|
205
|
+
if text.match?( /\A[\w.-]+@[\w.-]+:/ )
|
|
206
|
+
text = text.sub( /\A[\w.-]+@([\w.-]+):/, 'https://\1/' )
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
text = text.delete_suffix( ".git" )
|
|
210
|
+
text = text.chomp( "/" )
|
|
211
|
+
text.downcase
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# Groups remotes that share the same normalised URL. Returns a hash of
|
|
215
|
+
# normalised_url => [remote entries] for groups with more than one member.
|
|
216
|
+
def duplicate_remote_groups( remotes: )
|
|
217
|
+
by_url = {}
|
|
218
|
+
remotes.each do |entry|
|
|
219
|
+
key = normalise_remote_url( url: entry.fetch( :url ) )
|
|
220
|
+
next if key.empty?
|
|
221
|
+
|
|
222
|
+
( by_url[ key ] ||= [] ) << entry
|
|
223
|
+
end
|
|
224
|
+
by_url.select { |_url, entries| entries.length > 1 }
|
|
225
|
+
end
|
|
226
|
+
|
|
176
227
|
def build_main_branch_options
|
|
177
228
|
options = []
|
|
178
229
|
main_exists = branch_exists_locally_or_remote?( branch: "main" )
|
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.
|
|
4
|
+
version: 2.11.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hailei Wang
|
|
@@ -88,7 +88,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
88
88
|
requirements:
|
|
89
89
|
- - ">="
|
|
90
90
|
- !ruby/object:Gem::Version
|
|
91
|
-
version: '4
|
|
91
|
+
version: '3.4'
|
|
92
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - ">="
|