From 54196a2d4fc214c0640e2064c0af593b8b9ed5d1 Mon Sep 17 00:00:00 2001 From: Sami Date: Wed, 6 May 2026 20:28:45 -0700 Subject: [PATCH] chore: add scripts/release.sh for cutting + publishing releases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automates what was previously a six-step manual process that, twice in this codebase's history, has produced version skew between git and the released tarball (v1.2.0 was published with package.json 1.2.0 in the tarball but the bump was never committed back to gitea — making "what code is in v1.2.0?" answerable only by extracting the tarball). The script: - Refuses to run with a dirty tree, off main, or already at the target version. - Bumps dashcaddy-api/package.json, rebuilds status/dist/, commits + pushes to gitea — so the released artifact and gitea HEAD are always in lockstep. - Clones gitea HEAD on the release host, verifies the cloned commit matches what we just pushed (catches a stale clone or a missed push), tars it, computes sha256, writes version.json. - Refreshes install.sh on the release host alongside the tarball (fresh installs use the install.sh from the latest release). - Mirrors the release dir to the get2 backup via rsync. - Verifies live by curling version.json and re-hashing the served tarball. Hosts overridable via DASHCADDY_RELEASE_HOST / DASHCADDY_MIRROR_HOST / DASHCADDY_GITEA_URL env vars. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/release.sh | 133 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 scripts/release.sh diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100644 index 0000000..0eed420 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,133 @@ +#!/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 +# 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 " >&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 +# status/dist/ is .gitignored but its tracked files still need re-staging on rebuild. +# -f bypasses the ignore for these specific paths. +[[ -d status/dist ]] && git add -f status/dist/ 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 + 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 <&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 ✓" + +echo +echo "Done. v$VERSION published from commit $COMMIT."