2026-05-16 08:49:12 +00:00
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
# Tx OS — redeploy after `git pull` on the Mac (or any Docker host).
|
|
|
|
|
#
|
|
|
|
|
# Why this exists:
|
|
|
|
|
# The SPA is baked into the `web` (nginx) image at build time, and the
|
|
|
|
|
# API binary is baked into the `api` image. `docker compose up -d` on
|
|
|
|
|
# its own re-uses the existing images, so a plain `git pull` will NOT
|
|
|
|
|
# pick up new front-end assets (e.g. service tile images), code
|
|
|
|
|
# changes, or DB-seed updates.
|
|
|
|
|
#
|
|
|
|
|
# What it does:
|
|
|
|
|
# 1. git pull — fast-forward the working tree (skipped with --no-pull)
|
|
|
|
|
# 2. docker compose build api web
|
|
|
|
|
# — rebuild both images so source + static assets are fresh
|
|
|
|
|
# 3. docker compose run --rm migrate
|
2026-05-16 08:50:33 +00:00
|
|
|
# — apply Drizzle schema (migrate.sh also runs the seed,
|
|
|
|
|
# but tolerates seed failures — see step 4)
|
|
|
|
|
# 4. docker compose run --rm --entrypoint "" --no-deps migrate \
|
|
|
|
|
# pnpm --filter @workspace/scripts run seed
|
|
|
|
|
# — explicit seed pass that fails hard if the seed
|
|
|
|
|
# errors. Catches new-app / renamed-app regressions
|
|
|
|
|
# that migrate.sh would otherwise swallow.
|
|
|
|
|
# 5. docker compose up -d
|
2026-05-16 08:49:12 +00:00
|
|
|
# — restart api + web with the new images
|
|
|
|
|
#
|
|
|
|
|
# Safe to re-run. Each step is idempotent.
|
|
|
|
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
PULL=1
|
|
|
|
|
for arg in "$@"; do
|
|
|
|
|
case "$arg" in
|
|
|
|
|
--no-pull) PULL=0 ;;
|
|
|
|
|
-h|--help)
|
2026-05-16 08:50:33 +00:00
|
|
|
sed -n '2,27p' "$0"
|
2026-05-16 08:49:12 +00:00
|
|
|
exit 0
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
echo "Unknown argument: $arg" >&2
|
|
|
|
|
echo "Usage: $0 [--no-pull]" >&2
|
|
|
|
|
exit 2
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Resolve the repo root so the script works no matter where it's invoked from.
|
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
|
|
|
cd "$REPO_ROOT"
|
|
|
|
|
|
|
|
|
|
if [ ! -f docker-compose.yml ]; then
|
|
|
|
|
echo "[redeploy] error: docker-compose.yml not found in $REPO_ROOT" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
|
|
|
echo "[redeploy] error: docker CLI not found in PATH" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# Pick `docker compose` (v2) and fall back to `docker-compose` (v1) so this
|
|
|
|
|
# works on older hosts.
|
|
|
|
|
if docker compose version >/dev/null 2>&1; then
|
|
|
|
|
DC=(docker compose)
|
|
|
|
|
elif command -v docker-compose >/dev/null 2>&1; then
|
|
|
|
|
DC=(docker-compose)
|
|
|
|
|
else
|
|
|
|
|
echo "[redeploy] error: neither 'docker compose' nor 'docker-compose' is available" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
step() { echo; echo "==> $*"; }
|
|
|
|
|
|
|
|
|
|
if [ "$PULL" -eq 1 ]; then
|
2026-05-18 13:52:42 +00:00
|
|
|
step "1/6 git pull"
|
2026-05-16 08:49:12 +00:00
|
|
|
git pull --ff-only
|
|
|
|
|
else
|
2026-05-18 13:52:42 +00:00
|
|
|
step "1/6 git pull (skipped via --no-pull)"
|
2026-05-16 08:49:12 +00:00
|
|
|
fi
|
|
|
|
|
|
2026-05-18 13:52:42 +00:00
|
|
|
step "2/6 docker compose build api web (rebuilds SPA + API images)"
|
2026-05-18 10:39:17 +00:00
|
|
|
# Stamp the API image with the current git SHA + UTC build time so the
|
|
|
|
|
# admin "System Updates" panel shows visible proof that the new image
|
|
|
|
|
# is actually running (instead of the old, hand-edited version.json).
|
|
|
|
|
# Both are read inside docker/api-server.Dockerfile as ARGs.
|
|
|
|
|
export GIT_SHA="$(git rev-parse --short HEAD 2>/dev/null || echo unknown)"
|
|
|
|
|
export BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
|
|
|
echo "[redeploy] GIT_SHA=$GIT_SHA BUILD_DATE=$BUILD_DATE"
|
2026-05-16 08:49:12 +00:00
|
|
|
"${DC[@]}" build api web
|
|
|
|
|
|
2026-05-18 13:52:42 +00:00
|
|
|
step "3/6 docker compose run --rm migrate (schema push + tolerant seed)"
|
2026-05-16 08:49:12 +00:00
|
|
|
"${DC[@]}" run --rm migrate
|
|
|
|
|
|
2026-05-18 13:52:42 +00:00
|
|
|
step "4/6 explicit seed pass (fails hard on seed regressions)"
|
2026-05-16 08:50:33 +00:00
|
|
|
"${DC[@]}" run --rm --no-deps --entrypoint "" migrate \
|
|
|
|
|
pnpm --filter @workspace/scripts run seed
|
|
|
|
|
|
2026-05-18 13:52:42 +00:00
|
|
|
step "5/6 one-shot data migrations (idempotent)"
|
|
|
|
|
# #603: bump seeded app tile colours on existing installs. The seed
|
|
|
|
|
# step above uses onConflictDoNothing for `slug`, so already-seeded
|
|
|
|
|
# rows keep their old colours forever without this. The script only
|
|
|
|
|
# touches rows that still hold the OLD default — admin customisations
|
|
|
|
|
# from the Apps editor are left alone.
|
|
|
|
|
"${DC[@]}" run --rm --no-deps --entrypoint "" migrate \
|
|
|
|
|
pnpm --filter @workspace/scripts run update-app-colors
|
|
|
|
|
|
|
|
|
|
step "6/6 docker compose up -d (restart with the new images)"
|
2026-05-16 08:49:12 +00:00
|
|
|
"${DC[@]}" up -d
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo "[redeploy] done. Verify in a browser; hard-refresh once to bust the SPA cache."
|