Class: Bard::Api::App
- Inherits:
-
Object
- Object
- Bard::Api::App
- Defined in:
- lib/bard/api/app.rb
Constant Summary collapse
- DEPLOY_LOCK =
"tmp/bard-deploy.lock"- DEPLOYED_SHA =
"tmp/bard-deployed.sha"- FAILED_SHA =
"tmp/bard-deploy-failed.sha"- DEPLOY_LOG =
"log/bard-deploy.log"- FAILED_COOLDOWN =
How long a failed sha short-circuits re-deploy: long enough to end the caller's poll loop without a re-spawn, short enough that a fresh
bard deployrun can retry the same sha. 60- DEPLOY_COMMAND =
Record the deployed sha only after bin/setup fully succeeds — a bare
git pulladvances HEAD before the bundle/migrate/restart run, so HEAD alone would read as "done" too early. On any failure, stamp the attempted sha so we don't re-spawn (and restart Puma) on every poll; the marker is written inside the flock, so it's on disk before the lock releases. "flock -n #{DEPLOY_LOCK} -c '" \ "rm -f #{FAILED_SHA}; " \ "git pull --ff-only origin master && bin/setup && git rev-parse HEAD > #{DEPLOYED_SHA} " \ "|| git rev-parse origin/master > #{FAILED_SHA}'"
Class Attribute Summary collapse
-
.backup_runner ⇒ Object
Runs the backup task out-of-band so the request returns immediately.
-
.deploy_runner ⇒ Object
bin/setup restarts Puma via
procsd restart→systemctl --user restart, which SIGKILLs the app unit's whole cgroup.
Class Method Summary collapse
-
.spawn_detached_deploy(command) ⇒ Object
with_unbundled_env is essential: this worker runs under the app's bundle (RUBYOPT=-rbundler/setup, BUNDLE_GEMFILE).
Instance Method Summary collapse
Class Attribute Details
.backup_runner ⇒ Object
Runs the backup task out-of-band so the request returns immediately. Override to wire backups into a project's own job queue.
17 18 19 |
# File 'lib/bard/api/app.rb', line 17 def backup_runner @backup_runner ||= ->(task) { Thread.new { task.call } } end |
.deploy_runner ⇒ Object
bin/setup restarts Puma via procsd restart → systemctl --user restart, which
SIGKILLs the app unit's whole cgroup. So the deploy can't run in this worker (or any
plain child of it) — it'd be killed mid-deploy. We run it in its own systemd --user
scope: a separate cgroup that survives the restart.
25 26 27 |
# File 'lib/bard/api/app.rb', line 25 def deploy_runner @deploy_runner ||= method(:spawn_detached_deploy) end |
Class Method Details
.spawn_detached_deploy(command) ⇒ Object
with_unbundled_env is essential: this worker runs under the app's bundle (RUBYOPT=-rbundler/setup, BUNDLE_GEMFILE). Inherited into the deploy, that makes every ruby command — bundle install included — crash at startup the moment git pull lands a lockfile with not-yet-installed gems. The deploy must start from a clean env.
33 34 35 36 37 38 39 40 41 |
# File 'lib/bard/api/app.rb', line 33 def spawn_detached_deploy(command) Bundler.with_unbundled_env do pid = Process.spawn( "systemd-run", "--user", "--scope", "--collect", "--quiet", "bash", "-lc", command, :in => "/dev/null", %i[out err] => ["log/bard-deploy.log", "a"], ) Process.detach(pid) end end |
Instance Method Details
#call(env) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/bard/api/app.rb', line 62 def call(env) request = Rack::Request.new(env) method = request.request_method path = request.path_info case [method, path] when ["GET", "/health"] health(request) when ["POST", "/backups"] create_backup(request) when ["GET", "/backups/latest"] latest_backup(request) when ["GET", "/config"] config(request) when ["POST", "/deploy"] create_deploy(request) else not_found end rescue => e json_response(500, { error: e. }) end |