First to Site
Release 3.7

UAT & Staging Auto-Deploy Pipeline

GitHub Actions workflows for UAT and staging; dev-dashboard tab rewrite; release script cleanup

Overview

UAT and staging moved to push-triggered auto-deploys via GitHub Actions. Preprod and production remain manual-only by deliberate design - fts1 is IP-allowlisted and GitHub-hosted runners cannot SSH in. The dev dashboard's Release Preparation and Deployment tabs were rewritten to match the current 5-environment / 3-host reality.

What Changed

Auto-Deploy Workflows

Two new workflows in .github/workflows/:

deploy-uat.yml

  • Trigger: push to uat branch + workflow_dispatch for manual runs.
  • SSH target: root@ftsuat (Vultr host).
  • Runs: cd ~ && bun run release:uat.
  • Authenticated via DEPLOY_SSH_KEY + DEPLOY_KNOWN_HOSTS secrets.

deploy-staging.yml

  • Trigger: push to staging branch + workflow_dispatch.
  • SSH target: root@ftsstaging (Vultr host).
  • Same script pattern as UAT.

Preprod Stays Manual

An attempt at auto-deploying preprod via a self-hosted runner on fts1 was tried (feat(ci): add Deploy Preprod workflow, ci(preprod): use self-hosted runner on fts1) and reverted (revert(ci): remove preprod auto-deploy + self-hosted runner). Reasons:

  • fts1's IP allowlist makes manual-on-host the right default for preprod + production.
  • A self-hosted runner with sudo access on fts1 is a larger trust surface than a manual deploy.

Preprod deploys therefore remain: ssh ubuntu@fts1 && sudo -u ftsuser bash -c 'cd ~/preprod && bun run release:preprod'.

Release script now swaps ~/<env> directly instead of hard-coded ~/live. UAT and staging hosts therefore have ~/uat and ~/staging symlinks that nginx points at, so the swap is atomic and visible.

post-release.sh Extensions

  • New DEPLOY_ENV env var (falls back to LIVE_LINK_NAME for backwards compat).
  • UAT and staging branches added to the restart dispatch.
  • release:uat and release:staging npm scripts.

Dev Dashboard Tabs Rewritten

scripts/dev-dashboard-server.ts Tabs 3 (Release Preparation) and 4 (Deployment) rewritten end-to-end after ground-truth verification on fts1:

  • Overview table showing 5 envs × 3 hosts with per-env trigger (UAT/staging auto via CI; preprod/candidate/prod manual on fts1).
  • Separate "UAT & staging - automated via GitHub Actions" section covering push triggers.
  • "Preprod, candidate, production - manual on fts1" section with SSH + command reference.
  • ~/live symlink (not ~/source); release dirs named live-* (not source-*); PHP-FPM 8.3 (not 8.2).

Script Cleanup

  • Dropped the :safe suffix from release:prod:safe -> release:prod now that there is only one release script.
  • Deleted the dead scripts/release-prod-symlink.ts (old in-place build approach, no callers in operational code).
  • release:*:safe aliases were added in an interim step and then folded back into the short forms.

Files Touched

FileChange
.github/workflows/deploy-uat.ymlNew
.github/workflows/deploy-staging.ymlNew
.github/workflows/deploy-preprod.ymlAdded, then removed
scripts/post-release.shUAT/staging branches, DEPLOY_ENV
scripts/release-prod-safe.tsCONFIG_ENV_NAME override; env-named symlink swap
scripts/dev-dashboard-server.tsTabs 3 & 4 rewrite
scripts/dev-commands.tsDeploy section split into manual-on-fts1 vs automated-via-CI
scripts/release-prod-symlink.tsDeleted
package.jsonrelease:uat, release:staging, refresh:uat, refresh:staging
CLAUDE.mdDeployment pipeline docs
.test_credentialsSSH targets for all 5 envs

Changelog Reference

  • chore: set up UAT/staging deployment pipeline (4a3ee951)
  • feat(ci): restart services after UAT/staging deploys (a444abe2)
  • feat(ci): add Deploy Preprod workflow (31e91767)
  • ci(preprod): use self-hosted runner on fts1 (dbdbaa0f)
  • revert(ci): remove preprod auto-deploy + self-hosted runner (db1fdc56)
  • refactor(ci): hoist deploy target to workflow-level env block (59ac0fa0)
  • feat(deploy): swap env-named symlink directly instead of ~/live (14394ae0)
  • fix(ci): invoke release script by path with inline env vars (fcbe0f14)
  • fix(ci): resolve fts hosts in deploy workflows via /etc/hosts (2a7d406e)
  • docs(dashboard): rewrite deployment and release-prep tabs to match reality (1aa6393b)
  • chore(deploy): drop :safe suffix, delete dead release-prod-symlink script (cd5b2268)
  • chore: add release:*:safe variants and alias short forms (fbc1fb9b)