Files
familysync/.planning/phases/08-gitea-ci/08-01-PLAN.md
T
Lucas Berger 982438dc10 style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
2026-06-11 20:35:18 -04:00

171 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 08-gitea-ci
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- .gitea/workflows/runner-probe.yml
autonomous: false
requirements: [CI-01, CI-02]
user_setup:
- service: gitea-actions-runner
why: 'CI cannot run without a registered act_runner; 0 runners currently registered on git.bergerhouse.net'
dashboard_config:
- task: 'Install + register act_runner on the Unraid host against git.bergerhouse.net, prefer Docker-executor mode (service containers require it)'
location: 'Unraid Community Applications → act_runner template; register with a runner-registration token from Gitea → Site Admin → Actions → Runners'
- service: gitea-registry-pat
why: 'Publish job (CI-02) authenticates to the Gitea container registry; GITHUB_TOKEN/GITEA_TOKEN cannot push packages'
env_vars:
- name: GITEA_REGISTRY_PAT
source: 'Gitea → Settings → Applications → Generate Token with write:package (+ read:package) scope; add as repo secret GITEA_REGISTRY_PAT'
must_haves:
truths:
- 'A runner-probe workflow runs on the gsd/phase-08-gitea-ci branch and prints Node/pnpm versions, runner mode, Docker access, action resolution, and Playwright WebKit dep installability'
- 'The probe surfaces whether the runner is Docker-executor (services: works) or host-executor (docker run fallback needed) — the answer that forks W1/W2 DB bring-up'
- 'An act_runner is registered and visible in the Gitea Actions runners list (operator action)'
- 'A GITEA_REGISTRY_PAT repo secret with write:package scope exists (operator action)'
artifacts:
- path: '.gitea/workflows/runner-probe.yml'
provides: 'Probe-only workflow answering runner unknowns P-01..P-13'
contains: 'runner-probe'
key_links:
- from: '.gitea/workflows/runner-probe.yml'
to: 'the registered act_runner'
via: 'runs-on: ubuntu-latest (runner has no self-hosted label), on: push to gsd/phase-08-gitea-ci'
pattern: "runs-on:\\s*ubuntu-latest"
---
<objective>
Establish the Gitea CI foundation by (a) registering the act_runner and creating the registry PAT (operator actions), and (b) landing a probe-only workflow that answers every runner unknown BEFORE any real test/build/publish step is trusted. This is Pitfall 12 (runner-probe-first) and the critical fork in 08-RESEARCH §Runner-Probe Checklist: several downstream design choices (service containers vs docker run, action resolution, reporter override, artifact upload fork, WebKit deps) depend on the probe's answers.
Purpose: De-risk every assumption (A1A10 in 08-RESEARCH Assumptions Log) on the actual Unraid runner so Waves 12 are written against confirmed behavior, not guesses. Per D-03 the real CI lives in one ci.yml; the probe is a separate throwaway workflow on the feature branch.
Output: `.gitea/workflows/runner-probe.yml`, a registered runner, and a stored registry PAT.
</objective>
<execution_context>
@$HOME/.claude/gsd-core/workflows/execute-plan.md
@$HOME/.claude/gsd-core/templates/summary.md
</execution_context>
<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/08-gitea-ci/08-CONTEXT.md
@.planning/phases/08-gitea-ci/08-RESEARCH.md
@.planning/research/PITFALLS.md
</context>
<artifacts_this_phase_produces>
- `.gitea/workflows/runner-probe.yml` (NEW — this plan)
- `.gitea/workflows/ci.yml` (NEW — Plans 02/03/04)
</artifacts_this_phase_produces>
<tasks>
<task type="checkpoint:human-action" gate="blocking-human">
<name>Task 1: Register act_runner + create registry PAT (operator-only)</name>
<what-built>Nothing automated — these are infrastructure actions outside the repo that the executor cannot perform (08-VALIDATION Manual-Only table; CI-01/CI-02 prerequisites).</what-built>
<how-to-verify>
1. On the Unraid host, install/register act_runner against https://git.bergerhouse.net using a runner-registration token from Gitea → Site Admin → Actions → Runners. PREFER Docker-executor mode — service containers (the MariaDB the CI needs) only work in Docker mode (08-RESEARCH §Critical fork, Assumption A1). If only host mode is available, that is acceptable; the probe (Task 2) will detect it and Waves 12 will use the docker-run fallback.
2. Confirm the runner appears with status "idle"/online in Gitea → Site Admin → Actions → Runners.
3. In Gitea → Settings → Applications, generate a token with `write:package` (and `read:package`) scope. Add it as a repository secret named `GITEA_REGISTRY_PAT` (repo → Settings → Actions → Secrets). Do NOT paste the token anywhere in the repo.
</how-to-verify>
<resume-signal>Type "runner registered" once the runner is online AND the GITEA_REGISTRY_PAT secret exists, or describe what is blocking (e.g. host-mode only).</resume-signal>
</task>
<task type="auto">
<name>Task 2: Author the runner-probe workflow</name>
<files>.gitea/workflows/runner-probe.yml</files>
<read_first>
- .planning/phases/08-gitea-ci/08-RESEARCH.md (§Runner-Probe Checklist — the P-01..P-13 table is the canonical task list; §Critical fork Docker-vs-host)
- .planning/research/PITFALLS.md (Pitfall 12 runner-probe-first, Pitfall 11 MariaDB-11 healthcheck)
- apps/pwa/playwright.config.ts (reporter: 'github' under CI — probe must note whether annotations render)
</read_first>
<action>
Create `.gitea/workflows/runner-probe.yml` as a probe-only, non-destructive workflow. Trigger: `on: push` filtered to `branches: [gsd/phase-08-gitea-ci]` (runs on the current feature branch; never on main). `runs-on: self-hosted`.
The job MUST answer every check in 08-RESEARCH §Runner-Probe Checklist P-01..P-13. Implement each as a clearly-labeled step whose output is visible in the Gitea Actions log:
- P-01 Node: `node --version` (note if not 22; then test `actions/setup-node@v4` with node-version 22 — P-08).
- P-02 pnpm: `pnpm --version || (corepack enable pnpm && pnpm --version)`.
- P-03 Runner mode (THE critical fork): print `cat /proc/1/cgroup | head -5`, `hostname`, and `ls -la /.dockerenv 2>&1` so the log shows whether the job runs in a Docker container (Docker-executor → services: works) or on bare host (host-executor → docker run fallback). State the conclusion explicitly in an `echo` line.
- P-04 Docker socket: `docker info 2>&1 | head -20` and `docker ps 2>&1 | head`.
- P-05 Service container spawn: add `services: mariadb: { image: mariadb:11, env: { MARIADB_ROOT_PASSWORD: root, MARIADB_DATABASE: familysync, MARIADB_USER: familysync, MARIADB_PASSWORD: testpass }, options: --health-cmd="healthcheck.sh --connect --innodb_initialized" --health-interval=10s --health-timeout=5s --health-retries=10 --health-start-period=30s }` to the probe job; a step runs `docker ps | grep -i maria || echo "no mariadb container visible (likely host mode)"`.
- P-06 MariaDB reachability: try BOTH `mysql -h mariadb -P 3306 -u familysync -ptestpass -e "SELECT 1" 2>&1 | head` (Docker mode hostname) AND `mysql -h 127.0.0.1 ...` (host mode). Record which hostname resolves (do NOT fail the job if one path errors — capture both, `continue-on-error: true` on the step or `|| true`).
- P-07 checkout: `uses: actions/checkout@v4` as the first real step; reaching subsequent steps proves it resolves.
- P-08 setup-node: `uses: actions/setup-node@v4` with `node-version: '22'`; print resulting `node --version`.
- P-09 cache: `uses: actions/cache@v4` with a throwaway key, wrapped `continue-on-error: true` — log whether it completes or hangs/times out (08-RESEARCH Pitfall 7).
- P-10 Playwright WebKit deps: in `apps/pwa`, `npx playwright install --with-deps webkit chromium 2>&1 | tail -30` with `continue-on-error: true` — confirms WebKit system deps install without sudo/apt failure (A10).
- P-11 artifact upload: write a dummy file and `uses: https://github.com/ChristopherHX/gitea-upload-artifact@v4` (NOT actions/upload-artifact@v4 — broken on Gitea per 08-RESEARCH) with `continue-on-error: true`; note whether the artifact appears in the Gitea UI.
- P-13 short SHA: `echo "short sha = ${GITHUB_SHA:0:7}"` — confirms the D-04 tag expression produces 7 chars.
Do NOT include P-12 (docker login/push) here — defer registry login to Plan 04 to avoid exercising the PAT before the publish job is designed. Add a final summary step that echoes a one-line verdict per fork (Docker vs host mode; cache usable y/n; WebKit deps ok y/n; upload-artifact fork works y/n) so the SUMMARY can record the answers.
Keep the workflow non-destructive: no migrations, no pushes, no writes to main. All probe steps that may fail on this runner use `continue-on-error: true` or `|| true` so the probe reports findings instead of red-failing on an expected unknown.
</action>
<verify>
<automated>test -f .gitea/workflows/runner-probe.yml && grep -q "runs-on: self-hosted" .gitea/workflows/runner-probe.yml && grep -q "healthcheck.sh --connect --innodb_initialized" .gitea/workflows/runner-probe.yml && grep -q "ChristopherHX/gitea-upload-artifact@v4" .gitea/workflows/runner-probe.yml && ! grep -q "actions/upload-artifact@v4" .gitea/workflows/runner-probe.yml && ! grep -q "mysqladmin" .gitea/workflows/runner-probe.yml && echo PROBE_OK</automated>
</verify>
<done>runner-probe.yml exists, triggers only on the feature branch, uses `healthcheck.sh --connect --innodb_initialized` (never mysqladmin), uses the gitea-upload-artifact fork (never actions/upload-artifact@v4), and contains a step for each of P-01..P-11 + P-13.</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<name>Task 3: Run the probe and record the fork answers</name>
<what-built>The runner-probe workflow (Task 2), pushed to the gsd/phase-08-gitea-ci branch so the now-registered runner executes it.</what-built>
<how-to-verify>
1. Ensure the branch is pushed: `git push origin gsd/phase-08-gitea-ci` (this commit triggers the probe).
2. Open Gitea → repo → Actions; find the "runner-probe" workflow run.
3. Read the log and record the answers to the fork questions:
- P-03: Docker-executor mode or host-executor mode? (drives Waves 12 DB bring-up)
- P-05/P-06: did the MariaDB service container appear, and on which hostname (`mariadb` vs `127.0.0.1`)?
- P-09: did actions/cache complete or hang? (cache optional decision)
- P-10: did `playwright install --with-deps webkit` succeed? (WebKit feasibility)
- P-11: did the gitea-upload-artifact fork upload successfully and appear in the UI?
4. Confirm no secret/token is printed anywhere in the probe log (the probe must not touch the PAT).
</how-to-verify>
<resume-signal>Paste the fork answers (Docker vs host mode; service-container hostname; cache works y/n; WebKit deps y/n; artifact upload y/n) so the executor records them in the SUMMARY for Waves 12. Type "probe results recorded" to continue.</resume-signal>
</task>
</tasks>
<threat_model>
## Trust Boundaries
| Boundary | Description |
| -------------------------------- | --------------------------------------------------------------------------------- |
| CI workflow → self-hosted runner | Untrusted-ish: workflow YAML executes on operator infra with Docker socket access |
| Repo secret store → workflow env | PAT crosses into the job; must never echo |
## STRIDE Threat Register
| Threat ID | Category | Component | Disposition | Mitigation Plan |
| --------- | ---------------------- | --------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| T-08-01 | Information Disclosure | runner-probe.yml | mitigate | Probe NEVER references `secrets.GITEA_REGISTRY_PAT` or any secret; no `docker login` in the probe (P-12 deferred to Plan 04). Verified by checkpoint log audit. |
| T-08-02 | Elevation of Privilege | Docker socket on runner | accept | Docker socket access is inherent to act_runner Docker-executor mode; accepted per Gitea self-hosted docs (08-RESEARCH Security Domain). |
| T-08-SC | Tampering | gitea-upload-artifact@v4 (only new external action) | mitigate | [VERIFIED] in 08-RESEARCH Package Legitimacy Audit (github.com/ChristopherHX/gitea-upload-artifact) as the cited Gitea fix for the upload-artifact@v4 GHES block; pinned at @v4. All other actions are official GitHub/Docker actions. No [ASSUMED]/[SUS] packages → no install checkpoint required. |
</threat_model>
<verification>
- runner-probe.yml present, branch-scoped, non-destructive; passes the Task 2 grep gate.
- Probe run observed in Gitea Actions; fork answers recorded in SUMMARY.
- Runner online; GITEA_REGISTRY_PAT secret created (operator confirmed).
- No secret material printed in any probe log line.
</verification>
<success_criteria>
- Maps to CI-01/CI-02 prerequisites and Pitfall 12: the runner environment is probed BEFORE any real test/build/publish step is designed.
- The Docker-vs-host fork (A1) is answered; the answer is recorded so Plans 0204 pick the correct DB bring-up path.
- Operator infra (runner + PAT) is in place.
</success_criteria>
<output>
Create `.planning/phases/08-gitea-ci/08-01-SUMMARY.md` when done. MUST record the probe fork answers (runner mode, service-container hostname, cache usable y/n, WebKit deps y/n, upload-artifact fork y/n) — Plans 0204 consume them.
</output>