#!/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