ace-git-worktree 0.23.0 → 0.24.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/CHANGELOG.md +47 -0
- data/docs/usage.md +62 -10
- data/lib/ace/git/worktree/commands/cleanup_command.rb +49 -25
- data/lib/ace/git/worktree/commands/create_command.rb +67 -154
- data/lib/ace/git/worktree/molecules/cleanup_pr_resolver.rb +141 -133
- data/lib/ace/git/worktree/molecules/cleanup_reporter.rb +49 -11
- data/lib/ace/git/worktree/molecules/pull_request_checkout_preparer.rb +100 -0
- data/lib/ace/git/worktree/molecules/pull_request_creator.rb +67 -0
- data/lib/ace/git/worktree/molecules/pull_request_evidence_resolver.rb +40 -0
- data/lib/ace/git/worktree/molecules/worktree_creator.rb +52 -20
- data/lib/ace/git/worktree/organisms/task_worktree_orchestrator.rb +43 -9
- data/lib/ace/git/worktree/organisms/worktree_manager.rb +55 -11
- data/lib/ace/git/worktree/version.rb +1 -1
- data/lib/ace/git/worktree.rb +3 -1
- metadata +5 -17
- data/lib/ace/git/worktree/molecules/pr_creator.rb +0 -253
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 440da5b336b58817b1e6bbc1b8d57de396e78848b9e9c289d1492ca85fc7065a
|
|
4
|
+
data.tar.gz: 432c570346044a2351c72355b729501cef4723507fb64f0298307f7790edf289
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c4c1f7d225354653e5d6d4c3ff8e88c0c8b4cee1142707e5d51181569b961b58e81fd1f64bca355a78a6675205429654420ed9776481470204342f70e7bfc9a5
|
|
7
|
+
data.tar.gz: 46e6f7112657db3d867a69db98dacbe429883672beb37b83fa35bda5b059d071a8a318fe5d26adc90835da05a3b4f74d105b096244be92beb61a904007652358
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,53 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.24.0] - 2026-09-28
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Forge-neutral PR worktree checkout: the exact declared source
|
|
14
|
+
repository/ref is fetched and its head SHA verified against normalized PR
|
|
15
|
+
evidence before any worktree is created (fork and canonical sources are
|
|
16
|
+
equally valid).
|
|
17
|
+
- `--server` / `--default-server` selection for PR creation, task-PR
|
|
18
|
+
creation, and cleanup; `--remote` remains a local Git remote name only.
|
|
19
|
+
- Task PR creation proves the pushed branch head SHA to the provider
|
|
20
|
+
(`expected_head`); PR identity is never inferred from branch names.
|
|
21
|
+
- Cleanup report digests now bind the resolved server identity and each
|
|
22
|
+
item's provider proof (status, PR, proven head, merge commit); `--apply`
|
|
23
|
+
recomputes the full report immediately before executing and refuses any
|
|
24
|
+
drift from the approved digest.
|
|
25
|
+
|
|
26
|
+
### Removed
|
|
27
|
+
- Direct GitHub coupling from worktree correctness paths: `ace/git/github`
|
|
28
|
+
requires, `Github::PrFetcher` usage, `gh` CLI checks and troubleshooting,
|
|
29
|
+
the raw gh-based `PrCreator` molecule, and the `ace-git-github` gemspec
|
|
30
|
+
dependency. Provider execution lives behind the neutral ace-git provider
|
|
31
|
+
contract.
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
- Forge-neutral PR worktree checkout: the exact declared source
|
|
37
|
+
repository/ref is fetched and its head SHA verified against normalized PR
|
|
38
|
+
evidence before any worktree is created (fork and canonical sources are
|
|
39
|
+
equally valid).
|
|
40
|
+
- `--server` / `--default-server` selection for PR creation, task-PR
|
|
41
|
+
creation, and cleanup; `--remote` remains a local Git remote name only.
|
|
42
|
+
- Task PR creation proves the pushed branch head SHA to the provider
|
|
43
|
+
(`expected_head`); PR identity is never inferred from branch names.
|
|
44
|
+
- Cleanup report digests now bind the resolved server identity and each
|
|
45
|
+
item's provider proof (status, PR, proven head, merge commit); `--apply`
|
|
46
|
+
recomputes the full report immediately before executing and refuses any
|
|
47
|
+
drift from the approved digest.
|
|
48
|
+
|
|
49
|
+
### Removed
|
|
50
|
+
- Direct GitHub coupling from worktree correctness paths: `ace/git/github`
|
|
51
|
+
requires, `Github::PrFetcher` usage, `gh` CLI checks and troubleshooting,
|
|
52
|
+
the raw gh-based `PrCreator` molecule, and the `ace-git-github` gemspec
|
|
53
|
+
dependency. Provider execution lives behind the neutral ace-git provider
|
|
54
|
+
contract.
|
|
55
|
+
|
|
56
|
+
|
|
10
57
|
|
|
11
58
|
## [0.23.0] - 2026-09-27
|
|
12
59
|
|
data/docs/usage.md
CHANGED
|
@@ -76,12 +76,18 @@ Created task worktree and branch for 081
|
|
|
76
76
|
|
|
77
77
|
### Scenario 2: Review a Pull Request in Its Own Worktree
|
|
78
78
|
|
|
79
|
-
**Goal:** Create a dedicated workspace for PR review or patching
|
|
79
|
+
**Goal:** Create a dedicated workspace for PR review or patching, on any
|
|
80
|
+
configured forge (GitHub, default Forgejo, or a named Forgejo).
|
|
80
81
|
|
|
81
82
|
**Commands:**
|
|
82
83
|
|
|
83
84
|
```bash
|
|
84
|
-
|
|
85
|
+
# Preview provenance first (no mutation): source/base repositories, exact head
|
|
86
|
+
ace-git-worktree create --pr 26 --server forgejo-lab --dry-run
|
|
87
|
+
|
|
88
|
+
# Create the worktree; the declared source ref is fetched and its SHA is
|
|
89
|
+
# verified against the PR evidence before anything is created
|
|
90
|
+
ace-git-worktree create --pr 26 --server forgejo-lab
|
|
85
91
|
ace-git-worktree list
|
|
86
92
|
```
|
|
87
93
|
|
|
@@ -187,6 +193,8 @@ ace-git-worktree create --task 081 --dry-run
|
|
|
187
193
|
|
|
188
194
|
* `--task` - Task ID for a task-aware worktree
|
|
189
195
|
* `--pr`, `--pull-request` - PR number for a PR-aware worktree
|
|
196
|
+
* `--server <name>` - Named configured forge server for PR and task-PR operations
|
|
197
|
+
* `--default-server` - Use the configured default forge server
|
|
190
198
|
* `--from`, `-b` - Create from a specific local or remote branch
|
|
191
199
|
* `--path` - Override the destination worktree path
|
|
192
200
|
* `--source` - Override the git ref used as the start point
|
|
@@ -195,7 +203,8 @@ ace-git-worktree create --task 081 --dry-run
|
|
|
195
203
|
* `--no-commit` - Skip committing task metadata changes
|
|
196
204
|
* `--no-push` - Skip pushing task changes
|
|
197
205
|
* `--no-upstream` - Skip pushing the new branch with upstream tracking
|
|
198
|
-
* `--no-pr` - Skip automatic draft-PR creation
|
|
206
|
+
* `--no-pr` - Skip automatic draft-PR creation; keeps task creation fully
|
|
207
|
+
local and provider-free
|
|
199
208
|
* `--push-remote` - Override the remote used for task-related pushes
|
|
200
209
|
* `--no-auto-navigate` - Stay in the current directory after creation
|
|
201
210
|
* `--commit-message` - Use a custom commit message for task metadata updates
|
|
@@ -295,6 +304,42 @@ ace-git-worktree prune --cleanup-directories
|
|
|
295
304
|
* `-v`, `--verbose` - Show verbose output
|
|
296
305
|
* `-d`, `--debug` - Show debug output
|
|
297
306
|
|
|
307
|
+
### `ace-git-worktree cleanup`
|
|
308
|
+
|
|
309
|
+
Build a complete, deterministic, report-only cleanup inventory for merged
|
|
310
|
+
worktrees and refs. Local ancestry is the first safe proof; provider PR
|
|
311
|
+
evidence (from the selected forge server) proves candidates whose local
|
|
312
|
+
ancestry is unproven. Unavailable or failed provider proof is never relabeled
|
|
313
|
+
as merged.
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
ace-git-worktree cleanup --target main
|
|
317
|
+
ace-git-worktree cleanup --target main --format json
|
|
318
|
+
ace-git-worktree cleanup --target main --server forgejo-lab
|
|
319
|
+
ace-git-worktree cleanup --target main --offline
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Options:**
|
|
323
|
+
|
|
324
|
+
* `--target <ref>` - Target ref for ancestry proof (required)
|
|
325
|
+
* `--remote <name>` - Local Git remote name (default: `origin`); never forge selection
|
|
326
|
+
* `--server <name>` - Named configured forge server for PR proof
|
|
327
|
+
* `--default-server` - Use the configured default forge server
|
|
328
|
+
* `--offline` - Skip remote refresh and provider proof
|
|
329
|
+
* `--format <type>` - Output format: `table` (default), `json`
|
|
330
|
+
* `--apply` - Apply a reviewed plan (see below)
|
|
331
|
+
* `--approved-digest <sha256>` - Digest of the reviewed plan; required with `--apply`
|
|
332
|
+
* `--require-only-target` - Fail if the final rescan retains non-target state
|
|
333
|
+
|
|
334
|
+
**Approval binding:** the report carries a canonical SHA-256 digest over the
|
|
335
|
+
inventory, decisions, the resolved server identity, and each item's provider
|
|
336
|
+
proof (status, PR number, proven head, merge commit). `--apply` recomputes
|
|
337
|
+
the full report immediately before executing and refuses anything whose
|
|
338
|
+
digest differs from the approved one — changed repo state, changed server
|
|
339
|
+
configuration, or changed PR/head proof all invalidate consent. Only
|
|
340
|
+
confirmed merged evidence permits remote-merge cleanup; `--force` does not
|
|
341
|
+
exist and dirty, locked, and in-use worktrees are always retained.
|
|
342
|
+
|
|
298
343
|
### `ace-git-worktree bootstrap [IDENTIFIER]`
|
|
299
344
|
|
|
300
345
|
Rerun preparation phases (toolchain trust and bootstrap policy) for an existing worktree in place.
|
|
@@ -399,16 +444,23 @@ data is available.
|
|
|
399
444
|
|
|
400
445
|
### Problem: PR worktree creation fails
|
|
401
446
|
|
|
402
|
-
**Symptom:** `create --pr` reports a
|
|
447
|
+
**Symptom:** `create --pr` reports a classified provider error (missing
|
|
448
|
+
provider CLI, unauthenticated, unreachable, or identity mismatch).
|
|
403
449
|
|
|
404
450
|
**Solution:**
|
|
405
451
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
452
|
+
1. Configure the forge server in `git.servers` (see `ace-git` docs) and
|
|
453
|
+
select it with `--server` / `--default-server`, or make the repository
|
|
454
|
+
remote resolvable.
|
|
455
|
+
2. Install and authenticate the provider CLI for that server's provider
|
|
456
|
+
type (`gh` for GitHub, `fj` for Forgejo; check `gh auth status` or
|
|
457
|
+
`fj auth list`).
|
|
458
|
+
3. Preview with `--dry-run` to inspect the exact source/base provenance
|
|
459
|
+
before creating anything.
|
|
460
|
+
|
|
461
|
+
PR creation is forge-neutral: the exact declared source repository/ref is
|
|
462
|
+
fetched and its head SHA is verified against the PR evidence. Failures never
|
|
463
|
+
fall back to another provider.
|
|
412
464
|
|
|
413
465
|
### Problem: `switch` returns nothing useful
|
|
414
466
|
|
|
@@ -30,28 +30,16 @@ module Ace
|
|
|
30
30
|
|
|
31
31
|
validate_options(options)
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
target: options[:target],
|
|
35
|
-
remote: options[:remote],
|
|
36
|
-
offline: options[:offline]
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
result = reporter.report
|
|
33
|
+
result = build_reporter(options).report
|
|
40
34
|
|
|
41
35
|
if result[:success]
|
|
42
36
|
if options[:apply]
|
|
43
|
-
|
|
44
|
-
apply_result = applier.apply
|
|
37
|
+
apply_result = apply_fresh_report(options)
|
|
45
38
|
|
|
46
39
|
if apply_result[:success]
|
|
47
|
-
# Strict rescan
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
remote: options[:remote],
|
|
51
|
-
offline: options[:offline]
|
|
52
|
-
)
|
|
53
|
-
final_result = final_reporter.report
|
|
54
|
-
|
|
40
|
+
# Strict rescan with the same selection
|
|
41
|
+
final_result = build_reporter(options).report
|
|
42
|
+
|
|
55
43
|
if final_result[:success]
|
|
56
44
|
# Check require_only_target if needed
|
|
57
45
|
if options[:require_only_target] && has_non_target_state?(final_result)
|
|
@@ -59,7 +47,7 @@ module Ace
|
|
|
59
47
|
display_result(final_result, options)
|
|
60
48
|
return 1
|
|
61
49
|
end
|
|
62
|
-
|
|
50
|
+
|
|
63
51
|
display_apply_result(apply_result, final_result, options)
|
|
64
52
|
return 0
|
|
65
53
|
else
|
|
@@ -93,6 +81,35 @@ module Ace
|
|
|
93
81
|
1
|
|
94
82
|
end
|
|
95
83
|
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
# Build a reporter with the command's server selection; --remote
|
|
87
|
+
# stays a local Git remote name and is never forge selection.
|
|
88
|
+
def build_reporter(options)
|
|
89
|
+
Molecules::CleanupReporter.new(
|
|
90
|
+
target: options[:target],
|
|
91
|
+
remote: options[:remote],
|
|
92
|
+
offline: options[:offline],
|
|
93
|
+
server_name: options[:server],
|
|
94
|
+
use_default: options[:default_server] == true,
|
|
95
|
+
remote_name: options[:remote]
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Recompute the complete report immediately before apply; approval
|
|
100
|
+
# is bound to the reviewed digest over the exact evidence. Any
|
|
101
|
+
# drift (repo state, server identity, PR/head proof) invalidates
|
|
102
|
+
# the approved digest and refuses the apply.
|
|
103
|
+
def apply_fresh_report(options)
|
|
104
|
+
fresh = build_reporter(options).report
|
|
105
|
+
unless fresh[:success]
|
|
106
|
+
return {success: false, error: "Report recomputation failed before apply: #{fresh[:error]}", ledger: [], partial_failure: false}
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
applier = Molecules::CleanupApplier.new(fresh, options[:approved_digest])
|
|
110
|
+
applier.apply
|
|
111
|
+
end
|
|
112
|
+
|
|
96
113
|
# Show help
|
|
97
114
|
#
|
|
98
115
|
# @return [Integer] Exit code
|
|
@@ -103,8 +120,10 @@ module Ace
|
|
|
103
120
|
|
|
104
121
|
OPTIONS
|
|
105
122
|
--target <ref> Target ref for ancestry proof (required)
|
|
106
|
-
--remote <name>
|
|
107
|
-
--
|
|
123
|
+
--remote <name> Local Git remote name (default: origin); never forge selection
|
|
124
|
+
--server <name> Named configured forge server for PR proof
|
|
125
|
+
--default-server Use the configured default forge server
|
|
126
|
+
--offline Skip remote evidence refresh and provider proof
|
|
108
127
|
--apply Apply a reviewed plan
|
|
109
128
|
--approved-digest <sha256> Approved plan digest to apply
|
|
110
129
|
--require-only-target Fail if final rescan has retained non-target state
|
|
@@ -128,12 +147,11 @@ module Ace
|
|
|
128
147
|
0
|
|
129
148
|
end
|
|
130
149
|
|
|
131
|
-
private
|
|
132
|
-
|
|
133
150
|
def parse_arguments(args)
|
|
134
151
|
options = {
|
|
135
152
|
target: nil, remote: "origin", offline: false, format: "table", help: false,
|
|
136
|
-
apply: false, approved_digest: nil, require_only_target: false
|
|
153
|
+
apply: false, approved_digest: nil, require_only_target: false,
|
|
154
|
+
server: nil, default_server: false
|
|
137
155
|
}
|
|
138
156
|
i = 0
|
|
139
157
|
while i < args.length
|
|
@@ -146,6 +164,11 @@ module Ace
|
|
|
146
164
|
options[:remote] = args[i]
|
|
147
165
|
when "--offline"
|
|
148
166
|
options[:offline] = true
|
|
167
|
+
when "--server"
|
|
168
|
+
i += 1
|
|
169
|
+
options[:server] = args[i]
|
|
170
|
+
when "--default-server"
|
|
171
|
+
options[:default_server] = true
|
|
149
172
|
when "--apply"
|
|
150
173
|
options[:apply] = true
|
|
151
174
|
when "--approved-digest"
|
|
@@ -169,16 +192,17 @@ module Ace
|
|
|
169
192
|
def validate_options(options)
|
|
170
193
|
raise ArgumentError, "--target is required" unless options[:target]
|
|
171
194
|
raise ArgumentError, "--remote is required" unless options[:remote]
|
|
195
|
+
raise ArgumentError, "--server and --default-server are mutually exclusive" if options[:server] && options[:default_server]
|
|
172
196
|
|
|
173
197
|
valid_formats = %w[table json]
|
|
174
198
|
unless valid_formats.include?(options[:format])
|
|
175
199
|
raise ArgumentError, "Invalid format '#{options[:format]}'. Use: #{valid_formats.join(", ")}"
|
|
176
200
|
end
|
|
177
|
-
|
|
201
|
+
|
|
178
202
|
if options[:apply] && !options[:approved_digest]
|
|
179
203
|
raise ArgumentError, "--approved-digest is required when --apply is used"
|
|
180
204
|
end
|
|
181
|
-
|
|
205
|
+
|
|
182
206
|
if options[:approved_digest] && !options[:apply]
|
|
183
207
|
raise ArgumentError, "--apply is required when --approved-digest is used"
|
|
184
208
|
end
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "ace/git/github"
|
|
4
|
-
|
|
5
3
|
module Ace
|
|
6
4
|
module Git
|
|
7
5
|
module Worktree
|
|
@@ -60,6 +58,7 @@ module Ace
|
|
|
60
58
|
rescue Ace::Git::ProviderObjectNotFoundError,
|
|
61
59
|
Ace::Git::ProviderAuthenticationError,
|
|
62
60
|
Ace::Git::ProviderCliMissingError,
|
|
61
|
+
Ace::Git::ProviderIdentityMismatchError,
|
|
63
62
|
Ace::Git::TimeoutError => e
|
|
64
63
|
puts "Error: #{e.message}"
|
|
65
64
|
1
|
|
@@ -88,9 +87,12 @@ module Ace
|
|
|
88
87
|
Task ID formats: 081, task.081, v.0.9.0+081
|
|
89
88
|
|
|
90
89
|
PR-AWARE CREATION:
|
|
91
|
-
--pr <number> Create worktree for a
|
|
90
|
+
--pr <number> Create worktree for a pull request
|
|
92
91
|
--pull-request <number> (alias for --pr)
|
|
93
|
-
Requires
|
|
92
|
+
Requires a configured forge server and its
|
|
93
|
+
provider package; fetches the exact declared
|
|
94
|
+
source repository/ref and verifies the head
|
|
95
|
+
SHA against the PR evidence.
|
|
94
96
|
|
|
95
97
|
BRANCH-AWARE CREATION:
|
|
96
98
|
-b <branch> Create worktree from a branch (local or remote)
|
|
@@ -102,12 +104,14 @@ module Ace
|
|
|
102
104
|
--path <path> Custom worktree path (default: from config)
|
|
103
105
|
--source <ref> Git ref to use as branch start-point (default: current branch)
|
|
104
106
|
Examples: main, origin/develop, HEAD~3, commit-sha
|
|
107
|
+
--server <name> Use the named configured forge server (PR mode)
|
|
108
|
+
--default-server Use the configured default forge server
|
|
105
109
|
--dry-run Show what would be created without creating
|
|
106
110
|
--no-status-update Skip marking task as in-progress (task mode only)
|
|
107
111
|
--no-commit Skip committing task changes (task mode only)
|
|
108
112
|
--no-push Skip pushing task changes to remote (task mode only)
|
|
109
113
|
--no-upstream Skip pushing worktree branch with upstream tracking (task mode only)
|
|
110
|
-
--no-pr Skip creating draft PR
|
|
114
|
+
--no-pr Skip creating draft PR; keeps task creation local and provider-free
|
|
111
115
|
--no-bootstrap Skip worktree bootstrap preparation
|
|
112
116
|
--push-remote <name> Remote to push to (default: origin) (task mode only)
|
|
113
117
|
--no-auto-navigate Stay in current directory (default: navigate to worktree)
|
|
@@ -122,8 +126,8 @@ module Ace
|
|
|
122
126
|
# Create task worktree based on main instead of current branch
|
|
123
127
|
ace-git-worktree create --task 081 --source main
|
|
124
128
|
|
|
125
|
-
# Create PR worktree
|
|
126
|
-
ace-git-worktree create --pr 26
|
|
129
|
+
# Create PR worktree on a named forge server (no mutation)
|
|
130
|
+
ace-git-worktree create --pr 26 --server forgejo-lab --dry-run
|
|
127
131
|
|
|
128
132
|
# Create worktree from remote branch
|
|
129
133
|
ace-git-worktree create -b origin/feature/auth
|
|
@@ -150,9 +154,10 @@ module Ace
|
|
|
150
154
|
- hooks.after_create: Commands to run after worktree creation
|
|
151
155
|
|
|
152
156
|
REQUIREMENTS:
|
|
153
|
-
PR-aware creation requires
|
|
154
|
-
|
|
155
|
-
|
|
157
|
+
PR-aware creation requires a configured forge server
|
|
158
|
+
(git.servers) and the provider package for its provider
|
|
159
|
+
type. Task and branch creation are fully local and never
|
|
160
|
+
touch a forge.
|
|
156
161
|
HELP
|
|
157
162
|
0
|
|
158
163
|
end
|
|
@@ -170,6 +175,8 @@ module Ace
|
|
|
170
175
|
branch: nil,
|
|
171
176
|
path: nil,
|
|
172
177
|
source: nil,
|
|
178
|
+
server: nil,
|
|
179
|
+
default_server: false,
|
|
173
180
|
dry_run: false,
|
|
174
181
|
no_status_update: false,
|
|
175
182
|
no_commit: false,
|
|
@@ -205,6 +212,11 @@ module Ace
|
|
|
205
212
|
when "--source"
|
|
206
213
|
i += 1
|
|
207
214
|
options[:source] = args[i]
|
|
215
|
+
when "--server"
|
|
216
|
+
i += 1
|
|
217
|
+
options[:server] = args[i]
|
|
218
|
+
when "--default-server"
|
|
219
|
+
options[:default_server] = true
|
|
208
220
|
when "--dry-run"
|
|
209
221
|
options[:dry_run] = true
|
|
210
222
|
when "--no-status-update"
|
|
@@ -377,6 +389,8 @@ module Ace
|
|
|
377
389
|
push_remote: options[:push_remote],
|
|
378
390
|
commit_message: options[:commit_message],
|
|
379
391
|
target_branch: options[:target_branch],
|
|
392
|
+
server: options[:server],
|
|
393
|
+
default_server: options[:default_server],
|
|
380
394
|
no_mise_trust: options[:no_mise_trust],
|
|
381
395
|
force: options[:force]
|
|
382
396
|
}.compact
|
|
@@ -410,7 +424,7 @@ module Ace
|
|
|
410
424
|
end
|
|
411
425
|
end
|
|
412
426
|
|
|
413
|
-
# Create a PR worktree
|
|
427
|
+
# Create a PR worktree from normalized provider evidence.
|
|
414
428
|
#
|
|
415
429
|
# @param options [Hash] Command options
|
|
416
430
|
# @return [Integer] Exit code
|
|
@@ -434,47 +448,28 @@ module Ace
|
|
|
434
448
|
|
|
435
449
|
puts "Creating worktree for PR ##{pr_number}..."
|
|
436
450
|
|
|
437
|
-
# Check gh CLI availability via the GitHub provider (ace-git-github)
|
|
438
|
-
unless Ace::Git::Github::PrFetcher.installed?
|
|
439
|
-
puts "Error: gh CLI is required for PR worktree creation."
|
|
440
|
-
puts
|
|
441
|
-
puts gh_not_available_message
|
|
442
|
-
return 1
|
|
443
|
-
end
|
|
444
|
-
|
|
445
|
-
unless Ace::Git::Github::PrFetcher.authenticated?
|
|
446
|
-
puts "Error: gh CLI is not authenticated."
|
|
447
|
-
puts
|
|
448
|
-
puts "Authenticate with: gh auth login"
|
|
449
|
-
return 1
|
|
450
|
-
end
|
|
451
|
-
|
|
452
|
-
# Fetch PR data
|
|
453
|
-
puts "Fetching PR information..."
|
|
454
451
|
begin
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
#
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
puts "You will not be able to push to the PR branch directly."
|
|
452
|
+
# Fetch normalized evidence through the selected forge server.
|
|
453
|
+
puts "Fetching PR information..."
|
|
454
|
+
resolver = Molecules::PullRequestEvidenceResolver.new(
|
|
455
|
+
server_name: options[:server],
|
|
456
|
+
use_default: options[:default_server] == true
|
|
457
|
+
)
|
|
458
|
+
resolved = resolver.resolve(pr_number)
|
|
459
|
+
evidence = resolved[:evidence]
|
|
460
|
+
|
|
461
|
+
display_pr_evidence(evidence)
|
|
462
|
+
|
|
463
|
+
# Fetch and verify the exact declared source (skipped for dry
|
|
464
|
+
# runs, which may read evidence but never create anything).
|
|
465
|
+
checkout = nil
|
|
466
|
+
unless options[:dry_run]
|
|
467
|
+
checkout = Molecules::PullRequestCheckoutPreparer.new.prepare(evidence)
|
|
468
|
+
unless checkout[:success]
|
|
469
|
+
puts "Error: #{checkout[:error]}"
|
|
470
|
+
return 1
|
|
471
|
+
end
|
|
476
472
|
end
|
|
477
|
-
puts
|
|
478
473
|
|
|
479
474
|
# Prepare creation options
|
|
480
475
|
creation_options = {
|
|
@@ -485,7 +480,7 @@ module Ace
|
|
|
485
480
|
}.compact
|
|
486
481
|
|
|
487
482
|
# Create the worktree
|
|
488
|
-
result = @manager.create_pr(
|
|
483
|
+
result = @manager.create_pr(evidence, checkout, creation_options)
|
|
489
484
|
|
|
490
485
|
if result[:success]
|
|
491
486
|
display_pr_creation_result(result, options[:dry_run])
|
|
@@ -495,14 +490,30 @@ module Ace
|
|
|
495
490
|
display_warnings(result[:warnings]) if result[:warnings]
|
|
496
491
|
1
|
|
497
492
|
end
|
|
498
|
-
rescue Ace::Git::
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
handle_pr_fetch_error(e, pr_number)
|
|
493
|
+
rescue Ace::Git::Error => e
|
|
494
|
+
puts "Error: #{e.message}"
|
|
495
|
+
puts "Debug: #{e.class}" if ENV["DEBUG"]
|
|
496
|
+
1
|
|
503
497
|
end
|
|
504
498
|
end
|
|
505
499
|
|
|
500
|
+
# Display normalized PR evidence before checkout.
|
|
501
|
+
#
|
|
502
|
+
# @param evidence [Ace::Git::ProviderPullRequest] normalized evidence
|
|
503
|
+
def display_pr_evidence(evidence)
|
|
504
|
+
puts "PR ##{evidence.number}: #{evidence.title}"
|
|
505
|
+
puts "Branch: #{evidence.head_ref} -> #{evidence.base_ref}"
|
|
506
|
+
puts "Source: #{evidence.head_repository_url}@#{evidence.head_ref}" \
|
|
507
|
+
" (head #{evidence.head_sha.to_s[0, 8] || "unknown"})"
|
|
508
|
+
puts "Server: #{evidence.server_name}"
|
|
509
|
+
|
|
510
|
+
return unless evidence.head_repository_url != evidence.base_repository_url
|
|
511
|
+
|
|
512
|
+
puts
|
|
513
|
+
puts "Warning: This PR is from a fork (#{evidence.head_repository_url})."
|
|
514
|
+
puts "You will not be able to push to the PR branch directly."
|
|
515
|
+
end
|
|
516
|
+
|
|
506
517
|
# Create a branch worktree
|
|
507
518
|
#
|
|
508
519
|
# @param options [Hash] Command options
|
|
@@ -865,104 +876,6 @@ module Ace
|
|
|
865
876
|
system("which ace-tmux > /dev/null 2>&1")
|
|
866
877
|
end
|
|
867
878
|
|
|
868
|
-
# Convert PR metadata from gh CLI to internal pr_data format
|
|
869
|
-
#
|
|
870
|
-
# Anti-corruption layer that translates gh CLI JSON output to internal format.
|
|
871
|
-
# This isolates worktree creation from gh CLI output structure changes.
|
|
872
|
-
#
|
|
873
|
-
# Expected metadata schema from the GitHub provider PrFetcher (via gh pr view --json):
|
|
874
|
-
# {
|
|
875
|
-
# "number" => Integer,
|
|
876
|
-
# "title" => String,
|
|
877
|
-
# "headRefName" => String (PR source branch),
|
|
878
|
-
# "baseRefName" => String (PR target branch),
|
|
879
|
-
# "isCrossRepository" => Boolean (true for fork PRs),
|
|
880
|
-
# "headRepositoryOwner" => { "login" => String } (fork owner info)
|
|
881
|
-
# }
|
|
882
|
-
#
|
|
883
|
-
# @param metadata [Hash] PR metadata from gh CLI
|
|
884
|
-
# @return [Hash] pr_data format expected by worktree creator
|
|
885
|
-
def pr_data_from_metadata(metadata)
|
|
886
|
-
{
|
|
887
|
-
number: metadata["number"],
|
|
888
|
-
title: metadata["title"],
|
|
889
|
-
head_branch: metadata["headRefName"],
|
|
890
|
-
base_branch: metadata["baseRefName"],
|
|
891
|
-
is_cross_repository: metadata["isCrossRepository"] || false,
|
|
892
|
-
head_repository_owner: metadata.dig("headRepositoryOwner", "login") || "unknown"
|
|
893
|
-
}
|
|
894
|
-
end
|
|
895
|
-
|
|
896
|
-
# Handle errors during PR metadata fetch
|
|
897
|
-
#
|
|
898
|
-
# @param error [Exception] The error that occurred
|
|
899
|
-
# @param pr_number [Integer] PR number for context in error messages
|
|
900
|
-
# @return [Integer] Exit code (always 1 for errors)
|
|
901
|
-
def handle_pr_fetch_error(error, pr_number)
|
|
902
|
-
case error
|
|
903
|
-
when Ace::Git::ProviderObjectNotFoundError
|
|
904
|
-
handle_pr_not_found(error, pr_number)
|
|
905
|
-
when Ace::Git::ProviderAuthenticationError
|
|
906
|
-
handle_gh_auth_error(error)
|
|
907
|
-
when Ace::Git::ProviderCliMissingError
|
|
908
|
-
handle_gh_not_installed(error)
|
|
909
|
-
else
|
|
910
|
-
handle_unknown_error(error)
|
|
911
|
-
end
|
|
912
|
-
1
|
|
913
|
-
end
|
|
914
|
-
|
|
915
|
-
# Handle PR not found error with helpful suggestions
|
|
916
|
-
def handle_pr_not_found(error, pr_number)
|
|
917
|
-
puts "Error: #{error.message}"
|
|
918
|
-
puts
|
|
919
|
-
puts "Suggestions:"
|
|
920
|
-
puts " 1. Verify the PR number is correct"
|
|
921
|
-
puts " 2. Check if the PR exists: gh pr view #{pr_number}"
|
|
922
|
-
puts " 3. Ensure you're in the correct repository"
|
|
923
|
-
end
|
|
924
|
-
|
|
925
|
-
# Handle GitHub authentication error with troubleshooting steps
|
|
926
|
-
def handle_gh_auth_error(error)
|
|
927
|
-
puts "Error: #{error.message}"
|
|
928
|
-
puts
|
|
929
|
-
puts "Troubleshooting:"
|
|
930
|
-
puts " 1. Verify GitHub authentication: gh auth status"
|
|
931
|
-
puts " 2. Re-authenticate if needed: gh auth login"
|
|
932
|
-
puts " 3. Check repository access permissions"
|
|
933
|
-
end
|
|
934
|
-
|
|
935
|
-
# Handle gh CLI not installed error with installation guidance
|
|
936
|
-
def handle_gh_not_installed(error)
|
|
937
|
-
puts "Error: #{error.message}"
|
|
938
|
-
puts
|
|
939
|
-
puts gh_not_available_message
|
|
940
|
-
end
|
|
941
|
-
|
|
942
|
-
# Handle unknown/unexpected errors with debug info
|
|
943
|
-
def handle_unknown_error(error)
|
|
944
|
-
puts "Error: #{error.message}"
|
|
945
|
-
if ENV["DEBUG"]
|
|
946
|
-
puts "Debug: #{error.class}"
|
|
947
|
-
puts "Debug: #{error.backtrace&.first}" if error.backtrace
|
|
948
|
-
end
|
|
949
|
-
end
|
|
950
|
-
|
|
951
|
-
# Get helpful error message when gh CLI is unavailable
|
|
952
|
-
#
|
|
953
|
-
# @return [String] User-friendly error message with installation guidance
|
|
954
|
-
def gh_not_available_message
|
|
955
|
-
<<~MESSAGE
|
|
956
|
-
gh CLI is required for PR worktrees but is not installed.
|
|
957
|
-
|
|
958
|
-
Install gh CLI:
|
|
959
|
-
- macOS: brew install gh
|
|
960
|
-
- Linux: See https://github.com/cli/cli#installation
|
|
961
|
-
- Windows: See https://github.com/cli/cli#installation
|
|
962
|
-
|
|
963
|
-
After installation, authenticate with: gh auth login
|
|
964
|
-
MESSAGE
|
|
965
|
-
end
|
|
966
879
|
end
|
|
967
880
|
end
|
|
968
881
|
end
|