gitbroker 0.3.1 → 0.4.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/LICENSE.txt +1 -1
- data/lib/gitbroker/cli.rb +42 -2
- data/lib/gitbroker/daemon.rb +66 -5
- data/lib/gitbroker/hooks_installer.rb +3 -2
- data/lib/gitbroker/lifecycle.rb +3 -1
- data/lib/gitbroker/maintenance.rb +65 -0
- data/lib/gitbroker/run_script.rb +2 -2
- data/lib/gitbroker/service_installer.rb +35 -1
- data/lib/gitbroker/url_handler_installer.rb +16 -3
- data/lib/gitbroker/version.rb +1 -1
- data/lib/gitbroker.rb +4 -0
- data/plugin/.claude-plugin/plugin.json +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 227a743489c659dad707f6e11180988105a2ec34d89601a6ec61d62f5c4a8f53
|
|
4
|
+
data.tar.gz: 2ab80c483b24299e9c2776cc44fcda117e7fc5274069f1107f48e26c044190d0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ac95b2ec94aca69fcd5cc0f78b0cc1f5541098158722c13825d3f32746ec3ecd2c0c2c0f339f3a405ad12cca055d11894ebaabb96e462836bf69d2a9a71fcd5f
|
|
7
|
+
data.tar.gz: f0507d54eeb1efefebc493b2edbade05da29e6a589ae0260ab79b3c6d61090ffdeb6dacf7019a379c0237160ac5331029f2bdb20f65ca0fc072d03009681f0bf
|
data/LICENSE.txt
CHANGED
data/lib/gitbroker/cli.rb
CHANGED
|
@@ -16,6 +16,8 @@ module Gitbroker
|
|
|
16
16
|
launch Start the companion in the background if it is not running
|
|
17
17
|
stop Stop the companion (and its background service, so it stays stopped)
|
|
18
18
|
restart Restart the companion, e.g. after `gem update gitbroker`
|
|
19
|
+
upgrade Install the newest gem, remove old versions, restart the companion
|
|
20
|
+
uninstall Remove the service, the start link, the Claude Code hooks and the gem (keeps your pairing)
|
|
19
21
|
install-service Run `gitbroker start` in the background (launchd / systemd --user), and let
|
|
20
22
|
the website's "Start companion" button wake it (gitbroker:// handler)
|
|
21
23
|
install-skill Install the gitbroker skills into ~/.claude/skills
|
|
@@ -30,8 +32,10 @@ module Gitbroker
|
|
|
30
32
|
Agent activity is visible only to you; only status is sent, never prompts, file contents or tool input.
|
|
31
33
|
TEXT
|
|
32
34
|
|
|
33
|
-
def initialize(argv, out: $stdout, err: $stderr, env: ENV, home: Dir.home, input: $stdin)
|
|
35
|
+
def initialize(argv, out: $stdout, err: $stderr, env: ENV, home: Dir.home, input: $stdin, maintenance: nil, lifecycle: nil)
|
|
34
36
|
@argv = argv.dup
|
|
37
|
+
@maintenance = maintenance
|
|
38
|
+
@lifecycle = lifecycle
|
|
35
39
|
@out = out
|
|
36
40
|
@err = err
|
|
37
41
|
@env = env
|
|
@@ -50,6 +54,8 @@ module Gitbroker
|
|
|
50
54
|
when "launch" then launch
|
|
51
55
|
when "stop" then stop
|
|
52
56
|
when "restart" then restart
|
|
57
|
+
when "upgrade" then upgrade
|
|
58
|
+
when "uninstall" then uninstall
|
|
53
59
|
when "install-service" then install_service
|
|
54
60
|
when "install-skill" then install_skill
|
|
55
61
|
when "status" then status
|
|
@@ -93,7 +99,12 @@ module Gitbroker
|
|
|
93
99
|
raise Error, "This machine is not paired. Run `gitbroker login` first." unless config.paired?
|
|
94
100
|
|
|
95
101
|
path = ServiceInstaller.new(home: @home, env: @env).install!
|
|
96
|
-
@out.puts "Installed and started #{path}.
|
|
102
|
+
@out.puts "Installed and started #{path}."
|
|
103
|
+
if ServiceInstaller.pinned?
|
|
104
|
+
@out.puts "No RubyGems `gitbroker` wrapper was found, so it runs this version-pinned gem executable: run " \
|
|
105
|
+
"`gitbroker install-service` again after upgrading the gem."
|
|
106
|
+
end
|
|
107
|
+
refresh_hooks
|
|
97
108
|
handler = UrlHandlerInstaller.new(home: @home).install!
|
|
98
109
|
@out.puts "Registered #{handler} so the website's \"Start companion\" button can wake it."
|
|
99
110
|
end
|
|
@@ -120,6 +131,27 @@ module Gitbroker
|
|
|
120
131
|
@out.puts "Restarting the companion."
|
|
121
132
|
end
|
|
122
133
|
|
|
134
|
+
def maintenance = @maintenance ||= Maintenance.new(hooks: hooks_installer)
|
|
135
|
+
|
|
136
|
+
# The restart runs this (old) process's code, which only asks the service manager or spawns RubyGems' wrapper, so
|
|
137
|
+
# the companion comes back on the new version. An unpaired machine has no companion to restart.
|
|
138
|
+
def upgrade
|
|
139
|
+
maintenance.upgrade!
|
|
140
|
+
return @out.puts("Upgraded. Run `gitbroker login` to pair this machine.") unless config.paired?
|
|
141
|
+
|
|
142
|
+
lifecycle.restart
|
|
143
|
+
@out.puts "Upgraded. Restarting the companion."
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def uninstall
|
|
147
|
+
lifecycle.stop # a companion started by hand too, not only the service
|
|
148
|
+
error = nil
|
|
149
|
+
removed = maintenance.uninstall!(service: ServiceInstaller.new(home: @home, env: @env), url_handler: UrlHandlerInstaller.new(home: @home)) { error = _1 }
|
|
150
|
+
raise Error, "Removed the service, the start link and the hooks, but `gem uninstall gitbroker` failed: #{error}" unless removed
|
|
151
|
+
|
|
152
|
+
@out.puts "Uninstalled the service, the start link, the Claude Code hooks and the gem. Your pairing in #{config.dir} is kept."
|
|
153
|
+
end
|
|
154
|
+
|
|
123
155
|
def install_skill
|
|
124
156
|
SkillInstaller.new(home: @home, env: @env).install!.each { @out.puts "Installed the #{File.basename(_1)} skill to #{_1}." }
|
|
125
157
|
end
|
|
@@ -152,6 +184,14 @@ module Gitbroker
|
|
|
152
184
|
|
|
153
185
|
def hooks_installer = HooksInstaller.default(env: @env, home: @home)
|
|
154
186
|
|
|
187
|
+
# Hooks written by an older version name its versioned exe; point them at the current launcher.
|
|
188
|
+
def refresh_hooks
|
|
189
|
+
installer = hooks_installer
|
|
190
|
+
installer.install! if installer.installed?
|
|
191
|
+
rescue Error
|
|
192
|
+
nil
|
|
193
|
+
end
|
|
194
|
+
|
|
155
195
|
# Called by Claude Code on every hook event: silent, always exit 0.
|
|
156
196
|
def hook
|
|
157
197
|
Hook.new(event: @argv.shift, input: @input, socket_path: File.join(Config.default_dir(@env), "daemon.sock"), env: @env).run
|
data/lib/gitbroker/daemon.rb
CHANGED
|
@@ -38,7 +38,8 @@ module Gitbroker
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def initialize(scanner:, client_factory:, task_runner_factory:, out: $stdout, sleeper: ->(seconds) { sleep(seconds) },
|
|
41
|
-
clock: -> { Time.now }, spawner: ->(&block) { Thread.new(&block) }, relay: nil, process_scanner: nil, lifecycle: nil
|
|
41
|
+
clock: -> { Time.now }, spawner: ->(&block) { Thread.new(&block) }, relay: nil, process_scanner: nil, lifecycle: nil,
|
|
42
|
+
maintenance: Maintenance.new)
|
|
42
43
|
@scanner = scanner
|
|
43
44
|
@client_factory = client_factory
|
|
44
45
|
@task_runner = task_runner_factory.call(method(:report))
|
|
@@ -49,6 +50,7 @@ module Gitbroker
|
|
|
49
50
|
@relay = relay
|
|
50
51
|
@process_scanner = process_scanner
|
|
51
52
|
@lifecycle = lifecycle
|
|
53
|
+
@maintenance = maintenance
|
|
52
54
|
@repositories = []
|
|
53
55
|
@running = Set.new
|
|
54
56
|
@running_lock = Mutex.new
|
|
@@ -95,6 +97,8 @@ module Gitbroker
|
|
|
95
97
|
client.on_subscribed do
|
|
96
98
|
@backoff = 1
|
|
97
99
|
rescan(client)
|
|
100
|
+
client.perform("hello", "version" => VERSION, "platform" => Gitbroker.platform,
|
|
101
|
+
"service" => @lifecycle ? @lifecycle.service_installed? : false)
|
|
98
102
|
@out.puts "Connected. #{@repositories.size} repositories reported."
|
|
99
103
|
end
|
|
100
104
|
client.on_tick do
|
|
@@ -118,7 +122,8 @@ module Gitbroker
|
|
|
118
122
|
clean.each_slice(BATCH) { client.perform("observe", "events" => _1) }
|
|
119
123
|
end
|
|
120
124
|
|
|
121
|
-
# The website's Restart / Stop buttons (Device#control!).
|
|
125
|
+
# The website's Restart / Stop / Upgrade / Uninstall buttons (Device#control!). Only the action name is used, to pick
|
|
126
|
+
# one of these fixed behaviours; nothing from the message reaches a command.
|
|
122
127
|
def control(action)
|
|
123
128
|
return unless @lifecycle
|
|
124
129
|
|
|
@@ -134,9 +139,64 @@ module Gitbroker
|
|
|
134
139
|
|
|
135
140
|
@respawn = true
|
|
136
141
|
stop
|
|
142
|
+
when "upgrade" then upgrade
|
|
143
|
+
when "uninstall" then uninstall
|
|
137
144
|
end
|
|
138
145
|
end
|
|
139
146
|
|
|
147
|
+
# Never while a task runs: the restart would kill it. A deferred upgrade runs once, when the last task ends.
|
|
148
|
+
# Results name the control under "control", not "action": Action Cable's perform overwrites a data key "action" with
|
|
149
|
+
# the method name ("control_result").
|
|
150
|
+
def upgrade
|
|
151
|
+
return unless claim_upgrade
|
|
152
|
+
|
|
153
|
+
@out.puts "Upgrading (asked from GitBroker)."
|
|
154
|
+
@maintenance.upgrade!
|
|
155
|
+
report("control_result", "control" => "upgrade", "ok" => true, "error" => nil)
|
|
156
|
+
restart_into_new_version
|
|
157
|
+
rescue StandardError => e
|
|
158
|
+
@running_lock.synchronize { @upgrading = false } # the old companion keeps running; another request may retry
|
|
159
|
+
@out.puts "Upgrade failed: #{e.message}"
|
|
160
|
+
report("control_result", "control" => "upgrade", "ok" => false, "error" => e.message[0, 200])
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# True when the upgrade should run now. With tasks running it is remembered instead; one already under way wins.
|
|
164
|
+
def claim_upgrade
|
|
165
|
+
@running_lock.synchronize do
|
|
166
|
+
if @upgrading
|
|
167
|
+
false
|
|
168
|
+
elsif @running.any?
|
|
169
|
+
@pending_upgrade = true
|
|
170
|
+
false
|
|
171
|
+
else
|
|
172
|
+
@pending_upgrade = false
|
|
173
|
+
@upgrading = true
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# With a service the manager restarts us into the new gem (it may kill us mid-call); otherwise respawn after the loop.
|
|
179
|
+
def restart_into_new_version
|
|
180
|
+
@out.puts "Upgraded; restarting."
|
|
181
|
+
return if @lifecycle.restart_service
|
|
182
|
+
|
|
183
|
+
@respawn = true
|
|
184
|
+
stop
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Reports once the gem is gone (or failed to go), before removing the service: that step may end this process.
|
|
188
|
+
# Never respawns.
|
|
189
|
+
def uninstall
|
|
190
|
+
@running_lock.synchronize { @upgrading = true } # no task is claimed or started from here on
|
|
191
|
+
@out.puts "Uninstalling (asked from GitBroker)."
|
|
192
|
+
@maintenance.uninstall!(service: @lifecycle.service, url_handler: UrlHandlerInstaller.new) do |error|
|
|
193
|
+
report("control_result", "control" => "uninstall", "ok" => error.nil?, "error" => error && error[0, 200])
|
|
194
|
+
end
|
|
195
|
+
stop
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def upgrading? = @running_lock.synchronize { @upgrading }
|
|
199
|
+
|
|
140
200
|
def handle(client, message)
|
|
141
201
|
return control(message["action"]) if message["type"] == "control"
|
|
142
202
|
|
|
@@ -144,14 +204,14 @@ module Gitbroker
|
|
|
144
204
|
return unless id.is_a?(String)
|
|
145
205
|
|
|
146
206
|
case message["type"]
|
|
147
|
-
when "task_available" then client.perform("claim", "task_id" => id)
|
|
207
|
+
when "task_available" then client.perform("claim", "task_id" => id) unless upgrading? # left to another device or the sweep
|
|
148
208
|
when "cancel" then @task_runner.cancel(id)
|
|
149
209
|
when "task" then start_task(id, message)
|
|
150
210
|
end
|
|
151
211
|
end
|
|
152
212
|
|
|
153
213
|
def start_task(id, message)
|
|
154
|
-
return unless @running_lock.synchronize { @running.add?(id) }
|
|
214
|
+
return unless @running_lock.synchronize { !@upgrading && @running.add?(id) } # the upgrade's restart would kill it
|
|
155
215
|
|
|
156
216
|
repositories = @repositories
|
|
157
217
|
@spawner.call do
|
|
@@ -159,7 +219,8 @@ module Gitbroker
|
|
|
159
219
|
rescue StandardError => e
|
|
160
220
|
@out.puts "Task #{id} crashed: #{e.class}: #{e.message}"
|
|
161
221
|
ensure
|
|
162
|
-
@running_lock.synchronize { @running.delete(id) }
|
|
222
|
+
upgrade_now = @running_lock.synchronize { @running.delete(id) && @pending_upgrade && @running.empty? }
|
|
223
|
+
upgrade if upgrade_now
|
|
163
224
|
end
|
|
164
225
|
end
|
|
165
226
|
end
|
|
@@ -16,9 +16,10 @@ module Gitbroker
|
|
|
16
16
|
|
|
17
17
|
attr_reader :settings_path
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
# RubyGems' stable wrapper (ServiceInstaller.launcher): the hooks keep working after an upgrade's `gem cleanup`.
|
|
20
|
+
def self.default(env: ENV, home: Dir.home, exe: ServiceInstaller.launcher)
|
|
20
21
|
new(settings_path: File.join(env["CLAUDE_CONFIG_DIR"] || File.join(home, ".claude"), "settings.json"),
|
|
21
|
-
command_prefix: "#{Shellwords.escape(RbConfig.ruby)} #{Shellwords.escape(
|
|
22
|
+
command_prefix: "#{Shellwords.escape(RbConfig.ruby)} #{Shellwords.escape(exe)}")
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def initialize(settings_path:, command_prefix:, clock: -> { Time.now })
|
data/lib/gitbroker/lifecycle.rb
CHANGED
|
@@ -11,7 +11,7 @@ module Gitbroker
|
|
|
11
11
|
PID_FILE = "daemon.pid"
|
|
12
12
|
WAIT = 10
|
|
13
13
|
|
|
14
|
-
def initialize(dir:, home: Dir.home, service: ServiceInstaller.new(home:), runner: Runner.new, exe: ServiceInstaller
|
|
14
|
+
def initialize(dir:, home: Dir.home, service: ServiceInstaller.new(home:), runner: Runner.new, exe: ServiceInstaller.launcher,
|
|
15
15
|
ruby: RbConfig.ruby, spawner: nil, sleeper: ->(seconds) { sleep(seconds) }, pid: Process.pid)
|
|
16
16
|
@dir = dir
|
|
17
17
|
@home = home
|
|
@@ -24,6 +24,8 @@ module Gitbroker
|
|
|
24
24
|
@pid = pid
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
attr_reader :service
|
|
28
|
+
|
|
27
29
|
def pid_path = File.join(@dir, PID_FILE)
|
|
28
30
|
def service_installed? = @service.installed?
|
|
29
31
|
def log_path = File.join(@home, ".config", "gitbroker", "companion.log")
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbconfig"
|
|
4
|
+
|
|
5
|
+
module Gitbroker
|
|
6
|
+
# Upgrading and uninstalling the companion itself: fixed command sequences, nothing taken from the website's message.
|
|
7
|
+
class Maintenance
|
|
8
|
+
GEM_NAME = "gitbroker"
|
|
9
|
+
|
|
10
|
+
# `hooks`: the user's Claude Code hooks (HooksInstaller). `launcher`: what the service and the hooks run.
|
|
11
|
+
def initialize(runner: Runner.new, bindir: RbConfig::CONFIG["bindir"], hooks: HooksInstaller.default,
|
|
12
|
+
launcher: ServiceInstaller.launcher)
|
|
13
|
+
@runner = runner
|
|
14
|
+
@gem = File.join(bindir, "gem")
|
|
15
|
+
@hooks = hooks
|
|
16
|
+
@launcher = launcher
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Installs the newest release, then removes the old versions. The running process loads all its code first: cleanup
|
|
20
|
+
# deletes the files of the version it runs. Hooks written by an older version name its versioned exe, so they are
|
|
21
|
+
# rewritten to the launcher first. With no RubyGems wrapper the service runs this version's own exe, so nothing is
|
|
22
|
+
# cleaned up. Raises Error (with gem's output) when the install fails.
|
|
23
|
+
def upgrade!
|
|
24
|
+
@runner.run!(@gem, "install", GEM_NAME, "--no-document")
|
|
25
|
+
Gitbroker.eager_load!
|
|
26
|
+
refresh_hooks
|
|
27
|
+
@runner.success?(@gem, "cleanup", GEM_NAME) unless ServiceInstaller.pinned?(@launcher) # a failure is harmless
|
|
28
|
+
true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# True when the gem was uninstalled. The service goes last: stopping it ends the daemon when the daemon is the one
|
|
32
|
+
# uninstalling, so the gem's failure text (nil on success) is yielded before that step, for a report that gets out.
|
|
33
|
+
def uninstall!(service:, url_handler:)
|
|
34
|
+
Gitbroker.eager_load!
|
|
35
|
+
remove_hooks # they would run a missing file on every Claude Code event
|
|
36
|
+
error = uninstall_gem
|
|
37
|
+
url_handler.uninstall!
|
|
38
|
+
yield error if block_given?
|
|
39
|
+
service.uninstall!
|
|
40
|
+
error.nil?
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
# A broken settings file never blocks an upgrade or an uninstall.
|
|
46
|
+
def refresh_hooks
|
|
47
|
+
@hooks.install! if @hooks.installed?
|
|
48
|
+
rescue Error
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def remove_hooks
|
|
53
|
+
@hooks.uninstall!
|
|
54
|
+
rescue Error
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def uninstall_gem
|
|
59
|
+
@runner.run!(@gem, "uninstall", GEM_NAME, "--all", "--executables", "--ignore-dependencies")
|
|
60
|
+
nil
|
|
61
|
+
rescue Error => e
|
|
62
|
+
e.message
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/gitbroker/run_script.rb
CHANGED
|
@@ -10,9 +10,9 @@ module Gitbroker
|
|
|
10
10
|
# quoting never matters. When the agent exits, `gitbroker task-ended` tells GitBroker.
|
|
11
11
|
class RunScript
|
|
12
12
|
TASK_ID = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/
|
|
13
|
-
EXE = File.join(Gitbroker::ROOT, "exe", "gitbroker")
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
# RubyGems' stable wrapper (ServiceInstaller.launcher): a terminal task may outlive an upgrade's `gem cleanup`.
|
|
15
|
+
def initialize(state_dir:, ruby: RbConfig.ruby, exe: ServiceInstaller.launcher)
|
|
16
16
|
@state_dir = state_dir
|
|
17
17
|
@ruby = ruby
|
|
18
18
|
@exe = exe
|
|
@@ -9,7 +9,24 @@ module Gitbroker
|
|
|
9
9
|
LABEL = "broker.git.companion"
|
|
10
10
|
EXE = File.join(Gitbroker::ROOT, "exe", "gitbroker")
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
# What the service and the "Start companion" app run. RubyGems' own wrapper activates the newest installed gitbroker
|
|
13
|
+
# on every start, so a restart loads an upgraded gem and old versions can be removed. The gem's own exe is the
|
|
14
|
+
# fallback (and what a test passes), pinned to the version that ran `install-service`.
|
|
15
|
+
def self.launcher(bindir: launcher_bindirs)
|
|
16
|
+
Array(bindir).map { File.join(_1, "gitbroker") }.find { File.executable?(_1) } || EXE
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Where RubyGems puts the wrapper: the default bindir, the bindir of the install location this gem came from, and
|
|
20
|
+
# the per-user one (`gem install --user-install`).
|
|
21
|
+
def self.launcher_bindirs
|
|
22
|
+
spec = Gem.loaded_specs["gitbroker"]
|
|
23
|
+
[ Gem.bindir, (Gem.bindir(spec.base_dir) if spec), File.join(Gem.user_dir, "bin") ].compact.uniq
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# True when the launcher is this version's own exe, which `gem cleanup` deletes once a newer version is installed.
|
|
27
|
+
def self.pinned?(launcher = self.launcher) = launcher == EXE
|
|
28
|
+
|
|
29
|
+
def initialize(platform: Gitbroker.platform, home: Dir.home, ruby: RbConfig.ruby, exe: ServiceInstaller.launcher, env: ENV, runner: Runner.new,
|
|
13
30
|
uid: Process.uid)
|
|
14
31
|
@platform = platform
|
|
15
32
|
@home = home
|
|
@@ -60,6 +77,23 @@ module Gitbroker
|
|
|
60
77
|
end
|
|
61
78
|
end
|
|
62
79
|
|
|
80
|
+
# Stops and removes the background service. False when none was installed. The file goes first on macOS: booting
|
|
81
|
+
# out the job ends the daemon when it is the one uninstalling. systemd needs the unit file to disable it, so a
|
|
82
|
+
# daemon uninstalling itself may leave a disabled, stopped unit behind.
|
|
83
|
+
def uninstall!
|
|
84
|
+
return false unless installed?
|
|
85
|
+
|
|
86
|
+
if @platform == "macos"
|
|
87
|
+
File.delete(launchd_path)
|
|
88
|
+
@runner.success?("launchctl", "bootout", target)
|
|
89
|
+
else
|
|
90
|
+
@runner.success?("systemctl", "--user", "disable", "--now", "gitbroker.service")
|
|
91
|
+
File.delete(systemd_path)
|
|
92
|
+
@runner.success?("systemctl", "--user", "daemon-reload")
|
|
93
|
+
end
|
|
94
|
+
true
|
|
95
|
+
end
|
|
96
|
+
|
|
63
97
|
def plist
|
|
64
98
|
arguments = [ @ruby, @exe, "start" ].map { " <string>#{xml(_1)}</string>" }.join("\n")
|
|
65
99
|
environment = { "PATH" => @env["PATH"].to_s, "SHELL" => @env["SHELL"] || "/bin/zsh", "LANG" => @env["LANG"] || "en_US.UTF-8" }
|
|
@@ -13,7 +13,7 @@ module Gitbroker
|
|
|
13
13
|
APP_NAME = "GitBroker Companion"
|
|
14
14
|
LSREGISTER = "/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister"
|
|
15
15
|
|
|
16
|
-
def initialize(platform: Gitbroker.platform, home: Dir.home, ruby: RbConfig.ruby, exe: ServiceInstaller
|
|
16
|
+
def initialize(platform: Gitbroker.platform, home: Dir.home, ruby: RbConfig.ruby, exe: ServiceInstaller.launcher, runner: Runner.new)
|
|
17
17
|
@platform = platform
|
|
18
18
|
@home = home
|
|
19
19
|
@ruby = ruby
|
|
@@ -23,10 +23,23 @@ module Gitbroker
|
|
|
23
23
|
|
|
24
24
|
def install! = @platform == "macos" ? install_app! : install_desktop_entry!
|
|
25
25
|
|
|
26
|
+
# Removes the app bundle (unregistered from LaunchServices first) or the .desktop entry. False when absent.
|
|
27
|
+
def uninstall!
|
|
28
|
+
path = @platform == "macos" ? app_path : desktop_entry_path
|
|
29
|
+
return false unless File.exist?(path)
|
|
30
|
+
|
|
31
|
+
@runner.success?(LSREGISTER, "-u", path) if @platform == "macos"
|
|
32
|
+
FileUtils.rm_rf(path)
|
|
33
|
+
true
|
|
34
|
+
end
|
|
35
|
+
|
|
26
36
|
private
|
|
27
37
|
|
|
38
|
+
def app_path = File.join(@home, "Applications", "#{APP_NAME}.app")
|
|
39
|
+
def desktop_entry_path = File.join(@home, ".local", "share", "applications", "gitbroker.desktop")
|
|
40
|
+
|
|
28
41
|
def install_app!
|
|
29
|
-
app =
|
|
42
|
+
app = app_path
|
|
30
43
|
script = File.join(app, "Contents", "MacOS", "gitbroker-launch")
|
|
31
44
|
FileUtils.mkdir_p(File.dirname(script))
|
|
32
45
|
File.write(File.join(app, "Contents", "Info.plist"), info_plist)
|
|
@@ -69,7 +82,7 @@ module Gitbroker
|
|
|
69
82
|
end
|
|
70
83
|
|
|
71
84
|
def install_desktop_entry!
|
|
72
|
-
path =
|
|
85
|
+
path = desktop_entry_path
|
|
73
86
|
FileUtils.mkdir_p(File.dirname(path))
|
|
74
87
|
File.write(path, <<~ENTRY)
|
|
75
88
|
[Desktop Entry]
|
data/lib/gitbroker/version.rb
CHANGED
data/lib/gitbroker.rb
CHANGED
|
@@ -10,6 +10,9 @@ module Gitbroker
|
|
|
10
10
|
|
|
11
11
|
def self.platform = RUBY_PLATFORM.include?("darwin") ? "macos" : "linux"
|
|
12
12
|
|
|
13
|
+
# Loads every autoloaded constant. Called before `gem cleanup` removes the files of the running version.
|
|
14
|
+
def self.eager_load! = constants.each { const_get(_1) }
|
|
15
|
+
|
|
13
16
|
autoload :Agents, File.expand_path("gitbroker/agents", __dir__)
|
|
14
17
|
autoload :Api, File.expand_path("gitbroker/api", __dir__)
|
|
15
18
|
autoload :CableClient, File.expand_path("gitbroker/cable_client", __dir__)
|
|
@@ -23,6 +26,7 @@ module Gitbroker
|
|
|
23
26
|
autoload :HooksInstaller, File.expand_path("gitbroker/hooks_installer", __dir__)
|
|
24
27
|
autoload :Lifecycle, File.expand_path("gitbroker/lifecycle", __dir__)
|
|
25
28
|
autoload :Launcher, File.expand_path("gitbroker/launcher", __dir__)
|
|
29
|
+
autoload :Maintenance, File.expand_path("gitbroker/maintenance", __dir__)
|
|
26
30
|
autoload :LocalRelay, File.expand_path("gitbroker/local_relay", __dir__)
|
|
27
31
|
autoload :Login, File.expand_path("gitbroker/login", __dir__)
|
|
28
32
|
autoload :Neutralizer, File.expand_path("gitbroker/neutralizer", __dir__)
|
metadata
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: gitbroker
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
|
-
-
|
|
7
|
+
- Yuri Sidorov (@newstler)
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
@@ -52,6 +52,7 @@ files:
|
|
|
52
52
|
- lib/gitbroker/lifecycle.rb
|
|
53
53
|
- lib/gitbroker/local_relay.rb
|
|
54
54
|
- lib/gitbroker/login.rb
|
|
55
|
+
- lib/gitbroker/maintenance.rb
|
|
55
56
|
- lib/gitbroker/neutralizer.rb
|
|
56
57
|
- lib/gitbroker/process_scanner.rb
|
|
57
58
|
- lib/gitbroker/repo_scanner.rb
|