Repository navigation
fix(attest): pin attestations list API version to 2026-03-10 - #143
Conversation
There was a problem hiding this comment.
Code Review
Current Commit: e96a1ba
Summary
Clean fix for a real operational problem — every publish was logging a deprecation warning twice (once for provenance, once for SBOM), and this silences both without touching the public API. The implementation is correct, well-documented, and the test rewrite is the most impressive part of the PR.
Checks
| Check | Status |
|---|---|
| Tests | ✅ success |
| Code Quality | ✅ success |
| Conventional Commits | ✅ success |
| PR Title Validation | ✅ success |
| Markdown | ✅ success |
| Build |
The Build check (rslib, api-extractor, TSDoc) didn't appear in the CI results for this commit. The PR description says ci:build passed locally — if the Build job simply wasn't triggered on this branch that's fine, but worth a look if it's expected to run.
What's well done
Separation of concerns in AttestLive.ts — the refactor cleanly splits what was a monolithic parseListedAttestations into three focused helpers: parseRawAttestations (data extraction), attestationUrlFor (URL construction), and predicateTypeFromBundleUrl (lazy fetch + decode). Each one does exactly one thing.
ATTESTATIONS_API_VERSION constant — the TSDoc block is exactly the right amount of explanation: it names the deprecated shape, quotes the Sunset date, explains what the new shape drops, and links to the GitHub breaking-changes page. Future readers won't need to go spelunking.
FETCH_BUNDLE_REQUEST = "GET {bundle_url}" comment — flagging this explicitly is the right call; the Octokit template-expansion trick for absolute URLs is non-obvious and the inline doc prevents future confusion.
Test rewrite — attestationServer is a genuinely faithful simulator of the 2026-03-10 API: it honors predicate_type server-side, omits the inline bundle, serves bundles at bundle_url, and records every request for protocol-level assertions. The new tests cover all four behavioral commitments — API version pinning, filter forwarding, zero bundle fetches on the filtered path, and bundle fetches on the no-filter path. That's exactly the coverage this kind of API-version migration needs.
Dual payload shape in predicateTypeFromBundleUrl — checking both data.dsseEnvelope.payload and data.bundle.dsseEnvelope.payload is good defensive coding for a response shape that could vary by caller or future API iteration.
Observations
Sequential bundle fetches (no-filter path) — the for loop in the no-filter path fetches bundle_urls one at a time:
for (const entry of raw) {
const predicateType = await predicateTypeFromBundleUrl(octokit, entry.bundle_url);For the orchestrator's filtered path (the hot path — twice per publish) this code never runs. For the no-filter case — likely manual/diagnostic use — the sequential fetch adds latency linear in the number of attestations. A Promise.all would parallelize them if this path ever ends up under performance scrutiny, but it's not a problem for the current use case.
Server filter trusted without client-side sanity check — the filtered path assigns predicateType: predicateTypeFilter to every returned entry without verifying the server actually honored the filter. The old code had a client-side re-filter as a backstop (though the PR notes that backstop was motivated by a now-resolved short-alias mismatch concern). Trusting the server here is a reasonable design choice — the freeform URI is now passed directly so the aliasing problem is gone — just worth having on record in case GitHub ever returns mismatched entries.
Neither observation is blocking. The PR is correct and the tradeoffs are well-understood.
listForSubject called the repository attestations endpoint under Octokit's default API version, whose list-response shape GitHub deprecated (Sunset 2028-03-10), logging a deprecation warning on every publish. The request now pins X-GitHub-Api-Version 2026-03-10, which drops the inline Sigstore bundle. predicateType is recovered via the server-side predicate_type query when a filter is supplied, otherwise by fetching each entry's bundle_url; the AttestationListEntry type is unchanged. Closes #131 Signed-off-by: C. Spencer Beggs <spencer@savvyweb.systems>
e96a1ba to
576f668
Compare
Code ReviewCurrent Commit: SummaryClean, well-scoped fix. The implementation is correct, the tests are comprehensive, and all checks pass. No blocking issues. Checks
What the PR does
CorrectnessTwo-path split is sound. The filtered path trusts server narrowing and assigns Removal of client-side re-filter is correct. The old code re-filtered because the legacy API version's Dual-shape handling in
TestsThe
This level of test coverage is exactly what a behavioral change at an API boundary warrants. Minor observations (non-blocking)
✅ Approving. |
Summary
Attest.listForSubject(the attestation idempotency probe in the Phase-3 publish chain) calledGET /repos/{owner}/{repo}/attestations/{subject_digest}under Octokit's default REST API version. GitHub deprecated that version's list-response shape (Sunset 2028-03-10), so Octokit logged a deprecation warning on every call — twice per build digest (provenance + SBOM).What changed
X-GitHub-Api-Version: 2026-03-10on the list request (and on the newbundle_urlfetch) — the supported version.bundlefrom list responses, sopredicateTypeis now recovered two ways:predicate_typequery parameter, no per-entry bundle fetch.bundle_urland decodes the in-totopredicateType; undecodable/failed fetches drop just that entry.AttestationListEntrytype ({ attestationUrl, predicateType }) and thelistForSubjectsignature are unchanged. No breaking API change.Tests
Attest.list.test.tsto simulate the2026-03-10API faithfully (honorspredicate_type, servesbundle_url).2026-03-10and forwardspredicate_type, and that the filtered path fires no bundle fetch.Verification
typecheck,lint, fulltest(1251 passing), andci:build(TSDoc valid) all green. Changeset added (patch). Design docs + user docs updated to match.Closes #131
Signed-off-by: C. Spencer Beggs spencer@savvyweb.systems