Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,22 @@ jobs:
- name: Set up Python
run: uv python install ${{ env.PYTHON_VERSION }}

- name: Every service's uv.lock matches its pyproject.toml
# M-01: each image installs its service's uv.lock as it stands (``uv
# export --frozen``), while the install below resolves the pyproject
# ranges afresh. A pyproject edit made without a re-lock was therefore
# tested here and missing from the image, with nothing to fail it.
# ``uv lock --check`` fails when re-locking would change the lock.
run: |
stale=""
for service in core-api core-storage-api core-operations core-worker; do
uv lock --check --directory "${service}" || stale="${stale} ${service}/uv.lock"
done
if [ -n "${stale}" ]; then
echo "::error::Out of date:${stale}. Run uv lock in each one's directory and commit the result."
exit 1
fi

- name: Install dependencies
# core-operations is installed for its own ruff/mypy/pytest steps
# below. Before it was listed here those steps passed only because
Expand Down Expand Up @@ -773,6 +789,17 @@ jobs:
exit 1
}

# M-01: core-worker's image, the write path in deferred mode, was built by
# no workflow. Its tests run in the CI job's shared venv, where another
# service's dependency can stand in for one core-worker never declared,
# so import the app its CMD serves from what the image itself installed.
- name: Build core-worker image and import its app
run: |
docker build --platform linux/amd64 --target runtime \
-t core-worker-imagetest \
-f core-worker/Dockerfile .
docker run --rm core-worker-imagetest python -c "import uvicorn, core_worker.app"

- name: Build core-storage-api image for README quickstart
run: |
docker build --platform linux/amd64 --target runtime \
Expand Down
49 changes: 49 additions & 0 deletions tests/test_m01_every_lock_is_checked_and_every_image_built.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""M-01: CI checks every service lock and builds every service image.

Each service image installs its own ``uv.lock`` as it stands (``uv export
--frozen``), while CI tests the pyproject ranges, resolved afresh. A pyproject
edit made without a re-lock was therefore tested here but missing from the
image, and nothing failed. CI now runs ``uv lock --check`` for every service.
core-worker's image, the write path in deferred mode, was built by no workflow,
so a broken Dockerfile or a dependency it never declared would surface only in a
deploy; CI now builds it too.
"""

from __future__ import annotations

import re
from pathlib import Path

import pytest
import yaml

pytestmark = pytest.mark.unit

REPO = Path(__file__).resolve().parents[1]
CI = REPO / ".github/workflows/ci.yml"
SERVICES = {"core-api", "core-storage-api", "core-worker", "core-operations"}
LOCKED = sorted(path.parent.name for path in REPO.glob("*/uv.lock"))
IMAGES = sorted(path.parent.name for path in REPO.glob("*/Dockerfile"))


def _runs(command: str) -> str:
"""Every ci.yml ``run:`` script that runs ``command``, joined."""
jobs = yaml.safe_load(CI.read_text())["jobs"].values()
runs = (step.get("run", "") for job in jobs for step in job.get("steps", []))
return "\n".join(run for run in runs if command in run)


def test_the_scans_see_every_service() -> None:
assert set(LOCKED) >= SERVICES
assert set(IMAGES) >= SERVICES
assert "-f core-api/Dockerfile" in _runs("docker build")


def test_ci_checks_every_service_lock() -> None:
named = set(re.findall(r"[\w.-]+", _runs("uv lock --check")))
assert [name for name in LOCKED if name not in named] == []


def test_ci_builds_every_service_image() -> None:
builds = _runs("docker build")
assert [name for name in IMAGES if f"-f {name}/Dockerfile" not in builds] == []
Loading