@@ -61,13 +61,18 @@ jobs:
6161 ruff check .
6262 python -c "import fastapi, httpx, mcp, multipart, pydantic, uvicorn"
6363 python -m pytest -o addopts="" tests/ -q -rs
64+ python -m pytest -o addopts="" tests/test_public_research_boundary.py -q
65+ python -m pytest -o addopts="" tests/test_compact_recall.py tests/test_eval_performance.py -q
66+ python -m pytest -o addopts="" tests/test_eval_harness.py tests/test_benchmark_evidence.py -q
6467 python -m eval.harness --dataset eval/datasets/sample.jsonl --k 5
6568 python -m eval.harness --dataset eval/datasets/codemem.jsonl --k 5
6669 python -m eval.ablation
6770 python -m pip_audit --local
6871
6972 - name : Build source and universal wheel distributions
70- run : python -m build
73+ run : |
74+ python -m build
75+ python scripts/verify_distribution_contents.py dist/*
7176
7277 - name : Validate distributions
7378 run : python -m twine check dist/*
@@ -134,7 +139,7 @@ jobs:
134139 npm ci
135140 npx playwright install --with-deps chromium
136141 - name : Playwright desktop/mobile, keyboard, CSP, console, and axe checks
137- run : npx playwright test
142+ run : npm run test:e2e
138143
139144 docker-smoke :
140145 name : Production image release gate
@@ -176,9 +181,58 @@ jobs:
176181 if : always()
177182 run : docker rm -f engraphis-release || true
178183
184+ release-evidence :
185+ name : Generate public release evidence
186+ needs : [build, python-matrix, browser-accessibility, docker-smoke]
187+ if : github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
188+ runs-on : ubuntu-latest
189+ permissions :
190+ contents : read
191+
192+ steps :
193+ - uses : actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
194+ - uses : actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
195+ with :
196+ python-version : " 3.11"
197+ - name : Install SBOM generator and project dependencies
198+ run : >-
199+ python -m pip install --upgrade "pip>=26.1.2" "setuptools>=83"
200+ cyclonedx-bom==7.3.0 ".[all,test]"
201+ - name : Download distributions
202+ uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
203+ with :
204+ name : python-package-distributions
205+ path : dist/
206+ - name : Generate evidence and reproducible SBOM after all release gates
207+ shell : bash
208+ run : |
209+ mkdir release-evidence
210+ sbom="release-evidence/engraphis-${GITHUB_REF_NAME#v}.cdx.json"
211+ cyclonedx-py environment --output-reproducible --of JSON --pyproject pyproject.toml -o "$sbom"
212+ python scripts/release_evidence.py --dist dist --commit "$GITHUB_SHA" \
213+ --tag "$GITHUB_REF_NAME" \
214+ --sbom "$sbom" \
215+ --verified-check ruff \
216+ --verified-check pytest \
217+ --verified-check privacy-boundary \
218+ --verified-check token-efficiency \
219+ --verified-check benchmark-schema-evidence \
220+ --verified-check browser-e2e \
221+ --verified-check dependency-audit \
222+ --verified-check container-smoke \
223+ --verified-check retrieval-sample \
224+ --verified-check retrieval-codemem \
225+ --verified-check retrieval-ablation \
226+ --output release-evidence/release-evidence.json
227+ - name : Store public release evidence
228+ uses : actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
229+ with :
230+ name : public-release-evidence
231+ path : release-evidence/
232+
179233 publish :
180234 name : Publish to PyPI
181- needs : [build, python-matrix, browser-accessibility, docker-smoke]
235+ needs : release-evidence
182236 # Manual dispatch is intentionally build/check-only. Publication requires a pushed
183237 # semver tag, whose value was matched to pyproject.toml in the build job above.
184238 if : github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
@@ -236,6 +290,12 @@ jobs:
236290 name : python-package-distributions
237291 path : dist/
238292
293+ - name : Download public release evidence
294+ uses : actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
295+ with :
296+ name : public-release-evidence
297+ path : release-evidence/
298+
239299 - name : Create GitHub Release
240300 env :
241301 GH_TOKEN : ${{ github.token }}
@@ -246,11 +306,11 @@ jobs:
246306 # A previous partial attempt may have created the release before every
247307 # canonical package asset uploaded. Reconcile same-named assets from the
248308 # exact aggregate that passed the publish gate.
249- gh release upload "$GITHUB_REF_NAME" dist/* \
309+ gh release upload "$GITHUB_REF_NAME" dist/* release-evidence/release-evidence.json release-evidence/*.cdx.json \
250310 --repo "$GH_REPO" \
251311 --clobber
252312 else
253- gh release create "$GITHUB_REF_NAME" dist/* \
313+ gh release create "$GITHUB_REF_NAME" dist/* release-evidence/release-evidence.json release-evidence/*.cdx.json \
254314 --repo "$GH_REPO" \
255315 --verify-tag \
256316 --generate-notes \
@@ -319,11 +379,44 @@ jobs:
319379 (.conclusion == "success" or
320380 .conclusion == "failure"))] | length' \
321381 <<<"$jobs")" -eq 1
382+ test "$(jq '[.jobs[] | select(.name == "Generate public release evidence" and
383+ .conclusion == "success")] | length' \
384+ <<<"$jobs")" -eq 1
322385 gh run download "$run_id" \
323386 --repo "$GH_REPO" \
324387 --name python-package-distributions \
325388 --dir dist
326389
390+ gh run download "$run_id" \
391+ --repo "$GH_REPO" \
392+ --name public-release-evidence \
393+ --dir release-evidence
394+ python - "$RELEASE_TAG" "$tag_sha" <<'PY'
395+ import hashlib
396+ import json
397+ import sys
398+ from pathlib import Path
399+
400+ tag, commit = sys.argv[1:]
401+ with open("release-evidence/release-evidence.json", encoding="utf-8") as handle:
402+ evidence = json.load(handle)
403+ assert evidence.get("format") == "engraphis-release-evidence/2"
404+ assert evidence.get("package", {}).get("version") == tag.removeprefix("v")
405+ assert evidence.get("tag") == tag
406+ assert evidence.get("commit") == commit
407+ assert evidence.get("provenance", {}).get("source") == {"tag": tag, "commit": commit}
408+ expected = {
409+ item["filename"]: item["sha256"]
410+ for item in evidence.get("artifacts", [])
411+ }
412+ actual = {
413+ path.name: hashlib.sha256(path.read_bytes()).hexdigest()
414+ for path in Path("dist").iterdir()
415+ if path.is_file() and (path.name.endswith(".whl") or path.name.endswith(".tar.gz"))
416+ }
417+ assert expected == actual
418+ PY
419+
327420 - name : Verify any previously published subset
328421 env :
329422 RELEASE_TAG : ${{ inputs.release_tag }}
@@ -361,11 +454,11 @@ jobs:
361454 shell : bash
362455 run : |
363456 if gh release view "$RELEASE_TAG" --repo "$GH_REPO" >/dev/null 2>&1; then
364- gh release upload "$RELEASE_TAG" dist/* \
457+ gh release upload "$RELEASE_TAG" dist/* release-evidence/release-evidence.json release-evidence/*.cdx.json \
365458 --repo "$GH_REPO" \
366459 --clobber
367460 else
368- gh release create "$RELEASE_TAG" dist/* \
461+ gh release create "$RELEASE_TAG" dist/* release-evidence/release-evidence.json release-evidence/*.cdx.json \
369462 --repo "$GH_REPO" \
370463 --verify-tag \
371464 --generate-notes \
0 commit comments