# CI base image This repository uses a **per-repo Docker image** for Gitea Actions jobs instead of installing Python and `uv` on every run. Infrastructure images (redis, minio, etc.) are cached cluster-wide via Harbor (see homelab-platform [`docs/harbor-registry-mirror.md`](https://gitea.lille-vemmelund.dk/LilleVemmelund/homelab-platform/src/branch/main/docs/harbor-registry-mirror.md)). ## Image contents Built from [`docker/ci/Dockerfile`](../docker/ci/Dockerfile): - Node.js 20 (required by act_runner job containers) - Python 3.12 (installed via `uv python install`) and pinned `uv` 0.7.0 - Docker CLI (integration tests via testcontainers) - Dev dependencies from `uv.lock` (`uv sync --all-extras --no-install-project`) Published to the Gitea container registry: - `gitea.lille-vemmelund.dk/brian/python-repositories-ci:latest` - `gitea.lille-vemmelund.dk/brian/python-repositories-ci:YYYYMMDDHHmm` (timestamped rollback tag) ## Rebuild triggers [`ci-image.yml`](../.gitea/workflows/ci-image.yml) runs on: - Nightly cron (`0 2 * * *` UTC) - Manual `workflow_dispatch` - Push to `main` when `pyproject.toml`, `uv.lock`, or `docker/ci/**` change ## Workflow usage Python jobs use `runs-on: python-repositories-ci` and a fast incremental sync: ```yaml runs-on: python-repositories-ci steps: - uses: actions/checkout@v4 - name: Sync dependencies env: UV_LINK_MODE: copy UV_INDEX_GITEA_USERNAME: ci-bot UV_INDEX_GITEA_PASSWORD: ${{ secrets.CI_RUNNER_TOKEN }} run: uv sync --all-extras --frozen ``` `ci-image.yml` uses `runs-on: ubuntu-latest` so it can bootstrap before the custom image exists. ## Registry authentication act_runner pulls the job image **before any workflow step runs**, so a `docker login` step inside a job cannot authenticate that pull. Authentication must be configured on the runner (or via a job-level `container.credentials` block — not used here). ### k8s Gitea runners (automatic) The homelab-platform runners mount Gitea registry credentials automatically: - Secret: `gitea-runners/gitea-registry-dockerconfig` (created by [`scripts/create-gitea-registry-secret.sh`](https://gitea.lille-vemmelund.dk/LilleVemmelund/homelab-platform/src/branch/main/scripts/create-gitea-registry-secret.sh)) - Mounted at `/root/.docker/config.json` on the **runner** container (not DinD) - Configured in [`platform/gitea-runners/values.yaml`](https://gitea.lille-vemmelund.dk/LilleVemmelund/homelab-platform/src/branch/main/platform/gitea-runners/values.yaml) No manual `docker login` is required on the in-cluster runners once the secret exists. To create or rotate credentials: ```bash export KUBECONFIG=/path/to/homelab-cluster/talos/_out/kubeconfig export GITEA_REGISTRY_USERNAME='ci-bot' export GITEA_REGISTRY_PASSWORD='personal-access-token-with-read-package' bash scripts/create-gitea-registry-secret.sh ``` Then restart runner pods so they pick up the updated secret. See homelab-platform [`docs/gitea-actions-runners.md`](https://gitea.lille-vemmelund.dk/LilleVemmelund/homelab-platform/src/branch/main/docs/gitea-actions-runners.md) for full runner setup. Verify on a running pod: ```bash kubectl -n gitea-runners exec homelab-cluster-gitea-runner-0 -c runner -- \ test -f /root/.docker/config.json && echo "registry auth mounted" ``` ### Standalone runners (e.g. Unraid `homelab`) The 4th runner is outside the k8s cluster and does **not** get the automatic mount. Configure registry auth manually on that host: ```bash docker login gitea.lille-vemmelund.dk -u ci-bot -p ``` Also add the `python-repositories-ci` label to that runner's act_runner config. ### `ci-image.yml` push login The build workflow still runs `docker login` before `docker push`. That step authenticates **DinD inside the job** for pushing the image to Gitea — a different code path from act_runner pulling the job container. ## Runner label Jobs use the `python-repositories-ci` act_runner label, configured in homelab-platform [`platform/gitea-runners/values.yaml`](https://gitea.lille-vemmelund.dk/LilleVemmelund/homelab-platform/src/branch/main/platform/gitea-runners/values.yaml): ```yaml python-repositories-ci:docker://gitea.lille-vemmelund.dk/brian/python-repositories-ci:latest ``` After label changes, roll runner pods so they re-register with Gitea. ## Bootstrap order 1. Ensure homelab-platform runners have `gitea-registry-dockerconfig` and the `python-repositories-ci` label (see homelab-platform docs). 2. Merge `ci-image.yml`, `docker/ci/Dockerfile`, and workflow migrations to `main`. 3. Run **Build CI Image** manually once (Actions → Build CI Image → Run workflow). 4. Confirm a test workflow job starts on `python-repositories-ci`. Until step 3 completes, jobs targeting `python-repositories-ci` will fail because the image does not exist in the Gitea registry yet. ## Local build ```bash echo "$CI_RUNNER_TOKEN" > /tmp/uv_token docker build -f docker/ci/Dockerfile \ --secret id=uv_token,src=/tmp/uv_token \ -t python-repositories-ci:local . rm -f /tmp/uv_token ```