build.js rewrites three things during `node build.js`: - status/dist/*.js (bundle output) - status/index.html (CSP hash for inline bootstrap) - status/sw.js (cache name derived from bundle content) release.sh was only staging status/dist/. Result: when a release didn't touch index.html or sw.js source, the post-build modifications to those two files were left unstaged, the commit included only dist/, and the tarball shipped the stale sw.js. Clients then kept the previous SW cache name -> activate handler never wiped the cache -> precached old bundles served forever even after they were "updated" on disk. Now stage index.html and sw.js too. They're tracked (not gitignored) so a plain `git add` is enough; the commit is a no-op when nothing actually changed.
203 lines
9.6 KiB
Bash
203 lines
9.6 KiB
Bash
#!/usr/bin/env bash
|
|
# Cut a DashCaddy release: bump dashcaddy-api/package.json, commit, push,
|
|
# build tarball + version.json on DNS2 (the get.dashcaddy.net publishing
|
|
# host), refresh install.sh, then mirror everything to the dc-contabo-de
|
|
# get2 backup.
|
|
#
|
|
# Usage: scripts/release.sh <version>
|
|
# Example: scripts/release.sh 1.4.0
|
|
#
|
|
# Pre-flight: must be on `main`, working tree clean, gitea remote reachable.
|
|
#
|
|
# Hosts/URLs are overridable via env:
|
|
# DASHCADDY_RELEASE_HOST default root@100.104.4.5 (DNS2, hosts get.dashcaddy.net)
|
|
# DASHCADDY_MIRROR_HOST default root@dc-contabo-de (hosts get2.dashcaddy.net)
|
|
# DASHCADDY_GITEA_URL default http://100.98.123.59:3000/sami7777/dashcaddy.git
|
|
|
|
set -euo pipefail
|
|
|
|
VERSION="${1:-}"
|
|
[[ -z "$VERSION" ]] && { echo "Usage: $0 <version>" >&2; exit 1; }
|
|
[[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Invalid version (need X.Y.Z): $VERSION" >&2; exit 1; }
|
|
|
|
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
|
RELEASE_HOST="${DASHCADDY_RELEASE_HOST:-root@100.104.4.5}"
|
|
MIRROR_HOST="${DASHCADDY_MIRROR_HOST:-root@dc-contabo-de}"
|
|
GITEA_URL="${DASHCADDY_GITEA_URL:-http://100.98.123.59:3000/sami7777/dashcaddy.git}"
|
|
|
|
cd "$REPO_ROOT"
|
|
|
|
# ── Pre-flight ─────────────────────────────────────────────────────────────
|
|
[[ -f dashcaddy-api/package.json ]] || { echo "Run from dashcaddy repo root (no dashcaddy-api/package.json)" >&2; exit 1; }
|
|
[[ -n "$(git status --porcelain)" ]] && { echo "Working tree must be clean" >&2; exit 1; }
|
|
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
|
|
[[ "$BRANCH" == "main" ]] || { echo "Must be on main (current: $BRANCH)" >&2; exit 1; }
|
|
|
|
CURRENT="$(node -p "require('./dashcaddy-api/package.json').version")"
|
|
[[ "$CURRENT" == "$VERSION" ]] && { echo "package.json already at $VERSION — nothing to do" >&2; exit 1; }
|
|
|
|
echo "─── Cutting release ───"
|
|
echo " current: $CURRENT"
|
|
echo " target: $VERSION"
|
|
echo " release: $RELEASE_HOST"
|
|
echo " mirror: $MIRROR_HOST"
|
|
echo
|
|
|
|
# ── 1. Bump dashcaddy-api/package.json ────────────────────────────────────
|
|
echo "[1/6] Bumping dashcaddy-api/package.json"
|
|
node -e "
|
|
const fs = require('fs');
|
|
const pkg = require('./dashcaddy-api/package.json');
|
|
pkg.version = '$VERSION';
|
|
fs.writeFileSync('./dashcaddy-api/package.json', JSON.stringify(pkg, null, 2) + '\n');
|
|
"
|
|
|
|
# ── 2. Rebuild status frontend so dist/*.js matches source ────────────────
|
|
if [[ -f status/build.js ]]; then
|
|
echo "[2/6] Rebuilding status frontend"
|
|
(cd status && node build.js >/dev/null)
|
|
fi
|
|
|
|
# ── 3. Commit + push ──────────────────────────────────────────────────────
|
|
echo "[3/6] Committing + pushing"
|
|
git add dashcaddy-api/package.json
|
|
# Everything the build rewrites must be staged or the tarball ships stale
|
|
# copies. status/dist/ is .gitignored (-f bypasses); index.html and sw.js are
|
|
# tracked but get rewritten by build.js (CSP hash + SW cache tag derived from
|
|
# bundle content). Without staging sw.js, clients keep the old cache name
|
|
# and never see the new bundles.
|
|
[[ -d status/dist ]] && git add -f status/dist/ 2>/dev/null || true
|
|
[[ -f status/index.html ]] && git add status/index.html 2>/dev/null || true
|
|
[[ -f status/sw.js ]] && git add status/sw.js 2>/dev/null || true
|
|
git commit -m "chore(release): bump to $VERSION" >/dev/null
|
|
git push gitea main >/dev/null
|
|
COMMIT="$(git rev-parse --short HEAD)"
|
|
echo " → committed: $COMMIT"
|
|
|
|
# ── 4. Build tarball on the publishing host ───────────────────────────────
|
|
echo "[4/6] Building tarball on $RELEASE_HOST"
|
|
ssh "$RELEASE_HOST" "set -e
|
|
rm -rf /tmp/dashcaddy-build
|
|
mkdir -p /tmp/dashcaddy-build
|
|
cd /tmp/dashcaddy-build
|
|
git clone --depth 1 '$GITEA_URL' dashcaddy >/dev/null 2>&1
|
|
cd dashcaddy
|
|
ACTUAL_COMMIT=\$(git rev-parse --short HEAD)
|
|
if [ \"\$ACTUAL_COMMIT\" != \"$COMMIT\" ]; then
|
|
echo \" ! cloned commit \$ACTUAL_COMMIT does not match expected $COMMIT — aborting\" >&2
|
|
exit 1
|
|
fi
|
|
rm -rf .git
|
|
find . -type d -name node_modules -exec rm -rf {} + 2>/dev/null || true
|
|
# Bake the actual commit SHA into the api VERSION file so containers built
|
|
# from this tarball report a real commit (not the 'dev' placeholder).
|
|
echo \"$COMMIT\" > dashcaddy-api/VERSION
|
|
cd /tmp/dashcaddy-build
|
|
tar zcf dashcaddy-$VERSION.tar.gz dashcaddy/
|
|
"
|
|
echo " → built /tmp/dashcaddy-build/dashcaddy-$VERSION.tar.gz"
|
|
|
|
# ── 5. Publish on the release host ────────────────────────────────────────
|
|
echo "[5/6] Publishing v$VERSION + refreshed install.sh on $RELEASE_HOST"
|
|
ssh "$RELEASE_HOST" "set -e
|
|
cd /var/www/get.dashcaddy.net
|
|
cp -a release release.backup-\$(date -u +%Y%m%d-%H%M%S)
|
|
cp /tmp/dashcaddy-build/dashcaddy-$VERSION.tar.gz release/
|
|
cp /tmp/dashcaddy-build/dashcaddy-$VERSION.tar.gz release/latest.tar.gz
|
|
( cd release && sha256sum latest.tar.gz > latest.tar.gz.sha256 )
|
|
cp /tmp/dashcaddy-build/dashcaddy/dashcaddy-installer/install.sh release/install.sh
|
|
chmod +x release/install.sh
|
|
SHA256=\$(sha256sum release/dashcaddy-$VERSION.tar.gz | cut -d' ' -f1)
|
|
cat > release/version.json <<EOF
|
|
{
|
|
\"version\": \"$VERSION\",
|
|
\"commit\": \"$COMMIT\",
|
|
\"date\": \"\$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
|
|
\"sha256\": \"\$SHA256\",
|
|
\"changelog\": \"$COMMIT chore(release): bump to $VERSION\",
|
|
\"breaking\": false,
|
|
\"tarball\": \"dashcaddy-$VERSION.tar.gz\"
|
|
}
|
|
EOF
|
|
"
|
|
|
|
# ── 6. Mirror to backup host (non-fatal — primary is canonical) ───────────
|
|
echo "[6/6] Mirroring to $MIRROR_HOST"
|
|
MIRROR_OK=true
|
|
if ssh "$RELEASE_HOST" "rsync -aq --delete /var/www/get.dashcaddy.net/release/ $MIRROR_HOST:/var/www/get2.dashcaddy.net/release/" 2>&1; then
|
|
echo " → mirrored"
|
|
else
|
|
MIRROR_OK=false
|
|
echo " ! MIRROR FAILED — get2.dashcaddy.net is stale. Primary continues." >&2
|
|
fi
|
|
|
|
# ── Optional: notify known instances to update immediately ────────────────
|
|
# Set DASHCADDY_NOTIFY_TARGETS="<url>|<secret>,<url>|<secret>" to push.
|
|
# If unset, we try the co-located instance at localhost:3001 using the
|
|
# generated secret at /opt/dashcaddy/updates/notify-secret (silently skipped
|
|
# if either is missing).
|
|
if [[ -n "${DASHCADDY_NOTIFY_TARGETS:-}" ]]; then
|
|
echo "[notify] pushing release to configured targets"
|
|
IFS=',' read -ra TARGETS <<< "$DASHCADDY_NOTIFY_TARGETS"
|
|
for t in "${TARGETS[@]}"; do
|
|
url="${t%%|*}"
|
|
secret="${t#*|}"
|
|
[[ "$url" == "$secret" ]] && { echo " ! malformed target ($t) — need url|secret" >&2; continue; }
|
|
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 -X POST \
|
|
-H "X-DashCaddy-Notify-Secret: $secret" \
|
|
-H 'Content-Type: application/json' \
|
|
-d "{\"version\":\"$VERSION\",\"commit\":\"$COMMIT\"}" \
|
|
"$url" || true)
|
|
if [[ "$code" =~ ^2[0-9][0-9]$ ]]; then
|
|
echo " → $url notified (HTTP $code)"
|
|
else
|
|
echo " ! $url notify FAILED (HTTP $code)" >&2
|
|
fi
|
|
done
|
|
else
|
|
# Co-located default
|
|
LOCAL_NOTIFY=$(ssh "$RELEASE_HOST" '
|
|
if [[ -r /opt/dashcaddy/updates/notify-secret ]] && curl -fsS --max-time 2 http://localhost:3001/api/health >/dev/null 2>&1; then
|
|
secret=$(cat /opt/dashcaddy/updates/notify-secret)
|
|
curl -s -o /dev/null -w "%{http_code}" --max-time 5 -X POST \
|
|
-H "X-DashCaddy-Notify-Secret: $secret" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"version\":\"'"$VERSION"'\",\"commit\":\"'"$COMMIT"'\"}" \
|
|
http://localhost:3001/api/system/update-notify
|
|
else
|
|
echo skip
|
|
fi
|
|
' 2>/dev/null || true)
|
|
case "$LOCAL_NOTIFY" in
|
|
2*) echo "[notify] co-located instance on $RELEASE_HOST → HTTP $LOCAL_NOTIFY" ;;
|
|
skip) ;; # no secret or instance not up — silent
|
|
*) echo "[notify] co-located instance notify failed (HTTP $LOCAL_NOTIFY)" >&2 ;;
|
|
esac
|
|
fi
|
|
|
|
# ── Verify ───────────────────────────────────────────────────────────────
|
|
echo
|
|
echo "─── Verifying live ───"
|
|
SERVED_VER="$(curl -fsSL --max-time 5 https://get.dashcaddy.net/release/version.json | grep -oE '"version"[[:space:]]*:[[:space:]]*"[^"]+"' | head -1 | sed -E 's/.*"([^"]+)"$/\1/')"
|
|
[[ "$SERVED_VER" == "$VERSION" ]] || { echo "MISMATCH: get.dashcaddy.net serves $SERVED_VER, expected $VERSION" >&2; exit 1; }
|
|
echo " get.dashcaddy.net → $SERVED_VER ✓"
|
|
|
|
SHA_LOCAL="$(ssh "$RELEASE_HOST" "sha256sum /var/www/get.dashcaddy.net/release/dashcaddy-$VERSION.tar.gz | cut -d' ' -f1")"
|
|
SHA_HTTP="$(curl -fsSL --max-time 30 "https://get.dashcaddy.net/release/dashcaddy-$VERSION.tar.gz" | sha256sum | cut -d' ' -f1)"
|
|
[[ "$SHA_LOCAL" == "$SHA_HTTP" ]] || { echo "SHA mismatch on served tarball" >&2; exit 1; }
|
|
echo " tarball sha256 → $SHA_HTTP ✓"
|
|
|
|
if [[ "$MIRROR_OK" == "true" ]]; then
|
|
GET2_VER="$(curl -fsSL --max-time 5 https://get2.dashcaddy.net/release/version.json 2>/dev/null | grep -oE '"version"[[:space:]]*:[[:space:]]*"[^"]+"' | head -1 | sed -E 's/.*"([^"]+)"$/\1/')"
|
|
[[ -z "$GET2_VER" ]] && GET2_VER=unreachable
|
|
if [[ "$GET2_VER" == "$VERSION" ]]; then
|
|
echo " get2.dashcaddy.net → $GET2_VER ✓"
|
|
else
|
|
echo " ! get2.dashcaddy.net serves '$GET2_VER' (expected $VERSION) — check Caddy/DNS for get2" >&2
|
|
fi
|
|
fi
|
|
|
|
echo
|
|
echo "Done. v$VERSION published from commit $COMMIT."
|
|
[[ "$MIRROR_OK" == "true" ]] || echo "(reminder: mirror to get2 failed — investigate $MIRROR_HOST)"
|