#!/usr/bin/env bash # Deploy the metronome to the Caddy web root that serves # https://metronome.varasys.io # # Caddy config: /var/lib/caddy/Caddyfile (metronome.varasys.io:8443 block) # Bind-mount: /etc/containers/systemd/caddy.container # # The web root is bind-mounted read-only into the Caddy container and # served by file_server, which picks up changes immediately — so a plain # file copy is all that's needed (no container restart). set -euo pipefail SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DEST_DIR="/var/lib/caddy/www/metronome" DIST_DIR="$SRC_DIR/dist" [[ -f "$SRC_DIR/index.html" ]] || { echo "error: $SRC_DIR/index.html not found" >&2; exit 1; } [[ -d "$DEST_DIR" ]] || { echo "error: web root $DEST_DIR is missing — is Caddy set up?" >&2; exit 1; } # Assemble the self-contained pages (inlines assets/ into dist/). dist/ is git-ignored. "$SRC_DIR/build.sh" [[ -f "$DIST_DIR/index.html" ]] || { echo "error: build did not produce $DIST_DIR/index.html" >&2; exit 1; } # --- compute build version --------------------------------------------------- # Formal build: clean tree on a commit tagged v -> "X.Y.Z" # Dev build: anything else -> "X.Y.Z-dev..[.dirty]" VER="$(cat "$SRC_DIR/VERSION" 2>/dev/null || echo 0.0.0)" cd "$SRC_DIR" if git rev-parse --git-dir >/dev/null 2>&1; then tag="$(git describe --exact-match --tags HEAD 2>/dev/null || true)" dirty=""; [[ -n "$(git status --porcelain 2>/dev/null)" ]] && dirty=".dirty" if [[ "$tag" == "v$VER" && -z "$dirty" ]]; then BUILD="$VER" # formal release else BUILD="$VER-dev.$(date -u +%Y%m%dT%H%M%SZ).g$(git rev-parse --short HEAD 2>/dev/null || echo nogit)$dirty" fi else BUILD="$VER-dev.$(date -u +%Y%m%dT%H%M%SZ)" # not a git checkout fi # stamp the version into the built copy only (source stays clean) echo "deployed v$BUILD -> $DEST_DIR" for f in index.html editor.html editor-beta.html player.html teacher.html stage.html micro.html showcase.html kit.html explorer.html \ embed.html \ info-editor.html info-player.html info-teacher.html info-stage.html info-micro.html info-showcase.html info-kit.html info-explorer.html; do sed "s|const APP_VERSION = \"[^\"]*\";|const APP_VERSION = \"$BUILD\";|" "$DIST_DIR/$f" > "$DEST_DIR/$f" echo " $f ($(stat -c '%s' "$DEST_DIR/$f") bytes)" done cp "$DIST_DIR/embed.js" "$DEST_DIR/embed.js"; echo " embed.js ($(stat -c '%s' "$DEST_DIR/embed.js") bytes)" cp "$DIST_DIR/pico-main.py" "$DEST_DIR/pico-main.py"; echo " pico-main.py ($(stat -c '%s' "$DEST_DIR/pico-main.py") bytes)" # PM_K-1 firmware download cp "$DIST_DIR/pm_k1_circuitpy.zip" "$DEST_DIR/pm_k1_circuitpy.zip"; echo " pm_k1_circuitpy.zip ($(stat -c '%s' "$DEST_DIR/pm_k1_circuitpy.zip") bytes)" # PM_K-1 CircuitPython bundle cp "$DIST_DIR/pico-cp-app.py" "$DEST_DIR/pico-cp-app.py"; echo " pico-cp-app.py ($(stat -c '%s' "$DEST_DIR/pico-cp-app.py") bytes)" # served for version reading + reference cp "$DIST_DIR/pico-cp-app.mpy" "$DEST_DIR/pico-cp-app.mpy"; echo " pico-cp-app.mpy ($(stat -c '%s' "$DEST_DIR/pico-cp-app.mpy") bytes)" # precompiled firmware the editor pushes (base64) cp "$DIST_DIR/pm_x1_circuitpy.zip" "$DEST_DIR/pm_x1_circuitpy.zip"; echo " pm_x1_circuitpy.zip ($(stat -c '%s' "$DEST_DIR/pm_x1_circuitpy.zip") bytes)" # PM_X-1 Explorer CircuitPython bundle cp "$DIST_DIR/pico-explorer-app.py" "$DEST_DIR/pico-explorer-app.py"; echo " pico-explorer-app.py ($(stat -c '%s' "$DEST_DIR/pico-explorer-app.py") bytes)" # served for version reading cp "$DIST_DIR/pico-explorer-app.mpy" "$DEST_DIR/pico-explorer-app.mpy"; echo " pico-explorer-app.mpy ($(stat -c '%s' "$DEST_DIR/pico-explorer-app.mpy") bytes)" # PM_X-1 firmware (the editor pushes this when device id = X) rm -f "$DEST_DIR/player-asbuilt.html" # renamed to teacher.html rm -f "$DEST_DIR/concepts.html" # Concepts is now the landing (/) # info-*.html are first-class pages again: each form factor has a lean widget page # (.html) + a separate spec/BOM page (info-.html that embeds it). # If real audio samples are added later (see the plan's GM-sample note), # sync that directory too. if [[ -d "$SRC_DIR/samples" ]]; then rsync -a --delete "$SRC_DIR/samples/" "$DEST_DIR/samples/" echo "synced samples/ -> $DEST_DIR/samples" fi # Smoke test: Caddy serves on :8443 with tls internal; resolve the host # to localhost so SNI matches the site block. if command -v curl >/dev/null 2>&1; then code=$(curl -sk --resolve metronome.varasys.io:8443:127.0.0.1 \ https://metronome.varasys.io:8443/ -o /dev/null -w '%{http_code}' || echo "??") echo "smoke test: metronome.varasys.io -> HTTP $code" fi