- Production now runs from ~/freezmo-prod/current (release symlink) as a standalone server on :3207; releases are exported by scripts/deploy.sh, built and e2e-verified on a temp port, switched atomically, health- checked via the new /api/healthz (commit + build time) and rolled back automatically on failure. Last 5 releases kept; scripts/rollback.sh for manual rollback. Shared env + data live in ~/freezmo-prod/shared/ and are symlinked into every release (existing sync/push data migrated and verified identical). - Dev runs from ~/freezmo via freezmo-dev.service: next dev on :3217 (LAN only), its own build folder (.next-dev via NEXT_DIST_DIR), its own data (~/freezmo/.freezmo-dev), DEV badge, dev favicon/theme colour, "Freezmo Dev" PWA identity and never the production service worker. - /api/healthz + the Settings Details version row (short commit + build time). output: standalone in next.config (next start still works). - AGENTS.md documents the workflow rules: only dev updates during a task, temporary dirs/ports for test builds, and deploy-at-the-end as the only production path. README gains an Environments and deploy section. - Fix found by the rehearsal: lib/dev-mode.ts must not be a client module (a server component importing a constant from a "use client" module gets a truthy proxy, which baked dev metadata into production builds). - SW -> freezmo-v34.
59 lines
2 KiB
Bash
Executable file
59 lines
2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
#
|
|
# Roll production back to the previous release (or to a named one).
|
|
#
|
|
# scripts/rollback.sh # previous release
|
|
# scripts/rollback.sh 20261011-193000-5ca0de9
|
|
#
|
|
# It flips the `current` symlink, restarts the service and health-checks the
|
|
# result, so a manual rollback is the same atomic operation the deploy script
|
|
# performs on failure.
|
|
|
|
set -euo pipefail
|
|
|
|
PROD="${FREEZMO_PROD:-$HOME/freezmo-prod}"
|
|
SERVICE="${FREEZMO_SERVICE:-freezmo}"
|
|
PORT="${FREEZMO_PORT:-3207}"
|
|
HEALTH_TRIES="${FREEZMO_HEALTH_TRIES:-45}"
|
|
|
|
say() { printf '\033[1m[rollback]\033[0m %s\n' "$*"; }
|
|
fail() { printf '\033[1;31m[rollback]\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
LIVE="$(readlink -f "$PROD/current" 2>/dev/null || true)"
|
|
[[ -n "$LIVE" ]] || fail "no current release at $PROD/current"
|
|
|
|
if [[ -n "${1:-}" ]]; then
|
|
TARGET="$(readlink -f "$PROD/releases/$1" 2>/dev/null || true)"
|
|
[[ -n "$TARGET" && -d "$TARGET" ]] || fail "release not found: $1"
|
|
else
|
|
TARGET=""
|
|
for dir in "$PROD"/releases/*/; do
|
|
resolved="$(readlink -f "$dir")"
|
|
if [[ "$resolved" != "$LIVE" ]]; then
|
|
TARGET="$resolved"
|
|
break
|
|
fi
|
|
done
|
|
[[ -n "$TARGET" ]] || fail "no previous release to roll back to"
|
|
fi
|
|
|
|
say "current: $(basename "$LIVE")"
|
|
say "switching to: $(basename "$TARGET")"
|
|
ln -sfn "$TARGET" "$PROD/current.tmp"
|
|
mv -T "$PROD/current.tmp" "$PROD/current"
|
|
|
|
systemctl --user restart "$SERVICE"
|
|
|
|
EXPECTED="$(grep -o '"commit":"[^"]*"' "$TARGET/server/release.json" 2>/dev/null | cut -d'"' -f4 || true)"
|
|
[[ -n "$EXPECTED" ]] || EXPECTED="$(basename "$TARGET" | sed 's/.*-//')"
|
|
for ((i = 0; i < HEALTH_TRIES; i++)); do
|
|
body="$(curl -fsS --max-time 2 "http://127.0.0.1:$PORT/api/healthz" 2>/dev/null || true)"
|
|
commit="$(printf '%s' "$body" | grep -o '"commit":"[^"]*"' | cut -d'"' -f4 || true)"
|
|
if [[ -n "$commit" && "${commit:0:7}" == "${EXPECTED:0:7}" ]]; then
|
|
say "healthy — production is on $(basename "$TARGET")"
|
|
exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
fail "health check did not confirm $(basename "$TARGET") — check journalctl --user -u $SERVICE"
|