diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml new file mode 100644 index 0000000..2a006f0 --- /dev/null +++ b/.forgejo/workflows/deploy.yml @@ -0,0 +1,62 @@ +name: deploy + +on: + push: + branches: + - main + +jobs: + build-push-deploy: + runs-on: homelab + env: + REGISTRY: forgejo.labbity.unbiasedgeek.com + IMAGE: forgejo.labbity.unbiasedgeek.com/archeious/quartermaster/quartermaster + COMPOSE_PROJECT_NAME: quartermaster + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to Forgejo registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: archeious + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Build and push image + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ${{ env.IMAGE }}:${{ github.sha }} + ${{ env.IMAGE }}:latest + + - name: Deploy + run: | + set -euo pipefail + printf 'QUARTERMASTER_TAG=%s\n' '${{ github.sha }}' > .env + docker compose pull + docker compose up -d + + - name: Smoke test + env: + SMOKE_PASSWORD: ${{ secrets.QUARTERMASTER_SMOKE_PASSWORD }} + run: | + set -eu + for attempt in 1 2 3 4 5 6 7 8 9 10; do + code=$(curl -sS -o /dev/null -w '%{http_code}' \ + -u "admin:$SMOKE_PASSWORD" \ + https://quartermaster.unbiasedgeek.com/healthz || echo "000") + if [ "$code" = "200" ]; then + echo "smoke OK after $attempt attempt(s)" + exit 0 + fi + echo "attempt $attempt: got $code, retrying" + sleep 3 + done + echo "smoke FAILED — last code $code" + exit 1 diff --git a/README.md b/README.md index ba7d9e6..bc9fb77 100644 --- a/README.md +++ b/README.md @@ -167,3 +167,34 @@ editing the checked-in compose file. path. Three would resolve relative to the working directory, and the SQLite file would NOT land on the bind mount (on next restart the database would be empty). + +## CI/CD + +Push to `main` triggers `.forgejo/workflows/deploy.yml` on the +`homelab` runner. That runner lives on home-ctr-onyx itself in +container mode with the host's Docker socket mounted — so the +workflow talks to the same Docker daemon that hosts the production +container and no SSH round-trip is needed. + +The workflow: checks out the repo, builds the image, pushes it to +the Forgejo registry tagged with the commit SHA and `latest`, +writes `QUARTERMASTER_TAG=` to a `.env` file next to the +checked-out `compose.yml`, runs `docker compose pull && docker +compose up -d`, and finishes with one +`curl -fsS -u admin:… https://quartermaster.unbiasedgeek.com/healthz` +against the public URL — catching TLS, Traefik routing, and the +basic-auth middleware in a single probe. + +The workflow reads two Forgejo Actions secrets (repo-scoped under +`archeious/quartermaster`): + +* `REGISTRY_TOKEN` — archeious Forgejo personal token with + `write:package` scope; used as the docker-login password. +* `QUARTERMASTER_SMOKE_PASSWORD` — plaintext basic-auth password + for the `admin` user, delivered to the tenant out-of-band by the + platform team. + +Rollback is manual for v1: `git checkout` the previous SHA, set +`QUARTERMASTER_TAG` in `.env` to that SHA, and `docker compose up -d` +from a clone of the repo (or let the previous commit be the `main` +tip and the deploy workflow will roll it out).