Antoine Vandeplanque a353e1c683
All checks were successful
CI / Lint, PostgreSQL tests, and package (push) Successful in 12s
standardize car-tracker naming and verify container entrypoints
2026-09-30 16:29:53 +02:00

7.9 KiB

Gitea CI

This repository targets Gitea 1.23.8. The workflow is in .gitea/workflows/ci.yaml; it runs on pushes, pull requests, and manual dispatch. It adds continuous integration only: no application deployment, scheduled scraping, production migrations, or access to the homelab application database.

What Runs

One Docker-backed job, selected by the runner label python-ci, performs:

  1. Checkout using a commit-pinned action, without persisting checkout credentials.
  2. Install uv 0.9.3 and Python 3.13, then uv sync --locked --dev.
  3. Run Ruff's correctness-focused checks over src and tests.
  4. Run the full unittest suite, including integration tests against a fresh PostgreSQL 17 service named postgres and database car_tracker_test, matching the homelab's PostgreSQL major version.
  5. Build the source distribution and wheel, install that wheel in an independent environment, and verify that its SQL migrations are packaged.

The service password ci-test-only is deliberately public and only for the ephemeral test database. No database port is published on the runner host. Never replace TEST_DATABASE_URL with a production connection string: the integration tests truncate their test tables. No browser installation or live scraping occurs. No raw data, browser profiles, password files, or laptop environment is mounted.

The job image is node:22-bookworm so JavaScript actions have Node, Git, and the basic Linux tools they need; uv supplies Python. The image is not your application deployment image. Both action references use explicit GitHub URLs and commit SHAs. The setup-uv action receives an empty github-token, so the Gitea job token is not sent to GitHub's release API. Caching and problem matchers are disabled for this first Gitea setup. PostgreSQL and Node images track their declared version tags; action commits, uv, and the Python dependency lockfile are explicitly pinned.

Gitea 1.23 ignores several GitHub workflow keys, including job-level timeout-minutes, permissions, and concurrency; the workflow does not pretend they provide protections. Runner configuration enforces capacity 1 and a 30-minute job timeout; the test command also has a 10-minute timeout.

Enable Repository Actions

In Gitea, open the tonio/car-tracker repository's Settings and enable Repository Actions. Actions are enabled globally by default since Gitea 1.21, but disabled for repositories by default. If the repository option is unavailable, check the instance's [actions] ENABLED setting and your permissions.

Pushing a workflow is not enough: a runner with the matching label must be online. Until then, runs can remain waiting. If Actions were disabled when the workflow was pushed, enable them and use the manual run button afterward.

Register A Runner

Use a dedicated Linux CI VM with Docker and Docker Compose, preferably separate from the Gitea and PostgreSQL hosts. A laptop can host a temporary runner, but jobs will stop being serviced when it sleeps or disconnects. The included configuration uses gitea/act_runner:0.2.13, with the configuration format verified from that release. Do not substitute the newer renamed runner without checking compatibility with your Gitea version.

The runner daemon needs Docker socket access, which is effectively root access to the CI host. Do not put production database credentials, backups, or raw listing data on that host. Job containers do not receive the socket or arbitrary host bind mounts. These restrictions reduce exposure but do not make self-hosted execution safe for arbitrary hostile workflows. Register at repository scope, accept only trusted contributors/workflows, and review fork pull requests before allowing them to run. Runner and server updates should be planned independently of this initial setup; Gitea 1.23.8 is an older release.

On the CI host, check out this repository, then run from its root:

docker compose -f ops/ci-runner/compose.yaml pull
docker compose -f ops/ci-runner/compose.yaml run --rm runner \
  --config /config.yaml register

Registration is interactive, so a registration token is not placed in shell arguments or a committed environment file. Obtain a repository-scoped token from tonio/car-tracker -> Settings -> Actions -> Runners. Enter:

Prompt Value
Instance URL Your Gitea HTTP(S) web URL, including its port if needed
Registration token The repository token, entered locally; do not send it in chat
Runner name For example car-tracker-ci
Labels python-ci:docker://node:22-bookworm

The instance URL is not the Git SSH URL ending in port 2222. It must resolve and be reachable from both the runner container and its job containers. Avoid localhost, which would refer to a container. If using a VPN-only hostname, verify DNS and routing inside Docker as well as on the CI host. HTTPS certificate verification remains enabled; configure trust for private certificates rather than disabling it.

Registration writes credentials to /data/.runner in the persistent Docker volume. Treat that volume as a secret. No registration token is needed in Compose's environment. Start the registered daemon:

docker compose -f ops/ci-runner/compose.yaml up -d
docker compose -f ops/ci-runner/compose.yaml logs --tail=100 runner

Confirm in Gitea that the runner is online and advertises python-ci, then trigger the CI workflow. The runner needs outbound access to your Gitea web URL, GitHub/action releases, PyPI/Python downloads, and the container registry. It does not need your application PostgreSQL credentials or an HBA rule for that database. Restrict CI-host network access to production services where practical.

Do not run docker compose down -v unless you intend to erase runner registration. Normal container recreation preserves the registration volume.

First Run Checks

  • A job stuck waiting usually means Actions are disabled, the runner is offline, or its label does not match python-ci.
  • Checkout failures often mean job containers cannot resolve/reach Gitea's web URL or trust its TLS certificate. Successful Git SSH access from the laptop does not prove HTTP(S) checkout works from a Docker container.
  • postgres must resolve inside the job's Docker network. Leave container.network empty so act_runner creates the service network; do not use host networking.
  • The integration tests must execute, not be skipped. TEST_DATABASE_URL is set in the workflow and points only to the disposable service.
  • Caching is deliberately absent, so no cache-server ports or credentials are needed.

The workflow was executed locally using act_runner 0.2.13 against an archive of the staged source, with no laptop credentials or raw snapshots included. Linting, all 82 tests (including PostgreSQL integration tests), the build, and installed-wheel migration checks passed. Local execution substituted the source archive for remote checkout; Gitea HTTP(S) checkout and server-triggered execution still need verification after a runner is registered and the repository's Actions feature is enabled.

The suite was subsequently rerun against disposable PostgreSQL 17.11: all 82 tests passed, including all database integration tests. CI now uses postgres:17 to match the homelab's major version.

Local Checks

uv sync --locked --dev
uv run --locked ruff check src tests
uv run --locked python -m unittest discover -s tests -v
uv build --no-sources

Without TEST_DATABASE_URL, database integration tests are skipped locally. Use the disposable PostgreSQL instructions to run them, never the homelab application database. Keep .env, .pgpass, .runner, and data/ out of Git.

References: Gitea 1.23 quick start, runner setup, and compatibility differences.