From 4d52226a6a290b3d2abcb354ce09eaf4e2a9ebd8 Mon Sep 17 00:00:00 2001 From: Emi Griffith Date: Wed, 22 Jul 2026 11:52:37 -0700 Subject: [PATCH] Add release->prod deploy (per-service, PROD_SSH_* secrets) Claude-Session: https://claude.ai/code/session_01RdARHDJaYC1wSQRTYM6t3Z --- .forgejo/workflows/deploy-prod.yml | 55 ++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .forgejo/workflows/deploy-prod.yml diff --git a/.forgejo/workflows/deploy-prod.yml b/.forgejo/workflows/deploy-prod.yml new file mode 100644 index 0000000..0b43338 --- /dev/null +++ b/.forgejo/workflows/deploy-prod.yml @@ -0,0 +1,55 @@ +name: Deploy backend to prod VPS + +# On a push to `release`, SSH to prod and roll ONLY the backend service onto +# the image build-push.yml just published for this commit. This is the +# release->prod path, distinct from deploy.yml's main->beta path -- same +# shape, different branch, different target host, and a completely separate +# secret set (PROD_SSH_HOST/USER/KEY/PORT), so a beta credential leak can't +# touch prod and vice versa. Frontend is deployed independently by its own +# repo's deploy-prod.yml -- that is the whole point of the FE/BE CI-CD split: +# a backend change ships without touching frontend and vice versa. +# thermograph-infra/deploy/deploy.sh persists each service's live tag +# host-side, so a single-service roll never disturbs the other. +# +# The prod SSH user (agent) has full sudo because the SOPS secrets render +# sudo-reads the age key on that box, but that's handled inside deploy.sh -- +# nothing extra is needed here. appleboy/ssh-action is referenced by full +# GitHub URL because it isn't mirrored in Forgejo's default action registry. +# +# The tag MUST match build-push.yml's `sha-${GITHUB_SHA:0:12}` (12 hex), not +# the full 40-char ${{ github.sha }} -- default Actions expressions have no +# substring function, so the compute step below truncates it. deploy.sh also +# retries the pull for ~5 min because Forgejo `needs:` can't gate across the +# separate build-push.yml, so this push's build may still be in flight. + +on: + push: + branches: [release] + workflow_dispatch: {} + +concurrency: + group: prod-deploy-backend + cancel-in-progress: false + +jobs: + deploy: + runs-on: docker + steps: + - name: Compute image tag + id: tag + run: echo "tag=sha-${GITHUB_SHA:0:12}" >> "$GITHUB_OUTPUT" + + - name: Deploy backend over SSH + uses: https://github.com/appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.PROD_SSH_HOST }} + username: ${{ secrets.PROD_SSH_USER }} + key: ${{ secrets.PROD_SSH_KEY }} + port: ${{ secrets.PROD_SSH_PORT }} + # Forward the target service + the image tag to run. deploy.sh reads + # BACKEND_IMAGE_TAG and rolls just `backend`. + envs: SERVICE,BACKEND_IMAGE_TAG + script: /opt/thermograph/deploy/deploy.sh + env: + SERVICE: backend + BACKEND_IMAGE_TAG: ${{ steps.tag.outputs.tag }}