Merge pull request #47 from Coffey-Labs/demo-sync-script
Script the demo sync, so its exclusions are not retyped each time
This commit is contained in:
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Push this checkout to the demo host and restart the stack.
|
||||
#
|
||||
# The demo host does not hold a git checkout -- the tree was copied there --
|
||||
# so there is no `git pull` to run on the far end and this has to push.
|
||||
#
|
||||
# What it syncs: exactly the files git tracks, via `git ls-files`. That is the
|
||||
# whole point of the script rather than an rsync invocation typed from memory.
|
||||
# An exclude list has to name every host-owned file, and the one it missed --
|
||||
# `hack/dev-certs/out/`, gitignored and generated per machine -- took the demo
|
||||
# down on 2026-09-10: ingest mounts that directory and began serving a cert
|
||||
# signed by the *developer's* CA, while the host's agent simulator still
|
||||
# trusted the host's own. Every check-in failed `x509: certificate signed by
|
||||
# unknown authority` until the simulator was restarted. Syncing only tracked
|
||||
# files makes that class of mistake impossible: anything gitignored is, by
|
||||
# construction, never sent.
|
||||
#
|
||||
# What it still excludes, from the tracked set:
|
||||
# docker-compose.yml -- edited in place on the host to bind every published
|
||||
# port to 127.0.0.1. Overriding `ports:` from the override file does not
|
||||
# work (Compose concatenates list-type keys rather than replacing them),
|
||||
# so the host's copy is authoritative and must not be overwritten.
|
||||
# .env -- tracked, and so NOT covered by the git-tracked rule above. The
|
||||
# host's copy carries ALERTING_SERVICE_TOKEN, which the repo's does not;
|
||||
# sending the repo's would drop it and take alerting down.
|
||||
#
|
||||
# Untracked host state -- .env, docker-compose.override.yml, demo-*.json,
|
||||
# bin/, the dev certs -- is never in `git ls-files`, so it needs no mention.
|
||||
#
|
||||
# Usage:
|
||||
# deploy/demo-sync.sh # dry run: show what would change
|
||||
# deploy/demo-sync.sh --yes # sync, rebuild, restart
|
||||
#
|
||||
# Env:
|
||||
# DEMO_HOST ssh target (default: Web_Host)
|
||||
# DEMO_PATH remote directory (default: ~/cairnobs-demo)
|
||||
set -euo pipefail
|
||||
|
||||
HOST="${DEMO_HOST:-Web_Host}"
|
||||
# Tilde, not $HOME: rsync and ssh hand this to the *remote* shell, which
|
||||
# expands it there. A literal $HOME expands locally (or not at all) and
|
||||
# silently targets the wrong directory.
|
||||
DEST="${DEMO_PATH:-~/cairnobs-demo}"
|
||||
APPLY=0
|
||||
[ "${1:-}" = "--yes" ] || [ "${1:-}" = "-y" ] && APPLY=1
|
||||
|
||||
cd "$(dirname "$0")/.."
|
||||
command -v git >/dev/null || { echo "git not found" >&2; exit 1; }
|
||||
git rev-parse --git-dir >/dev/null 2>&1 || { echo "not a git checkout" >&2; exit 1; }
|
||||
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "==> working tree is dirty; the demo would get uncommitted changes:" >&2
|
||||
git status --short >&2
|
||||
[ "$APPLY" = "1" ] && { echo "==> refusing to sync a dirty tree" >&2; exit 1; }
|
||||
fi
|
||||
|
||||
echo "==> source: $(git log --oneline -1)"
|
||||
echo "==> target: $HOST:$DEST"
|
||||
|
||||
# Only tracked files, minus the one the host owns.
|
||||
git ls-files | grep -vxE 'docker-compose\.yml|\.env' > /tmp/cairnobs-demo-files.$$
|
||||
trap 'rm -f /tmp/cairnobs-demo-files.$$' EXIT
|
||||
echo "==> $(wc -l < /tmp/cairnobs-demo-files.$$) tracked files to consider"
|
||||
|
||||
RSYNC_ARGS=(-az --files-from=/tmp/cairnobs-demo-files.$$ ./ "$HOST:$DEST/")
|
||||
if [ "$APPLY" = "0" ]; then
|
||||
echo "==> DRY RUN (pass --yes to apply)"
|
||||
rsync --dry-run --itemize-changes "${RSYNC_ARGS[@]}" | grep -v '^\.d' || true
|
||||
exit 0
|
||||
fi
|
||||
|
||||
rsync "${RSYNC_ARGS[@]}"
|
||||
echo "==> synced; rebuilding"
|
||||
ssh "$HOST" "cd $DEST && docker compose build && docker compose up -d"
|
||||
|
||||
# The simulator reads the CA and its client cert once, at startup. It runs on
|
||||
# the host, outside Compose, so `docker compose up -d` does not touch it -- and
|
||||
# if anything about the mTLS material changed underneath it, every check-in
|
||||
# fails until it is restarted. Cheap to do unconditionally.
|
||||
echo "==> restarting the agent simulator"
|
||||
ssh "$HOST" 'systemctl restart cairnobs-demo-simulator.service 2>/dev/null || sudo systemctl restart cairnobs-demo-simulator.service'
|
||||
|
||||
echo "==> health"
|
||||
ssh "$HOST" 'curl -sS -m 10 -o /dev/null -w " api /healthz -> %{http_code}\n" http://127.0.0.1:8080/healthz; docker compose -f '"$DEST"'/docker-compose.yml ps --format " {{.Service}}\t{{.State}}" 2>/dev/null | head -12'
|
||||
Reference in New Issue
Block a user