name: Sync infra to hosts # The infra DOMAIN's own pipeline (new with the monorepo -- the split era had # no infra deploy workflow at all; infra changes rode along lazily with the # next app deploy's `git reset`). On a push touching infra/**, SSH to every # host, fast-forward each ENVIRONMENT's monorepo checkout and re-render that # environment's env file from the SOPS vault -- so a compose edit or a secret # rotation lands push-button instead of waiting for the next app deploy. # # Deliberately does NOT roll any service: image tags are the app domains' # axis, not infra's. A compose or stack change that must recreate containers # takes effect on the next app deploy, or a by-hand # `SERVICE=all ... infra/deploy/deploy.sh` run (see that script's header). # # THREE CHECKOUTS, TWO HOSTS. This is the shape the vps1/vps2 split forces: # # vps1 /opt/thermograph-dev branch dev -> /etc/thermograph.env # vps2 /opt/thermograph-beta branch main -> /etc/thermograph-beta.env # vps2 /opt/thermograph branch main -> /etc/thermograph.env # # The two checkouts on vps2 are separate directories on purpose: a `git reset # --hard` for beta must not be able to move prod's tree, and each environment # renders its own env file from its own vault file. Because the paths differ, # the beta and prod jobs below can safely run at the same time on one box. # # Beta and prod both track `main` (infra is not environment-staged -- only app # IMAGES are, via tags). Dev tracks `dev`, which is why this workflow now # triggers on both branches: a dev-branch infra change has to reach the dev # checkout, and only that one. # # Each `render_thermograph_secrets` call passes the environment name and the # output path EXPLICITLY. It used to rely on the host's # /etc/thermograph/secrets-env marker, which cannot answer the question on vps2 # -- one marker, two environments. Passing them here means a beta sync can # never render prod's vault, and vice versa. on: push: branches: [dev, main] paths: ['infra/**'] workflow_dispatch: {} jobs: sync-dev: # Only the dev branch feeds the dev checkout. if: github.ref_name == 'dev' runs-on: docker concurrency: group: infra-sync-dev cancel-in-progress: false steps: - name: Sync dev checkout on vps1 + re-render secrets uses: https://github.com/appleboy/ssh-action@v1.2.0 with: host: ${{ secrets.VPS1_SSH_HOST }} username: ${{ secrets.VPS1_SSH_USER }} key: ${{ secrets.VPS1_SSH_KEY }} port: ${{ secrets.VPS1_SSH_PORT }} script: | set -euo pipefail cd /opt/thermograph-dev git fetch --prune origin dev git reset --hard origin/dev echo "synced to $(git log --oneline -1)" # RENDER ONLY IF THIS HOST IS ACTUALLY DEV. # # dev's env file is /etc/thermograph.env — the same path beta uses, # and during the vps1/vps2 cutover beta is STILL ON VPS1. Rendering # unconditionally would overwrite the live env file of the # environment currently serving beta.thermograph.org with dev's # credentials: beta's running containers would survive (env_file is # read at container start) and then come up wrong on its next # deploy, pointed at dev's database password. # # The host marker is the box's own statement of which environment it # is, and it is the right authority for "is it safe to write this # file here". It flips to `dev` in provision-dev.sh, which is run # only after beta has vacated. Until then this skips loudly rather # than failing: the checkout sync above is still useful and correct. marker=$(cat /etc/thermograph/secrets-env 2>/dev/null || true) if [ "$marker" != dev ]; then echo "!! /etc/thermograph/secrets-env says '${marker:-}', not 'dev'." echo "!! Skipping the secrets render — this host is not dev yet, and" echo "!! /etc/thermograph.env here belongs to '${marker:-another environment}'." exit 0 fi if [ -f infra/deploy/render-secrets.sh ]; then . infra/deploy/render-secrets.sh # SKIP_COMMON is dev's standing rule, not a preference: common.yaml # is the fleet's shared production credential set, and vps1 also # runs Forgejo and its CI. See render-secrets.sh. THERMOGRAPH_SECRETS_SKIP_COMMON=1 \ render_thermograph_secrets /opt/thermograph-dev/infra dev /etc/thermograph.env fi sync-beta: if: github.ref_name == 'main' runs-on: docker concurrency: group: infra-sync-beta cancel-in-progress: false steps: - name: Sync beta checkout on vps2 + re-render secrets uses: https://github.com/appleboy/ssh-action@v1.2.0 with: host: ${{ secrets.VPS2_SSH_HOST }} username: ${{ secrets.VPS2_SSH_USER }} key: ${{ secrets.VPS2_SSH_KEY }} port: ${{ secrets.VPS2_SSH_PORT }} script: | set -euo pipefail cd /opt/thermograph-beta git fetch --prune origin main git reset --hard origin/main if [ -f infra/deploy/render-secrets.sh ]; then . infra/deploy/render-secrets.sh render_thermograph_secrets /opt/thermograph-beta/infra beta /etc/thermograph-beta.env fi echo "synced to $(git log --oneline -1)" sync-prod: if: github.ref_name == 'main' runs-on: docker concurrency: group: infra-sync-prod cancel-in-progress: false steps: - name: Sync prod checkout on vps2 + re-render secrets uses: https://github.com/appleboy/ssh-action@v1.2.0 with: host: ${{ secrets.VPS2_SSH_HOST }} username: ${{ secrets.VPS2_SSH_USER }} key: ${{ secrets.VPS2_SSH_KEY }} port: ${{ secrets.VPS2_SSH_PORT }} script: | set -euo pipefail cd /opt/thermograph git fetch --prune origin main git reset --hard origin/main if [ -f infra/deploy/render-secrets.sh ]; then . infra/deploy/render-secrets.sh render_thermograph_secrets /opt/thermograph/infra prod /etc/thermograph.env fi echo "synced to $(git log --oneline -1)"