Files
mirror/.gitea/workflows/build-and-push.yml
T
ducoterra 28dfc26500
Build and Push Containers / test (push) Successful in 22s
Build and Push Containers / build-and-push-app (push) Successful in 8s
ci: name the Containerfile in the build smoke step
Run #2 got through Checkout, the runtime probe and the whole suite (157 passed,
3 skipped — the `node --check` structural tests skip with no node in a python
image, which is the graceful degradation they were written to have) and then
failed on `docker build -t mirror:ci .`: with no -f, docker looks for
"Dockerfile", finds none, and says "failed to read dockerfile" after
transferring a 2-byte context. The publish job never hit this because
docker/build-push-action passes `file: Containerfile` on its own.

The gate is worth keeping rather than dropping: it is the only step that builds
the image on a push, so without it a broken Containerfile surfaces only as a red
publish job that quietly leaves :main stale.

LLM-Generated: true
2026-11-01 18:08:47 -05:00

109 lines
4.1 KiB
YAML

name: Build and Push Containers
on:
push:
branches:
- main
release:
types:
- published
jobs:
# Gate: the image is only published if the suite is green. A failing job here
# 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
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Locate the container runtime
id: runtime
run: |
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: 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 failing uv
# install step) here rather than in the publish job below.
run: |
set -euo pipefail
# -f is not optional: without it docker looks for "Dockerfile", finds
# none, and reports "failed to read dockerfile" with an empty context —
# which is exactly how run #2 failed. The publish job gets away without
# naming it because docker/build-push-action passes `file:` for us.
"${{ steps.runtime.outputs.path }}" build -f Containerfile -t mirror:ci .
"${{ steps.runtime.outputs.path }}" run --rm mirror:ci --help >/dev/null
build-and-push-app:
needs: test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Login to Gitea Container Registry
uses: docker/login-action@v3
with:
registry: gitea.reeseapps.com
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: gitea.reeseapps.com/experimental/mirror
tags: |
type=raw,value=main,enable=${{ github.ref == 'refs/heads/main' }}
type=ref,event=tag
- name: Build and push app image
uses: docker/build-push-action@v6
with:
context: .
file: Containerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}