name: Deploy release to production # Pushing a release tag (vX.Y.Z) deploys that tag to the portal at # pms.pelagiamarine.com. Runs on the pms1 host runner (label: host), # which executes as shad0w with direct access to the pm2-managed app. on: push: tags: - "v*" jobs: deploy: runs-on: host steps: - name: Deploy tag to ~/pms and restart ppms run: | set -euo pipefail export NVM_DIR="$HOME/.nvm" . "$NVM_DIR/nvm.sh" TAG="${GITHUB_REF_NAME}" echo "=== Deploying $TAG ===" cd "$HOME/pms" git fetch origin --tags --force git checkout -f "refs/tags/$TAG" cd App pnpm install --frozen-lockfile pnpm build # includes prisma generate pnpm db:migrate:deploy # NOT --update-env: this job runs inside the Forgejo Actions runner, whose # environment includes an ephemeral FORGEJO_TOKEN (the per-job token, revoked # when the job ends). --update-env would inject it into ppms, where it shadows # the real PAT from .env (Next.js does not override an already-set process.env # var) and breaks the Report Issue button once the job token expires. A plain # restart re-execs ppms from the pm2 daemon's clean env, so .env wins. 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" # ~/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 # 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. 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 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 code=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/login) echo "Portal /login returned HTTP $code" test "$code" = "200"