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.
171 lines
14 KiB
Markdown
171 lines
14 KiB
Markdown
---
|
||
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 (A1–A10 in 08-RESEARCH Assumptions Log) on the actual Unraid runner so Waves 1–2 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 1–2 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 1–2 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 1–2. 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 02–04 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 02–04 consume them.
|
||
</output>
|