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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87aef62f0df0f4a298b22d5fd2dcc033b3fb8ff382e7c43fcc37399fb9e28b17
4
- data.tar.gz: 50120a7c2252236e86736fbab59eeade47b80488c89b2901849b16dc77ed2c7f
3
+ metadata.gz: fbc67303ff1efe52235731586e0fb13830d943764edfb67119ff3b8cd5605102
4
+ data.tar.gz: 6facbe0d10eebe0ef1f89d491799fa65d2993221574b1d3e7e898d82e3c0fc32
5
5
  SHA512:
6
- metadata.gz: 790d1b70d2df69440acc5c7d13ed7566b243e3b69a7f6d5735bc6713c275702e72273cd13ff6c958e3635669ea2411af6289eefc69e6782d3182a7dd390e1f61
7
- data.tar.gz: e9367dd0605946d9a2f7202c0f41cb2ebebfe6e19b463e606bb88e4e1d719b3c5fbdfc76911bf8b7be1a225f5dbbdd4518b88d5f9f0a5a92ad9a841d11f1f92d
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 setup --source https://github.com/wanghailei/ai.git --ref main --force
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 setup`.
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.12.0
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 setup --source <path-or-git-url>" >&2
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", "refresh", "offboard"
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"
@@ -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
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.12.0
4
+ version: 2.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hailei Wang