gitbroker 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2718632c166f839f99b68dcb7cd9fbf4b516f5c91d607c7208ae2dd16374f684
4
- data.tar.gz: 8e4d8d1ee8a100efa10c3f9d63b9754210f05b740d17dc578ec7834499b0f9a9
3
+ metadata.gz: b6c41dc04d47bc8bfbad2ccb322b3a952b58e4957c2e282c25cb9075e58e83b2
4
+ data.tar.gz: 6d2f1b148005c310ed3538fe6fc4349cbaa38821bd1aa0f2ef25117477f146fa
5
5
  SHA512:
6
- metadata.gz: befca93d9a8f18e2afb179ca45a8b7b6973570cc380f1a7765958e04e3dff86691f266428b9de4124a2b8e3e7477f2abfc2206e13f300f16f4f77b6cca1b493a
7
- data.tar.gz: 14aa5ff4b302825ca46a766620087d5978482da9c6f7120adda2fc2402e2826e4939ef836b2a30a6869409caca4813c926ac83f2d8ff0306bc9324716b8a8d23
6
+ metadata.gz: 271fb887ec812d171902d32a53ed027bd3107194c19e0d67f14a1e5d22352f143add06b8090f2ba9ddd57e5853e35c773541234ad50d4666a064d49a9683513f
7
+ data.tar.gz: 9a259bd5c66d1836d85b23f7e017a4011ff28722cd068f5eaeaf746eb7be8e0d2d6dbf578067cd529ed9a8858e9970fd1712d6feabc39a505afca5d9f3bd69bc
data/lib/gitbroker/cli.rb CHANGED
@@ -103,13 +103,33 @@ module Gitbroker
103
103
  parser.on("--name NAME") { options[:name] = _1 }
104
104
  parser.on("--foreground") { options[:foreground] = true }
105
105
  end.parse!(@argv)
106
- @config = config.save!("server" => options[:server]) if options[:server]
106
+ foreground = options[:foreground] || !@input.tty?
107
+ if options[:server]
108
+ # Setting a machine up remembers its server (the background service must use it too). A daemon run by hand only
109
+ # uses it for this run: it must not rewrite the settings the service reads.
110
+ @config = foreground ? Config.load(dir: Config.default_dir(@env), env: @env.merge("GITBROKER_SERVER" => options[:server])) : config.save!("server" => options[:server])
111
+ end
107
112
 
108
- return run_daemon if options[:foreground] || !@input.tty?
113
+ return run_daemon if foreground
114
+ return set_up_separate_instance(options[:name] || Socket.gethostname) if separate_instance?
109
115
 
110
116
  set_up(options[:name] || Socket.gethostname)
111
117
  end
112
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
+
113
133
  def run_daemon
114
134
  raise Error, "This machine is not paired. Run `gitbroker start` in a terminal to pair it." unless config.paired?
115
135
 
@@ -26,7 +26,7 @@ module Gitbroker
26
26
  new(scanner: RepoScanner.new(roots: config.roots, depth: config.scan_depth, exclude: [ config.worktree_root ]),
27
27
  client_factory: -> { CableClient.new(url: config.cable_url, token: config.device_token, origin: config.server) },
28
28
  task_runner_factory: ->(report) { TaskRunner.new(config:, report:, out:) }, out:, relay:, process_scanner:,
29
- lifecycle: Lifecycle.new(dir: config.dir))
29
+ lifecycle: Lifecycle.new(dir: config.dir, server: config.server))
30
30
  end
31
31
 
32
32
  # Claude sessions with hooks installed report real working/waiting status, so the scanner only looks for `claude`
@@ -12,7 +12,7 @@ module Gitbroker
12
12
  WAIT = 10
13
13
 
14
14
  def initialize(dir:, home: Dir.home, service: ServiceInstaller.new(home:), runner: Runner.new, exe: ServiceInstaller.launcher,
15
- ruby: RbConfig.ruby, spawner: nil, sleeper: ->(seconds) { sleep(seconds) }, pid: Process.pid)
15
+ ruby: RbConfig.ruby, spawner: nil, sleeper: ->(seconds) { sleep(seconds) }, pid: Process.pid, env: ENV, server: nil)
16
16
  @dir = dir
17
17
  @home = home
18
18
  @service = service
@@ -22,6 +22,8 @@ module Gitbroker
22
22
  @spawner = spawner || method(:spawn_process)
23
23
  @sleeper = sleeper
24
24
  @pid = pid
25
+ @env = env
26
+ @server = server
25
27
  end
26
28
 
27
29
  attr_reader :service
@@ -41,9 +43,15 @@ module Gitbroker
41
43
  nil
42
44
  end
43
45
 
44
- # Used by the daemon itself: false when no service is installed or the manager refused.
45
- def stop_service = @service.installed? && @service.stop!
46
- def restart_service = @service.installed? && @service.restart!
46
+ # True only for the daemon the service manager started (launchd sets XPC_SERVICE_NAME to the job's label, systemd sets
47
+ # INVOCATION_ID). A daemon started by hand (`gitbroker start --server http://localhost:3000`) is not the service's:
48
+ # asking the manager to restart or stop "the service" would act on a different process (the one pointed at the
49
+ # default server) and leave this one running its old code for ever.
50
+ def service_process? = @env["XPC_SERVICE_NAME"] == ServiceInstaller::LABEL || @env.key?("INVOCATION_ID")
51
+
52
+ # Used by the daemon itself: false when this is not the service's daemon, no service is installed or the manager refused.
53
+ def stop_service = service_process? && @service.installed? && @service.stop!
54
+ def restart_service = service_process? && @service.installed? && @service.restart!
47
55
 
48
56
  # Starts the background service, or the daemon itself when none is installed. False when a service was refused.
49
57
  def launch
@@ -109,8 +117,11 @@ module Gitbroker
109
117
  false
110
118
  end
111
119
 
120
+ # The respawned daemon talks to the server this one did (a daemon started by hand against a local server must not come
121
+ # back pointed at the default one).
112
122
  def spawn_process(argv, log)
113
- Process.detach(Process.spawn(*argv, in: File::NULL, out: [ log, "a" ], err: [ log, "a" ], pgroup: true))
123
+ env = @server ? { "GITBROKER_SERVER" => @server } : {}
124
+ Process.detach(Process.spawn(env, *argv, in: File::NULL, out: [ log, "a" ], err: [ log, "a" ], pgroup: true))
114
125
  end
115
126
  end
116
127
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Gitbroker
4
- VERSION = "0.6.2"
4
+ VERSION = "0.6.4"
5
5
  end
@@ -30,6 +30,7 @@ module Gitbroker
30
30
  end
31
31
 
32
32
  local = "gitbroker/pr-#{number}-#{kind}"
33
+ retire_leftovers!(repo_path, dir, local, head_sha) if branch && kind == "update_branch"
33
34
  keep_local_commits!(repo_path, local, head_sha) if branch
34
35
  if File.exist?(File.join(dir, ".git"))
35
36
  reuse(dir, local, head_sha, branch:)
@@ -64,6 +65,26 @@ module Gitbroker
64
65
  branch ? @runner.git(dir, "checkout", "--quiet", "-B", local, head_sha) : @runner.git(dir, "checkout", "--quiet", "--detach", head_sha)
65
66
  end
66
67
 
68
+ # An update_branch task rewrites its branch, so what an earlier attempt left behind (an unpushed rebase, a half-finished
69
+ # one, a dirty worktree) is not work to protect but a result to redo: it is set aside, never deleted (the worktree is
70
+ # moved and the branch renamed, both with a `-leftover-<time>` suffix that `gitbroker clean` can remove), and the task
71
+ # starts from the PR head.
72
+ def retire_leftovers!(repo_path, dir, local, head_sha)
73
+ stamp = Time.now.strftime("%Y%m%d%H%M%S")
74
+ if File.exist?(File.join(dir, ".git"))
75
+ pristine = @runner.git(dir, "status", "--porcelain").strip.empty? && @runner.git(dir, "rev-parse", "HEAD").strip.start_with?(head_sha)
76
+ unless pristine
77
+ moved = "#{dir}-leftover-#{stamp}"
78
+ @runner.git(repo_path, "worktree", "move", dir, moved)
79
+ @runner.git(moved, "checkout", "--quiet", "--detach") # frees the task branch; the files stay as they were
80
+ end
81
+ end
82
+ return unless @runner.git_success?(repo_path, "rev-parse", "--verify", "--quiet", "refs/heads/#{local}")
83
+ return if @runner.git_success?(repo_path, "merge-base", "--is-ancestor", "refs/heads/#{local}", head_sha)
84
+
85
+ @runner.git(repo_path, "branch", "--quiet", "-m", local, "#{local}-leftover-#{stamp}")
86
+ end
87
+
67
88
  # `-B` resets the task branch to the head; refuse when that would drop commits made there that the head lacks.
68
89
  def keep_local_commits!(repo_path, local, head_sha)
69
90
  return unless @runner.git_success?(repo_path, "rev-parse", "--verify", "--quiet", "refs/heads/#{local}")
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "gitbroker",
3
- "version": "0.6.2",
3
+ "version": "0.6.4",
4
4
  "description": "Skills for working on Git Broker agent tasks: the gitbroker MCP tools, write rules and report_task."
5
5
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gitbroker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuri Sidorov (@newstler)