From 51992814d2b2e6adb129df4a5f237395ec0a0e5e Mon Sep 17 00:00:00 2001 From: Joshua Oosthuizen Date: Mon, 2 Nov 2026 14:03:12 +0200 Subject: [PATCH] Deploy: stop first when files in the install were changed by hand (1.52.2) A setup wizard run on one server added files to backend/src that the backend build picked up, so the deploy failed part-way and rolled back. deploy.sh now lists files git reports as changed or added (ignored files and the PM2 config don't count) and stops before backing up or checking out. --check runs only that; ALLOW_LOCAL_CHANGES=1 deploys anyway; the script also runs from a copy outside the install (the folder you're in). Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 13 +++++++++++++ DEPLOYMENT.md | 18 +++++++++++++++++ WHATS-NEW.md | 5 +++++ backend/package-lock.json | 4 ++-- backend/package.json | 2 +- docs/HANDOVER.md | 5 +++-- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- package-lock.json | 4 ++-- package.json | 2 +- scripts/deploy.sh | 40 +++++++++++++++++++++++++++++++++++++- 11 files changed, 87 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aee493..e47fabf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project follows [Semantic Versioning](https://semver.org/). ## [Unreleased] +## [1.52.2] - 2026-11-02 + +### Changed + +- **`scripts/deploy.sh` stops before touching anything when files in the install folder were + changed or added by hand**, and lists them. A setup wizard run on one server had added files to + `backend/src` that the backend build picked up, so the deploy failed halfway and rolled back. + Ignored files (`.env`, uploads, backups, `ecosystem.config.js`) never count. + `ALLOW_LOCAL_CHANGES=1` deploys anyway. +- `bash scripts/deploy.sh --check` runs only that check (no backup, fetch, build or reload). The + script can also be run from a copy outside the install (e.g. `/tmp/deploy.sh`, from the folder + you're in), so an older install can check before updating — see DEPLOYMENT.md, "Updating". + ## [1.52.1] - 2026-11-02 ### Changed diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index fa0c249..87e5fc4 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -311,6 +311,24 @@ and goes back to the previous version if anything fails first. `backend/.env`, `frontend/.env.production`, `ecosystem.config.js` and the nginx/frpc configs are not in git, so updates keep your changes from this guide. +Since 1.52.2 it first checks for files in the install folder that were changed or added by hand +(e.g. by a setup wizard run on the server) and stops, listing them, before it backs anything up +or checks anything out: they would be built into the release, or block the checkout. Move added +files out of the folder and put changed ones back with `git checkout -- `, or deploy anyway +with `ALLOW_LOCAL_CHANGES=1`. To check without deploying: + +```bash +sudo -u hopeevents -H bash scripts/deploy.sh --check +``` + +An install still on an older release can run the new check without updating anything — fetch the +script on its own and run it from the install folder: + +```bash +git fetch -q origin && git show origin/main:scripts/deploy.sh > /tmp/deploy.sh +bash /tmp/deploy.sh --check +``` + ### Upgrading Node Node 20 stopped getting security fixes in April 2026; Cross Events runs on Node 24 LTS (supported diff --git a/WHATS-NEW.md b/WHATS-NEW.md index a3fa280..829f0f8 100644 --- a/WHATS-NEW.md +++ b/WHATS-NEW.md @@ -12,6 +12,11 @@ Write for church admins, not developers: no environment variables, endpoints or developer log is CHANGELOG.md. A tagged version without a section here shows "No details given". --> +## 1.52.2 + +- **Safer updates.** If files on your server were changed by hand, an update now stops straight + away and says which files, instead of failing part-way. Nothing else changes. + ## 1.52.1 - **Better documentation for developers.** If someone builds on your Cross Events site (for example diff --git a/backend/package-lock.json b/backend/package-lock.json index e58dae9..860527f 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,12 +1,12 @@ { "name": "event-management-backend", - "version": "1.52.1", + "version": "1.52.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "event-management-backend", - "version": "1.52.1", + "version": "1.52.2", "hasInstallScript": true, "dependencies": { "@cj-tech-master/excelts": "^10.2.0", diff --git a/backend/package.json b/backend/package.json index 83799d7..6728aae 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "event-management-backend", - "version": "1.52.1", + "version": "1.52.2", "description": "Event Management System Backend", "main": "src/index.js", "scripts": { diff --git a/docs/HANDOVER.md b/docs/HANDOVER.md index e4d2df7..8f52988 100644 --- a/docs/HANDOVER.md +++ b/docs/HANDOVER.md @@ -1,6 +1,6 @@ # Handover — where things stand and how to carry on -Last updated: 2026-11-02, after **1.52.1** (and Hub 1.5.0). The plan itself is in +Last updated: 2026-11-02, after **1.52.2** (and Hub 1.5.0). The plan itself is in [ROADMAP-v2.md](./ROADMAP-v2.md) (decisions table at the end); the previous round of features is in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md). @@ -14,6 +14,7 @@ encrypted settings unreadable or break existing installs. | Version | What | |---------|------| +| 1.52.2 | `scripts/deploy.sh` stops first (listing them) when files in the install were changed or added by hand; `--check` runs only that; runs from a copy outside the install too; `ALLOW_LOCAL_CHANGES=1` overrides | | 1.52.1 | **API documentation complete:** every v2 route documents its body, answer and parameters — the ~300 answered by v1 handlers through `documented` shapes (`src/v2/docs.ts`), shown but not enforced, logged outside production when they don't fit; empty bodies say "No body needed". Test `tests/v2Docs.test.ts` | | 1.52.0 | **Urgent fix + Corrections:** required attendee forms no longer leave paid door/manual registrations without tickets — popup with "Fill in later" (`Registration.formWaivedAt`, `POST /api/v2/registrations/{id}/form-later`); one form per person on picked sessions; forms with no questions don't hold tickets; kiosk asks for the forms the server says are missing. Admin **Corrections** page (`src/services/corrections.ts`, `/api/v2/corrections/…`): issue missing tickets, pay later (`ticketsReleasedAt`), make it free (`complimentaryDiscount`), check in after the event. Migration `20261102120000_corrections`. e2e `e2e/corrections.spec.ts` | | 1.51.17 | **Security:** forgot-password and sign-in don't reveal whether an account exists (same answer, status and timing); forgot-password rate-limited | @@ -128,7 +129,7 @@ Languages (Afrikaans, §10) and Wallet passes (§7) are removed from scope (2026 The backend declares `engines.node >= 22`. pg-boss 11 (1.51.7) runs in its own schema, `pgboss_v11` — it can't migrate pg-boss 10's `pgboss` schema; there's no `archive` table any more (finished runs stay in `job` until they expire). -- **Deploying:** `bash scripts/deploy.sh [tag]` on the server (no tag = latest main). Each install +- **Deploying:** `bash scripts/deploy.sh [tag]` on the server (no tag = latest main). It stops first if files in the install were changed or added by hand (`--check` to only check; `ALLOW_LOCAL_CHANGES=1` to go ahead anyway). Each install keeps its own `ecosystem.config.js` (PM2 app names, port), which is not in git. The script runs from a temp copy of itself and rolls back on failure before the PM2 reload. It was tested by running it against a throwaway clone with npm/pm2 stubbed: across the tracked → diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3849399..86d877d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "hope-events-frontend", - "version": "1.52.1", + "version": "1.52.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hope-events-frontend", - "version": "1.52.1", + "version": "1.52.2", "dependencies": { "@dnd-kit/core": "^6.3.1", "@hookform/resolvers": "^5.2.1", diff --git a/frontend/package.json b/frontend/package.json index ce23b1b..b7080d1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "hope-events-frontend", - "version": "1.52.1", + "version": "1.52.2", "private": true, "scripts": { "dev": "next dev", diff --git a/package-lock.json b/package-lock.json index fa17352..152a885 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "hope-events", - "version": "1.52.1", + "version": "1.52.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "hope-events", - "version": "1.52.1", + "version": "1.52.2", "license": "ISC", "devDependencies": { "@axe-core/playwright": "^4.14.0", diff --git a/package.json b/package.json index 68fc075..0a0617f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hope-events", - "version": "1.52.1", + "version": "1.52.2", "main": "index.js", "scripts": { "dev:backend": "cd backend && npm run dev", diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 872f5af..4cf1312 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -6,20 +6,34 @@ # 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 +# bash scripts/deploy.sh --check # only check for files changed by hand (changes nothing) # # 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. +# +# A deploy stops before touching anything when files in the repo folder were changed or added +# by hand: they'd be built into the release (or block the checkout). ALLOW_LOCAL_CHANGES=1 +# deploys anyway. Ignored files (.env, uploads, backups, the PM2 config) never count. set -Eeuo pipefail # 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 + DIR="${HOPE_DEPLOY_DIR:-$(cd "$(dirname "$0")/.." && pwd)}" + # Run from a copy outside the repo (e.g. downloaded to /tmp): the folder you're in. + if [ ! -d "$DIR/.git" ] || [ ! -d "$DIR/backend" ]; then DIR="$PWD"; fi COPY="$(mktemp)" cp "$0" "$COPY" - HOPE_DEPLOY_COPY=1 HOPE_DEPLOY_DIR="$(cd "$(dirname "$0")/.." && pwd)" exec bash "$COPY" "$@" + HOPE_DEPLOY_COPY=1 HOPE_DEPLOY_DIR="$DIR" exec bash "$COPY" "$@" fi cd "$HOPE_DEPLOY_DIR" +if [ ! -d .git ] || [ ! -d backend ]; then + echo "!!> $PWD isn't a Cross Events install. Run this from the install's folder (e.g. /var/www/hope-events)." + exit 1 +fi +CHECK_ONLY=0 +if [ "${1:-}" = "--check" ]; then CHECK_ONLY=1; shift; fi TARGET="${1:-main}" PREVIOUS="$(git describe --tags --exact-match 2>/dev/null || git rev-parse --short HEAD)" @@ -51,6 +65,30 @@ checkout() { restore_pm2_config } +# Files changed or added by hand (anything git reports, apart from this install's PM2 config, +# which releases before 1.37.4 tracked). Ignored files — .env, uploads, backups — aren't listed. +local_changes() { + git status --porcelain --untracked-files=all | awk -v keep="$PM2_CONFIG" 'substr($0, 4) != keep' +} + +CHANGES="$(local_changes)" +if [ -n "$CHANGES" ]; then + echo "!!> Files in $PWD were changed or added by hand:" + echo "$CHANGES" | sed 's/^/ /' + echo " A release would build them in (or refuse to replace them), so the deploy can fail." + echo " Move added files (??) somewhere outside this folder, and put changed ones ( M) back with" + echo " \"git checkout -- \" — after checking nothing in them is needed." + if [ "$CHECK_ONLY" = 1 ]; then exit 1; fi + if [ "${ALLOW_LOCAL_CHANGES:-}" != 1 ]; then + echo " Nothing was changed. To deploy anyway: ALLOW_LOCAL_CHANGES=1 bash scripts/deploy.sh $TARGET" + exit 1 + fi + echo " ALLOW_LOCAL_CHANGES=1 — deploying anyway." +elif [ "$CHECK_ONLY" = 1 ]; then + echo "==> No files changed by hand in $PWD — ready to deploy." +fi +if [ "$CHECK_ONLY" = 1 ]; then exit 0; fi + echo "==> Deploying $TARGET (currently $PREVIOUS)" # Node 20 reached end of life in April 2026 (no more security fixes). Releases still run on it,