11name : CI
22
3+ # Contract: this workflow runs the same gate as `make ci`
4+ # (fmt-check -> lint -> type -> test -> example). Each step below invokes a
5+ # Makefile target so the local gate and CI cannot drift (see Makefile and
6+ # docs/agent-context/workflows.md). Change the steps here only by changing the
7+ # Makefile.
8+
39on :
410 push :
511 branches : ["main", "copilot/**"]
612 pull_request :
713 branches : ["main"]
814 workflow_call :
915
16+ # Least-privilege by default; jobs needing more declare it explicitly.
17+ permissions :
18+ contents : read
19+
20+ # Cancel superseded runs on the same ref so a new push to a PR stops the
21+ # previous run instead of burning runner time. Keyed on the ref so distinct
22+ # branches/PRs stay independent.
23+ concurrency :
24+ group : ci-${{ github.workflow }}-${{ github.ref }}
25+ cancel-in-progress : true
26+
1027jobs :
1128 test :
1229 name : " Python ${{ matrix.python-version }}"
@@ -18,64 +35,162 @@ jobs:
1835 python-version : ["3.10", "3.11", "3.12"]
1936
2037 steps :
21- - uses : actions/checkout@v4
38+ - uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
2239
2340 - name : Set up Python ${{ matrix.python-version }}
24- uses : actions/setup-python@v5
41+ uses : actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
2542 with :
2643 python-version : ${{ matrix.python-version }}
44+ cache : pip
45+ cache-dependency-path : pyproject.toml
2746
2847 - name : Install dependencies
2948 run : pip install -e ".[dev]"
3049
31- - name : Lint (ruff check)
32- run : ruff check src/ tests/ examples/
50+ # Each step is a Makefile target — see the contract note at the top.
51+ - name : Format check
52+ run : make fmt-check
3353
34- - name : Format check (ruff format)
35- run : ruff format --check src/ tests/ examples/
54+ - name : Lint
55+ run : make lint
3656
37- - name : Type check (mypy)
38- run : mypy src/
57+ - name : Type check
58+ run : make type
3959
40- - name : Test (pytest)
41- run : python -m pytest -q --cov=weaver_kernel --cov-report=term-missing
60+ - name : Test
61+ run : make test
4262
4363 - name : Examples
44- run : |
45- python examples/basic_cli.py
46- python examples/billing_demo.py
47- python examples/http_driver_demo.py
48- python examples/tutorial.py
49- python examples/readme_quickstart.py
50- python examples/trace_export_demo.py
51- python examples/ocsf_export_demo.py
52- python examples/trace_replay_demo.py
53-
54- conformance_stub :
55- name : " Weaver Spec Conformance Stub (v0.1.0)"
64+ run : make example
65+
66+ - name : Coverage HTML report
67+ if : always()
68+ run : python -m coverage html
69+
70+ - name : Upload coverage HTML
71+ if : always()
72+ uses : actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
73+ with :
74+ name : coverage-html-${{ matrix.python-version }}
75+ path : htmlcov/
76+ if-no-files-found : ignore
77+
78+ bare-install :
79+ name : " Bare install (no extras)"
5680 runs-on : ubuntu-latest
5781 needs : test
5882 permissions :
5983 contents : read
6084
6185 steps :
62- - uses : actions/checkout@v4
86+ - uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
6387
6488 - name : Set up Python
65- uses : actions/setup-python@v5
89+ uses : actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
6690 with :
6791 python-version : " 3.12"
92+ cache : pip
93+ cache-dependency-path : pyproject.toml
6894
69- - name : Install dependencies
70- run : pip install -e ".[dev]"
95+ # No extras: proves the "minimal deps (httpx + pydantic)" claim holds.
96+ - name : Install (no extras)
97+ run : pip install .
98+
99+ - name : Import the full public API
100+ run : |
101+ python -c "import weaver_kernel as w; \
102+ missing = [n for n in w.__all__ if not hasattr(w, n)]; \
103+ assert not missing, f'missing public symbols: {missing}'; \
104+ print(f'imported {len(w.__all__)} public symbols')"
105+
106+ - name : Run the README quickstart
107+ run : python examples/readme_quickstart.py
108+
109+ - name : Assert optional extras are genuinely absent
110+ run : |
111+ for mod in mcp yaml opentelemetry tiktoken weaver_contracts; do
112+ if python -c "import $mod" 2>/dev/null; then
113+ echo "::error::optional dependency '$mod' is importable in a bare install"
114+ exit 1
115+ fi
116+ done
117+ echo "no optional extras leaked into the base install"
71118
72- # Placeholder: activate once dgenio/weaver-spec#4 ships the conformance runner.
73- # weaver-spec and weaver-contracts are published on PyPI.
74- # weaver_contracts.conformance does not yet exist (dgenio/weaver-spec#4).
75- # Replace this step with:
76- # pip install weaver-contracts # PyPI dist name uses a hyphen
77- # python -m weaver_contracts.conformance --target weaver_kernel
78- - name : weaver-spec conformance suite (stub)
119+ - name : Assert the MCP-extra-missing error is helpful
79120 run : |
80- echo "weaver-contracts 0.2.0 is on PyPI; weaver_contracts.conformance runner not yet available (dgenio/weaver-spec#4)."
81- echo "Stub passes. Activate when dgenio/weaver-spec#4 ships."
121+ python - <<'PY'
122+ from weaver_kernel.drivers.mcp_support import import_optional
123+ try:
124+ import_optional("mcp.client.session")
125+ except ImportError as exc:
126+ assert "weaver-kernel[mcp]" in str(exc), f"unhelpful error: {exc}"
127+ print("MCP-extra-missing error is documented and actionable")
128+ else:
129+ raise AssertionError("expected ImportError without the mcp extra")
130+ PY
131+
132+ security-audit :
133+ name : " Dependency audit (pip-audit)"
134+ runs-on : ubuntu-latest
135+ permissions :
136+ contents : read
137+
138+ steps :
139+ - uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
140+
141+ - name : Set up Python
142+ uses : actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
143+ with :
144+ python-version : " 3.12"
145+ cache : pip
146+ cache-dependency-path : pyproject.toml
147+
148+ - name : Install pip-audit
149+ run : pip install pip-audit
150+
151+ # Resolve weaver-kernel's *runtime* dependency tree in an isolated venv
152+ # (no extras, no pip-audit) and audit exactly that, so pip-audit's own
153+ # dependencies can never cause a failure unrelated to what adopters ship.
154+ - name : Resolve runtime dependency tree
155+ run : |
156+ python -m venv /tmp/runtime
157+ /tmp/runtime/bin/pip install .
158+ /tmp/runtime/bin/pip freeze --exclude-editable \
159+ | grep -viE '^(weaver-kernel|pip|setuptools)([=@ ]|$)' > runtime-requirements.txt
160+ echo "Auditing:"; cat runtime-requirements.txt
161+
162+ # Policy: fail on any known vulnerability in the runtime tree. Document a
163+ # false positive by appending `--ignore-vuln <ID>` here with a comment
164+ # (see README "Security automation").
165+ - name : Audit runtime dependencies
166+ run : pip-audit --strict --desc --requirement runtime-requirements.txt
167+
168+ conformance :
169+ name : " Weaver-spec conformance"
170+ runs-on : ubuntu-latest
171+ needs : test
172+ permissions :
173+ contents : read
174+
175+ steps :
176+ - uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
177+
178+ - name : Set up Python
179+ uses : actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
180+ with :
181+ python-version : " 3.12"
182+ cache : pip
183+ cache-dependency-path : pyproject.toml
184+
185+ # Real validation (no echo): map kernel Frame/ActionTrace/token onto the
186+ # published weaver-contracts dataclasses and assert they validate. When
187+ # dgenio/weaver-spec#4 ships weaver_contracts.conformance, add its runner
188+ # here as an additional step.
189+ - name : Install conformance extra
190+ run : pip install ".[conformance]" pytest pytest-asyncio
191+
192+ - name : Report contract version
193+ run : python -c "from weaver_kernel.conformance import contract_version; print('weaver-contracts', contract_version())"
194+
195+ - name : Run conformance mapping tests
196+ run : python -m pytest tests/test_conformance.py -q
0 commit comments