thermograph/.forgejo/workflows/infra-sync.yml
emi 7583258509
Some checks failed
Sync infra to hosts / sync-beta (push) Has been skipped
Sync infra to hosts / sync-prod (push) Has been skipped
Sync infra to hosts / sync-dev (push) Failing after 5s
secrets-guard / encrypted (push) Successful in 5s
shell-lint / shellcheck (push) Successful in 8s
PR build (required check) / changes (pull_request) Successful in 7s
secrets-guard / encrypted (pull_request) Successful in 7s
PR build (required check) / build-backend (pull_request) Has been skipped
PR build (required check) / build-frontend (pull_request) Has been skipped
shell-lint / shellcheck (pull_request) Successful in 11s
PR build (required check) / validate-observability (pull_request) Successful in 21s
PR build (required check) / gate (pull_request) Successful in 2s
infra-sync: refuse to render dev's vault onto a host that is not dev (#107)
2026-07-26 07:05:09 +00:00

145 lines
6.4 KiB
YAML

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:-<unset>}', 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)"