Deploy: latest main without a tag; per-install PM2 config out of git
- scripts/deploy.sh with no tag deploys the latest main (detached origin/main); with a tag, that release. - ecosystem.config.js is no longer in git (now ecosystem.config.example.js; the deploy creates the install's copy from it once). The deploy keeps the install's copy safe across checkouts and rollbacks, including releases that still tracked it. - The script runs from a temp copy, so checking out a new deploy.sh can't change it mid-run; set -E so failures in functions still roll back. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,9 @@ yarn-error.log*
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# this install's PM2 config (made from ecosystem.config.example.js; deploys never touch it)
|
||||
/ecosystem.config.js
|
||||
|
||||
# misc scratch / generated files
|
||||
temp/
|
||||
backend/public/uploads/
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// PM2 config template. Each install keeps its own copy as ecosystem.config.js (not in git,
|
||||
// so deploys never change it); scripts/deploy.sh creates it from this file the first time.
|
||||
// Change the app names (unique per install on a server) and the frontend port to suit.
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
+61
-13
@@ -1,31 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
# Updates this install to a tagged release: back up the database, check out the tag,
|
||||
# install, migrate, build, and reload PM2. If anything fails before the reload, the
|
||||
# previous version keeps running and the checkout goes back to it.
|
||||
# Updates this install: back up the database, check out the release, install, migrate,
|
||||
# build, and reload PM2. If anything fails before the reload, the previous version keeps
|
||||
# running and the checkout goes back to it.
|
||||
#
|
||||
# Usage (on the server, from the repo folder): bash scripts/deploy.sh v1.34.0
|
||||
# CI runs this over SSH when a v* tag is pushed (see .gitea/workflows/ci.yml).
|
||||
set -euo pipefail
|
||||
# Usage (on the server, from the repo folder):
|
||||
# bash scripts/deploy.sh # the latest main
|
||||
# bash scripts/deploy.sh v1.37.3 # a tagged release
|
||||
# CI runs it with the tag over SSH when a v* tag is pushed (see .gitea/workflows/ci.yml).
|
||||
#
|
||||
# PM2 uses this install's own ecosystem.config.js (app names, ports). It isn't in git, so
|
||||
# deploys never change it; the first deploy creates it from ecosystem.config.example.js.
|
||||
set -Eeuo pipefail
|
||||
|
||||
TAG="${1:?Usage: scripts/deploy.sh <tag>}"
|
||||
cd "$(dirname "$0")/.."
|
||||
# The checkout below replaces this file, and bash reads a script as it runs — so run from a
|
||||
# copy that can't change underneath us.
|
||||
if [ -z "${HOPE_DEPLOY_COPY:-}" ]; then
|
||||
COPY="$(mktemp)"
|
||||
cp "$0" "$COPY"
|
||||
HOPE_DEPLOY_COPY=1 HOPE_DEPLOY_DIR="$(cd "$(dirname "$0")/.." && pwd)" exec bash "$COPY" "$@"
|
||||
fi
|
||||
|
||||
cd "$HOPE_DEPLOY_DIR"
|
||||
TARGET="${1:-main}"
|
||||
PREVIOUS="$(git describe --tags --exact-match 2>/dev/null || git rev-parse --short HEAD)"
|
||||
|
||||
echo "==> Deploying $TAG (currently $PREVIOUS)"
|
||||
# Keep this install's PM2 config safe across the checkout (older releases tracked it in git).
|
||||
PM2_CONFIG="ecosystem.config.js"
|
||||
PM2_KEEP="$(mktemp)"
|
||||
HAVE_PM2_CONFIG=0
|
||||
if [ -f "$PM2_CONFIG" ]; then cp "$PM2_CONFIG" "$PM2_KEEP"; HAVE_PM2_CONFIG=1; fi
|
||||
trap 'rm -f "$0" "$PM2_KEEP"' EXIT
|
||||
# Out of the way before a checkout: back to the committed version if git tracks it in this
|
||||
# release, otherwise removed (a release that still tracks it would refuse to overwrite it).
|
||||
clear_pm2_config() {
|
||||
git checkout -q -- "$PM2_CONFIG" 2>/dev/null || rm -f "$PM2_CONFIG"
|
||||
}
|
||||
restore_pm2_config() {
|
||||
if [ "$HAVE_PM2_CONFIG" = 1 ]; then cp "$PM2_KEEP" "$PM2_CONFIG"; fi
|
||||
}
|
||||
|
||||
# Check out a tag, or the latest main (detached, so local main is never in the way).
|
||||
checkout() {
|
||||
clear_pm2_config
|
||||
if [ "$1" = "main" ]; then
|
||||
git fetch --quiet origin main
|
||||
git checkout -q --detach origin/main
|
||||
else
|
||||
git fetch --tags --quiet origin
|
||||
git checkout -q "$1"
|
||||
fi
|
||||
restore_pm2_config
|
||||
}
|
||||
|
||||
echo "==> Deploying $TARGET (currently $PREVIOUS)"
|
||||
|
||||
echo "==> Backing up the database"
|
||||
(cd backend && node -e "require('dotenv').config(); require('./src/utils/backupUtils').runBackup().then(r => console.log(' ', r.filename)).catch(e => { console.error(' Backup failed:', e.message); process.exit(1); })")
|
||||
|
||||
rollback() {
|
||||
echo "!!> Deploy failed — going back to $PREVIOUS (the running version was not touched)"
|
||||
clear_pm2_config
|
||||
git checkout -q "$PREVIOUS"
|
||||
restore_pm2_config
|
||||
(cd backend && npm ci --silent) || true
|
||||
(cd frontend && npm ci --silent && npm run build --silent) || true
|
||||
}
|
||||
trap rollback ERR
|
||||
|
||||
git fetch --tags --quiet origin
|
||||
git checkout -q "$TAG"
|
||||
checkout "$TARGET"
|
||||
DEPLOYED="$(git describe --tags --exact-match 2>/dev/null || git rev-parse --short HEAD)"
|
||||
|
||||
if [ ! -f "$PM2_CONFIG" ]; then
|
||||
cp ecosystem.config.example.js "$PM2_CONFIG"
|
||||
echo " Created $PM2_CONFIG from the example — check its app names and port."
|
||||
fi
|
||||
|
||||
echo "==> Backend: install and migrate"
|
||||
(cd backend && npm ci && npm run prisma:deploy)
|
||||
@@ -35,6 +83,6 @@ echo "==> Frontend: install and build"
|
||||
|
||||
trap - ERR
|
||||
echo "==> Reloading PM2"
|
||||
pm2 reload ecosystem.config.js --update-env
|
||||
pm2 reload "$PM2_CONFIG" --update-env
|
||||
|
||||
echo "==> Deployed $TAG"
|
||||
echo "==> Deployed $TARGET ($DEPLOYED)"
|
||||
|
||||
Reference in New Issue
Block a user