ace-git-worktree 0.22.1 → 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 +52 -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 +70 -155
- data/lib/ace/git/worktree/molecules/cleanup_pr_resolver.rb +141 -132
- 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 +7 -5
- 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,7 +7,59 @@ 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
|
|
10
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
|
+
|
|
57
|
+
|
|
58
|
+
## [0.23.0] - 2026-09-27
|
|
59
|
+
|
|
60
|
+
### Changed
|
|
61
|
+
- PR lookups now go through the `ace-git-github` provider (`Ace::Git::Github::PrFetcher`) instead of the `PrMetadataFetcher` molecule removed from the ace-git 0.24 core; provider failures surface as classified `Provider*Error`s instead of the removed `Gh*Error` classes.
|
|
62
|
+
- Raised the `ace-git` dependency floor to `~> 0.24` and added `ace-git-github ~> 0.1.1` (cross-repository PR argument fix). Resolves the require-time `LoadError` (`ace/git/molecules/pr_metadata_fetcher`) that made released 0.22.1 incompatible with ace-git 0.24 and blocked adopting ace-review 0.55.0.
|
|
11
63
|
## [0.22.1] - 2026-09-02
|
|
12
64
|
|
|
13
65
|
### Technical
|
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
|
|
@@ -55,9 +55,10 @@ module Ace
|
|
|
55
55
|
puts
|
|
56
56
|
show_help
|
|
57
57
|
1
|
|
58
|
-
rescue Ace::Git::
|
|
59
|
-
Ace::Git::
|
|
60
|
-
Ace::Git::
|
|
58
|
+
rescue Ace::Git::ProviderObjectNotFoundError,
|
|
59
|
+
Ace::Git::ProviderAuthenticationError,
|
|
60
|
+
Ace::Git::ProviderCliMissingError,
|
|
61
|
+
Ace::Git::ProviderIdentityMismatchError,
|
|
61
62
|
Ace::Git::TimeoutError => e
|
|
62
63
|
puts "Error: #{e.message}"
|
|
63
64
|
1
|
|
@@ -86,9 +87,12 @@ module Ace
|
|
|
86
87
|
Task ID formats: 081, task.081, v.0.9.0+081
|
|
87
88
|
|
|
88
89
|
PR-AWARE CREATION:
|
|
89
|
-
--pr <number> Create worktree for a
|
|
90
|
+
--pr <number> Create worktree for a pull request
|
|
90
91
|
--pull-request <number> (alias for --pr)
|
|
91
|
-
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.
|
|
92
96
|
|
|
93
97
|
BRANCH-AWARE CREATION:
|
|
94
98
|
-b <branch> Create worktree from a branch (local or remote)
|
|
@@ -100,12 +104,14 @@ module Ace
|
|
|
100
104
|
--path <path> Custom worktree path (default: from config)
|
|
101
105
|
--source <ref> Git ref to use as branch start-point (default: current branch)
|
|
102
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
|
|
103
109
|
--dry-run Show what would be created without creating
|
|
104
110
|
--no-status-update Skip marking task as in-progress (task mode only)
|
|
105
111
|
--no-commit Skip committing task changes (task mode only)
|
|
106
112
|
--no-push Skip pushing task changes to remote (task mode only)
|
|
107
113
|
--no-upstream Skip pushing worktree branch with upstream tracking (task mode only)
|
|
108
|
-
--no-pr Skip creating draft PR
|
|
114
|
+
--no-pr Skip creating draft PR; keeps task creation local and provider-free
|
|
109
115
|
--no-bootstrap Skip worktree bootstrap preparation
|
|
110
116
|
--push-remote <name> Remote to push to (default: origin) (task mode only)
|
|
111
117
|
--no-auto-navigate Stay in current directory (default: navigate to worktree)
|
|
@@ -120,8 +126,8 @@ module Ace
|
|
|
120
126
|
# Create task worktree based on main instead of current branch
|
|
121
127
|
ace-git-worktree create --task 081 --source main
|
|
122
128
|
|
|
123
|
-
# Create PR worktree
|
|
124
|
-
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
|
|
125
131
|
|
|
126
132
|
# Create worktree from remote branch
|
|
127
133
|
ace-git-worktree create -b origin/feature/auth
|
|
@@ -148,9 +154,10 @@ module Ace
|
|
|
148
154
|
- hooks.after_create: Commands to run after worktree creation
|
|
149
155
|
|
|
150
156
|
REQUIREMENTS:
|
|
151
|
-
PR-aware creation requires
|
|
152
|
-
|
|
153
|
-
|
|
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.
|
|
154
161
|
HELP
|
|
155
162
|
0
|
|
156
163
|
end
|
|
@@ -168,6 +175,8 @@ module Ace
|
|
|
168
175
|
branch: nil,
|
|
169
176
|
path: nil,
|
|
170
177
|
source: nil,
|
|
178
|
+
server: nil,
|
|
179
|
+
default_server: false,
|
|
171
180
|
dry_run: false,
|
|
172
181
|
no_status_update: false,
|
|
173
182
|
no_commit: false,
|
|
@@ -203,6 +212,11 @@ module Ace
|
|
|
203
212
|
when "--source"
|
|
204
213
|
i += 1
|
|
205
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
|
|
206
220
|
when "--dry-run"
|
|
207
221
|
options[:dry_run] = true
|
|
208
222
|
when "--no-status-update"
|
|
@@ -375,6 +389,8 @@ module Ace
|
|
|
375
389
|
push_remote: options[:push_remote],
|
|
376
390
|
commit_message: options[:commit_message],
|
|
377
391
|
target_branch: options[:target_branch],
|
|
392
|
+
server: options[:server],
|
|
393
|
+
default_server: options[:default_server],
|
|
378
394
|
no_mise_trust: options[:no_mise_trust],
|
|
379
395
|
force: options[:force]
|
|
380
396
|
}.compact
|
|
@@ -408,7 +424,7 @@ module Ace
|
|
|
408
424
|
end
|
|
409
425
|
end
|
|
410
426
|
|
|
411
|
-
# Create a PR worktree
|
|
427
|
+
# Create a PR worktree from normalized provider evidence.
|
|
412
428
|
#
|
|
413
429
|
# @param options [Hash] Command options
|
|
414
430
|
# @return [Integer] Exit code
|
|
@@ -432,47 +448,28 @@ module Ace
|
|
|
432
448
|
|
|
433
449
|
puts "Creating worktree for PR ##{pr_number}..."
|
|
434
450
|
|
|
435
|
-
# Check gh CLI availability using ace-git's PrMetadataFetcher
|
|
436
|
-
unless Ace::Git::Molecules::PrMetadataFetcher.gh_installed?
|
|
437
|
-
puts "Error: gh CLI is required for PR worktree creation."
|
|
438
|
-
puts
|
|
439
|
-
puts gh_not_available_message
|
|
440
|
-
return 1
|
|
441
|
-
end
|
|
442
|
-
|
|
443
|
-
unless Ace::Git::Molecules::PrMetadataFetcher.gh_authenticated?
|
|
444
|
-
puts "Error: gh CLI is not authenticated."
|
|
445
|
-
puts
|
|
446
|
-
puts "Authenticate with: gh auth login"
|
|
447
|
-
return 1
|
|
448
|
-
end
|
|
449
|
-
|
|
450
|
-
# Fetch PR data
|
|
451
|
-
puts "Fetching PR information..."
|
|
452
451
|
begin
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
#
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
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
|
|
474
472
|
end
|
|
475
|
-
puts
|
|
476
473
|
|
|
477
474
|
# Prepare creation options
|
|
478
475
|
creation_options = {
|
|
@@ -483,7 +480,7 @@ module Ace
|
|
|
483
480
|
}.compact
|
|
484
481
|
|
|
485
482
|
# Create the worktree
|
|
486
|
-
result = @manager.create_pr(
|
|
483
|
+
result = @manager.create_pr(evidence, checkout, creation_options)
|
|
487
484
|
|
|
488
485
|
if result[:success]
|
|
489
486
|
display_pr_creation_result(result, options[:dry_run])
|
|
@@ -493,14 +490,30 @@ module Ace
|
|
|
493
490
|
display_warnings(result[:warnings]) if result[:warnings]
|
|
494
491
|
1
|
|
495
492
|
end
|
|
496
|
-
rescue Ace::Git::
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
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
|
|
501
497
|
end
|
|
502
498
|
end
|
|
503
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
|
+
|
|
504
517
|
# Create a branch worktree
|
|
505
518
|
#
|
|
506
519
|
# @param options [Hash] Command options
|
|
@@ -863,104 +876,6 @@ module Ace
|
|
|
863
876
|
system("which ace-tmux > /dev/null 2>&1")
|
|
864
877
|
end
|
|
865
878
|
|
|
866
|
-
# Convert PR metadata from gh CLI to internal pr_data format
|
|
867
|
-
#
|
|
868
|
-
# Anti-corruption layer that translates gh CLI JSON output to internal format.
|
|
869
|
-
# This isolates worktree creation from gh CLI output structure changes.
|
|
870
|
-
#
|
|
871
|
-
# Expected metadata schema from ace-git PrMetadataFetcher (via gh pr view --json):
|
|
872
|
-
# {
|
|
873
|
-
# "number" => Integer,
|
|
874
|
-
# "title" => String,
|
|
875
|
-
# "headRefName" => String (PR source branch),
|
|
876
|
-
# "baseRefName" => String (PR target branch),
|
|
877
|
-
# "isCrossRepository" => Boolean (true for fork PRs),
|
|
878
|
-
# "headRepositoryOwner" => { "login" => String } (fork owner info)
|
|
879
|
-
# }
|
|
880
|
-
#
|
|
881
|
-
# @param metadata [Hash] PR metadata from gh CLI
|
|
882
|
-
# @return [Hash] pr_data format expected by worktree creator
|
|
883
|
-
def pr_data_from_metadata(metadata)
|
|
884
|
-
{
|
|
885
|
-
number: metadata["number"],
|
|
886
|
-
title: metadata["title"],
|
|
887
|
-
head_branch: metadata["headRefName"],
|
|
888
|
-
base_branch: metadata["baseRefName"],
|
|
889
|
-
is_cross_repository: metadata["isCrossRepository"] || false,
|
|
890
|
-
head_repository_owner: metadata.dig("headRepositoryOwner", "login") || "unknown"
|
|
891
|
-
}
|
|
892
|
-
end
|
|
893
|
-
|
|
894
|
-
# Handle errors during PR metadata fetch
|
|
895
|
-
#
|
|
896
|
-
# @param error [Exception] The error that occurred
|
|
897
|
-
# @param pr_number [Integer] PR number for context in error messages
|
|
898
|
-
# @return [Integer] Exit code (always 1 for errors)
|
|
899
|
-
def handle_pr_fetch_error(error, pr_number)
|
|
900
|
-
case error
|
|
901
|
-
when Ace::Git::PrNotFoundError
|
|
902
|
-
handle_pr_not_found(error, pr_number)
|
|
903
|
-
when Ace::Git::GhAuthenticationError
|
|
904
|
-
handle_gh_auth_error(error)
|
|
905
|
-
when Ace::Git::GhNotInstalledError
|
|
906
|
-
handle_gh_not_installed(error)
|
|
907
|
-
else
|
|
908
|
-
handle_unknown_error(error)
|
|
909
|
-
end
|
|
910
|
-
1
|
|
911
|
-
end
|
|
912
|
-
|
|
913
|
-
# Handle PR not found error with helpful suggestions
|
|
914
|
-
def handle_pr_not_found(error, pr_number)
|
|
915
|
-
puts "Error: #{error.message}"
|
|
916
|
-
puts
|
|
917
|
-
puts "Suggestions:"
|
|
918
|
-
puts " 1. Verify the PR number is correct"
|
|
919
|
-
puts " 2. Check if the PR exists: gh pr view #{pr_number}"
|
|
920
|
-
puts " 3. Ensure you're in the correct repository"
|
|
921
|
-
end
|
|
922
|
-
|
|
923
|
-
# Handle GitHub authentication error with troubleshooting steps
|
|
924
|
-
def handle_gh_auth_error(error)
|
|
925
|
-
puts "Error: #{error.message}"
|
|
926
|
-
puts
|
|
927
|
-
puts "Troubleshooting:"
|
|
928
|
-
puts " 1. Verify GitHub authentication: gh auth status"
|
|
929
|
-
puts " 2. Re-authenticate if needed: gh auth login"
|
|
930
|
-
puts " 3. Check repository access permissions"
|
|
931
|
-
end
|
|
932
|
-
|
|
933
|
-
# Handle gh CLI not installed error with installation guidance
|
|
934
|
-
def handle_gh_not_installed(error)
|
|
935
|
-
puts "Error: #{error.message}"
|
|
936
|
-
puts
|
|
937
|
-
puts gh_not_available_message
|
|
938
|
-
end
|
|
939
|
-
|
|
940
|
-
# Handle unknown/unexpected errors with debug info
|
|
941
|
-
def handle_unknown_error(error)
|
|
942
|
-
puts "Error: #{error.message}"
|
|
943
|
-
if ENV["DEBUG"]
|
|
944
|
-
puts "Debug: #{error.class}"
|
|
945
|
-
puts "Debug: #{error.backtrace&.first}" if error.backtrace
|
|
946
|
-
end
|
|
947
|
-
end
|
|
948
|
-
|
|
949
|
-
# Get helpful error message when gh CLI is unavailable
|
|
950
|
-
#
|
|
951
|
-
# @return [String] User-friendly error message with installation guidance
|
|
952
|
-
def gh_not_available_message
|
|
953
|
-
<<~MESSAGE
|
|
954
|
-
gh CLI is required for PR worktrees but is not installed.
|
|
955
|
-
|
|
956
|
-
Install gh CLI:
|
|
957
|
-
- macOS: brew install gh
|
|
958
|
-
- Linux: See https://github.com/cli/cli#installation
|
|
959
|
-
- Windows: See https://github.com/cli/cli#installation
|
|
960
|
-
|
|
961
|
-
After installation, authenticate with: gh auth login
|
|
962
|
-
MESSAGE
|
|
963
|
-
end
|
|
964
879
|
end
|
|
965
880
|
end
|
|
966
881
|
end
|