Fix deploy.sh skipping SPA build on same-ref/aborted deploys

The build was gated on `git diff BEFORE AFTER`, where BEFORE was HEAD
before checkout. Re-deploying the same ref (or re-running after an
aborted deploy) made BEFORE == AFTER, so the diff was empty and the SPA
build silently skipped — shipping stale assets while migrations still ran.

- Always rebuild the SPA; only gate the heavy dep installs.
- npm ci / composer install also run when node_modules / vendor are missing.
- Track the last successfully deployed commit in .deploy-last-commit and
  diff against that, so an aborted run never advances the baseline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ovidiu U
2026-06-12 10:41:47 +01:00
parent 61adc133aa
commit 347a71154b
2 changed files with 32 additions and 14 deletions

1
.gitignore vendored
View File

@@ -24,4 +24,5 @@ yarn-error.log
/.zed /.zed
/.tmp/ /.tmp/
/.worktrees/ /.worktrees/
/.deploy-last-commit
/ONSPD_Online_Latest_Centroids_*.csv /ONSPD_Online_Latest_Centroids_*.csv

View File

@@ -6,10 +6,11 @@
# ./deploy.sh v0.1.3 # deploy a specific tag # ./deploy.sh v0.1.3 # deploy a specific tag
# #
# It puts the site in maintenance mode, updates the code, runs migrations and # It puts the site in maintenance mode, updates the code, runs migrations and
# cache rebuilds, restarts the queue, then brings the site back up. composer # cache rebuilds, restarts the queue, then brings the site back up. The SPA is
# install and npm build only run when their inputs actually changed, so most # always rebuilt; the heavier dependency installs (composer install, npm ci)
# deploys skip them. If any step fails the script aborts and the site stays in # only run when their lockfiles changed or the installed dir is missing. If any
# maintenance mode on purpose — fix the issue, then re-run. # step fails the script aborts and the site stays in maintenance mode on
# purpose — fix the issue, then re-run.
# #
# See docs/ops/deployment.md for first-time setup and troubleshooting. # See docs/ops/deployment.md for first-time setup and troubleshooting.
set -euo pipefail set -euo pipefail
@@ -19,8 +20,13 @@ cd "$(dirname "$0")"
REF="${1:-main}" REF="${1:-main}"
echo "==> Deploying ref: ${REF}" echo "==> Deploying ref: ${REF}"
# Remember the current commit so we can see what changed after checkout. # Baseline for change detection: the last *fully* deployed commit, recorded at
BEFORE="$(git rev-parse HEAD)" # the end of a successful run (falls back to current HEAD the first time). Using
# a persisted marker instead of the pre-checkout HEAD keeps the diff honest even
# when the same ref is re-deployed or a previous run aborted partway — both of
# which otherwise make BEFORE == AFTER and silently skip build/install steps.
MARKER=".deploy-last-commit"
BEFORE="$(cat "${MARKER}" 2>/dev/null || git rev-parse HEAD)"
echo "==> Maintenance mode on" echo "==> Maintenance mode on"
php artisan down --retry=15 php artisan down --retry=15
@@ -36,23 +42,29 @@ fi
AFTER="$(git rev-parse HEAD)" AFTER="$(git rev-parse HEAD)"
CHANGED="$(git diff --name-only "${BEFORE}" "${AFTER}" || true)" CHANGED="$(git diff --name-only "${BEFORE}" "${AFTER}" || true)"
# Reinstall PHP deps only if the lockfile moved. # Reinstall PHP deps only when the lockfile moved or vendor is missing.
if grep -q '^composer\.lock$' <<<"${CHANGED}"; then if grep -q '^composer\.lock$' <<<"${CHANGED}" || [ ! -d vendor ]; then
echo "==> composer.lock changed — installing PHP deps" echo "==> Installing PHP deps (composer.lock changed or vendor missing)"
composer install --no-dev --optimize-autoloader composer install --no-dev --optimize-autoloader
else else
echo "==> composer.lock unchanged — skipping composer install" echo "==> composer.lock unchanged — skipping composer install"
fi fi
# Rebuild the Vue SPA only if frontend sources or the JS lockfile moved. # Install JS deps only when the lockfile moved or node_modules is missing.
if grep -qE '^(package(-lock)?\.json|vite\.config\.|resources/(js|css)/)' <<<"${CHANGED}"; then if grep -qE '^package(-lock)?\.json$' <<<"${CHANGED}" || [ ! -d node_modules ]; then
echo "==> Frontend changed — rebuilding SPA" echo "==> Installing JS deps (lockfile changed or node_modules missing)"
npm ci npm ci
npm run build
else else
echo "==> No frontend changes — skipping npm build" echo "==> JS deps unchanged — skipping npm ci"
fi fi
# Always rebuild the SPA. The build is cheap (a few seconds), and gating it on a
# git diff silently shipped stale assets whenever BEFORE == AFTER — re-deploying
# the same ref, or re-running after an aborted deploy. Correctness over the few
# seconds saved.
echo "==> Building SPA"
npm run build
echo "==> Running migrations" echo "==> Running migrations"
php artisan migrate --force php artisan migrate --force
@@ -68,5 +80,10 @@ php artisan queue:restart
echo "==> Maintenance mode off" echo "==> Maintenance mode off"
php artisan up php artisan up
# Record the just-deployed commit as the baseline for the next run. Only reached
# on full success — `set -e` aborts earlier on any failure, so a broken deploy
# never advances the baseline and the next run re-evaluates from the last good one.
git rev-parse HEAD > "${MARKER}"
echo "==> Deploy complete" echo "==> Deploy complete"
php artisan about php artisan about