Files
spencermandClaude Fable 5.1 6ac62c0bd0
ci / frontend (push) Successful in 25s
ci / server (push) Successful in 2m41s
ci / image (push) Failing after 8s
ci / e2e (push) Successful in 1m12s
Docs: the Gitea runner runs the full workflow, e2e included; open question 15 resolved
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-10-16 10:56:09 -06:00

3.8 KiB

Gitea Actions runner on docker-1

.gitea/workflows/ci.yml runs on a Gitea Actions runner. This folder holds the runner's compose file and configuration. It lives on docker-1 at ~/gitea/runner, next to the Gitea compose project, and runs as the spencerm user through the Docker socket. No sudo is needed.

What it runs

  • One act_runner container (docker.gitea.com/act_runner, pinned by digest in compose.yaml), named docker-1 in Gitea, with the label ubuntu-latest mapped to docker.gitea.com/runner-images:ubuntu-latest (Ubuntu 24.04 with git, node, python 3.12, docker and compose).
  • Every job of ci.yml runs in a fresh job container from that image. The server job's PostgreSQL service container and the e2e job's compose stack (postgres, migrate, app, worker, the Samba DC, Playwright) are started on docker-1's daemon through the socket. The job workspace is a host directory (~/gitea/runner/work/<task>/spencerm/PacketStation, container.bind_workdir) mounted into the job container at that same path, so the compose bind mounts (deploy/postgres/initdb, tests/e2e, the CI secrets) resolve on the host. A Docker-volume workspace does not work here: the first run failed with "bind source path does not exist". The runner removes what the job started when the job ends; the workflow also runs down -v itself.
  • Capacity 2: server and frontend run at the same time; e2e waits for both.
  • The actions cache (setup-python pip cache, setup-node npm cache) is served by the runner on docker-1's docker0 address, port 8088, published in compose.yaml. Job containers cannot reach the runner's own network; before this was set, every cache restore and save timed out after about four and a half minutes, which made the server job take 13 minutes instead of 4.

Install (once)

mkdir -p ~/gitea/runner/data ~/gitea/runner/work
cp deploy/runner/compose.yaml ~/gitea/runner/
cp deploy/runner/config.yaml ~/gitea/runner/data/
cd ~/gitea/runner
docker compose run --rm --entrypoint act_runner runner register --no-interactive \
  --instance https://gt.pescoinc.biz --name docker-1 --config /data/config.yaml \
  --labels ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest \
  --token "$(docker exec -u git gitea gitea actions generate-runner-token)"
docker compose up -d
docker compose logs --tail 20 runner

The registration token is generated by Gitea's own CLI inside the gitea container and goes straight into the register command. It is never printed or written to a file. The result is data/.runner, which holds the runner's own token: keep the data folder private (it is owned by spencerm).

Operate

  • Logs: docker compose -f ~/gitea/runner/compose.yaml logs -f runner. Job logs are also kept under ~/gitea/runner/data/logs for a week.
  • Update: change the digest in compose.yaml (docker buildx imagetools inspect docker.gitea.com/act_runner:0.4), then docker compose up -d. The registration survives updates.
  • Re-register (new instance URL or labels): remove data/.runner and run the register command again. Gitea keeps the old runner row; delete it in Site administration, Actions, Runners.
  • The runner shows in Gitea under Site administration, Actions, Runners. Repository secrets for the image job (REGISTRY_USER, REGISTRY_TOKEN) are set per repository under Settings, Actions, Secrets.

Known limits

  • The job images and the actions (actions/checkout, setup-python, setup-node, the docker ones) are fetched from docker.gitea.com, GitHub and GitHub releases, so docker-1 needs outbound internet.
  • The Samba DC in the e2e stack needs cap_add: SYS_ADMIN. The runner passes it through: the third run (2026-10-16) brought the stack up and passed the 10 Playwright scenarios in under two minutes.
  • Timings with the cache working (run 3): server 3 min 46 s, frontend 1 min 06 s, e2e 1 min 48 s.