Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

auto-update.sh

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

auto-update.shBlame126 lines · 3 contributors
f635e2fClaude1#!/usr/bin/env bash
2#
3# Fast auto-deploy for the standalone box. Installed as a ~60s systemd timer by
4# scripts/standalone-deploy.sh. Polls the deploy branch; when new commits land,
5# it pulls, rebuilds, and runs migrations. Push -> live in ~1-2 min, hands-off.
6#
7# Exits immediately (cheap) when there is nothing new, so a tight interval is fine.
3b77289ccantynz-alt8#
9# Rollback (2026-07-21): a bad deploy used to leave the box with the OLD
10# container already destroyed and the NEW one unhealthy -- a failed health
11# check just exited 1 with nothing serving traffic until a human noticed and
12# fixed it by hand. The previous image is now tagged before every build, and
13# a failed health check re-deploys it automatically instead of leaving a
14# broken container live. A SHA that fails is remembered (FAILED_MARKER) so a
15# persistently broken commit doesn't retry-and-fail every 60s forever -- it
16# waits for a newer commit (or a human clearing the marker) instead.
f635e2fClaude17set -euo pipefail
18
19REPO_DIR="/opt/gluecron"
9de2807Claude20BRANCH="main"
f635e2fClaude21COMPOSE="docker compose -f docker-compose.standalone.yml"
3b77289ccantynz-alt22IMAGE="gluecron-gluecron"
23FAILED_MARKER="$REPO_DIR/.last-failed-deploy-sha"
f635e2fClaude24
25cd "$REPO_DIR"
26git fetch origin "$BRANCH" --quiet
27
28local_sha=$(git rev-parse HEAD)
29remote_sha=$(git rev-parse "origin/$BRANCH")
30[ "$local_sha" = "$remote_sha" ] && exit 0
31
3b77289ccantynz-alt32if [ -f "$FAILED_MARKER" ] && [ "$(cat "$FAILED_MARKER")" = "$remote_sha" ]; then
33 # Already tried and rolled back from this exact commit -- don't loop.
34 exit 0
35fi
36
f635e2fClaude37echo "$(date -Is) deploying $local_sha -> $remote_sha"
3b77289ccantynz-alt38
39# Preserve the last-known-good image BEFORE touching anything, so a bad
40# deploy can roll back to exactly what was running a moment ago. `|| true`:
41# the very first deploy on a fresh box has no prior image to tag.
42docker tag "$IMAGE:latest" "$IMAGE:last-good" 2>/dev/null || true
43prev_sha="$local_sha"
44
f635e2fClaude45git reset --hard "origin/$BRANCH" # untracked .env / backups are preserved
46
30e70f3ccanty labs47# NOTE: `|| true` is deliberate. On this Coolify co-tenant box the compose
48# also defines a `caddy` service that tries to bind host :80/:443, which
49# Coolify's proxy already owns — so `up` reports a non-zero exit for caddy
50# even though the app (gluecron) started fine. Under `set -e` that would abort
51# the deploy BEFORE the coolify reattach + health gate below. We tolerate the
52# partial failure here and instead gate on the app's OWN health further down.
53$COMPOSE up -d --build || echo "$(date -Is) compose up returned non-zero (likely the co-tenant caddy port conflict) — continuing to app health gate"
5bac517ccanty labs54
55# Co-tenant ingress reattach (Coolify boxes only).
56# `docker compose up --build` recreates the gluecron container, which DROPS
57# any network attachment not declared in docker-compose.standalone.yml. On
58# this box the public ingress is Coolify's Traefik ("coolify-proxy"), which
59# reaches the app over the external "coolify" network via the file route
60# /traefik/dynamic/gluecron.yaml in the coolify-proxy container. Without this
61# reattach, every deploy 502s the site until someone reconnects by hand.
30e70f3ccanty labs62# Idempotent: a no-op if already attached; skipped entirely on a dedicated VPS
3b77289ccantynz-alt63# that has no "coolify" network. Traefik's file provider watches with fsnotify
64# (--providers.file.watch=true), so it notices the container is reachable
65# again within a second or two of the reconnect — no manual nudge needed.
5bac517ccanty labs66if docker network inspect coolify >/dev/null 2>&1; then
67 docker network connect coolify gluecron-gluecron-1 2>/dev/null \
68 && echo "$(date -Is) reattached gluecron to coolify network" \
69 || echo "$(date -Is) coolify network already attached (ok)"
70fi
71
f635e2fClaude72$COMPOSE exec -T gluecron bun run db:migrate || true
73docker image prune -f >/dev/null 2>&1 || true
74
30e70f3ccanty labs75# App health gate — the real success signal (not caddy). Poll the container's
3b77289ccantynz-alt76# own /healthz; if the app itself never comes up, roll back instead of
77# leaving a broken deploy live.
30e70f3ccanty labs78healthy=0
79for _ in $(seq 1 20); do
80 if docker exec gluecron-gluecron-1 wget -qO- --timeout=4 http://localhost:3000/healthz >/dev/null 2>&1; then
81 healthy=1; break
82 fi
83 sleep 3
84done
3b77289ccantynz-alt85
86if [ "$healthy" = "1" ]; then
87 rm -f "$FAILED_MARKER"
88 echo "$(date -Is) deploy complete: $remote_sha (app healthy)"
89 exit 0
90fi
91
92echo "$(date -Is) DEPLOY FAILED: app /healthz never came up for $remote_sha" >&2
93docker logs --tail 80 gluecron-gluecron-1 >&2 || true
94
95if ! docker image inspect "$IMAGE:last-good" >/dev/null 2>&1; then
96 echo "$(date -Is) no last-good image to roll back to (first deploy on this box?) — human intervention required" >&2
97 echo "$remote_sha" > "$FAILED_MARKER"
98 exit 1
99fi
100
101echo "$(date -Is) rolling back to $prev_sha" >&2
102git reset --hard "$prev_sha"
103docker tag "$IMAGE:last-good" "$IMAGE:latest"
104$COMPOSE up -d --no-build || echo "$(date -Is) compose up (rollback) returned non-zero (likely the co-tenant caddy port conflict) — continuing to health gate"
105
106if docker network inspect coolify >/dev/null 2>&1; then
107 docker network connect coolify gluecron-gluecron-1 2>/dev/null || true
108fi
109
110rb_healthy=0
111for _ in $(seq 1 20); do
112 if docker exec gluecron-gluecron-1 wget -qO- --timeout=4 http://localhost:3000/healthz >/dev/null 2>&1; then
113 rb_healthy=1; break
114 fi
115 sleep 3
116done
117
118echo "$remote_sha" > "$FAILED_MARKER"
119
120if [ "$rb_healthy" = "1" ]; then
121 echo "$(date -Is) rollback to $prev_sha succeeded — app healthy again. $remote_sha will not be retried automatically; push a fix or clear $FAILED_MARKER to try again." >&2
30e70f3ccanty labs122 exit 1
123fi
124
3b77289ccantynz-alt125echo "$(date -Is) ROLLBACK ALSO FAILED — human intervention required NOW (site may be down)" >&2
126exit 1