carson 3.13.2 → 3.15.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/MANUAL.md +37 -0
- data/README.md +2 -0
- data/RELEASE.md +26 -0
- data/VERSION +1 -1
- data/lib/carson/cli.rb +45 -2
- data/lib/carson/runtime/housekeep.rb +120 -0
- data/lib/carson/runtime/local/onboard.rb +1 -5
- data/lib/carson/runtime/local/worktree.rb +3 -2
- data/lib/carson/runtime/repos.rb +29 -0
- data/lib/carson/runtime/setup.rb +4 -12
- data/lib/carson/runtime.rb +2 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 83272d790537e52d22639bbf4831f772496b9ffa5938b23da6d2865ae73d8e38
|
|
4
|
+
data.tar.gz: ff4d8db79fc54bcd12d74a4c6c74332861ea58dc3fef78f93b3188237d27c772
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 655f2cb1f8caa036e96dc17e4e360ced9256840ee5f2050d33e9f72f4187a2f0d8fefbe27d82ff2da8e3bd21f5c8bb460886bf31af922818fa74efeb3fe9d59f
|
|
7
|
+
data.tar.gz: 44490df6057142b3f58745ac24041e501207599684d9a10816ee505ed192c681661fd4e23003d637a6ccf69f4a86efbd6d754ada2d6079ee99db89b431133495
|
data/MANUAL.md
CHANGED
|
@@ -142,6 +142,36 @@ carson prune
|
|
|
142
142
|
|
|
143
143
|
After squash or rebase merge, the content matches main — removal proceeds without `--force`.
|
|
144
144
|
|
|
145
|
+
### Carson vs Claude Code EnterWorktree
|
|
146
|
+
|
|
147
|
+
Claude Code has a built-in `EnterWorktree` tool. Both create a git worktree under `.claude/worktrees/` with a new branch — but they solve different problems and have different trade-offs.
|
|
148
|
+
|
|
149
|
+
**What Carson adds over EnterWorktree:**
|
|
150
|
+
|
|
151
|
+
| Concern | EnterWorktree | Carson |
|
|
152
|
+
|---|---|---|
|
|
153
|
+
| Auto-sync main before branching | No — branches from current HEAD, which may be stale | Yes — `git pull --ff-only` before branch creation |
|
|
154
|
+
| CWD guard on removal | No | Yes — blocks if shell is inside the worktree |
|
|
155
|
+
| Unpushed-commits guard | No | Yes — blocks if work hasn't been pushed |
|
|
156
|
+
| Content-aware squash/rebase detection | No | Yes — compares tree content, not SHAs |
|
|
157
|
+
| Branch cleanup (local + remote) | No | Yes — one command removes worktree, local branch, and remote branch |
|
|
158
|
+
| `.git/info/exclude` management | No | Yes — prevents `.claude/` appearing as untracked |
|
|
159
|
+
| Recovery-aware errors | No | Yes — every error includes a concrete recovery command |
|
|
160
|
+
| `--json` output | No | Yes — machine-readable for agent consumption |
|
|
161
|
+
|
|
162
|
+
**What EnterWorktree does that Carson does not:**
|
|
163
|
+
|
|
164
|
+
- **Automatic session CWD switch.** After creating the worktree, Claude Code moves the agent's working directory into it — no manual `cd` required. This is genuine friction that Carson should learn from.
|
|
165
|
+
|
|
166
|
+
**What EnterWorktree gets wrong:**
|
|
167
|
+
|
|
168
|
+
- **Session-exit cleanup prompt.** On session exit, Claude Code asks the user whether to keep or remove each worktree. In an agent workflow this is pure friction — the user cannot verify the state of bot-created worktrees and is forced to choose "keep" every time. Carson's approach is better: deferred deletion by default, with safety guards when you do choose to clean up.
|
|
169
|
+
- **Random names.** Without a name argument, EnterWorktree generates a random string. `git branch` output becomes unreadable. Carson requires a meaningful name that doubles as the branch name.
|
|
170
|
+
|
|
171
|
+
**Relationship — complementary, not competing:**
|
|
172
|
+
|
|
173
|
+
The two tools serve different layers. EnterWorktree owns the session (CWD switch); Carson owns the git lifecycle (sync, safety, cleanup). The ideal integration is Claude Code's `WorktreeCreate`/`WorktreeRemove` hook mechanism — Carson registers as the hook handler, so `EnterWorktree` delegates creation to `carson worktree create` and gets both Carson's safety and Claude Code's automatic CWD switch.
|
|
174
|
+
|
|
145
175
|
## Daily Operations
|
|
146
176
|
|
|
147
177
|
**Start of work:**
|
|
@@ -170,6 +200,13 @@ carson template apply
|
|
|
170
200
|
carson review gate
|
|
171
201
|
```
|
|
172
202
|
|
|
203
|
+
**Portfolio overview:**
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
carson repos # list all governed repositories
|
|
207
|
+
carson repos --json # machine-readable output
|
|
208
|
+
```
|
|
209
|
+
|
|
173
210
|
**Periodic maintenance:**
|
|
174
211
|
|
|
175
212
|
```bash
|
data/README.md
CHANGED
|
@@ -29,6 +29,8 @@ Carson is an autonomous governance runtime that lives on your workstation and in
|
|
|
29
29
|
|
|
30
30
|
This separation is Carson's defining trait — the **outsider boundary**: no Carson scripts, config files, or governance payloads are ever placed inside a governed repository.
|
|
31
31
|
|
|
32
|
+
**Agent workspace management** — `carson worktree create` and `carson worktree remove` give coding agents safe, isolated workspaces. Unlike Claude Code's built-in `EnterWorktree`, Carson auto-syncs main before branching, guards against removing worktrees with unpushed work or an active shell inside, detects squash/rebase merges so removal doesn't falsely block, and cleans up the local and remote branch in one step. The two tools are complementary — see `MANUAL.md § Carson vs Claude Code EnterWorktree` for the full comparison.
|
|
33
|
+
|
|
32
34
|
### The Governance Loop
|
|
33
35
|
|
|
34
36
|
Carson orchestrates a closed governance loop across two layers:
|
data/RELEASE.md
CHANGED
|
@@ -5,6 +5,32 @@ 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
|
+
## 3.15.0
|
|
9
|
+
|
|
10
|
+
### What changed
|
|
11
|
+
|
|
12
|
+
- **`carson housekeep` command** — sync + prune for repositories. Carson knocks each gate humbly:
|
|
13
|
+
- `carson housekeep` — serve the repo you are standing in.
|
|
14
|
+
- `carson housekeep <repo>` — serve a named governed repo (resolved by basename, case-insensitive).
|
|
15
|
+
- `carson housekeep --all` — knock each governed repo's gate in turn.
|
|
16
|
+
- Supports `--json` for machine-readable output in all three modes.
|
|
17
|
+
|
|
18
|
+
### UX improvement
|
|
19
|
+
|
|
20
|
+
- Fixed double-badge display when housekeep summarises prune results.
|
|
21
|
+
- JSON mode suppresses human-readable lines cleanly.
|
|
22
|
+
|
|
23
|
+
## 3.14.0
|
|
24
|
+
|
|
25
|
+
### What changed
|
|
26
|
+
|
|
27
|
+
- **`carson repos` command** — lists all governed repositories from Carson's global config. Shows which repos Carson is serving at a glance. Supports `--json` for machine-readable output. Portfolio-level command — works from any directory.
|
|
28
|
+
- **Auto-register on onboard** — `carson onboard` now automatically registers the repository for portfolio governance. No more TTY-only Y/n prompt — onboard means govern.
|
|
29
|
+
|
|
30
|
+
### UX improvement
|
|
31
|
+
|
|
32
|
+
- Onboarding is now one step: `carson onboard` sets up hooks, templates, and portfolio registration in a single pass. Works identically in TTY and non-TTY (agent) sessions.
|
|
33
|
+
|
|
8
34
|
## 3.13.2
|
|
9
35
|
|
|
10
36
|
### What changed
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.15.0
|
data/lib/carson/cli.rb
CHANGED
|
@@ -12,7 +12,7 @@ module Carson
|
|
|
12
12
|
return Runtime::EXIT_OK
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
if %w[refresh:all prune:all].include?( command )
|
|
15
|
+
if %w[repos refresh:all prune:all housekeep:all housekeep:target].include?( command )
|
|
16
16
|
verbose = parsed.fetch( :verbose, false )
|
|
17
17
|
runtime = Runtime.new( repo_root: repo_root, tool_root: tool_root, out: out, err: err, verbose: verbose )
|
|
18
18
|
return dispatch( parsed: parsed, runtime: runtime )
|
|
@@ -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 [status [--json]|setup|audit [--json]|sync [--json]|deliver [--merge] [--json] [--title T] [--body-file F]|prune [--all] [--json]|worktree [--json] create|remove <name>|onboard|refresh [--all]|offboard|template check|apply|review gate|sweep|govern [--dry-run] [--json] [--loop SECONDS]|version]"
|
|
56
|
+
opts.banner = "Usage: carson [status [--json]|setup|audit [--json]|sync [--json]|deliver [--merge] [--json] [--title T] [--body-file F]|prune [--all] [--json]|worktree [--json] create|remove <name>|housekeep [repo] [--json]|repos [--json]|onboard|refresh [--all]|offboard|template check|apply|review gate|sweep|govern [--dry-run] [--json] [--loop SECONDS]|version]"
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -86,6 +86,10 @@ module Carson
|
|
|
86
86
|
parse_prune_command( argv: argv, parser: parser, err: err )
|
|
87
87
|
when "worktree"
|
|
88
88
|
parse_worktree_subcommand( argv: argv, parser: parser, err: err )
|
|
89
|
+
when "repos"
|
|
90
|
+
parse_repos_command( argv: argv, err: err )
|
|
91
|
+
when "housekeep"
|
|
92
|
+
parse_housekeep_command( argv: argv, err: err )
|
|
89
93
|
when "review"
|
|
90
94
|
parse_named_subcommand( command: command, usage: "gate|sweep", argv: argv, parser: parser, err: err )
|
|
91
95
|
when "audit"
|
|
@@ -299,6 +303,37 @@ module Carson
|
|
|
299
303
|
{ command: :invalid }
|
|
300
304
|
end
|
|
301
305
|
|
|
306
|
+
def self.parse_repos_command( argv:, err: )
|
|
307
|
+
json_flag = argv.delete( "--json" ) ? true : false
|
|
308
|
+
unless argv.empty?
|
|
309
|
+
err.puts "#{BADGE} Unexpected arguments for repos: #{argv.join( ' ' )}"
|
|
310
|
+
return { command: :invalid }
|
|
311
|
+
end
|
|
312
|
+
{ command: "repos", json: json_flag }
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def self.parse_housekeep_command( argv:, err: )
|
|
316
|
+
all_flag = argv.delete( "--all" ) ? true : false
|
|
317
|
+
json_flag = argv.delete( "--json" ) ? true : false
|
|
318
|
+
|
|
319
|
+
if all_flag && !argv.empty?
|
|
320
|
+
err.puts "#{BADGE} --all and repo target are mutually exclusive. Use: carson housekeep --all OR carson housekeep [repo]"
|
|
321
|
+
return { command: :invalid }
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
return { command: "housekeep:all", json: json_flag } if all_flag
|
|
325
|
+
|
|
326
|
+
if argv.length > 1
|
|
327
|
+
err.puts "#{BADGE} Too many arguments for housekeep. Use: carson housekeep [repo]"
|
|
328
|
+
return { command: :invalid }
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
target = argv.shift
|
|
332
|
+
return { command: "housekeep:target", target: target, json: json_flag } if target
|
|
333
|
+
|
|
334
|
+
{ command: "housekeep", json: json_flag }
|
|
335
|
+
end
|
|
336
|
+
|
|
302
337
|
def self.parse_govern_subcommand( argv:, err: )
|
|
303
338
|
options = {
|
|
304
339
|
dry_run: false,
|
|
@@ -376,6 +411,14 @@ module Carson
|
|
|
376
411
|
runtime.review_gate!
|
|
377
412
|
when "review:sweep"
|
|
378
413
|
runtime.review_sweep!
|
|
414
|
+
when "repos"
|
|
415
|
+
runtime.repos!( json_output: parsed.fetch( :json, false ) )
|
|
416
|
+
when "housekeep"
|
|
417
|
+
runtime.housekeep!( json_output: parsed.fetch( :json, false ) )
|
|
418
|
+
when "housekeep:target"
|
|
419
|
+
runtime.housekeep_target!( target: parsed.fetch( :target ), json_output: parsed.fetch( :json, false ) )
|
|
420
|
+
when "housekeep:all"
|
|
421
|
+
runtime.housekeep_all!( json_output: parsed.fetch( :json, false ) )
|
|
379
422
|
when "govern"
|
|
380
423
|
runtime.govern!(
|
|
381
424
|
dry_run: parsed.fetch( :dry_run, false ),
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Housekeeping — sync + prune for a repository.
|
|
2
|
+
# carson housekeep <repo> — serve one repo by name or path.
|
|
3
|
+
# carson housekeep — serve the repo you are standing in.
|
|
4
|
+
# carson housekeep --all — serve all governed repos.
|
|
5
|
+
require "json"
|
|
6
|
+
require "stringio"
|
|
7
|
+
|
|
8
|
+
module Carson
|
|
9
|
+
class Runtime
|
|
10
|
+
module Housekeep
|
|
11
|
+
# Serves the current repo: sync + prune.
|
|
12
|
+
def housekeep!( json_output: false )
|
|
13
|
+
housekeep_one( repo_path: repo_root, json_output: json_output )
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Resolves a target name to a governed repo, then serves it.
|
|
17
|
+
def housekeep_target!( target:, json_output: false )
|
|
18
|
+
repo_path = resolve_governed_repo( target: target )
|
|
19
|
+
unless repo_path
|
|
20
|
+
result = { command: "housekeep", status: "error", error: "Not a governed repository: #{target}", recovery: "Run carson repos to see governed repositories." }
|
|
21
|
+
return housekeep_finish( result: result, exit_code: EXIT_ERROR, json_output: json_output )
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
housekeep_one( repo_path: repo_path, json_output: json_output )
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Knocks each governed repo's gate in turn.
|
|
28
|
+
def housekeep_all!( json_output: false )
|
|
29
|
+
repos = config.govern_repos
|
|
30
|
+
if repos.empty?
|
|
31
|
+
result = { command: "housekeep", status: "error", error: "No governed repositories configured.", recovery: "Run carson onboard in each repo to register." }
|
|
32
|
+
return housekeep_finish( result: result, exit_code: EXIT_ERROR, json_output: json_output )
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
results = []
|
|
36
|
+
repos.each { |repo_path| results << housekeep_one_entry( repo_path: repo_path, silent: json_output ) }
|
|
37
|
+
|
|
38
|
+
succeeded = results.count { |r| r[ :status ] == "ok" }
|
|
39
|
+
failed = results.count { |r| r[ :status ] != "ok" }
|
|
40
|
+
result = { command: "housekeep", status: failed.zero? ? "ok" : "partial", repos: results, succeeded: succeeded, failed: failed }
|
|
41
|
+
housekeep_finish( result: result, exit_code: failed.zero? ? EXIT_OK : EXIT_ERROR, json_output: json_output, results: results, succeeded: succeeded, failed: failed )
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Runs sync + prune on one repo and returns the exit code directly.
|
|
47
|
+
def housekeep_one( repo_path:, json_output: false )
|
|
48
|
+
entry = housekeep_one_entry( repo_path: repo_path, silent: json_output )
|
|
49
|
+
ok = entry[ :status ] == "ok"
|
|
50
|
+
result = { command: "housekeep", status: ok ? "ok" : "error", repos: [ entry ], succeeded: ok ? 1 : 0, failed: ok ? 0 : 1 }
|
|
51
|
+
housekeep_finish( result: result, exit_code: ok ? EXIT_OK : EXIT_ERROR, json_output: json_output, results: [ entry ], succeeded: result[ :succeeded ], failed: result[ :failed ] )
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Runs sync + prune on a single repository. Returns a result hash.
|
|
55
|
+
def housekeep_one_entry( repo_path:, silent: false )
|
|
56
|
+
repo_name = File.basename( repo_path )
|
|
57
|
+
unless Dir.exist?( repo_path )
|
|
58
|
+
puts_line "#{repo_name}: SKIP (path not found)" unless silent
|
|
59
|
+
return { name: repo_name, path: repo_path, status: "error", error: "path not found" }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
buf = verbose? ? out : StringIO.new
|
|
63
|
+
err_buf = verbose? ? err : StringIO.new
|
|
64
|
+
rt = Runtime.new( repo_root: repo_path, tool_root: tool_root, out: buf, err: err_buf, verbose: verbose? )
|
|
65
|
+
|
|
66
|
+
sync_status = rt.sync!
|
|
67
|
+
prune_status = rt.prune! if sync_status == EXIT_OK
|
|
68
|
+
|
|
69
|
+
ok = sync_status == EXIT_OK && prune_status == EXIT_OK
|
|
70
|
+
unless verbose? || silent
|
|
71
|
+
summary = strip_badge( buf.string.lines.last.to_s.strip )
|
|
72
|
+
puts_line "#{repo_name}: #{summary.empty? ? 'OK' : summary}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
{ name: repo_name, path: repo_path, status: ok ? "ok" : "error" }
|
|
76
|
+
rescue StandardError => e
|
|
77
|
+
puts_line "#{repo_name}: FAIL (#{e.message})" unless silent
|
|
78
|
+
{ name: repo_name, path: repo_path, status: "error", error: e.message }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Strips the Carson badge prefix from a message to avoid double-badging.
|
|
82
|
+
def strip_badge( text )
|
|
83
|
+
text.sub( /\A#{Regexp.escape( BADGE )}\s*/, "" )
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Resolves a user-supplied target to a governed repository path.
|
|
87
|
+
# Accepts: exact path, expandable path, or basename match (case-insensitive).
|
|
88
|
+
def resolve_governed_repo( target: )
|
|
89
|
+
repos = config.govern_repos
|
|
90
|
+
expanded = File.expand_path( target )
|
|
91
|
+
return expanded if repos.include?( expanded )
|
|
92
|
+
|
|
93
|
+
downcased = File.basename( target ).downcase
|
|
94
|
+
repos.find { |r| File.basename( r ).downcase == downcased }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Unified output — JSON or human-readable.
|
|
98
|
+
def housekeep_finish( result:, exit_code:, json_output:, results: nil, succeeded: nil, failed: nil )
|
|
99
|
+
result[ :exit_code ] = exit_code
|
|
100
|
+
|
|
101
|
+
if json_output
|
|
102
|
+
out.puts JSON.pretty_generate( result )
|
|
103
|
+
else
|
|
104
|
+
if results && ( succeeded || failed )
|
|
105
|
+
total = ( succeeded || 0 ) + ( failed || 0 )
|
|
106
|
+
puts_line ""
|
|
107
|
+
puts_line "Housekeep complete: #{succeeded} cleaned, #{failed} failed (#{total} repo#{plural_suffix( count: total )})."
|
|
108
|
+
elsif result[ :error ]
|
|
109
|
+
puts_line result[ :error ]
|
|
110
|
+
puts_line " #{result[ :recovery ]}" if result[ :recovery ]
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
exit_code
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
include Housekeep
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -229,11 +229,7 @@ module Carson
|
|
|
229
229
|
puts_line ""
|
|
230
230
|
puts_line "Carson at your service."
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
prompt_govern_registration!
|
|
234
|
-
else
|
|
235
|
-
puts_line "To register for portfolio governance: carson onboard (in a TTY)"
|
|
236
|
-
end
|
|
232
|
+
auto_register_govern!
|
|
237
233
|
|
|
238
234
|
puts_line ""
|
|
239
235
|
puts_line "Your repository is set up. Carson has placed files in your"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Safe worktree lifecycle management for coding agents.
|
|
2
|
-
# Two operations: create and remove.
|
|
3
|
-
# CWD-inside-worktree and unpushed
|
|
2
|
+
# Two operations: create and remove. Create auto-syncs main before branching.
|
|
3
|
+
# Remove is safe by default — guards against CWD-inside-worktree and unpushed
|
|
4
|
+
# commits. Content-aware: allows removal after squash/rebase merge without --force.
|
|
4
5
|
# Supports --json for machine-readable structured output with recovery commands.
|
|
5
6
|
module Carson
|
|
6
7
|
class Runtime
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Lists governed repositories from Carson's global config.
|
|
2
|
+
# Portfolio-level query — not scoped to any single repository.
|
|
3
|
+
require "json"
|
|
4
|
+
|
|
5
|
+
module Carson
|
|
6
|
+
class Runtime
|
|
7
|
+
module Repos
|
|
8
|
+
def repos!( json_output: false )
|
|
9
|
+
repos = config.govern_repos
|
|
10
|
+
|
|
11
|
+
if json_output
|
|
12
|
+
out.puts JSON.pretty_generate( { command: "repos", repos: repos } )
|
|
13
|
+
else
|
|
14
|
+
if repos.empty?
|
|
15
|
+
puts_line "No governed repositories."
|
|
16
|
+
puts_line " Run carson onboard in a repo to register it."
|
|
17
|
+
else
|
|
18
|
+
puts_line "Governed repositories (#{repos.length}):"
|
|
19
|
+
repos.each { |path| puts_line " #{path}" }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
EXIT_OK
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
include Repos
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/carson/runtime/setup.rb
CHANGED
|
@@ -368,24 +368,16 @@ module Carson
|
|
|
368
368
|
!path.empty? && File.file?( path )
|
|
369
369
|
end
|
|
370
370
|
|
|
371
|
-
#
|
|
372
|
-
def
|
|
371
|
+
# Automatically registers the repo for portfolio governance during onboard.
|
|
372
|
+
def auto_register_govern!
|
|
373
373
|
expanded = File.expand_path( repo_root )
|
|
374
374
|
if config.govern_repos.include?( expanded )
|
|
375
375
|
puts_verbose "govern_registration: already registered #{expanded}"
|
|
376
376
|
return
|
|
377
377
|
end
|
|
378
378
|
|
|
379
|
-
|
|
380
|
-
puts_line "
|
|
381
|
-
puts_line " Register this repo so carson refresh --all and carson govern include it?"
|
|
382
|
-
accepted = prompt_yes_no( default: true )
|
|
383
|
-
if accepted
|
|
384
|
-
append_govern_repo!( repo_path: expanded )
|
|
385
|
-
puts_line "Registered. Run carson refresh --all to keep all repos in sync."
|
|
386
|
-
else
|
|
387
|
-
puts_line "Skipped. Run carson onboard here again to register later."
|
|
388
|
-
end
|
|
379
|
+
append_govern_repo!( repo_path: expanded )
|
|
380
|
+
puts_line "Registered for portfolio governance."
|
|
389
381
|
end
|
|
390
382
|
|
|
391
383
|
# Reusable Y/n prompt following existing prompt_choice conventions.
|
data/lib/carson/runtime.rb
CHANGED
|
@@ -217,6 +217,8 @@ end
|
|
|
217
217
|
|
|
218
218
|
require_relative "runtime/local"
|
|
219
219
|
require_relative "runtime/audit"
|
|
220
|
+
require_relative "runtime/housekeep"
|
|
221
|
+
require_relative "runtime/repos"
|
|
220
222
|
require_relative "runtime/review"
|
|
221
223
|
require_relative "runtime/govern"
|
|
222
224
|
require_relative "runtime/setup"
|
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: 3.
|
|
4
|
+
version: 3.15.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hailei Wang
|
|
@@ -54,6 +54,7 @@ files:
|
|
|
54
54
|
- lib/carson/runtime/audit.rb
|
|
55
55
|
- lib/carson/runtime/deliver.rb
|
|
56
56
|
- lib/carson/runtime/govern.rb
|
|
57
|
+
- lib/carson/runtime/housekeep.rb
|
|
57
58
|
- lib/carson/runtime/local.rb
|
|
58
59
|
- lib/carson/runtime/local/hooks.rb
|
|
59
60
|
- lib/carson/runtime/local/onboard.rb
|
|
@@ -61,6 +62,7 @@ files:
|
|
|
61
62
|
- lib/carson/runtime/local/sync.rb
|
|
62
63
|
- lib/carson/runtime/local/template.rb
|
|
63
64
|
- lib/carson/runtime/local/worktree.rb
|
|
65
|
+
- lib/carson/runtime/repos.rb
|
|
64
66
|
- lib/carson/runtime/review.rb
|
|
65
67
|
- lib/carson/runtime/review/data_access.rb
|
|
66
68
|
- lib/carson/runtime/review/gate_support.rb
|