From 97d95052db369ee87211c7648a3c49c0a7cc7fdc Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Jul 2026 05:03:01 -0500 Subject: [PATCH] ci(deploy): auto-deploy prod stack after image build on merge to main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a `deploy` job to build-images.yml that runs on the self-hosted `uom_deploy` runner and, via `needs: build`, fires only after a clean image build+push. It pulls the fresh :latest images and recreates the stack (pull → down → up -d) from /home/perry/website. Guarded on refs/heads/main so a workflow_dispatch off another branch can't deploy. Co-Authored-By: Claude Opus 4.8 --- .gitea/workflows/build-images.yml | 39 ++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/build-images.yml b/.gitea/workflows/build-images.yml index 6acdd24..5271794 100644 --- a/.gitea/workflows/build-images.yml +++ b/.gitea/workflows/build-images.yml @@ -1,11 +1,21 @@ -# Build and publish the app + bot container images to Gitea's container registry -# on every merge to main. Production then pulls prebuilt images instead of -# building on the host. +# Build the app + bot container images, publish them to Gitea's container +# registry, then roll the production stack onto the fresh images — all on every +# merge to main. Production only ever pulls prebuilt images; it never builds. +# +# Two jobs run in sequence: +# build — builds & pushes website-app / website-bot images (on ubuntu-latest) +# deploy — `needs: build`, so it starts only after a clean build+push, and +# pulls + recreates the stack on the production host (on uom_deploy) # # Prerequisites (one-time): # • An always-on Gitea runner with label `ubuntu-latest` whose jobs have the # host Docker socket mounted (/var/run/docker.sock), so `docker build` talks # to the host daemon. This also gives free layer caching between runs. +# • A second self-hosted runner labelled `uom_deploy` ON the production host, +# with access to the Docker daemon and to /home/perry/website (the directory +# holding the production docker-compose.yml + .env). This is what actually +# rolls the stack; it must be able to `docker compose pull` from the registry +# (log in once on the host, or ensure the images are public-read). # • Two repo secrets (Settings → Actions → Secrets): # REGISTRY_USER — the Gitea username that owns the token below # REGISTRY_TOKEN — a Gitea access token with `write:package` (+ read:package) @@ -14,6 +24,7 @@ # Produces, in gitea.whitlocktech.com// : # website-app:latest + website-app:sha-<7> # website-bot:latest + website-bot:sha-<7> +# then deploys the `:latest` images (docker-compose.yml defaults IMAGE_TAG=latest). name: Build container images @@ -85,3 +96,25 @@ jobs: - name: Log out (clear cached credentials from the runner) if: always() run: docker logout "${REGISTRY}" || true + + deploy: + # Roll production onto the images `build` just pushed. `needs: build` makes + # this wait for a clean build+push — if the build fails, deploy never fires, + # so the running stack is left untouched rather than torn down for nothing. + needs: build + runs-on: uom_deploy + # Guard against a workflow_dispatch fired from a non-main branch: only ever + # deploy the main line to production. + if: github.ref == 'refs/heads/main' + steps: + - name: Pull the fresh images and recreate the stack + # `pull` grabs the new :latest images the build job published; `down` + # then `up -d` recreates the containers on them. Compose only recreates + # services whose image digest changed, so the DB stays put. + run: | + set -euo pipefail + cd /home/perry/website + docker compose pull + docker compose down + docker compose up -d + docker compose ps -- 2.49.1