chore(deploy): throttle production deploys to one per 24h

The timer deployed on every site-affecting commit at 15 Netlify credits each
(~68 deploys / ~1,020 credits in a week). auto-deploy.sh now enforces a minimum
gap between successful deploys, default 24h, overridable with
SKATESPOTS_DEPLOY_MIN_INTERVAL=<seconds> (0 disables). A throttled tick records
nothing so the change still deploys later; failures retry immediately.

Deploy on demand with: scripts/auto-deploy.sh --force

scripts/docs only — skipped by the timer, so this costs 0 credits.
This commit is contained in:
SkateSpots 2026-09-28 13:20:31 +07:00
parent c89d32d92e
commit f46f6a8967
2 changed files with 26 additions and 0 deletions

View file

@ -657,6 +657,7 @@ Rules:
### Deploy
- **Web (auto)**: a systemd user timer (`skatespots-deploy.timer`) runs `scripts/auto-deploy.sh` every ~3 min. When `origin/main` changes it builds and runs `netlify deploy --prod --dir=dist`. It deploys **only committed code** (skips if tracked files are modified) and never mutates a dirty tree. **Doc/scripts-only commits are skipped** (matches `AGENTS.md`/`*.md`/`scripts/`/`cvm-server/`/`skills/`) — Netlify bills **15 credits per production deploy**, so don't spend them on non-site changes. One-time setup: `netlify login` + `netlify link`. Logs: `journalctl --user -u skatespots-deploy`. Manual run: `scripts/auto-deploy.sh`.
- ⚠️ **Deploy throttle (2026-09-28):** production deploys are capped to one per **24h** by default (after a week of 68 deploys ≈ **1,020 credits** — a third of the monthly allowance). Batching a day's commits into one 15-credit deploy is the single biggest credit lever. Override with `SKATESPOTS_DEPLOY_MIN_INTERVAL=<seconds>` (`0` disables); deploy on demand with **`scripts/auto-deploy.sh --force`**. A throttled tick deliberately records nothing, so the pending change still deploys on a later tick; a **failed** deploy doesn't set the clock, so it retries immediately.
- ⚠️ Shakespeare's "Deploy" rebuilds **its own workspace**, which is NOT updated by git pushes — so pushing to the repo does not change what Shakespeare deploys. Prefer the auto-deploy timer (or drag `dist/` to Netlify).
- **Web (manual)**: `npm run build` produces the web `dist/` (with `_headers`/`_redirects`); deploy by dragging `dist/` to the Netlify site's Deploys tab.
- **Zapstore (Android)**, from the repo root: `SIGN_WITH='bunker://...' zsp publish zapstore.yaml`

View file

@ -23,8 +23,19 @@ export PATH="/home/sovereignsk8/.nvm/versions/node/v22.23.1/bin:$PATH"
STATE_DIR="$HOME/.local/state/skatespots-deploy"
SHA_FILE="$STATE_DIR/last-deployed-sha"
LOCK_FILE="$STATE_DIR/last-lock-hash"
INTERVAL_FILE="$STATE_DIR/last-deploy-epoch"
mkdir -p "$STATE_DIR"
# Production deploys cost 15 Netlify credits each, and this timer polls every
# ~3 minutes — so a busy session can spend hundreds of credits on deploys. Enforce
# a minimum gap between successful production deploys (default 24h). Override with
# SKATESPOTS_DEPLOY_MIN_INTERVAL=<seconds> (0 disables). To deploy immediately,
# bypassing the wait: scripts/auto-deploy.sh --force
FORCE=0
[ "${1:-}" = "--force" ] && FORCE=1
MIN_INTERVAL="${SKATESPOTS_DEPLOY_MIN_INTERVAL:-86400}"
case "$MIN_INTERVAL" in ''|*[!0-9]*) MIN_INTERVAL=0 ;; esac
log() { echo "[$(date -Is)] $*"; }
# Serialise runs (a slow deploy must not overlap the next timer tick).
@ -68,6 +79,19 @@ if [ -n "$LAST_SHA" ] && git cat-file -e "${LAST_SHA}^{commit}" 2>/dev/null; the
fi
fi
# 2c. Minimum interval between production deploys (15 credits each). On skip we do
# NOT record the SHA, so the pending change is still waiting next tick.
if [ "$FORCE" -eq 0 ] && [ "$MIN_INTERVAL" -gt 0 ]; then
LAST_EPOCH="$(cat "$INTERVAL_FILE" 2>/dev/null || echo 0)"
case "$LAST_EPOCH" in ''|*[!0-9]*) LAST_EPOCH=0 ;; esac
ELAPSED=$(( $(date +%s) - LAST_EPOCH ))
if [ "$ELAPSED" -lt "$MIN_INTERVAL" ]; then
WAIT_MIN=$(( (MIN_INTERVAL - ELAPSED + 59) / 60 ))
log "origin/main moved to ${REMOTE_SHA:0:9} but last deploy was $((ELAPSED / 60))m ago — batching (min ${MIN_INTERVAL}s, ~${WAIT_MIN}m left). Use --force to deploy now."
exit 0
fi
fi
# 3. Move the repo to the published commit (fast-forward only).
if [ "$(git rev-parse HEAD)" != "$REMOTE_SHA" ]; then
if git merge --ff-only --quiet origin/main; then
@ -106,4 +130,5 @@ log "deploying to Netlify…"
netlify deploy --prod --dir=dist
echo "$REMOTE_SHA" > "$SHA_FILE"
date +%s > "$INTERVAL_FILE"
log "deployed ${REMOTE_SHA:0:9} ✓"