From 91d216cae492627cfb9f3235e7005a8a153a1471 Mon Sep 17 00:00:00 2001 From: Serge Koenigsmann Date: Tue, 4 Aug 2026 11:21:17 +0200 Subject: [PATCH] feat: ci-python CI-Basis-Image + Build-Workflow Vorgebackenes Python-CI-Image (debian:bookworm-slim + uv, gepinnte CPython 3.12.13, ruff 0.16.1, pytest 9.1.1) fuer Gitea Actions. Basis + uv per Digest gepinnt, Versionen zentral im Dockerfile-Kopf. Build-Workflow pusht :latest + : in die Gitea-Registry (sko/ci-python), woechentlicher Rebuild. Lokal verifiziert: Build gruen, 397 MB, uv sync --frozen + Exporter-Imports OK. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitea/workflows/build-ci-python.yml | 48 ++++++++++++++++++++++ .gitignore | 2 + README.md | 54 +++++++++++++++++++++++++ python/Dockerfile | 60 ++++++++++++++++++++++++++++ 4 files changed, 164 insertions(+) create mode 100644 .gitea/workflows/build-ci-python.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 python/Dockerfile diff --git a/.gitea/workflows/build-ci-python.yml b/.gitea/workflows/build-ci-python.yml new file mode 100644 index 0000000..87af163 --- /dev/null +++ b/.gitea/workflows/build-ci-python.yml @@ -0,0 +1,48 @@ +name: Build ci-python image + +# Baut das ci-python-Image und pusht es in die Gitea-Container-Registry. +# Tags: :latest (mitlaufend) + : (unveränderlich, zum Pinnen in Workflows). +# +# Voraussetzungen im Runner: +# - Docker verfügbar (Socket oder DinD) — Image-Bau braucht einen Docker-Daemon. +# - Secrets: REGISTRY_USER (Gitea-User) + REGISTRY_TOKEN (PAT, Scope package:write). + +on: + push: + branches: [main] + paths: + - 'python/Dockerfile' + - '.gitea/workflows/build-ci-python.yml' + schedule: + - cron: '0 3 * * 1' # wöchentlicher Rebuild für Basis-Sicherheitsupdates + workflow_dispatch: + +env: + REGISTRY: gitea.inmedias.it + IMAGE: sko/ci-python + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - name: Login to Gitea registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Build & push + uses: docker/build-push-action@v6 + with: + context: python + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE }}:latest + ${{ env.REGISTRY }}/${{ env.IMAGE }}:${{ github.sha }} + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE }}:buildcache + cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE }}:buildcache,mode=max diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1330247 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.log +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..b92c487 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# ci-images + +Vorgebackene CI-Basis-Images für Gitea Actions. Ziel: schneller Cold-Start und +reproduzierbare Läufe, statt Werkzeuge bei jedem Lauf per `curl | sh` / +`pip install` nachzuladen (siehe Recherche „CI/CD-Optimierung mit Docker & Ansible"). + +## Images + +| Image | Registry-Pfad | Inhalt | +|-------|---------------|--------| +| `ci-python` | `gitea.inmedias.it/sko/ci-python` | `debian:bookworm-slim` + uv + gepinnte CPython, ruff, pytest | + +Weitere Stacks (z. B. `ci-node`) kommen als eigene Unterordner nach demselben +Muster dazu, sobald der Python-Pilot trägt. + +## Versionen pflegen (eine Quelle der Wahrheit) + +Alle Versionen stehen als `ARG` bzw. gepinnter Digest oben im jeweiligen +`Dockerfile` (z. B. `python/Dockerfile`). Ändern → auf `main` pushen → der +Workflow `build-ci-python` baut und pusht neu. + +Digests auffrischen: + +```bash +docker buildx imagetools inspect debian:bookworm-slim +docker buildx imagetools inspect ghcr.io/astral-sh/uv:0.12.1 +``` + +## Verwendung in einem Repo (Weg B: `container:`, kein Runner-Eingriff) + +```yaml +jobs: + test: + runs-on: ubuntu-latest + container: gitea.inmedias.it/sko/ci-python:latest + steps: + - uses: actions/checkout@v4 + - run: uv sync --frozen # repo-eigene Deps aus uv.lock + - run: ruff check . + - run: pytest +``` + +Zum reproduzierbaren Pinnen statt `:latest` einen unveränderlichen `:`-Tag +verwenden. + +Registry-Login für den Pull passiert im Runner über einen `container.credentials`- +Block oder `docker/login-action` gegen `gitea.inmedias.it` (PAT, Scope `package:read`). + +## Build lokal testen + +```bash +docker build -t ci-python:test python +docker run --rm ci-python:test python --version +``` diff --git a/python/Dockerfile b/python/Dockerfile new file mode 100644 index 0000000..ef51ea0 --- /dev/null +++ b/python/Dockerfile @@ -0,0 +1,60 @@ +# ci-python — vorgebackenes CI-Image für Python-Repos (Gitea Actions) +# +# Basis: debian:bookworm-slim (firmenweite Distro-Linie), Toolchain über uv. +# Ziel: schneller Cold-Start + reproduzierbare Läufe (kein `curl | sh` / kein +# `pip install` zur Laufzeit mehr). Repo-eigene Deps kommen weiterhin per +# `uv sync --frozen` aus dem jeweiligen uv.lock — dieses Image liefert nur die +# Toolchain (Python, uv, ruff, pytest). +# +# Versionen stehen ausschliesslich hier (eine Quelle der Wahrheit). Aktualisieren +# heisst: ARG/Digest anpassen -> Push -> Build-Workflow baut & pusht neu. + +# Basis per Digest gepinnt (reproduzierbar). Auflösen mit: +# docker buildx imagetools inspect debian:bookworm-slim +FROM debian:bookworm-slim@sha256:7b140f374b289a7c2befc338f42ebe6441b7ea838a042bbd5acbfca6ec875818 + +# --- zentrale Versionsliste --------------------------------------------------- +ARG PYTHON_VERSION=3.12.13 +ARG RUFF_VERSION=0.16.1 +ARG PYTEST_VERSION=9.1.1 + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# 1) Selten ändernde Systemschicht zuerst -> Cache greift lange. +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + git \ + && rm -rf /var/lib/apt/lists/* + +# 2) uv aus dem offiziellen, digest-gepinnten uv-Image kopieren. +# Digest auflösen mit: docker buildx imagetools inspect ghcr.io/astral-sh/uv:0.12.1 +COPY --from=ghcr.io/astral-sh/uv:0.12.1@sha256:cf4eedcaa81655197f625739489effcbe71b61ceb1506f332c3facae5deceded \ + /uv /uvx /usr/local/bin/ + +# 3) uv systemweit verdrahten: managed Python, keine Suche nach System-Python, +# Tool-Bins landen in /usr/local/bin (auf PATH). +ENV UV_PYTHON_INSTALL_DIR=/opt/uv/python \ + UV_TOOL_DIR=/opt/uv/tools \ + UV_TOOL_BIN_DIR=/usr/local/bin \ + UV_PYTHON_PREFERENCE=only-managed \ + UV_LINK_MODE=copy \ + UV_NO_PROGRESS=1 + +# 4) Gepinnte CPython einbacken und als Default `python`/`python3` verdrahten. +RUN uv python install "${PYTHON_VERSION}" \ + && ln -sf "$(uv python find "${PYTHON_VERSION}")" /usr/local/bin/python3 \ + && ln -sf /usr/local/bin/python3 /usr/local/bin/python + +# 5) Lint-/Test-Tools global (schnelle CI ohne per-Repo-Install). +RUN uv tool install "ruff==${RUFF_VERSION}" \ + && uv tool install "pytest==${PYTEST_VERSION}" + +# 6) Smoke-Test beim Bau: bricht den Build ab, falls etwas nicht auf PATH liegt. +RUN python --version \ + && uv --version \ + && ruff --version \ + && pytest --version + +CMD ["/bin/bash"]