#!/usr/bin/env bash # # One-command SkateSpots release: bump → test → build APK → publish to # Zapstore → verify → commit/push (+ optional git tag). # # Usage: # scripts/release.sh patch|minor|major [--yes] [--deploy] # scripts/release.sh 1.7.1 [--yes] [--deploy] # --deploy also forces the web deploy (bypasses the 24h Netlify throttle, # costs 15 credits); without it the deploy timer ships it shortly. # # Before running: # 1. write the new section at the TOP of release-notes.txt (do not commit it — # this script commits it with the version bump); # 2. the signer is read from ~/.config/skatespots/zsp.env (chmod 600) by # default. You can also export SIGN_WITH for the current shell, or set # SKATES_RELEASE_ENV to another 0600 file. # # What it does NOT do: deploy the web (the skatespots-deploy.timer does that # within ~3 min, 24h throttle) or touch the relay. set -euo pipefail REPO="/home/sovereignsk8/SkateSpots" ZSP="${ZSP:-$HOME/go/bin/zsp}" export ANDROID_HOME="${ANDROID_HOME:-$HOME/Android/Sdk}" SPEC="${1:-}" YES=0 DEPLOY=0 shift || true for a in "$@"; do [ "$a" = "--yes" ] && YES=1 [ "$a" = "--deploy" ] && DEPLOY=1 done die() { echo "✗ $*" >&2; exit 1; } log() { echo "• $*"; } [ -n "$SPEC" ] || die "usage: scripts/release.sh patch|minor|major| [--yes]" [ -x "$ZSP" ] || die "zsp not found/executable at $ZSP (set ZSP=...)" [ -d "$ANDROID_HOME" ] || die "Android SDK not found at $ANDROID_HOME (set ANDROID_HOME=...)" # Protected env file (never commit it). Prefer the file documented in AGENTS. if [ -z "${SKATES_RELEASE_ENV:-}" ] && [ -f "$HOME/.config/skatespots/zsp.env" ]; then SKATES_RELEASE_ENV="$HOME/.config/skatespots/zsp.env" fi if [ -z "${SIGN_WITH:-}" ] && [ -n "${SKATES_RELEASE_ENV:-}" ]; then [ -f "$SKATES_RELEASE_ENV" ] || die "SKATES_RELEASE_ENV does not exist: $SKATES_RELEASE_ENV" mode="$(stat -c %a "$SKATES_RELEASE_ENV" 2>/dev/null || echo '?')" case "$mode" in 600|400) ;; *) log "warning: $SKATES_RELEASE_ENV is mode $mode (chmod 600 recommended)" ;; esac set -a # shellcheck disable=SC1090 . "$SKATES_RELEASE_ENV" set +a fi [ -n "${SIGN_WITH:-}" ] || die "SIGN_WITH is not set (use ~/.config/skatespots/zsp.env, SKATES_RELEASE_ENV, or export it)" # Resolve the newest nvm Node (the old hard-coded path breaks after upgrades). if [ -d "$HOME/.nvm/versions/node" ]; then NODE_BIN="$(ls -d "$HOME"/.nvm/versions/node/*/bin 2>/dev/null | sort -V | tail -1)" [ -n "$NODE_BIN" ] && export PATH="$NODE_BIN:$PATH" fi export PATH="$HOME/go/bin:$PATH" cd "$REPO" CURRENT="$(node -p "require('./package.json').version")" log "current version: $CURRENT" # The notes must be the only uncommitted change, so the release commit is clean. if git diff --quiet -- release-notes.txt; then die "release-notes.txt is unchanged — write the new section first (and don't commit it)" fi DIRTY="$(git status --porcelain | grep -vE '^ M release-notes\.txt$' || true)" [ -z "$DIRTY" ] || die "working tree has other changes: $DIRTY" git fetch --quiet origin main [ "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" ] || \ die "local main is not in sync with origin/main — push or pull first" if [ "$YES" -eq 0 ]; then [ -t 0 ] || die "non-interactive: pass --yes to confirm" printf 'Release %s from %s? [y/N] ' "$SPEC" "$CURRENT" read -r ans case "$ans" in y|Y|yes|YES) ;; *) die "aborted" ;; esac fi log "running the full test gate (this also builds the web bundle)…" npm test log "bumping version ($SPEC)…" # --force: the only dirty file is release-notes.txt, which `npm version` # otherwise refuses to bump over. The tree was already validated above. npm version "$SPEC" --no-git-tag-version --force >/dev/null VERSION="$(node -p "require('./package.json').version")" [ "$VERSION" != "$CURRENT" ] || die "version did not change" log "version: $VERSION" log "building the signed release APK…" npm run android:build APK="android/app/build/outputs/apk/release/app-release.apk" [ -f "$APK" ] || die "APK not found at $APK" SHA="$(sha256sum "$APK" | awk '{print $1}')" log "APK: $APK (sha256 ${SHA:0:16}…)" log "publishing to Zapstore…" # shellcheck disable=SC2086 "$ZSP" publish --quiet zapstore.yaml log "verifying the Zapstore release event…" FOUND=0 for _ in 1 2 3 4 5 6; do if command -v nak >/dev/null 2>&1; then OUT="$(timeout 15 nak req -k 30063 -d "org.skatespots.app@$VERSION" wss://relay.zapstore.dev 2>/dev/null || true)" if printf '%s' "$OUT" | grep -qE "\"version\"[[:space:]]*,[[:space:]]*\"$VERSION\""; then FOUND=1; break; fi else log "nak not installed — skipping relay verification" FOUND=1 break fi sleep 4 done [ "$FOUND" -eq 1 ] || log "warning: the release event did not appear on the relay yet (it usually propagates shortly)" log "committing the release…" git add package.json package-lock.json release-notes.txt git commit -m "release: $VERSION" >/dev/null git tag -a "v$VERSION" -m "SkateSpots $VERSION" git push origin main if ! git push origin "v$VERSION" 2>/dev/null; then log "warning: tag v$VERSION was created locally but the push failed" fi if [ "$DEPLOY" -eq 1 ]; then log "forcing the web deploy (bypasses the 24h Netlify throttle)…" scripts/auto-deploy.sh --force || log "warning: web deploy failed — check journalctl --user -u skatespots-deploy" fi echo echo "✓ SkateSpots $VERSION published" echo " APK: $APK" echo " SHA256: $SHA" if [ "$DEPLOY" -eq 1 ]; then echo " Web: forced deploy requested above" else echo " Web: the deploy timer picks up the commit within ~3 min (24h throttle)" fi echo " Zapstore: https://zapstore.dev/apps/org.skatespots.app"