#!/usr/bin/env bash # Cut over live stack freenotes → silverbullet on koopa (hernani). # Default: dry-run. Mutate only with --apply. # # bash /path/to/koopa-admin-log/scripts/notes/cutover-to-silverbullet.sh # bash /path/to/koopa-admin-log/scripts/notes/cutover-to-silverbullet.sh --apply # # Keeps: space/, .env. Refreshes: compose.yml, Containerfile, entrypoint.sh, systemd unit. # Image: retag localhost/koopa-freenotes:cachyos → localhost/koopa-silverbullet:cachyos (no rebuild). # Does NOT touch Caddy (sudo) — print next steps only. set -euo pipefail APPLY=0 for a in "$@"; do case "$a" in --apply) APPLY=1 ;; -h|--help) sed -n '2,12p' "$0" exit 0 ;; *) echo "ERROR: unknown arg: $a (use --apply)" >&2 exit 2 ;; esac done OLD_DIR="${HOME}/koopa-freenotes" NEW_DIR="${HOME}/koopa-silverbullet" OLD_CT=koopa-freenotes NEW_CT=koopa-silverbullet OLD_IMG=localhost/koopa-freenotes:cachyos NEW_IMG=localhost/koopa-silverbullet:cachyos PORT=9028 ROOT="$(cd "$(dirname "$0")/../.." && pwd)" CFG="${ROOT}/configs/notes" [[ -d "$CFG" ]] || { echo "ERROR: missing $CFG" >&2; exit 1; } [[ -f "$CFG/compose.yml" ]] || { echo "ERROR: missing $CFG/compose.yml" >&2; exit 1; } mode=dry-run [[ "$APPLY" -eq 1 ]] && mode=apply echo "STATUS=start MODE=$mode HOST=$(hostname -s) OLD_DIR=$OLD_DIR NEW_DIR=$NEW_DIR" have_old=0 have_new=0 [[ -d "$OLD_DIR" ]] && have_old=1 [[ -d "$NEW_DIR" ]] && have_new=1 echo "STATUS=dirs OLD=$have_old NEW=$have_new" ct_old=$(podman ps -a --format '{{.Names}}' 2>/dev/null | grep -Fx "$OLD_CT" || true) ct_new=$(podman ps -a --format '{{.Names}}' 2>/dev/null | grep -Fx "$NEW_CT" || true) echo "STATUS=containers OLD=${ct_old:-none} NEW=${ct_new:-none}" img_old=$(podman images -q "$OLD_IMG" 2>/dev/null || true) img_new=$(podman images -q "$NEW_IMG" 2>/dev/null || true) echo "STATUS=images OLD=${img_old:-none} NEW=${img_new:-none}" dir_err=0 if [[ "$have_old" -eq 0 && "$have_new" -eq 1 ]]; then echo "STATUS=already_cutover DIR=$NEW_DIR" elif [[ "$have_old" -eq 0 && "$have_new" -eq 0 ]]; then echo "STATUS=warn neither $OLD_DIR nor $NEW_DIR (ok for laptop dry-run; apply needs koopa)" dir_err=1 elif [[ "$have_old" -eq 1 && "$have_new" -eq 1 ]]; then echo "STATUS=warn both dirs exist — resolve manually before --apply" dir_err=1 fi plan() { echo "PLAN: $*"; } plan "stop+rm container $OLD_CT (if present)" plan "mv $OLD_DIR → $NEW_DIR (if old present)" plan "copy compose/Containerfile/entrypoint/unit from $CFG (keep space .env)" plan "podman tag $OLD_IMG $NEW_IMG (if new image missing)" plan "podman-compose up -d in $NEW_DIR" plan "health curl :$PORT" plan "optional: systemctl --user enable container-koopa-silverbullet.service" if [[ "$APPLY" -ne 1 ]]; then echo "STATUS=dry-run NEXT=re-run on koopa with --apply (Freigabe)" echo "STATUS=caddy_next sudo bash $ROOT/scripts/caddy/merge-notes-site.sh" echo "STATUS=caddy_next sudo NOTES_AUTH_SEED=/tmp/silverbullet-basicauth-seed.txt bash $ROOT/scripts/caddy/apply-notes-basicauth.sh" echo "STATUS=dns_need silverbullet.hacktivism.ch → koopa" exit 0 fi if [[ "$dir_err" -eq 1 ]]; then echo "ERROR: refuse --apply with ambiguous/missing live dirs" >&2 exit 1 fi if [[ -n "$ct_old" ]]; then echo "STATUS=stop $OLD_CT" podman stop -t 20 "$OLD_CT" || true podman rm "$OLD_CT" || true fi if [[ "$have_old" -eq 1 ]]; then echo "STATUS=mv $OLD_DIR → $NEW_DIR" mv "$OLD_DIR" "$NEW_DIR" fi cd "$NEW_DIR" # Refresh stack files from admin-log; never overwrite space/ or .env cp -a "$CFG/compose.yml" "$NEW_DIR/compose.yml" cp -a "$CFG/Containerfile" "$NEW_DIR/Containerfile" cp -a "$CFG/entrypoint.sh" "$NEW_DIR/entrypoint.sh" chmod +x "$NEW_DIR/entrypoint.sh" cp -a "$CFG/container-koopa-silverbullet.service" "$NEW_DIR/container-koopa-silverbullet.service" cp -a "$CFG/.env.example" "$NEW_DIR/.env.example" # Drop obsolete unit name if present rm -f "$NEW_DIR/container-koopa-freenotes.service" echo "STATUS=refreshed_stack_files DIR=$NEW_DIR" if [[ -z "$img_new" ]]; then if [[ -n "$img_old" ]]; then echo "STATUS=tag $OLD_IMG → $NEW_IMG" podman tag "$OLD_IMG" "$NEW_IMG" else echo "STATUS=build missing image via build.sh" bash "$ROOT/scripts/notes/build.sh" fi fi if [[ -n "$ct_new" ]]; then echo "STATUS=rm stale $NEW_CT" podman stop -t 10 "$NEW_CT" || true podman rm "$NEW_CT" || true fi echo "STATUS=compose_up" podman-compose up -d ok=0 for i in $(seq 1 40); do code=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 5 "http://127.0.0.1:${PORT}/" || true) echo "STATUS=health try=$i code=$code" case "$code" in 2*|3*) ok=1; break ;; esac sleep 3 done [[ "$ok" -eq 1 ]] || { echo "ERROR: no answer on :${PORT}" >&2; podman-compose ps; exit 1; } # User systemd unit (optional; ignore failures) UNIT_SRC="$NEW_DIR/container-koopa-silverbullet.service" UNIT_DST="${HOME}/.config/systemd/user/container-koopa-silverbullet.service" if [[ -f "$UNIT_SRC" ]] && command -v systemctl >/dev/null; then mkdir -p "${HOME}/.config/systemd/user" cp -a "$UNIT_SRC" "$UNIT_DST" systemctl --user daemon-reload || true systemctl --user disable --now container-koopa-freenotes.service 2>/dev/null || true systemctl --user enable --now container-koopa-silverbullet.service 2>/dev/null || true echo "STATUS=systemd_user unit=container-koopa-silverbullet.service" fi echo "STATUS=ok CONTAINER=$NEW_CT DIR=$NEW_DIR PORT=$PORT" echo "STATUS=caddy_next sudo bash $ROOT/scripts/caddy/merge-notes-site.sh" echo "STATUS=caddy_next sudo NOTES_AUTH_SEED=/tmp/silverbullet-basicauth-seed.txt bash $ROOT/scripts/caddy/apply-notes-basicauth.sh" echo "STATUS=dns_need silverbullet.hacktivism.ch → koopa"