bard-api 0.6.0 → 0.6.2
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/bard/api/app.rb +59 -7
- data/lib/bard/api/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4d4da564c789355e60c447afcd4509dab027ae176561da244b95b31787df786b
|
|
4
|
+
data.tar.gz: b6307c6ee310ce199ee18b8760e846272cf7c66276cb3d5d5a64ecf60d580f5e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d0c3651eaa2dd8ad9ba27693f942d7b9f29acbc72d9104d012d24bfa3b8179ea13531c000bd7c7d9b883a9b70a9d99fcda5235ddeb03773c7b3bd6c57ba5a496
|
|
7
|
+
data.tar.gz: c8e8658f60a5327f7e6770c457501c2b73769b17842ad73830c7ce81159736be48542fda169bf6bd500db6698e5088af772d52cf0c4f6c15c88b722d7ce7170b
|
data/lib/bard/api/app.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "rack"
|
|
4
4
|
require "json"
|
|
5
|
+
require "bundler"
|
|
5
6
|
require_relative "auth"
|
|
6
7
|
require "bard/backup"
|
|
7
8
|
|
|
@@ -25,17 +26,38 @@ module Bard
|
|
|
25
26
|
@deploy_runner ||= method(:spawn_detached_deploy)
|
|
26
27
|
end
|
|
27
28
|
|
|
29
|
+
# with_unbundled_env is essential: this worker runs under the app's bundle
|
|
30
|
+
# (RUBYOPT=-rbundler/setup, BUNDLE_GEMFILE). Inherited into the deploy, that makes every
|
|
31
|
+
# ruby command — bundle install included — crash at startup the moment git pull lands a
|
|
32
|
+
# lockfile with not-yet-installed gems. The deploy must start from a clean env.
|
|
28
33
|
def spawn_detached_deploy(command)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
Bundler.with_unbundled_env do
|
|
35
|
+
pid = Process.spawn(
|
|
36
|
+
"systemd-run", "--user", "--scope", "--collect", "--quiet", "bash", "-lc", command,
|
|
37
|
+
:in => "/dev/null", %i[out err] => ["log/bard-deploy.log", "a"],
|
|
38
|
+
)
|
|
39
|
+
Process.detach(pid)
|
|
40
|
+
end
|
|
34
41
|
end
|
|
35
42
|
end
|
|
36
43
|
|
|
37
44
|
DEPLOY_LOCK = "tmp/bard-deploy.lock"
|
|
38
|
-
|
|
45
|
+
DEPLOYED_SHA = "tmp/bard-deployed.sha"
|
|
46
|
+
FAILED_SHA = "tmp/bard-deploy-failed.sha"
|
|
47
|
+
DEPLOY_LOG = "log/bard-deploy.log"
|
|
48
|
+
# How long a failed sha short-circuits re-deploy: long enough to end the caller's poll loop
|
|
49
|
+
# without a re-spawn, short enough that a fresh `bard deploy` run can retry the same sha.
|
|
50
|
+
FAILED_COOLDOWN = 60
|
|
51
|
+
|
|
52
|
+
# Record the deployed sha only after bin/setup fully succeeds — a bare `git pull` advances
|
|
53
|
+
# HEAD before the bundle/migrate/restart run, so HEAD alone would read as "done" too early.
|
|
54
|
+
# On any failure, stamp the attempted sha so we don't re-spawn (and restart Puma) on every
|
|
55
|
+
# poll; the marker is written inside the flock, so it's on disk before the lock releases.
|
|
56
|
+
DEPLOY_COMMAND =
|
|
57
|
+
"flock -n #{DEPLOY_LOCK} -c '" \
|
|
58
|
+
"rm -f #{FAILED_SHA}; " \
|
|
59
|
+
"git pull --ff-only origin master && bin/setup && git rev-parse HEAD > #{DEPLOYED_SHA} " \
|
|
60
|
+
"|| git rev-parse origin/master > #{FAILED_SHA}'"
|
|
39
61
|
|
|
40
62
|
def call(env)
|
|
41
63
|
request = Rack::Request.new(env)
|
|
@@ -126,12 +148,42 @@ module Bard
|
|
|
126
148
|
return json_response(200, { status: "noop", sha: sha }) if target == sha
|
|
127
149
|
return json_response(409, { status: "deploying", sha: sha }) if deploy_in_progress?
|
|
128
150
|
|
|
151
|
+
# A prior attempt at this exact sha just failed. Report it instead of re-spawning the
|
|
152
|
+
# deploy on every poll (which would restart Puma every few seconds and never converge).
|
|
153
|
+
if target == failed_sha && recently_failed?
|
|
154
|
+
return json_response(500, {
|
|
155
|
+
status: "failed",
|
|
156
|
+
sha: sha,
|
|
157
|
+
error: "deploy failed on the server; see #{DEPLOY_LOG}",
|
|
158
|
+
log: deploy_log_tail,
|
|
159
|
+
})
|
|
160
|
+
end
|
|
161
|
+
|
|
129
162
|
self.class.deploy_runner.call(DEPLOY_COMMAND)
|
|
130
163
|
json_response(202, { status: "deploying", sha: sha })
|
|
131
164
|
end
|
|
132
165
|
|
|
166
|
+
# The last FULLY deployed sha (bin/setup succeeded), not merely where git HEAD points.
|
|
167
|
+
# Absent marker => nothing has completed yet, so it can never match the requested sha and
|
|
168
|
+
# the caller keeps polling until bin/setup writes it — or times out on a real failure.
|
|
133
169
|
def current_sha
|
|
134
|
-
|
|
170
|
+
return "" unless File.exist?(DEPLOYED_SHA)
|
|
171
|
+
File.read(DEPLOYED_SHA).chomp
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# The sha whose deploy most recently failed (bin/setup or the git pull errored out).
|
|
175
|
+
def failed_sha
|
|
176
|
+
return "" unless File.exist?(FAILED_SHA)
|
|
177
|
+
File.read(FAILED_SHA).chomp
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def recently_failed?
|
|
181
|
+
File.exist?(FAILED_SHA) && (Time.now - File.mtime(FAILED_SHA)) < FAILED_COOLDOWN
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def deploy_log_tail(lines = 30)
|
|
185
|
+
return "" unless File.exist?(DEPLOY_LOG)
|
|
186
|
+
File.readlines(DEPLOY_LOG).last(lines).join
|
|
135
187
|
end
|
|
136
188
|
|
|
137
189
|
def deploy_in_progress?
|
data/lib/bard/api/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bard-api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Micah Geisel
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-07-
|
|
10
|
+
date: 2026-07-10 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: jwt
|