mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-08 04:08:24 +00:00
Phase 7: Cosmos deployment
- Dockerfile with a check target (typecheck and tests) and one image per service on a pinned Node 22.20 Alpine base, each with only its own production dependencies and a health check. - deploy/compose.yaml: gk-content, gk-admin and gk-site on homelab_network with no host ports, read-only root filesystems, no capabilities, the vault's Site folder mounted into the content service, and backup.dump=sqlite on it. - deploy/deploy.sh builds and checks the commit, restarts the stack, and goes back to the last good tag if it doesn't come up healthy; rollback.sh swaps tags. .gitea/workflows/deploy.yml runs it on a host runner on Cosmos. - docs/deploy.md runbook (settings, runner, mirror, NPM hosts, Turnstile, Healthchecks, backups, cutover) and the stack's inventory note in vault-kit. - Site: trusts X-Forwarded-Host/Proto for gregkilmer.com hosts so form posts pass Astro's origin check behind the proxy; rate limits read CF-Connecting-IP; site URL set for absolute links. - Content service: the held-change webhook carries a Discord-ready message; .env.example lists the core settings. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
This commit is contained in:
Executable
+61
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds the three images for the checked-out commit, runs the checks, and
|
||||
# restarts the stack on it. If the new containers don't come up healthy, the
|
||||
# stack goes back to the last good tag. Run from the repo root on Cosmos, by the
|
||||
# Gitea Actions workflow or by hand. See docs/deploy.md.
|
||||
set -euo pipefail
|
||||
|
||||
STACK="${GK_STACK_DIR:-/home/greg/homelab/gregkilmer}"
|
||||
ENV_FILE="$STACK/.env"
|
||||
TAG="$(git rev-parse --short=12 HEAD)"
|
||||
compose() { docker compose --project-directory "$STACK" -f "$STACK/compose.yaml" "$@"; }
|
||||
setting() { grep -E "^$1=" "$ENV_FILE" | tail -n 1 | cut -d= -f2- || true; }
|
||||
set_setting() {
|
||||
if grep -qE "^$1=" "$ENV_FILE"; then sed -i "s|^$1=.*|$1=$2|" "$ENV_FILE"; else echo "$1=$2" >> "$ENV_FILE"; fi
|
||||
}
|
||||
|
||||
for f in .env content.env admin.env site.env; do
|
||||
[ -f "$STACK/$f" ] || { echo "Missing $STACK/$f (see docs/deploy.md)"; exit 1; }
|
||||
done
|
||||
|
||||
CURRENT="$(setting GK_TAG)"
|
||||
PREVIOUS="$(setting GK_PREVIOUS_TAG)"
|
||||
if [ "$CURRENT" = "$TAG" ] && [ "${FORCE:-}" != "1" ]; then
|
||||
echo "Already running $TAG; nothing to do (FORCE=1 to rebuild)."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "== Checks"
|
||||
docker build --target check .
|
||||
|
||||
echo "== Images for $TAG"
|
||||
for t in content admin site; do
|
||||
docker build --target "$t" -t "gregkilmer/$t:$TAG" .
|
||||
done
|
||||
|
||||
echo "== Deploy"
|
||||
mkdir -p "$STACK/data/content"
|
||||
cp deploy/compose.yaml "$STACK/compose.yaml"
|
||||
set_setting GK_TAG "$TAG"
|
||||
[ -n "$CURRENT" ] && set_setting GK_PREVIOUS_TAG "$CURRENT"
|
||||
|
||||
if ! compose up -d --remove-orphans --wait --wait-timeout 120; then
|
||||
echo "!! $TAG did not come up healthy"
|
||||
compose ps
|
||||
compose logs --tail 50 || true
|
||||
if [ -n "$CURRENT" ]; then
|
||||
echo "== Rolling back to $CURRENT"
|
||||
set_setting GK_TAG "$CURRENT"
|
||||
set_setting GK_PREVIOUS_TAG "$PREVIOUS"
|
||||
compose up -d --wait --wait-timeout 120
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "== Prune old images (keeping $TAG and ${CURRENT:-none})"
|
||||
for t in content admin site; do
|
||||
docker images "gregkilmer/$t" --format '{{.Tag}}' | while read -r old; do
|
||||
[ "$old" = "$TAG" ] || [ "$old" = "$CURRENT" ] || docker rmi "gregkilmer/$t:$old" >/dev/null || true
|
||||
done
|
||||
done
|
||||
echo "Deployed $TAG"
|
||||
Reference in New Issue
Block a user