Files
esh-pfi-infrastructure/persistent-memory.d/2026-07-18-fleet-gitea-runner-build-recipe.md
T
vh abc8f0ceab memory: snapshot — soong-lab cutover + zonos 0.2.1 presets + fleet CI recipe + peer creds
End-of-session snapshot for /clear. In-flight compressed (four majors landed:
zonos-gateway 0.2.1 emotion presets, soong-lab containerized cutover, Wyrd +
wtsdk credential provisions). New Recent-decisions + Tried entries and three
detail files capture the durable lessons (fleet Gitea build recipe, the
vh-is-a-user package-write constraint, soong-lab deploy layout).
2026-07-18 16:40:49 -07:00

3.4 KiB

  • [2026-07-18] Fleet Gitea-Actions build recipe + the vh-is-a-user package-write constraint (learned the hard way across 3 failed soong-lab validation builds; reusable for ANY fleet CI image build or package publish).

The runner. One act_runner (gitea/act_runner) on ana-docker, labels pfi-fleet / ana-docker → both map to job image node:20-bookworm-slim, which has NO docker and NO git. Config /opt/docker/conf/gitea-runner/data/config.yaml: valid_volumes: [] (no socket propagated to job containers). So:

  • actions/checkout@v4 fails (needs git); docker/* marketplace actions fail (need docker) — a workflow built on those dies at the first step (~15s).

The working recipe (mirror Worldtree deploy.yml). Run the job in a docker-capable image + drive docker with RAW commands, not the JS actions:

runs-on: pfi-fleet
container:
  image: docker:24.0.7-cli        # has docker+buildx; add git+node
steps:
  - run: apk add --no-cache git nodejs   # so actions/checkout@v4 works
  - uses: actions/checkout@v4
  - name: login   # RAW, not docker/login-action
    run: echo "$REGISTRY_TOKEN" | docker login gitea.phasefinal.com -u "$REGISTRY_USER" --password-stdin
  - name: buildx builder
    run: docker buildx create --name X --driver docker-container --use; docker buildx inspect --bootstrap
  - name: build+push   # RAW, not docker/build-push-action
    run: docker buildx build --secret id=<name>,env=<TOKEN> -t <img>:latest --push .

The runner mounts the host docker socket into ITSELF; the docker:cli job reaches the daemon through that. The docker/* JS actions are unreliable on act_runner — raw commands are the fleet convention.

vh is a USER account, not an org. Consequences that bit repeatedly:

  1. GET /api/v1/orgs/vh → 404 "user redirect"; there are no org teams to add a service account to.
  2. User-owned packages are OWNER-WRITE-ONLY. claude-bot (even repo admin-collaborator on vh/soong-lab, even with write:package scope + full basic-auth) gets 401 unauthorized on docker push to vh/soong-lab, and npm publish to vh/npm/ would 401 too. Only vh itself can write vh packages. → CI must authenticate AS vh for the push (a vh-owned write:package PAT as REGISTRY_TOKEN + REGISTRY_USER=vh), exactly how WT pushes vh/worldtree. claude-bot CAN still: clone/read repos, READ packages (pulled the image fine), dispatch workflows, mint demo Worldtree keys.
  3. Repo Actions secrets are OWNER-ONLY tooPUT .../actions/secrets/X as claude-bot (repo admin-collab) → 403 "user should be the owner of the repo". Only vh can set a repo's secrets.

Other gotchas:

  • Gitea reserves the GITEA_ secret-name prefix — a secret named GITEA_PYPI_TOKEN is illegal; use e.g. PYPI_TOKEN.
  • Gitea package auth is token-based / username-lenientdocker login / PyPI basic-auth authenticate via the token; the username is nominal (tested -u gitea and -u claude-bot both 200 against the vh PyPI). So a Dockerfile hardcoding UV_INDEX_GITEA_USERNAME=gitea is fine with any valid token.
  • Homepage (esh-docker-vm) docker-label auto-discovery only covers the 5 endpoints in its docker.yaml (esh-vm-docker, ana-docker, ana-ml2, nh3-docker, irv-ml1); corviduo-dev is NOT watched → services there need a manual services.yaml entry, not labels.

Applied in the soong-lab CI: 2026-07-18-soong-lab-containerize-cutover.