- 08-04-SUMMARY.md: publish job verified green (run #14), both tags pushed, security audit pass (PAT masked, --password-stdin), REGISTRY_PAT naming note - 08-VERIFICATION.md: all six phase-8 ROADMAP criteria passed with evidence table - REQUIREMENTS.md: CI-01 + CI-02 marked complete (checkboxes + traceability table) - ROADMAP.md: phase 8 marked [x] complete (2026-06-11), 08-04 plan ticked - STATE.md: phase complete, D-PAT-NAMING decision recorded, operator next steps updated
112 lines
6.5 KiB
Markdown
112 lines
6.5 KiB
Markdown
---
|
|
phase: 08-gitea-ci
|
|
plan: "04"
|
|
subsystem: infra
|
|
tags: [gitea, docker, ci, registry, publish, security]
|
|
|
|
requires:
|
|
- phase: 08-03
|
|
provides: harness job in ci.yml; dev-stack bring-up confirmed in CI
|
|
|
|
provides:
|
|
- publish job in .gitea/workflows/ci.yml (push to main → build + push API Docker image)
|
|
- familysync-api:latest and familysync-api:v1.1-<7charsha> tags in Gitea registry
|
|
- --password-stdin docker login pattern (REGISTRY_PAT secret, never plaintext)
|
|
|
|
affects: [phase-09, phase-10, phase-11, phase-12]
|
|
|
|
tech-stack:
|
|
added: []
|
|
patterns:
|
|
- "docker login --password-stdin (PAT piped via stdin; -p flag forbidden)"
|
|
- "dual-tag publish: :latest + :<milestone>-<shortsha> (D-04 traceability)"
|
|
- "docker build from repo root: -f apps/api/Dockerfile . (Dockerfile requires workspace manifest)"
|
|
- "docker logout in always() step to drop credential from runner after push"
|
|
- "Gitea secret prefix exclusion: REGISTRY_PAT not GITEA_REGISTRY_PAT (Gitea reserves GITEA_ prefix)"
|
|
|
|
key-files:
|
|
created: []
|
|
modified:
|
|
- .gitea/workflows/ci.yml
|
|
|
|
key-decisions:
|
|
- "D-PAT-NAMING: Gitea runner silently ignores secrets with the GITEA_ prefix; secret renamed from GITEA_REGISTRY_PAT to REGISTRY_PAT in both the Gitea repo secret and ci.yml (commit 73eecf7). This is the load-bearing fix that unblocked the publish job."
|
|
- "D-SHELL-FORM: Used shell docker login/build/push (not docker/login-action or docker/build-push-action) — matches 08-RESEARCH §Pattern 6 recommendation; simpler and has no marketplace action dependency."
|
|
- "D-DUAL-TAG: Two docker push calls for :latest and :v1.1-<shortsha>; same digest, two tags. Traceability tag links image to the exact merge commit."
|
|
- "D-LOGOUT: docker logout in always() step — drops stored credential from runner regardless of push success/failure (security hygiene)."
|
|
|
|
requirements-completed: [CI-02]
|
|
|
|
duration: checkpoint-verified
|
|
completed: "2026-06-11"
|
|
---
|
|
|
|
# Phase 08 Plan 04: Publish Job Summary
|
|
|
|
**Publish job wired to push-to-main: API Docker production image pushed to Gitea registry under :latest and :v1.1-<7charsha>, PAT authenticated via --password-stdin (never plaintext), security audit passed — CI-02 delivered.**
|
|
|
|
## Performance
|
|
|
|
- **Duration:** multi-step (Task 1 executed, Task 2 was a human-verify checkpoint — verified green on run #14)
|
|
- **Started:** 2026-06-11
|
|
- **Completed:** 2026-06-11
|
|
- **Tasks:** 2 (1 auto + 1 human-verify checkpoint)
|
|
- **Files modified:** 1
|
|
|
|
## Accomplishments
|
|
|
|
- Added the `publish` job to `.gitea/workflows/ci.yml`: gated on `github.event_name == 'push' && github.ref == 'refs/heads/main'`, never fires on pull_request.
|
|
- Run #14 (merge commit 98acff8 pushed to main) confirmed SUCCESS (1m): docker login succeeded, both image tags pushed (same digest sha256:ce724852…) — `familysync-api:latest` and `familysync-api:v1.1-98acff8` present in Gitea Packages API.
|
|
- Security audit of the CI log passed: PAT masked as `***` throughout (Gitea secret scrubber); `--password-stdin` used exclusively; no `-p`/`--password` form anywhere; `docker logout` ran in the `always()` cleanup step. No plaintext token anywhere in the log.
|
|
- Identified and fixed the GITEA_-prefix secret naming bug: Gitea runner silently drops secrets whose names start with `GITEA_`; renamed secret from `GITEA_REGISTRY_PAT` to `REGISTRY_PAT` in both the repo secret and ci.yml (commit 73eecf7).
|
|
|
|
## Task Commits
|
|
|
|
1. **Task 1: Add the publish job** - `ebcc38d` (feat)
|
|
2. **Task 1 fix: REGISTRY_PAT naming** - `73eecf7` (fix — GITEA_ prefix exclusion)
|
|
3. **Pre-merge cleanup: remove throwaway runner-probe.yml** - `dcf4242` (chore)
|
|
4. **Task 2: Human-verify checkpoint** — verified green (run #14); no code commit required
|
|
|
|
## Files Created/Modified
|
|
|
|
- `.gitea/workflows/ci.yml` — publish job added (push-to-main guard, --password-stdin docker login, build --target production from repo root, dual push :latest + :v1.1-<sha>, docker logout always())
|
|
|
|
## Decisions Made
|
|
|
|
- **REGISTRY_PAT naming (D-PAT-NAMING):** Gitea reserves the `GITEA_` prefix for built-in variables and silently strips any user-defined secret with that prefix from the runner environment. The original plan named the secret `GITEA_REGISTRY_PAT`; the fix renames it to `REGISTRY_PAT` (commit 73eecf7). Downstream plans must use `REGISTRY_PAT` if they add registry operations.
|
|
- **Shell form over marketplace actions:** `echo "${{ secrets.REGISTRY_PAT }}" | docker login ... --password-stdin` + `docker build` + `docker push` rather than `docker/login-action` / `docker/build-push-action`. This matches the 08-RESEARCH §Pattern 6 preferred path; no external action marketplace dependency.
|
|
- **Dual push:** two separate `docker push` calls (`:latest` + `:<milestone>-<sha>`) — same digest, two references. `:latest` is the moving pointer; `:<milestone>-<sha>` is the immutable traceability tag per D-04.
|
|
|
|
## Deviations from Plan
|
|
|
|
### Auto-fixed Issues
|
|
|
|
**1. [Rule 1 - Bug] GITEA_REGISTRY_PAT secret prefix rejected by Gitea runner**
|
|
- **Found during:** Task 1 → human-verify checkpoint (run #14 initially failed login)
|
|
- **Issue:** Gitea Actions runner silently ignores secrets with the `GITEA_` prefix (reserved namespace). The `GITEA_REGISTRY_PAT` secret was never injected into the job environment, causing `docker login` to fail with an empty password.
|
|
- **Fix:** Renamed the Gitea repo secret from `GITEA_REGISTRY_PAT` to `REGISTRY_PAT` and updated the ci.yml reference accordingly (commit 73eecf7).
|
|
- **Files modified:** `.gitea/workflows/ci.yml`
|
|
- **Verification:** Run #14 succeeded — "Login Succeeded" in log; both tags pushed; PAT masked.
|
|
- **Committed in:** `73eecf7`
|
|
|
|
---
|
|
|
|
**Total deviations:** 1 auto-fixed (Rule 1 - Bug)
|
|
**Impact on plan:** Required for the publish job to function at all. No scope creep. Security outcome (no plaintext PAT) unchanged.
|
|
|
|
## Issues Encountered
|
|
|
|
The GITEA_-prefix restriction was not documented in the plan or research notes; it was discovered empirically when the first publish run failed at docker login. The fix was straightforward once identified. No other issues encountered.
|
|
|
|
## Next Phase Readiness
|
|
|
|
- Phase 8 (Gitea CI) is fully complete — all six ROADMAP success criteria met (see 08-VERIFICATION.md).
|
|
- CI-01 (PR regression: lint + typecheck + unit + API integration + mobile harness) delivered by plans 02 + 03.
|
|
- CI-02 (push-to-main publish) delivered by this plan (04).
|
|
- The Gitea registry now has a pullable `familysync-api:latest` image. Future phases can reference it for deployment documentation or smoke tests.
|
|
- Phase 9 (Faster Write-Back) is fully independent and can start immediately.
|
|
|
|
---
|
|
*Phase: 08-gitea-ci*
|
|
*Completed: 2026-06-11*
|