diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index a2dae27..1d8a0e0 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -3,11 +3,6 @@ name: CI on: pull_request: branches: [main] - push: - branches: [main] - -env: - MILESTONE: v1.1 jobs: fast-checks: @@ -310,53 +305,3 @@ 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. - # Secret is named REGISTRY_PAT (not GITEA_REGISTRY_PAT): Gitea reserves the GITEA_ prefix - # for secret names, so the GITEA_-prefixed name cannot be created. - - name: Docker login - run: | - echo "${{ secrets.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 diff --git a/.gitea/workflows/publish.yml b/.gitea/workflows/publish.yml new file mode 100644 index 0000000..d6a2449 --- /dev/null +++ b/.gitea/workflows/publish.yml @@ -0,0 +1,77 @@ +# Publishing / Releases +# +# Trigger: push to main — i.e. when any PR merges. +# Image: git.bergerhouse.net/luckberg/familysync-api +# Tags: +# :latest — moving pointer for easy pulls +# :- — immutable, rollback-traceable (e.g. v1.1-98acff8) +# +# Required secret: REGISTRY_PAT — a Gitea Actions secret holding a PAT with write:package scope. +# Named REGISTRY_PAT (not GITEA_*): Gitea reserves the GITEA_ prefix for secret names, so +# GITEA_-prefixed names cannot be created. GITEA_TOKEN / GITHUB_TOKEN cannot push packages. +# +# Safety gate: branch protection on main, NOT a needs: dependency in this file. +# The PR test jobs (fast-checks, api, harness in ci.yml) run on pull_request — they never +# run in the same workflow invocation as publish.yml. Tests gate the PR; main is trusted to +# be green because direct push and force push are blocked and the three required checks +# (CI / fast-checks, CI / api, CI / harness) must pass before merge. +# +# To bump the milestone tag at a milestone boundary: edit MILESTONE below. + +name: Publish + +on: + push: + branches: [main] + +env: + MILESTONE: v1.1 + +jobs: + publish: + runs-on: ubuntu-latest + 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. + # Secret is named REGISTRY_PAT (not GITEA_REGISTRY_PAT): Gitea reserves the GITEA_ prefix + # for secret names, so the GITEA_-prefixed name cannot be created. + - name: Docker login + run: | + echo "${{ secrets.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