carson 2.12.0 → 2.13.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/.github/workflows/carson_policy.yml +1 -1
- data/MANUAL.md +3 -2
- data/README.md +1 -0
- data/RELEASE.md +6 -0
- data/VERSION +1 -1
- data/hooks/pre-commit +1 -1
- data/lib/carson/cli.rb +37 -2
- data/lib/carson/runtime/local.rb +58 -0
- 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: fbc67303ff1efe52235731586e0fb13830d943764edfb67119ff3b8cd5605102
|
|
4
|
+
data.tar.gz: 6facbe0d10eebe0ef1f89d491799fa65d2993221574b1d3e7e898d82e3c0fc32
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c606ed005e274ab872ccf9b50da945edcc8bffcf2a7e146679e0f6f9673c572d826a679e9575d9866a4c91ffe71ca648a89b415237cb63e97f7d023ffa05a453
|
|
7
|
+
data.tar.gz: fc3e7c8ba85a437aafc643f498c381bb8f617ee71c5527b08462af993f1b94cbcf51dbfc9298dd45b3dee92dbfad45c611513e449114051878e4c8239e4b7c23
|
|
@@ -71,7 +71,7 @@ jobs:
|
|
|
71
71
|
env:
|
|
72
72
|
CARSON_READ_TOKEN: ${{ secrets.CARSON_READ_TOKEN }}
|
|
73
73
|
run: |
|
|
74
|
-
carson lint
|
|
74
|
+
carson lint policy --source https://github.com/wanghailei/ai.git --ref main --force
|
|
75
75
|
carson prepare
|
|
76
76
|
carson audit
|
|
77
77
|
|
data/MANUAL.md
CHANGED
|
@@ -94,7 +94,7 @@ jobs:
|
|
|
94
94
|
|
|
95
95
|
Notes:
|
|
96
96
|
- When upgrading Carson, update both `carson_ref` and `carson_version` together.
|
|
97
|
-
- `CARSON_READ_TOKEN` must have read access to your policy source repository so CI can run `carson lint
|
|
97
|
+
- `CARSON_READ_TOKEN` must have read access to your policy source repository so CI can run `carson lint policy`.
|
|
98
98
|
- The reusable workflow installs a pinned RuboCop gem before `carson audit`; mirror the same pin in host governance workflows for deterministic checks.
|
|
99
99
|
|
|
100
100
|
## Daily Operations
|
|
@@ -241,7 +241,7 @@ Where lint configuration files come from and where they land.
|
|
|
241
241
|
- Default source: **`wanghailei/lint.git`**. A central repository containing lint configs for all languages.
|
|
242
242
|
- Target: **`<repo>/.github/linters/`**. MegaLinter auto-discovers configs here in CI.
|
|
243
243
|
|
|
244
|
-
Change: `carson lint policy --source <path-or-git-url>` or `lint.policy_source` in config.
|
|
244
|
+
Change: `carson lint policy --source <path-or-git-url>` or `lint.policy_source` in config. After changing policy, run `carson refresh --all` to propagate to all governed repositories.
|
|
245
245
|
|
|
246
246
|
#### Scope integrity
|
|
247
247
|
|
|
@@ -337,6 +337,7 @@ carson template check
|
|
|
337
337
|
|
|
338
338
|
**Hook version mismatch after upgrade**
|
|
339
339
|
- Run `carson refresh` to re-apply hooks and templates for the new Carson version.
|
|
340
|
+
- Run `carson refresh --all` to refresh all governed repositories at once.
|
|
340
341
|
|
|
341
342
|
## Offboard a Repository
|
|
342
343
|
|
data/README.md
CHANGED
|
@@ -78,6 +78,7 @@ The data flow:
|
|
|
78
78
|
| `carson onboard` | One-command baseline: hooks + templates + first audit. |
|
|
79
79
|
| `carson prepare` | Install or refresh Carson-managed global hooks. |
|
|
80
80
|
| `carson refresh` | Re-apply hooks, templates, and audit after upgrading Carson. |
|
|
81
|
+
| `carson refresh --all` | Refresh all governed repositories at once. |
|
|
81
82
|
| `carson offboard` | Remove Carson from a repository. |
|
|
82
83
|
|
|
83
84
|
**Daily** — regular development workflow:
|
data/RELEASE.md
CHANGED
|
@@ -5,6 +5,12 @@ 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.13.0 — Refresh All Governed Repositories
|
|
9
|
+
|
|
10
|
+
### What changed
|
|
11
|
+
|
|
12
|
+
- **`carson refresh --all`** refreshes every governed repository in a single command. Iterates `govern.repos`, runs hooks + templates + audit on each, prints a per-repo summary line, and returns non-zero if any repo fails. Verbose mode streams full diagnostics per repo.
|
|
13
|
+
|
|
8
14
|
## 2.12.0 — Language-Agnostic Lint Policy Distribution + MegaLinter
|
|
9
15
|
|
|
10
16
|
### What changed
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.
|
|
1
|
+
2.13.0
|
data/hooks/pre-commit
CHANGED
|
@@ -14,6 +14,6 @@ fi
|
|
|
14
14
|
if ! "${carson_command[@]}" audit; then
|
|
15
15
|
echo "Carson policy: commit blocked by governance audit." >&2
|
|
16
16
|
echo "Resolve reported policy blocks before committing." >&2
|
|
17
|
-
echo "If lint policy files are missing, run: carson lint
|
|
17
|
+
echo "If lint policy files are missing, run: carson lint policy --source <path-or-git-url>" >&2
|
|
18
18
|
exit 1
|
|
19
19
|
fi
|
data/lib/carson/cli.rb
CHANGED
|
@@ -12,6 +12,12 @@ module Carson
|
|
|
12
12
|
return Runtime::EXIT_OK
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
if command == "refresh:all"
|
|
16
|
+
verbose = parsed.fetch( :verbose, false )
|
|
17
|
+
runtime = Runtime.new( repo_root: repo_root, tool_root: tool_root, out: out, err: err, verbose: verbose )
|
|
18
|
+
return dispatch( parsed: parsed, runtime: runtime )
|
|
19
|
+
end
|
|
20
|
+
|
|
15
21
|
target_repo_root = parsed.fetch( :repo_root, nil )
|
|
16
22
|
target_repo_root = repo_root if target_repo_root.to_s.strip.empty?
|
|
17
23
|
unless Dir.exist?( target_repo_root )
|
|
@@ -47,7 +53,7 @@ module Carson
|
|
|
47
53
|
|
|
48
54
|
def self.build_parser
|
|
49
55
|
OptionParser.new do |opts|
|
|
50
|
-
opts.banner = "Usage: carson [setup|audit|sync|prune|prepare|inspect|onboard [repo_path]|refresh [repo_path]|offboard [repo_path]|template check|template apply|lint policy --source <path-or-git-url>|review gate|review sweep|govern [--dry-run] [--json] [--loop SECONDS]|housekeep|version]"
|
|
56
|
+
opts.banner = "Usage: carson [setup|audit|sync|prune|prepare|inspect|onboard [repo_path]|refresh [--all|repo_path]|offboard [repo_path]|template check|template apply|lint policy --source <path-or-git-url>|review gate|review sweep|govern [--dry-run] [--json] [--loop SECONDS]|housekeep|version]"
|
|
51
57
|
end
|
|
52
58
|
end
|
|
53
59
|
|
|
@@ -68,8 +74,10 @@ module Carson
|
|
|
68
74
|
when "version"
|
|
69
75
|
parser.parse!( argv )
|
|
70
76
|
{ command: "version" }
|
|
71
|
-
when "onboard", "
|
|
77
|
+
when "onboard", "offboard"
|
|
72
78
|
parse_repo_path_command( command: command, argv: argv, parser: parser, err: err )
|
|
79
|
+
when "refresh"
|
|
80
|
+
parse_refresh_command( argv: argv, parser: parser, err: err )
|
|
73
81
|
when "template"
|
|
74
82
|
parse_named_subcommand( command: command, usage: "check|apply", argv: argv, parser: parser, err: err )
|
|
75
83
|
when "lint"
|
|
@@ -99,6 +107,31 @@ module Carson
|
|
|
99
107
|
}
|
|
100
108
|
end
|
|
101
109
|
|
|
110
|
+
def self.parse_refresh_command( argv:, parser:, err: )
|
|
111
|
+
all_flag = argv.delete( "--all" ) ? true : false
|
|
112
|
+
parser.parse!( argv )
|
|
113
|
+
|
|
114
|
+
if all_flag && !argv.empty?
|
|
115
|
+
err.puts "#{BADGE} --all and repo_path are mutually exclusive. Use: carson refresh --all OR carson refresh [repo_path]"
|
|
116
|
+
err.puts parser
|
|
117
|
+
return { command: :invalid }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
return { command: "refresh:all" } if all_flag
|
|
121
|
+
|
|
122
|
+
if argv.length > 1
|
|
123
|
+
err.puts "#{BADGE} Too many arguments for refresh. Use: carson refresh [repo_path]"
|
|
124
|
+
err.puts parser
|
|
125
|
+
return { command: :invalid }
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
repo_path = argv.first
|
|
129
|
+
{
|
|
130
|
+
command: "refresh",
|
|
131
|
+
repo_root: repo_path.to_s.strip.empty? ? nil : File.expand_path( repo_path )
|
|
132
|
+
}
|
|
133
|
+
end
|
|
134
|
+
|
|
102
135
|
def self.parse_named_subcommand( command:, usage:, argv:, parser:, err: )
|
|
103
136
|
action = argv.shift
|
|
104
137
|
parser.parse!( argv )
|
|
@@ -206,6 +239,8 @@ module Carson
|
|
|
206
239
|
runtime.onboard!
|
|
207
240
|
when "refresh"
|
|
208
241
|
runtime.refresh!
|
|
242
|
+
when "refresh:all"
|
|
243
|
+
runtime.refresh_all!
|
|
209
244
|
when "offboard"
|
|
210
245
|
runtime.offboard!
|
|
211
246
|
when "template:check"
|
data/lib/carson/runtime/local.rb
CHANGED
|
@@ -247,6 +247,40 @@ module Carson
|
|
|
247
247
|
audit_status
|
|
248
248
|
end
|
|
249
249
|
|
|
250
|
+
# Re-applies hooks, templates, and audit across all governed repositories.
|
|
251
|
+
def refresh_all!
|
|
252
|
+
repos = config.govern_repos
|
|
253
|
+
if repos.empty?
|
|
254
|
+
puts_line "ERROR: no governed repositories configured. Add repos via carson setup or govern.repos in ~/.carson/config.json."
|
|
255
|
+
return EXIT_ERROR
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
puts_line ""
|
|
259
|
+
puts_line "Refresh all (#{repos.length} repo#{plural_suffix( count: repos.length )})"
|
|
260
|
+
refreshed = 0
|
|
261
|
+
failed = 0
|
|
262
|
+
|
|
263
|
+
repos.each do |repo_path|
|
|
264
|
+
repo_name = File.basename( repo_path )
|
|
265
|
+
unless Dir.exist?( repo_path )
|
|
266
|
+
puts_line "#{repo_name}: FAIL (path not found)"
|
|
267
|
+
failed += 1
|
|
268
|
+
next
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
status = refresh_single_repo( repo_path: repo_path, repo_name: repo_name )
|
|
272
|
+
if status == EXIT_ERROR
|
|
273
|
+
failed += 1
|
|
274
|
+
else
|
|
275
|
+
refreshed += 1
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
puts_line ""
|
|
280
|
+
puts_line "Refresh all complete: #{refreshed} refreshed, #{failed} failed."
|
|
281
|
+
failed.zero? ? EXIT_OK : EXIT_ERROR
|
|
282
|
+
end
|
|
283
|
+
|
|
250
284
|
# Removes Carson-managed repository integration so a host repository can retire Carson cleanly.
|
|
251
285
|
def offboard!
|
|
252
286
|
puts_verbose ""
|
|
@@ -365,6 +399,30 @@ module Carson
|
|
|
365
399
|
|
|
366
400
|
private
|
|
367
401
|
|
|
402
|
+
# Refreshes a single governed repository using a scoped Runtime.
|
|
403
|
+
def refresh_single_repo( repo_path:, repo_name: )
|
|
404
|
+
if verbose?
|
|
405
|
+
rt = Runtime.new( repo_root: repo_path, tool_root: tool_root, out: out, err: err, verbose: true )
|
|
406
|
+
else
|
|
407
|
+
rt = Runtime.new( repo_root: repo_path, tool_root: tool_root, out: StringIO.new, err: StringIO.new )
|
|
408
|
+
end
|
|
409
|
+
status = rt.refresh!
|
|
410
|
+
label = refresh_status_label( status: status )
|
|
411
|
+
puts_line "#{repo_name}: #{label}"
|
|
412
|
+
status
|
|
413
|
+
rescue StandardError => e
|
|
414
|
+
puts_line "#{repo_name}: FAIL (#{e.message})"
|
|
415
|
+
EXIT_ERROR
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def refresh_status_label( status: )
|
|
419
|
+
case status
|
|
420
|
+
when EXIT_OK then "OK"
|
|
421
|
+
when EXIT_BLOCK then "BLOCK"
|
|
422
|
+
else "FAIL"
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
368
426
|
def template_results
|
|
369
427
|
config.template_managed_files.map { |managed_file| template_result_for_file( managed_file: managed_file ) }
|
|
370
428
|
end
|