From 6b0210078a9bdd83e3df09ff525022753629b4b6 Mon Sep 17 00:00:00 2001 From: Hardik Date: Wed, 24 Jun 2026 02:59:36 +0530 Subject: [PATCH 1/2] chore(deploy): build & (re)start microservices on release tag The v* tag deploy previously only updated the Next app (ppms); GstService / EpfoService / PdfService were never built or restarted by automation. Now the same deploy manages them. - ecosystem.config.js (root): pm2 definitions for gst-service (3003) / epfo-service (3004) / pdf-service (3005). Registers only services whose source is checked out (keyed on package.json), so a not-yet-merged service is skipped and adopted automatically once its PR lands. Secrets come from the env at pm2 invocation; ports are fixed here. - deploy.yml: after the app restart, export the few service secrets out of App/.env (never PORT or the ephemeral FORGEJO_TOKEN), npm install + playwright install chromium + build each present service, then `pm2 startOrReload ecosystem.config.js --update-env` (create on first release, reload after) + pm2 save, and health-check :3003/:3004/:3005. - automation/README.md: documents the flow + the one-time alignment for any pre-existing differently-named pm2 process. Co-Authored-By: Claude Opus 4.8 (1M context) --- .forgejo/workflows/deploy.yml | 50 +++++++++++++++++++++++++++++++++++ automation/README.md | 27 +++++++++++++++++++ ecosystem.config.js | 43 ++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 ecosystem.config.js diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 40b7e2a..57cc197 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -40,6 +40,56 @@ jobs: pm2 restart ppms echo "=== Deployed $TAG ===" + - name: Build & (re)start microservices + run: | + set -euo pipefail + export NVM_DIR="$HOME/.nvm" + . "$NVM_DIR/nvm.sh" + + cd "$HOME/pms" + + # Pull only the few keys the services need out of the app's .env (the + # single source of truth on the host). Never import PORT (each service's + # port is fixed in ecosystem.config.js) or the runner's ephemeral + # FORGEJO_TOKEN. Missing keys → empty, which the services tolerate. + envget() { grep -E "^$1=" App/.env 2>/dev/null | head -1 | sed -E 's/^[^=]+=//; s/^"//; s/"$//'; } + export PDF_SERVICE_TOKEN="$(envget PDF_SERVICE_TOKEN)" + export ALLOWED_ORIGIN="$(envget ALLOWED_ORIGIN)" + export EPFO_LIVE="$(envget EPFO_LIVE)" + + # Build each present service (skip any not yet in the tree, e.g. before + # its feature PR has merged). npm install (not ci) — not every service + # carries a lockfile. Playwright's postinstall fetches the browser; the + # explicit install is a cached, idempotent backstop. + for svc in GstService EpfoService PdfService; do + [ -f "$svc/package.json" ] || { echo "skip $svc (absent)"; continue; } + echo "=== Building $svc ===" + ( cd "$svc" && npm install --no-audit --no-fund && npx playwright install chromium && npm run build ) + done + + # Create on first release, zero-downtime reload thereafter. The + # ecosystem registers only services whose dirs exist. + pm2 startOrReload ecosystem.config.js --update-env + pm2 save + pm2 list + echo "=== Microservices up ===" + + - name: Verify services respond + run: | + sleep 3 + cd "$HOME/pms" + check() { + local dir="$1" port="$2" + [ -f "$dir/package.json" ] || { echo "skip $dir (absent)"; return 0; } + local code + code=$(curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1:$port/health" || echo "000") + echo "$dir on :$port /health → HTTP $code" + test "$code" = "200" + } + check GstService 3003 + check EpfoService 3004 + check PdfService 3005 + - name: Verify portal responds run: | sleep 5 diff --git a/automation/README.md b/automation/README.md index 76e3c03..042a36a 100644 --- a/automation/README.md +++ b/automation/README.md @@ -178,6 +178,33 @@ The runner checks out the tag in `~/pms`, runs `pnpm install` + `build` + `prisma migrate deploy`, `pm2 restart ppms`, and verifies `/login` returns 200. Watch progress under **Actions** on the Forgejo repo, or `pm2 logs forgejo-runner` on pms1. +## Microservices (GstService / EpfoService / PdfService) + +The standalone Playwright services are deployed by the **same `v*` tag** as the app. +After the app restart, `deploy.yml`: + +1. exports the few secrets the services need out of `~/pms/App/.env` + (`PDF_SERVICE_TOKEN`, `ALLOWED_ORIGIN`, `EPFO_LIVE`) — never `PORT` or the + runner's ephemeral `FORGEJO_TOKEN`; +2. for each service folder present, runs `npm install` + `npx playwright install + chromium` + `npm run build`; +3. runs `pm2 startOrReload ecosystem.config.js --update-env` — which **creates** + the pm2 processes on the first release and **reloads** them on every release + after — then `pm2 save`; +4. health-checks `:3003` / `:3004` / `:3005` (`/health` → 200). + +`ecosystem.config.js` (repo root) is the source of truth: canonical pm2 names +`gst-service` / `epfo-service` / `pdf-service`, fixed ports, and it registers +**only services whose folder is checked out** (so a not-yet-merged service is +skipped, and adopted automatically once its PR lands). + +**One-time alignment:** if a service is already running on pms1 under a *different* +pm2 name, delete it once (`pm2 delete && pm2 save`) so the canonical +process can bind its port — otherwise the new one fails on a port clash. PdfService +additionally needs Chromium system libs the first time (`npx playwright install +--with-deps chromium`, which needs sudo); the deploy's plain `playwright install +chromium` only fetches the browser binary. + ## Operational notes - The watcher runs Claude Code with `--dangerously-skip-permissions` inside the diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 0000000..47a536f --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,43 @@ +// pm2 process definitions for the Pelagia microservices. +// +// The Next app (pm2 `ppms`) is deployed/restarted separately by +// .forgejo/workflows/deploy.yml — deliberately, so its delicate env handling +// (avoiding the runner's ephemeral FORGEJO_TOKEN) is untouched. This file covers +// only the standalone Playwright services. The deploy builds each, then runs +// pm2 startOrReload ecosystem.config.js --update-env +// which CREATES them on first release and RELOADS them on subsequent ones. +// +// Resilient to services that aren't in the tree yet (e.g. PdfService lands with +// its feature PR): only directories that exist are registered. Ports are fixed +// here (matching the app's *_SERVICE_URL defaults). Secrets are read from the +// deploy environment at `pm2` invocation time — the deploy exports the few keys +// these services need out of App/.env first (never PORT or FORGEJO_*). Unset → +// harmless defaults (GST/EPFO stay stub-capable, PdfService skips token/origin checks). +const path = require("path"); +const fs = require("fs"); +const root = __dirname; + +const DEFS = [ + { name: "gst-service", dir: "GstService", env: { PORT: "3003" } }, + { name: "epfo-service", dir: "EpfoService", env: { PORT: "3004", EPFO_LIVE: process.env.EPFO_LIVE || "" } }, + { + name: "pdf-service", + dir: "PdfService", + env: { + PORT: "3005", + PDF_SERVICE_TOKEN: process.env.PDF_SERVICE_TOKEN || "", + ALLOWED_ORIGIN: process.env.ALLOWED_ORIGIN || "", + }, + }, +]; + +module.exports = { + // Key on package.json — present only when the service source is checked out + // (a lingering gitignored node_modules/ dir must not register a phantom app). + apps: DEFS.filter((d) => fs.existsSync(path.join(root, d.dir, "package.json"))).map((d) => ({ + name: d.name, + cwd: path.join(root, d.dir), + script: "dist/index.js", + env: { NODE_ENV: "production", ...d.env }, + })), +}; From cd2bcefdbd9cb38aa0130ea395a452900978f3bf Mon Sep 17 00:00:00 2001 From: Hardik Date: Wed, 24 Jun 2026 03:12:54 +0530 Subject: [PATCH 2/2] fix(deploy): expand sparse checkout so microservices/ecosystem are on disk Run #120 (v0.3.0 deploy) failed at the microservice step: every service folder and ecosystem.config.js were "absent", and pm2 reported "File ecosystem.config.js not found". Root cause: ~/pms on pms1 is a sparse checkout limited to App/, so `git checkout -f $TAG` never materialised the service folders or the root ecosystem.config.js. The app itself deployed fine (App/ is in the sparse set) and prod stayed healthy. - deploy.yml: before managing services, disable sparse-checkout (and clear the legacy core.sparseCheckout config + .git/info/sparse-checkout), then re-checkout the tag to materialise the full tree. Idempotent / no-op once expanded. - Guard the pm2 call: if ecosystem.config.js is still absent, fail with a clear diagnostic (+ sparse-checkout list) instead of the cryptic PM2 error. - README: note the sparse-checkout expansion. Needs a fresh tag (e.g. v0.3.1) to re-run the deploy. Co-Authored-By: Claude Opus 4.8 (1M context) --- .forgejo/workflows/deploy.yml | 14 ++++++++++++++ automation/README.md | 8 ++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 57cc197..6b5c45d 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -48,6 +48,15 @@ jobs: cd "$HOME/pms" + # ~/pms has historically been a SPARSE checkout limited to App/ (only the + # app deployed), so the service folders + ecosystem.config.js never landed + # on disk. Expand the working tree to the full repo, then re-materialise + # the tag. Idempotent: a no-op once sparse is disabled / if never sparse. + git sparse-checkout disable 2>/dev/null || true + git config --unset core.sparseCheckout 2>/dev/null || true + rm -f .git/info/sparse-checkout 2>/dev/null || true + git checkout -f "refs/tags/${GITHUB_REF_NAME}" + # Pull only the few keys the services need out of the app's .env (the # single source of truth on the host). Never import PORT (each service's # port is fixed in ecosystem.config.js) or the runner's ephemeral @@ -69,6 +78,11 @@ jobs: # Create on first release, zero-downtime reload thereafter. The # ecosystem registers only services whose dirs exist. + if [ ! -f ecosystem.config.js ]; then + echo "ERROR: ecosystem.config.js absent in $(pwd) after checkout — sparse-checkout not expanded?" + git sparse-checkout list 2>/dev/null || true + exit 1 + fi pm2 startOrReload ecosystem.config.js --update-env pm2 save pm2 list diff --git a/automation/README.md b/automation/README.md index 042a36a..4b4a88e 100644 --- a/automation/README.md +++ b/automation/README.md @@ -181,9 +181,13 @@ progress under **Actions** on the Forgejo repo, or `pm2 logs forgejo-runner` on ## Microservices (GstService / EpfoService / PdfService) The standalone Playwright services are deployed by the **same `v*` tag** as the app. -After the app restart, `deploy.yml`: +`~/pms` was historically a **sparse checkout limited to `App/`**, so the service +folders never landed on disk; the deploy now disables sparse-checkout (idempotent) +to materialise the whole tree before managing the services. After the app restart, +`deploy.yml`: -1. exports the few secrets the services need out of `~/pms/App/.env` +1. expands the working tree (sparse-checkout disable) and exports the few secrets + the services need out of `~/pms/App/.env` (`PDF_SERVICE_TOKEN`, `ALLOWED_ORIGIN`, `EPFO_LIVE`) — never `PORT` or the runner's ephemeral `FORGEJO_TOKEN`; 2. for each service folder present, runs `npm install` + `npx playwright install