Skip to content

Make release metadata deterministic across CI #87

Make release metadata deterministic across CI

Make release metadata deterministic across CI #87

Workflow file for this run

name: CI
on:
push:
pull_request:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Sync dependencies
run: uv sync --extra dev
- name: Lint
run: uv run ruff check .
- name: Format check
run: uv run ruff format --check .
typecheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Sync dependencies
run: uv sync --extra dev
- name: Mypy (new modules only)
run: |
uv run mypy \
src/exposure_scenario_mcp/solvers/two_zone.py \
src/exposure_scenario_mcp/plugins/inhalation.py \
src/exposure_scenario_mcp/worker_routing.py
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Sync dependencies
run: uv sync --extra dev
- name: pip-audit
run: uv run pip-audit --desc
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Sync dependencies
run: uv sync --extra dev
- name: Test
run: uv run pytest
- name: Build distribution artifacts
run: uv build
- name: Generate contract assets
if: matrix.python-version == '3.12'
run: uv run generate-exposure-contracts
- name: Validate evals against current surface
if: matrix.python-version == '3.12'
run: uv run validate-evals
- name: Check generated assets are committed
if: matrix.python-version == '3.12'
run: |
RELEASE_VERSION=$(python - <<'PY'
import tomllib
from pathlib import Path
print(tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["project"]["version"])
PY
)
git diff --exit-code -- \
defaults/manifest.json \
docs/contracts/contract_manifest.json \
docs/contracts/schemas \
"docs/releases/v${RELEASE_VERSION}.md" \
"docs/releases/v${RELEASE_VERSION}.release_metadata.json" \
evals/exposure_scenario_mcp_readonly.xml \
schemas
git ls-files --error-unmatch "docs/releases/v${RELEASE_VERSION}.md" >/dev/null
git ls-files --error-unmatch "docs/releases/v${RELEASE_VERSION}.release_metadata.json" >/dev/null
- name: Verify release artifact metadata
if: matrix.python-version == '3.12'
run: uv run check-exposure-release-artifacts
- name: Wheel install smoke test
if: matrix.python-version == '3.12'
run: |
RELEASE_VERSION=$(python - <<'PY'
import tomllib
from pathlib import Path
print(tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["project"]["version"])
PY
)
python -m venv .wheel-smoke
. .wheel-smoke/bin/activate
pip install "dist/exposure_scenario_mcp-${RELEASE_VERSION}-py3-none-any.whl"
python -m exposure_scenario_mcp --help >/dev/null
python - <<'PY'
import json
from pathlib import Path
from exposure_scenario_mcp import __version__
from exposure_scenario_mcp.archetypes import ArchetypeLibraryRegistry
from exposure_scenario_mcp.benchmarks import load_benchmark_manifest
from exposure_scenario_mcp.contracts import build_release_metadata_report
from exposure_scenario_mcp.defaults import DefaultsRegistry
from exposure_scenario_mcp.probability_profiles import ProbabilityBoundsProfileRegistry
from exposure_scenario_mcp.scenario_probability_packages import (
ScenarioProbabilityPackageRegistry,
)
repo_root = Path.cwd()
defaults_manifest = json.loads(
(repo_root / "defaults" / "manifest.json").read_text(encoding="utf-8")
)
archetype_payload = json.loads(
(
repo_root
/ "archetypes"
/ "v1"
/ "envelope_archetype_library.json"
).read_text(encoding="utf-8")
)
probability_profiles_payload = json.loads(
(
repo_root
/ "probability_bounds"
/ "v1"
/ "driver_probability_profiles.json"
).read_text(encoding="utf-8")
)
scenario_package_payload = json.loads(
(
repo_root
/ "probability_bounds"
/ "v1"
/ "scenario_package_probability_profiles.json"
).read_text(encoding="utf-8")
)
registry = DefaultsRegistry.load()
archetypes = ArchetypeLibraryRegistry.load()
profiles = ProbabilityBoundsProfileRegistry.load()
packages = ScenarioProbabilityPackageRegistry.load()
assert registry.version == defaults_manifest["defaults_version"]
assert archetypes.version == archetype_payload["library_version"]
assert profiles.version == probability_profiles_payload["profile_version"]
assert packages.version == scenario_package_payload["profile_version"]
assert len(load_benchmark_manifest()["cases"]) >= 2
assert build_release_metadata_report(registry).package_version == __version__
PY
docker-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build Docker image
run: docker build -t exposure-scenario-mcp:ci .
- name: Docker smoke test
run: docker run --rm exposure-scenario-mcp:ci --help >/dev/null