diff --git a/.gitea/workflows/build-and-push.yml b/.gitea/workflows/build-and-push.yml index bbe8e94..c6f998b 100644 --- a/.gitea/workflows/build-and-push.yml +++ b/.gitea/workflows/build-and-push.yml @@ -13,41 +13,63 @@ jobs: # leaves :main on the last good build, which is what `podman auto-update` # keeps serving on reeseapps-mirror — so red means "nothing shipped", not # "production is broken". + # + # The suite runs *inside* a pinned python:3.13-slim container rather than via a + # `container:` job key. That is not stylistic: actions/checkout is a JavaScript + # action and a python image has no Node runtime, so run #1 died in Checkout + # before running a single command. The runner itself has both (it is where + # build-push-action builds images), so the job stays on the runner and shells + # into the container it needs. + # + # 3.13 is deliberate: it is the floor declared in pyproject's requires-python, + # so CI verifies the floor while development happens on 3.14. test: runs-on: ubuntu-latest - container: - image: python:3.13-slim steps: - name: Checkout uses: actions/checkout@v4 - - name: Install the package and the test tooling + - name: Locate the container runtime + id: runtime run: | - pip install --no-cache-dir --upgrade pip - pip install --no-cache-dir -e . - pip install --no-cache-dir \ - pytest pytest-asyncio pytest-cov pytest-timeout httpx ruff - - - name: Lint - run: | - ruff check . - ruff format --check . + set -euo pipefail + runtime="$(command -v podman || command -v docker || true)" + if [ -z "$runtime" ]; then + echo "Neither podman nor docker is on this runner, so this job cannot" + echo "run the suite in a pinned interpreter. Fix here rather than" + echo "falling back to whatever python the runner image happens to ship." + exit 1 + fi + echo "path=$runtime" >> "$GITHUB_OUTPUT" + echo "using $runtime" # Unit + API only. tests/e2e drives a real Chromium through capture -> # encode -> SRTP -> decode and needs a browser install and, for the # realdisplay rung, an X server; both stay local gates (see AGENTS.md). # --ignore keeps them out of collection entirely: -m filtering happens # after import, and tests/e2e/conftest.py imports playwright. - - name: Test - run: pytest -q --no-cov --ignore=tests/e2e + - name: Lint and test + run: | + set -euo pipefail + "${{ steps.runtime.outputs.path }}" run --rm \ + -v "$GITHUB_WORKSPACE:/src" -w /src \ + docker.io/library/python:3.13-slim \ + /bin/bash -euo pipefail -c ' + pip install --no-cache-dir -q . + pip install --no-cache-dir -q \ + pytest pytest-asyncio pytest-cov pytest-timeout httpx ruff + ruff check . + ruff format --check . + pytest -q --no-cov --ignore=tests/e2e + ' - name: Smoke the image build - # Catches a broken Containerfile (a missing COPY source, a bad uv - # install) here rather than in the publish job below. + # Catches a broken Containerfile (a missing COPY source, a failing uv + # install step) here rather than in the publish job below. run: | - apt-get update -qq && apt-get install -y -qq --no-install-recommends podman - podman build -t mirror:ci . - podman run --rm mirror:ci --help >/dev/null + set -euo pipefail + "${{ steps.runtime.outputs.path }}" build -t mirror:ci . + "${{ steps.runtime.outputs.path }}" run --rm mirror:ci --help >/dev/null build-and-push-app: needs: test