Use LilleVemmelund instead of lille-vemmelund so container registry pushes resolve the correct namespace. Co-authored-by: Cursor <[email protected]>
5.9 KiB
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).
Image contents
Built from docker/ci/Dockerfile:
- Node.js 20 (required by act_runner job containers)
- Python 3.12 (installed via
uv python installfrom.python-version) and pinneduv0.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/LilleVemmelund/python-repositories-ci:latestgitea.lille-vemmelund.dk/LilleVemmelund/python-repositories-ci:YYYYMMDDHHmm(timestamped rollback tag)
Rebuild triggers
ci-image.yml runs on:
- Nightly cron (
0 2 * * *UTC) - Manual
workflow_dispatch - Push to
mainwhenpyproject.toml,uv.lock, ordocker/ci/**change
Workflow usage
Python jobs use runs-on: python-repositories-ci and a fast incremental sync:
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 byscripts/create-gitea-registry-secret.sh) - Mounted at
/root/.docker/config.jsonon the runner container (not DinD) - Configured in
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:
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
for full runner setup.
Verify on a running pod:
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:
docker login gitea.lille-vemmelund.dk -u ci-bot -p <token>
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:
python-repositories-ci:docker://gitea.lille-vemmelund.dk/LilleVemmelund/python-repositories-ci:latest
After label changes, roll runner pods so they re-register with Gitea.
Bootstrap order
- Ensure homelab-platform runners have
gitea-registry-dockerconfigand thepython-repositories-cilabel (see homelab-platform docs). - Merge
ci-image.yml,docker/ci/Dockerfile, and workflow migrations tomain. - Seed the registry with a first image (see below).
- 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.
Seed the image
After merge to main: Actions → Build CI Image → Run workflow.
Before merge (e.g. PR branch): the workflow file is not on main yet — build
and push locally with scripts/ci/build-ci-image.sh:
export CI_RUNNER_TOKEN='ci-bot-personal-access-token'
bash scripts/ci/build-ci-image.sh --push
Run from the repo root on the branch you want to test. Runners pull
gitea.lille-vemmelund.dk/LilleVemmelund/python-repositories-ci:latest from the registry;
they do not care which git branch built it.
Override registry settings if needed:
export REGISTRY=gitea.lille-vemmelund.dk
export REGISTRY_USER=ci-bot
export CI_RUNNER_TOKEN='...'
bash scripts/ci/build-ci-image.sh --push
Local build
Build only (no registry login or push):
export CI_RUNNER_TOKEN='ci-bot-personal-access-token'
bash scripts/ci/build-ci-image.sh --local
Or manually:
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