From ebcc38d81045ae769980aa922a98d22f3136b91b Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Thu, 11 Jun 2026 15:56:37 -0400 Subject: [PATCH] =?UTF-8?q?feat(08-04):=20publish=20job=20=E2=80=94=20buil?= =?UTF-8?q?d=20+=20push=20API=20image=20on=20merge=20to=20main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add publish job gated on push to refs/heads/main (never pull_request) - docker login via --password-stdin with secrets.GITEA_REGISTRY_PAT (Pitfall 13) - docker build --target production -f apps/api/Dockerfile . (repo-root context, T-08-10) - Push :latest and :${MILESTONE}-${SHORT_SHA} tags per D-04 - docker logout in always() step to drop credential after push - No dev-bypass flag in publish job (T-08-09 boundary) --- .gitea/workflows/ci.yml | 48 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 0f457cd..fc496d9 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -310,3 +310,51 @@ jobs: name: playwright-traces-${{ github.run_id }} path: apps/pwa/test-results/ retention-days: 14 + + publish: + runs-on: ubuntu-latest + # Push to main only — never on pull_request (D-03). No dev-bypass flag in this job (T-08-09). + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + steps: + - uses: actions/checkout@v4 + + # Compute both image tags per D-04: + # :latest — moving pointer for easy pulls + # :- — immutable, rollback-traceable (e.g. v1.1-4303a1b) + # GITHUB_SHA is confirmed available in Gitea Actions (probe P-13). + # MILESTONE is read from the workflow-level env var (set to v1.1 above) — update at milestone boundaries. + - name: Compute image tags + id: tags + run: | + SHORT_SHA=${GITHUB_SHA:0:7} + MILESTONE="${{ env.MILESTONE }}" + echo "latest=git.bergerhouse.net/luckberg/familysync-api:latest" >> $GITHUB_OUTPUT + echo "sha_tag=git.bergerhouse.net/luckberg/familysync-api:${MILESTONE}-${SHORT_SHA}" >> $GITHUB_OUTPUT + + # Pitfall 13 (load-bearing security step): PAT piped via stdin — never via -p/--password. + # GITEA_TOKEN/GITHUB_TOKEN cannot push packages; a PAT with write:package scope is required + # (confirmed: Gitea forum + registry docs). Token is masked by Gitea's secret-log scrubber + # and never echoed elsewhere or set as a plain env var. + - name: Docker login + run: | + echo "${{ secrets.GITEA_REGISTRY_PAT }}" | \ + docker login git.bergerhouse.net \ + --username luckberg \ + --password-stdin + + # Build from REPO ROOT (T-08-10): the Dockerfile copies the pnpm workspace manifest + + # lockfile from the root context; building from apps/api/ would fail to find them. + - name: Build and push + run: | + docker build --target production \ + -f apps/api/Dockerfile \ + -t ${{ steps.tags.outputs.latest }} \ + -t ${{ steps.tags.outputs.sha_tag }} \ + . + docker push ${{ steps.tags.outputs.latest }} + docker push ${{ steps.tags.outputs.sha_tag }} + + # Always drop the stored credential from the runner after push (defence in depth). + - name: Docker logout + if: always() + run: docker logout git.bergerhouse.net || true