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