Merge branch 'fix/deploy-script'
This commit is contained in:
@@ -31,6 +31,9 @@ yarn-error.log*
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
|
# this install's PM2 config (made from ecosystem.config.example.js; deploys never touch it)
|
||||||
|
/ecosystem.config.js
|
||||||
|
|
||||||
# misc scratch / generated files
|
# misc scratch / generated files
|
||||||
temp/
|
temp/
|
||||||
backend/public/uploads/
|
backend/public/uploads/
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.37.4] - 2026-10-23
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Deploy script** (`scripts/deploy.sh`): without a tag it now deploys the latest `main`. The PM2 config is per install: `ecosystem.config.js` is no longer in git (the repo has `ecosystem.config.example.js`), so deploys stop overwriting the install's app names and port; the script keeps the install's copy across checkouts and rollbacks, and creates it from the example on a fresh install. The script also runs from a temporary copy (so checking out a new version of it mid-deploy is safe), and a failure rolls back once.
|
||||||
|
|
||||||
## [1.37.3] - 2026-10-23
|
## [1.37.3] - 2026-10-23
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -98,8 +98,18 @@ Documentation: see Frontend README for environment and scripts.
|
|||||||
|
|
||||||
## Deployment
|
## Deployment
|
||||||
|
|
||||||
To update a running install to a release: `bash scripts/deploy.sh v1.34.0` on the server
|
To update a running install, on the server in the repo folder:
|
||||||
(backs up the database, migrates, builds, reloads PM2).
|
|
||||||
|
- `bash scripts/deploy.sh` — the latest `main`
|
||||||
|
- `bash scripts/deploy.sh v1.37.4` — a tagged release
|
||||||
|
|
||||||
|
It backs up the database, checks the version out, installs, migrates, builds and reloads PM2.
|
||||||
|
If anything fails before the reload, it goes back to the previous version (the running app
|
||||||
|
isn't touched).
|
||||||
|
|
||||||
|
PM2 uses the install's own `ecosystem.config.js` (app names, frontend port). It isn't in git,
|
||||||
|
so deploys never change it; the first deploy creates it from `ecosystem.config.example.js` —
|
||||||
|
set names that match your PM2 apps (`pm2 ls`) and the port you use.
|
||||||
|
|
||||||
### Backend Deployment
|
### Backend Deployment
|
||||||
|
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "event-management-backend",
|
"name": "event-management-backend",
|
||||||
"version": "1.37.3",
|
"version": "1.37.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "event-management-backend",
|
"name": "event-management-backend",
|
||||||
"version": "1.37.3",
|
"version": "1.37.4",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/adapter-pg": "^7.10.1",
|
"@prisma/adapter-pg": "^7.10.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "event-management-backend",
|
"name": "event-management-backend",
|
||||||
"version": "1.37.3",
|
"version": "1.37.4",
|
||||||
"description": "Event Management System Backend",
|
"description": "Event Management System Backend",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+7
-1
@@ -1,6 +1,6 @@
|
|||||||
# Handover — where things stand and how to carry on
|
# Handover — where things stand and how to carry on
|
||||||
|
|
||||||
Last updated: 2026-10-23, after **1.37.3**. The plan itself is in
|
Last updated: 2026-10-23, after **1.37.4**. The plan itself is in
|
||||||
[ROADMAP-v2.md](./ROADMAP-v2.md) (decisions table at the end); the previous round of
|
[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).
|
features is in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md).
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ features is in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md).
|
|||||||
| 1.33.0 | Multi-day events phase A — sessions, late joining + late pricing, per-session check-in, attendance register, session reminders, certificates (4 templates, verify page) |
|
| 1.33.0 | Multi-day events phase A — sessions, late joining + late pricing, per-session check-in, attendance register, session reminders, certificates (4 templates, verify page) |
|
||||||
| 1.34.0 | Background jobs on pg-boss (admin Background jobs page), Playwright e2e tests, Gitea CI workflow, `scripts/deploy.sh` |
|
| 1.34.0 | Background jobs on pg-boss (admin Background jobs page), Playwright e2e tests, Gitea CI workflow, `scripts/deploy.sh` |
|
||||||
| 1.35.0 | Multi-day events phase B — "pick sessions" (per-session tickets and prices, session capacity) |
|
| 1.35.0 | Multi-day events phase B — "pick sessions" (per-session tickets and prices, session capacity) |
|
||||||
|
| 1.37.4 | Deploy script: no tag = latest main; per-install `ecosystem.config.js` out of git (`ecosystem.config.example.js`) |
|
||||||
| 1.37.3 | Fix: the production build failed on unescaped quotes in a help text |
|
| 1.37.3 | Fix: the production build failed on unescaped quotes in a help text |
|
||||||
| 1.37.2 | Payment status checks (PayFast transaction history; job every 15 min; Payments → Reconcile → Waiting for payment), PayFast Test connection |
|
| 1.37.2 | Payment status checks (PayFast transaction history; job every 15 min; Payments → Reconcile → Waiting for payment), PayFast Test connection |
|
||||||
| 1.37.1 | "Pay online" hidden when no provider is on or under R15; SnapScan/Zapper dropped from the roadmap |
|
| 1.37.1 | "Pay online" hidden when no provider is on or under R15; SnapScan/Zapper dropped from the roadmap |
|
||||||
@@ -44,6 +45,11 @@ features is in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md).
|
|||||||
need a migration.
|
need a migration.
|
||||||
- **Server Node version:** the deployment notes install Node 20, so dependencies must
|
- **Server Node version:** the deployment notes install Node 20, so dependencies must
|
||||||
support Node 20 (that's why pg-boss is pinned to 10.x).
|
support Node 20 (that's why pg-boss is pinned to 10.x).
|
||||||
|
- **Deploying:** `bash scripts/deploy.sh [tag]` on the server (no tag = latest main). 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 →
|
||||||
|
untracked change for the PM2 file, deploying main, failed builds and a bad tag.
|
||||||
- **Pushing:** pushing from some shells fails with "Failed to authenticate user" (a stale
|
- **Pushing:** pushing from some shells fails with "Failed to authenticate user" (a stale
|
||||||
saved credential); pushing from your own terminal works.
|
saved credential); pushing from your own terminal works.
|
||||||
|
|
||||||
|
|||||||
@@ -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 = {
|
module.exports = {
|
||||||
apps: [
|
apps: [
|
||||||
{
|
{
|
||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "hope-events-frontend",
|
"name": "hope-events-frontend",
|
||||||
"version": "1.37.3",
|
"version": "1.37.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "hope-events-frontend",
|
"name": "hope-events-frontend",
|
||||||
"version": "1.37.3",
|
"version": "1.37.4",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@hookform/resolvers": "^5.2.1",
|
"@hookform/resolvers": "^5.2.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hope-events-frontend",
|
"name": "hope-events-frontend",
|
||||||
"version": "1.37.3",
|
"version": "1.37.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "hope-events",
|
"name": "hope-events",
|
||||||
"version": "1.37.3",
|
"version": "1.37.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "hope-events",
|
"name": "hope-events",
|
||||||
"version": "1.37.3",
|
"version": "1.37.4",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.64.0",
|
"@playwright/test": "^1.64.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hope-events",
|
"name": "hope-events",
|
||||||
"version": "1.37.3",
|
"version": "1.37.4",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev:backend": "cd backend && npm run dev",
|
"dev:backend": "cd backend && npm run dev",
|
||||||
|
|||||||
+63
-13
@@ -1,31 +1,81 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Updates this install to a tagged release: back up the database, check out the tag,
|
# Updates this install: back up the database, check out the release, install, migrate,
|
||||||
# install, migrate, build, and reload PM2. If anything fails before the reload, the
|
# build, and reload PM2. If anything fails before the reload, the previous version keeps
|
||||||
# previous version keeps running and the checkout goes back to it.
|
# running and the checkout goes back to it.
|
||||||
#
|
#
|
||||||
# Usage (on the server, from the repo folder): bash scripts/deploy.sh v1.34.0
|
# Usage (on the server, from the repo folder):
|
||||||
# CI runs this over SSH when a v* tag is pushed (see .gitea/workflows/ci.yml).
|
# bash scripts/deploy.sh # the latest main
|
||||||
set -euo pipefail
|
# 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>}"
|
# The checkout below replaces this file, and bash reads a script as it runs — so run from a
|
||||||
cd "$(dirname "$0")/.."
|
# 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)"
|
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"
|
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); })")
|
(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() {
|
rollback() {
|
||||||
|
# set -E passes this trap into ( … ) subshells too; only the script itself rolls back.
|
||||||
|
if [ "$BASH_SUBSHELL" -gt 0 ]; then return; fi
|
||||||
echo "!!> Deploy failed — going back to $PREVIOUS (the running version was not touched)"
|
echo "!!> Deploy failed — going back to $PREVIOUS (the running version was not touched)"
|
||||||
|
clear_pm2_config
|
||||||
git checkout -q "$PREVIOUS"
|
git checkout -q "$PREVIOUS"
|
||||||
|
restore_pm2_config
|
||||||
(cd backend && npm ci --silent) || true
|
(cd backend && npm ci --silent) || true
|
||||||
(cd frontend && npm ci --silent && npm run build --silent) || true
|
(cd frontend && npm ci --silent && npm run build --silent) || true
|
||||||
}
|
}
|
||||||
trap rollback ERR
|
trap rollback ERR
|
||||||
|
|
||||||
git fetch --tags --quiet origin
|
checkout "$TARGET"
|
||||||
git checkout -q "$TAG"
|
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"
|
echo "==> Backend: install and migrate"
|
||||||
(cd backend && npm ci && npm run prisma:deploy)
|
(cd backend && npm ci && npm run prisma:deploy)
|
||||||
@@ -35,6 +85,6 @@ echo "==> Frontend: install and build"
|
|||||||
|
|
||||||
trap - ERR
|
trap - ERR
|
||||||
echo "==> Reloading PM2"
|
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