Files
fuel-alert/deploy.sh
Ovidiu U 347a71154b 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>
2026-06-12 10:41:47 +01:00

90 lines
3.2 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# FuelAlert deploy script — run on the VPS from the project root:
#
# ./deploy.sh # deploy the latest main
# ./deploy.sh v0.1.3 # deploy a specific tag
#
# It puts the site in maintenance mode, updates the code, runs migrations and
# cache rebuilds, restarts the queue, then brings the site back up. The SPA is
# always rebuilt; the heavier dependency installs (composer install, npm ci)
# only run when their lockfiles changed or the installed dir is missing. If any
# 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.
set -euo pipefail
cd "$(dirname "$0")"
REF="${1:-main}"
echo "==> Deploying ref: ${REF}"
# Baseline for change detection: the last *fully* deployed commit, recorded at
# 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"
php artisan down --retry=15
git fetch --tags --prune origin
git checkout "${REF}"
# Fast-forward to the remote only when on a branch (a tag leaves a detached HEAD).
if git symbolic-ref -q HEAD >/dev/null; then
git pull --ff-only
fi
AFTER="$(git rev-parse HEAD)"
CHANGED="$(git diff --name-only "${BEFORE}" "${AFTER}" || true)"
# Reinstall PHP deps only when the lockfile moved or vendor is missing.
if grep -q '^composer\.lock$' <<<"${CHANGED}" || [ ! -d vendor ]; then
echo "==> Installing PHP deps (composer.lock changed or vendor missing)"
composer install --no-dev --optimize-autoloader
else
echo "==> composer.lock unchanged — skipping composer install"
fi
# Install JS deps only when the lockfile moved or node_modules is missing.
if grep -qE '^package(-lock)?\.json$' <<<"${CHANGED}" || [ ! -d node_modules ]; then
echo "==> Installing JS deps (lockfile changed or node_modules missing)"
npm ci
else
echo "==> JS deps unchanged — skipping npm ci"
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"
php artisan migrate --force
echo "==> Rebuilding caches"
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan event:cache
echo "==> Restarting queue workers"
php artisan queue:restart
echo "==> Maintenance mode off"
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"
php artisan about