gitbroker 0.6.3 → 0.6.5
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/lib/gitbroker/branch_publisher.rb +8 -7
- data/lib/gitbroker/cli.rb +16 -0
- data/lib/gitbroker/git_context.rb +10 -0
- data/lib/gitbroker/neutralizer.rb +2 -2
- data/lib/gitbroker/task_runner.rb +4 -3
- data/lib/gitbroker/version.rb +1 -1
- data/lib/gitbroker/worktree.rb +6 -5
- data/plugin/.claude-plugin/plugin.json +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: be4bff227a1a036f59d8124a89b2f6dc782dda63c5b6d471f7e51f9fde126e5d
|
|
4
|
+
data.tar.gz: c984663cf01008495cca098619d95e5476b57b6d9b8626c7bb047f20dee8f576
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0ec15dc9d6642178cf85e8ba9ddc874b6662af3e88f0106d45797c1839a4baf62bce28e16261f28acdb1124b4fb056f46001804bd846e082681a0d9f9a2b2aac
|
|
7
|
+
data.tar.gz: b3b4fb6d648a3bb7e9cf0e4fdc5032da58b6794df9eae5adbc62eb8606145d2317beabbc7bbee2c043f2076470908e07e0bd015ff36bfa9c5d15400c22027251
|
|
@@ -14,16 +14,17 @@ module Gitbroker
|
|
|
14
14
|
|
|
15
15
|
REF = %r{\A\w[\w./-]{0,200}\z}
|
|
16
16
|
|
|
17
|
-
def initialize(dir:, head_ref:, expected_sha:, base_ref:, runner: Runner.new)
|
|
17
|
+
def initialize(dir:, head_ref:, expected_sha:, base_ref:, remote: "origin", runner: Runner.new)
|
|
18
18
|
@dir = dir
|
|
19
19
|
@head_ref = head_ref.to_s
|
|
20
20
|
@expected = expected_sha.to_s
|
|
21
21
|
@base_ref = base_ref.to_s
|
|
22
|
+
@remote = remote.to_s
|
|
22
23
|
@runner = runner
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def publish!
|
|
26
|
-
return failed("No branch name to push.") unless valid_ref?(@head_ref) && valid_ref?(@base_ref)
|
|
27
|
+
return failed("No branch name to push.") unless valid_ref?(@head_ref) && valid_ref?(@base_ref) && valid_ref?(@remote)
|
|
27
28
|
return failed("No starting head to protect.") unless @expected.match?(/\A[0-9a-f]{7,40}\z/)
|
|
28
29
|
return failed("A rebase or merge is still in progress in the worktree: finish it first.") if in_progress?
|
|
29
30
|
return failed("The worktree has uncommitted changes.") unless git("status", "--porcelain").strip.empty?
|
|
@@ -31,16 +32,16 @@ module Gitbroker
|
|
|
31
32
|
head = git("rev-parse", "HEAD").strip
|
|
32
33
|
return Result.new(:nothing, "Nothing to push: the branch did not change.") if head.start_with?(@expected) || @expected.start_with?(head)
|
|
33
34
|
|
|
34
|
-
git("fetch", "--quiet",
|
|
35
|
-
unless @runner.git_success?(@dir, "merge-base", "--is-ancestor", "
|
|
36
|
-
return failed("The branch is not on top of
|
|
35
|
+
git("fetch", "--quiet", @remote, @base_ref)
|
|
36
|
+
unless @runner.git_success?(@dir, "merge-base", "--is-ancestor", "#{@remote}/#{@base_ref}", "HEAD")
|
|
37
|
+
return failed("The branch is not on top of #{@remote}/#{@base_ref}: nothing pushed.")
|
|
37
38
|
end
|
|
38
39
|
unless git("rev-list", "--merges", "#{@expected}..HEAD").strip.empty?
|
|
39
40
|
return failed("The branch contains merge commits; it must be a clean rebase: nothing pushed.")
|
|
40
41
|
end
|
|
41
42
|
|
|
42
|
-
git("push", lease_option,
|
|
43
|
-
Result.new(:pushed, "Pushed #{@head_ref} rebased onto
|
|
43
|
+
git("push", lease_option, @remote, "HEAD:refs/heads/#{@head_ref}")
|
|
44
|
+
Result.new(:pushed, "Pushed #{@head_ref} rebased onto #{@remote}/#{@base_ref}.")
|
|
44
45
|
rescue Error => e
|
|
45
46
|
failed("Push failed: #{e.message.lines.first.to_s.strip}")
|
|
46
47
|
end
|
data/lib/gitbroker/cli.rb
CHANGED
|
@@ -111,10 +111,25 @@ module Gitbroker
|
|
|
111
111
|
end
|
|
112
112
|
|
|
113
113
|
return run_daemon if foreground
|
|
114
|
+
return set_up_separate_instance(options[:name] || Socket.gethostname) if separate_instance?
|
|
114
115
|
|
|
115
116
|
set_up(options[:name] || Socket.gethostname)
|
|
116
117
|
end
|
|
117
118
|
|
|
119
|
+
# A GITBROKER_CONFIG_DIR that is not the default is another companion (say, one for a local dev server). The background
|
|
120
|
+
# service belongs to the default one, so this one is paired and then kept in this window; it never touches the service.
|
|
121
|
+
def separate_instance?
|
|
122
|
+
custom = @env["GITBROKER_CONFIG_DIR"]
|
|
123
|
+
custom && File.expand_path(custom) != File.expand_path(Config.default_dir(@env.reject { |key, _| key == "GITBROKER_CONFIG_DIR" }))
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def set_up_separate_instance(name)
|
|
127
|
+
Login.new(config:, out: @out, name:).run unless config.paired?
|
|
128
|
+
@out.puts "This companion uses its own settings (#{config.dir}), so the background service is left alone. Keeping it in this " \
|
|
129
|
+
"window, connected to #{config.server}; press Ctrl-C to stop it."
|
|
130
|
+
run_daemon
|
|
131
|
+
end
|
|
132
|
+
|
|
118
133
|
def run_daemon
|
|
119
134
|
raise Error, "This machine is not paired. Run `gitbroker start` in a terminal to pair it." unless config.paired?
|
|
120
135
|
|
|
@@ -304,6 +319,7 @@ module Gitbroker
|
|
|
304
319
|
parser.on("--head-ref REF") { options[:head_ref] = _1 }
|
|
305
320
|
parser.on("--expected SHA") { options[:expected_sha] = _1 }
|
|
306
321
|
parser.on("--base REF") { options[:base_ref] = _1 }
|
|
322
|
+
parser.on("--remote NAME") { options[:remote] = _1 }
|
|
307
323
|
end.parse!(@argv)
|
|
308
324
|
result = BranchPublisher.new(**options).publish!
|
|
309
325
|
@out.puts result.message
|
|
@@ -61,6 +61,16 @@ module Gitbroker
|
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
# The remote that is `full_name` on GitHub (origin first when several are): a checkout found through its `github` remote
|
|
65
|
+
# has an origin that is something else, so fetching and pushing must name the remote, never assume origin.
|
|
66
|
+
def remote_for(dir, full_name)
|
|
67
|
+
wanted = full_name.to_s.downcase
|
|
68
|
+
matches = remote_urls(dir).select { |_name, url| RepoScanner.full_name_from(url).to_s.downcase == wanted }.map(&:first)
|
|
69
|
+
(matches.include?("origin") ? "origin" : matches.find { _1.match?(REMOTE_NAME) }) || "origin"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
REMOTE_NAME = /\A\w[\w.-]{0,60}\z/
|
|
73
|
+
|
|
64
74
|
def repository_from(remotes)
|
|
65
75
|
remotes.sort_by { |name, _url| name == "origin" ? 0 : 1 }.lazy.filter_map { |_name, url| RepoScanner.full_name_from(url) }.first
|
|
66
76
|
end
|
|
@@ -17,13 +17,13 @@ module Gitbroker
|
|
|
17
17
|
@runner = runner
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def neutralize!(dir, repo_path:, number:, base_ref:)
|
|
20
|
+
def neutralize!(dir, repo_path:, number:, base_ref:, remote: "origin")
|
|
21
21
|
raise Error, "Invalid base branch #{base_ref.inspect}" unless Worktree.valid_ref?(base_ref)
|
|
22
22
|
raise Error, "Invalid PR number #{number.inspect}" unless number.to_s.match?(/\A[1-9]\d{0,9}\z/)
|
|
23
23
|
|
|
24
24
|
@dir = File.realpath(dir)
|
|
25
25
|
base = "refs/gitbroker/base-#{number}"
|
|
26
|
-
@runner.git(repo_path, "fetch", "--quiet",
|
|
26
|
+
@runner.git(repo_path, "fetch", "--quiet", remote, "+refs/heads/#{base_ref}:#{base}")
|
|
27
27
|
|
|
28
28
|
removed = paths(@runner.git(@dir, "ls-files", "-z", "--", *REMOVE))
|
|
29
29
|
removed.each { delete(_1) }
|
|
@@ -58,7 +58,7 @@ module Gitbroker
|
|
|
58
58
|
cross_repository: task["cross_repository"] == true)
|
|
59
59
|
# Agents report real paths (macOS: /private/var, not /var); progress paths are matched against this form.
|
|
60
60
|
dir = File.realpath(dir)
|
|
61
|
-
restored = task["untrusted"] ? @neutralizer.neutralize!(dir, repo_path:, number: task["pr_number"], base_ref: base_ref(task)) : []
|
|
61
|
+
restored = task["untrusted"] ? @neutralizer.neutralize!(dir, repo_path:, number: task["pr_number"], base_ref: base_ref(task), remote: GitContext.remote_for(repo_path, task["repo"])) : []
|
|
62
62
|
task = task.merge("prompt" => prompt_for(task))
|
|
63
63
|
agent = Agents.for(@config, worktree: dir)
|
|
64
64
|
say(id, "#{task["kind"]} on #{task["repo"]}##{task["pr_number"]}: starting (#{agent.name}, #{task["mode"] == "terminal" ? "terminal" : "autonomous"})")
|
|
@@ -143,7 +143,8 @@ module Gitbroker
|
|
|
143
143
|
def publish_branch(task, outcome, dir)
|
|
144
144
|
return outcome unless publishes_branch?(task)
|
|
145
145
|
|
|
146
|
-
result = BranchPublisher.new(dir:, head_ref: task["head_ref"], expected_sha: task["head_sha"], base_ref: base_ref(task)
|
|
146
|
+
result = BranchPublisher.new(dir:, head_ref: task["head_ref"], expected_sha: task["head_sha"], base_ref: base_ref(task),
|
|
147
|
+
remote: GitContext.remote_for(dir, task["repo"])).publish!
|
|
147
148
|
say(task["task_id"], result.message)
|
|
148
149
|
return outcome unless result.failed?
|
|
149
150
|
|
|
@@ -155,7 +156,7 @@ module Gitbroker
|
|
|
155
156
|
return unless publishes_branch?(task)
|
|
156
157
|
|
|
157
158
|
[ RbConfig.ruby, ServiceInstaller.launcher, "publish-branch", "--dir", dir, "--head-ref", task["head_ref"].to_s,
|
|
158
|
-
"--expected", task["head_sha"].to_s, "--base", base_ref(task) ]
|
|
159
|
+
"--expected", task["head_sha"].to_s, "--base", base_ref(task), "--remote", GitContext.remote_for(dir, task["repo"]) ]
|
|
159
160
|
end
|
|
160
161
|
|
|
161
162
|
def publishes_branch?(task) = task["kind"] == "update_branch" && task["cross_repository"] != true && !task["read_only"]
|
data/lib/gitbroker/version.rb
CHANGED
data/lib/gitbroker/worktree.rb
CHANGED
|
@@ -24,7 +24,8 @@ module Gitbroker
|
|
|
24
24
|
validate!(full_name:, number:, head_sha:, kind:)
|
|
25
25
|
dir = File.join(@root, full_name.downcase.tr("/", "-"), "pr-#{number}-#{kind}")
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
remote = GitContext.remote_for(repo_path, full_name)
|
|
28
|
+
@runner.git(repo_path, "fetch", "--quiet", remote, "+refs/pull/#{number}/head:refs/gitbroker/pr-#{number}")
|
|
28
29
|
unless @runner.git_success?(repo_path, "cat-file", "-e", "#{head_sha}^{commit}")
|
|
29
30
|
raise Error, "Head #{head_sha[0, 7]} is not in the fetched PR; the PR may have moved. Start the task again from the card."
|
|
30
31
|
end
|
|
@@ -41,7 +42,7 @@ module Gitbroker
|
|
|
41
42
|
@runner.git(repo_path, "worktree", "add", "--quiet", *target, dir, head_sha)
|
|
42
43
|
end
|
|
43
44
|
if branch
|
|
44
|
-
cross_repository ? drop_upstream(repo_path, local) : track_upstream(repo_path, local, head_ref)
|
|
45
|
+
cross_repository ? drop_upstream(repo_path, local) : track_upstream(repo_path, local, head_ref, remote)
|
|
45
46
|
end
|
|
46
47
|
dir
|
|
47
48
|
end
|
|
@@ -93,11 +94,11 @@ module Gitbroker
|
|
|
93
94
|
raise Error, "Branch #{local} has commits that are not on the PR head; push or delete it, then start the task again."
|
|
94
95
|
end
|
|
95
96
|
|
|
96
|
-
def track_upstream(repo_path, local, head_ref)
|
|
97
|
+
def track_upstream(repo_path, local, head_ref, remote)
|
|
97
98
|
return unless self.class.valid_ref?(head_ref)
|
|
98
|
-
return unless @runner.git_success?(repo_path, "fetch", "--quiet",
|
|
99
|
+
return unless @runner.git_success?(repo_path, "fetch", "--quiet", remote, "+refs/heads/#{head_ref}:refs/remotes/#{remote}/#{head_ref}")
|
|
99
100
|
|
|
100
|
-
@runner.git(repo_path, "branch", "--quiet", "--set-upstream-to
|
|
101
|
+
@runner.git(repo_path, "branch", "--quiet", "--set-upstream-to=#{remote}/#{head_ref}", local)
|
|
101
102
|
end
|
|
102
103
|
|
|
103
104
|
def drop_upstream(repo_path, local)
|