Compare commits

...
Author SHA1 Message Date
joshuaandClaude Opus 5.5 96adf20cf0 Deploy script: roll back once (not inside subshells); docs; bump to 1.37.4
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:20:22 +02:00
joshuaandClaude Opus 5.5 a9c7a1129b 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>
2026-10-23 16:15:29 +02:00
joshua d8eb9f22cc Merge branch 'fix/help-entities'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
2026-10-23 15:57:46 +02:00
joshuaandClaude Opus 5.5 4cae0fc672 Fix the production build: escape quotes in the Payments help text
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
The deploy's next build fails on react/no-unescaped-entities. Bump to
1.37.3; the handover now says to lint the whole frontend before tagging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:57:46 +02:00
joshua cd33ba281e Merge branch 'feat/payment-status'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
2026-10-23 15:28:31 +02:00
13 changed files with 112 additions and 26 deletions
+3
View File
@@ -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/
+12
View File
@@ -7,6 +7,18 @@ and this project follows [Semantic Versioning](https://semver.org/).
## [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
### Fixed
- The production build (and so the deploy) failed on unescaped quotes in the Payments help text.
## [1.37.2] - 2026-10-23
### Added
+12 -2
View File
@@ -98,8 +98,18 @@ Documentation: see Frontend README for environment and scripts.
## Deployment
To update a running install to a release: `bash scripts/deploy.sh v1.34.0` on the server
(backs up the database, migrates, builds, reloads PM2).
To update a running install, on the server in the repo folder:
- `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
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "event-management-backend",
"version": "1.37.2",
"version": "1.37.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "event-management-backend",
"version": "1.37.2",
"version": "1.37.4",
"hasInstallScript": true,
"dependencies": {
"@prisma/adapter-pg": "^7.10.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "event-management-backend",
"version": "1.37.2",
"version": "1.37.4",
"description": "Event Management System Backend",
"main": "src/index.js",
"scripts": {
+9 -1
View File
@@ -1,6 +1,6 @@
# Handover — where things stand and how to carry on
Last updated: 2026-10-23, after **1.37.2**. 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
features is in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md).
@@ -12,6 +12,8 @@ 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.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.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.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.0 | PayFast (signed form, ITN checks), `ProviderCheckout` with amount checks, the "Pay online" page (`/pay/<id>`) where payers choose a provider |
@@ -31,6 +33,7 @@ features is in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md).
`## [X.Y.Z] - date` heading under `[Unreleased]` in CHANGELOG.md → `git merge --no-ff`
into `main` → `git tag vX.Y.Z` → `git push origin main <branch> vX.Y.Z`. One minor
version per feature.
- **Before tagging:** run `npx eslint src` in `frontend` over the **whole** app (the deploy runs `next build`, which fails on any lint error, e.g. an unescaped ' or " in JSX text). If a dev server is running, a full check build needs its own folder: `NEXT_DIST_DIR=.next-buildcheck npx next build` (then delete it and `git checkout frontend/tsconfig.json`).
- **Changelog:** add entries to `[Unreleased]` as you go, in plain language.
- **Docs to keep in step:** CHANGELOG.md, FEATURES.md, README.md, SETTINGS.md,
backend/API_DOCUMENTATION.md, the `/docs` route list in `backend/src/index.js`, and
@@ -42,6 +45,11 @@ features is in [FEATURES-ROUND-1.md](./FEATURES-ROUND-1.md).
need a migration.
- **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).
- **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
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 = {
apps: [
{
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "hope-events-frontend",
"version": "1.37.2",
"version": "1.37.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "hope-events-frontend",
"version": "1.37.2",
"version": "1.37.4",
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@hookform/resolvers": "^5.2.1",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hope-events-frontend",
"version": "1.37.2",
"version": "1.37.4",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
@@ -25,7 +25,7 @@ export const supervisorPaymentsHelpContent: HelpContent = {
Assign an unassigned donation to a registration with an outstanding balance. Donations can be partially allocated, leaving the remainder available for another registration. Money from &quot;Sponsor a spot&quot; is marked as a sponsorship. Tick &quot;Only the sponsorship pool&quot; to assign just those, and you&apos;ll see the sponsor&apos;s message. The Donations breakdown report shows each event&apos;s pool: received, assigned and remaining.
</GuideItem>
<GuideItem icon={ScanLine} title="Reconcile" tone="green">
Match unreconciled online payments (from Yoco, PayFast…) to a registration, log them as a donation, or ignore them. A payment of a different amount from its checkout lands here with a note — check it in the provider's dashboard first. "Waiting for payment" lists online checkouts with no payment yet: if someone paid but it hasn't shown up, press Check status (PayFast) — it's also checked automatically every 15 minutes. Yoco can't be checked automatically: find the payment in the Yoco portal.
Match unreconciled online payments (from Yoco, PayFast…) to a registration, log them as a donation, or ignore them. A payment of a different amount from its checkout lands here with a note — check it in the provider&apos;s dashboard first. &quot;Waiting for payment&quot; lists online checkouts with no payment yet: if someone paid but it hasn&apos;t shown up, press Check status (PayFast) — it&apos;s also checked automatically every 15 minutes. Yoco can&apos;t be checked automatically: find the payment in the Yoco portal.
</GuideItem>
<GuideItem icon={Link2} title="Payment Link" tone="violet">
Generate an online payment link for a user — copy it or send by email/WhatsApp. It uses the default payment provider (Site settings → Payments). Online payments must be at least R15. Generating a new link invalidates the previous one.
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "hope-events",
"version": "1.37.2",
"version": "1.37.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "hope-events",
"version": "1.37.2",
"version": "1.37.4",
"license": "ISC",
"devDependencies": {
"@playwright/test": "^1.64.0",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "hope-events",
"version": "1.37.2",
"version": "1.37.4",
"main": "index.js",
"scripts": {
"dev:backend": "cd backend && npm run dev",
+63 -13
View File
@@ -1,31 +1,81 @@
#!/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() {
# 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)"
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 +85,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)"