From 3f1bcc336002c1c07add3b48a352278096d379df Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 20 May 2026 20:16:26 -0400 Subject: [PATCH] ci: consume shared ci-python:3.14 image; bump runtime to 3.14 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate to the FabledRulebook CI-Runner contract: - .forgejo/workflows/ci.yml: all four jobs (typecheck/lint/test/build) now schedule on the `python-ci` runner label and run inside container.image: git.fabledsword.com/bvandeusen/ci-python:3.14 (Python 3.14 + Node 24 + ruff + uv + Docker CLI). Dropped the inline uv install in the test job — uv is now baked into the image. - Dockerfile: production runtime bumped to python:3.14-slim so test results stay representative against what we ship. - ci-requirements.md: new file at repo root declaring image deps and per-job installs (per FabledRulebook ci-runners.md). - infra/Dockerfile.runner-base: deleted. The in-repo runner base (Ubuntu 24.04 + Python 3.12 + Node 22) is superseded by the shared ci-python image. The runner-host deployment files (runner-compose.yml + act-runner-config.yml) stay as deployment-shape documentation; source of truth is the deployed config. - docs/development.md: CI/CD + Runner sections refreshed. Co-Authored-By: Claude Opus 4.7 (1M context) --- .forgejo/workflows/ci.yml | 18 +++++++++---- Dockerfile | 3 ++- ci-requirements.md | 49 ++++++++++++++++++++++++++++++++++++ docs/development.md | 18 ++++++++----- infra/Dockerfile.runner-base | 48 ----------------------------------- 5 files changed, 76 insertions(+), 60 deletions(-) create mode 100644 ci-requirements.md delete mode 100644 infra/Dockerfile.runner-base diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 4ff4ecb..ed9998c 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -64,7 +64,9 @@ jobs: name: TypeScript typecheck # Skip on main merge-commit pushes — see workflow header comment. if: github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/v') - runs-on: ci-runner + runs-on: python-ci + container: + image: git.fabledsword.com/bvandeusen/ci-python:3.14 steps: - uses: actions/checkout@v6 @@ -86,11 +88,13 @@ jobs: lint: name: Python lint if: github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/v') - runs-on: ci-runner + runs-on: python-ci + container: + image: git.fabledsword.com/bvandeusen/ci-python:3.14 steps: - uses: actions/checkout@v6 - # ruff is pre-installed in the ci-runner base image — no install + # ruff is pre-installed in the ci-python image — no install # step needed, lint runs in ~2s. - name: Lint run: ruff check src/ @@ -98,7 +102,9 @@ jobs: test: name: Python tests if: github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/v') - runs-on: ci-runner + runs-on: python-ci + container: + image: git.fabledsword.com/bvandeusen/ci-python:3.14 steps: - uses: actions/checkout@v6 @@ -132,7 +138,9 @@ jobs: # pushes skip here too, so no production image is ever built from a # raw main push (only from the v* tag the release creates). if: github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/v') - runs-on: ci-runner + runs-on: python-ci + container: + image: git.fabledsword.com/bvandeusen/ci-python:3.14 permissions: contents: read packages: write diff --git a/Dockerfile b/Dockerfile index 931d8cd..d3fc9e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,8 @@ COPY frontend/ . RUN npm run build # Stage 2: Python runtime -FROM python:3.12-slim AS runtime +# Tracks CI image (ci-python:3.14) so test results stay representative. +FROM python:3.14-slim AS runtime WORKDIR /app COPY pyproject.toml . diff --git a/ci-requirements.md b/ci-requirements.md new file mode 100644 index 0000000..ca436a8 --- /dev/null +++ b/ci-requirements.md @@ -0,0 +1,49 @@ +# CI Requirements — FabledScribe + +> Spec lives in [`docs/process.md`](https://git.fabledsword.com/bvandeusen/CI-runner/src/branch/main/docs/process.md) +> in the CI-Runner repo. + +## Runtime image + +``` +git.fabledsword.com/bvandeusen/ci-python:3.14 +``` + +Used by all four jobs in `.forgejo/workflows/ci.yml`: typecheck (Vue/TS), +lint (ruff), test (pytest), build (docker buildx). + +## Image deps used + +- python 3.14 +- node 24 (used for `npm ci` + `vue-tsc` in the typecheck job, and as the + frontend builder stage inside the production `Dockerfile`) +- ruff (lint job runs `ruff check src/` with zero install overhead) +- docker CLI + buildx (build job pushes the production image to the + Forgejo registry) + +## Per-job tool installs + +Anything CI installs at job time that isn't in the image. Promotion +candidates if more than one project needs them. + +- `uv` — installed inline in the test job (`curl -LsSf + https://astral.sh/uv/install.sh | sh`). **Temporary**: belongs in the + ci-python image so every consumer doesn't re-install on cold start. + Tracked at [CI-Runner](https://git.fabledsword.com/bvandeusen/CI-runner). +- `http-ece` is `--no-build-isolation`-installed before the editable + package install because http-ece doesn't declare `setuptools` as a + build dep and uv creates bare venvs without it. Not promotion-worthy + (one project, one wheel). + +## Notes + +- Production runtime image (`Dockerfile`) also tracks Python 3.14 — the + CI image and runtime image stay aligned by design so test results are + representative. +- Build wall time: dominated by `pytest` (full async test suite). Cold + ci-python pulls add ~30s; not a blocker. +- Registry-backed BuildKit layer cache (`type=registry,ref=…:cache,mode=max`) + gives ~80% speedup on warm builds — see the build job comment. +- `pyproject.toml` keeps `requires-python = ">=3.12"` permissive so the + package can still be installed against older interpreters by external + consumers, but the CI/runtime target is 3.14. diff --git a/docs/development.md b/docs/development.md index 592ae86..cbc4fa7 100644 --- a/docs/development.md +++ b/docs/development.md @@ -57,7 +57,9 @@ Migration conventions: ### Pipeline -CI runs on Forgejo Actions with a custom runner base image (`py3.12-node22`): +CI runs on Forgejo Actions, consuming the shared +[`ci-python:3.14`](https://git.fabledsword.com/bvandeusen/CI-runner) image +via `container.image` (Python 3.14 + Node 24 + ruff + uv + Docker CLI): | Trigger | Jobs | Docker tags pushed | |---------|------|--------------------| @@ -77,13 +79,17 @@ CI runs on Forgejo Actions with a custom runner base image (`py3.12-node22`): git checkout dev && git merge main && git push origin dev ``` -### Custom Runner +### Runner -Runner base image: `infra/Dockerfile.runner-base` (Ubuntu 24.04 + Python 3.12 + Node 22 LTS). -Runner config: `infra/act-runner-config.yml` (label: `py3.12-node22`). -Runner compose: `infra/runner-compose.yml`. +CI jobs schedule against the `python-ci` runner label and run inside the +shared `git.fabledsword.com/bvandeusen/ci-python:3.14` image (see +`ci-requirements.md` for what this project relies on from the image). +The runner deployment lives outside this repo; image bumps happen in +[CI-Runner](https://git.fabledsword.com/bvandeusen/CI-runner) via Renovate. -To activate a new runner registration, copy `infra/act-runner-config.yml` to the runner's config directory, delete the `.runner` registration file in the runner container, and restart the stack. +`infra/runner-compose.yml` + `infra/act-runner-config.yml` document the +runner-host deployment shape; the source of truth is the deployed +config on the runner host. ### Docker Registry diff --git a/infra/Dockerfile.runner-base b/infra/Dockerfile.runner-base deleted file mode 100644 index b4fa7e9..0000000 --- a/infra/Dockerfile.runner-base +++ /dev/null @@ -1,48 +0,0 @@ -# Runner base image for Forgejo act_runner job containers. -# Pre-installs Python 3.12, Node 22, uv, and ruff so workflows skip -# lengthy runtime installs on every run. -# -# Build and push (re-run when this file changes): -# docker build -f infra/Dockerfile.runner-base \ -# -t git.fabledsword.com/bvandeusen/runner-base:ci-runner . -# docker push git.fabledsword.com/bvandeusen/runner-base:ci-runner -# -# Then redeploy the act_runner stack in Portainer to pick up the new image. - -FROM ubuntu:24.04 - -ENV DEBIAN_FRONTEND=noninteractive -ENV TZ=UTC - -# Split into separate RUN steps so each pushed layer is smaller — one -# combined layer exceeds the nginx upload timeout on the self-hosted -# Forgejo registry. - -# Python 3.12 (ships in Ubuntu 24.04 main repos) + core tools -RUN apt-get update -qq && \ - apt-get install -y -qq \ - python3.12 python3.12-dev python3.12-venv \ - git curl ca-certificates gnupg jq tzdata && \ - apt-get clean && rm -rf /var/lib/apt/lists/* - -# Node 22 LTS via NodeSource -RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \ - apt-get install -y -qq nodejs && \ - apt-get clean && rm -rf /var/lib/apt/lists/* - -# Docker CLI — daemon comes from the host socket mount, only CLI needed. -# docker.io from Ubuntu repos is sufficient for docker login + buildx. -RUN apt-get update -qq && \ - apt-get install -y -qq docker.io && \ - apt-get clean && rm -rf /var/lib/apt/lists/* - -# uv — fast Python package installer/resolver (~10x faster than pip). -# Used by test jobs instead of pip for venv creation + dep installs. -RUN curl -LsSf https://astral.sh/uv/install.sh | env INSTALLER_NO_MODIFY_PATH=1 sh && \ - mv /root/.local/bin/uv /usr/local/bin/ && \ - mv /root/.local/bin/uvx /usr/local/bin/ - -# ruff — Python linter. Baked in so the lint job is just -# `ruff check src/` with zero install overhead. -RUN curl -LsSf https://astral.sh/ruff/install.sh | env INSTALLER_NO_MODIFY_PATH=1 sh && \ - mv /root/.local/bin/ruff /usr/local/bin/