Make release metadata deterministic across CI #87
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |