You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[quality] release.yml and pre-release.yml are hand-mirrored copies of one SBOM/sign/attest pipeline — add a contract test keeping them and docs/releasing.md in agreement #267
.github/workflows/release.yml (tag push) and .github/workflows/pre-release.yml (workflow_dispatch) are two hand-maintained copies of one pipeline. On main @ c48bd6d both carry, line for line:
the same env.WAYBILL_VERSION / env.WAYBILL_SHA256 pin (v0.2.0, sha256-verified download);
the same verify job (npm ci, npm run build && lint && test, the npm run pack + git status --porcelain dist/ drift gate);
the same Install waybill, Generate SBOM (SPDX 2.3), Sign SBOM, Attest build provenance and Rename provenance bundle steps;
the same softprops/action-gh-release inputs attaching prow-github-actions-<version>.spdx.json, .spdx.json.bundle and .intoto.jsonl;
the same Prepend curated release notes step.
Nothing shares code between the two files and nothing tests that they agree. __tests__/workflows.test.ts only asserts that every uses: is sha-pinned with a version comment; held #234 only asserts the setup-node steps read node-version-file: package.json. docs/releasing.md promises that both workflows "run the same verification and SBOM pipeline", lists the three artifact names, states the X.Y.Z-(rc|alpha|beta).N input form, and says pre-releases never move the floating major tag — none of that is checked either.
The failure mode is silent: a waybill bump, a --exclude-scope change, a renamed artifact or a new attestation subject landing in one file and not the other does not break build-test; it shows up as a release and a release candidate with different SBOM contents or different provenance subjects, which is exactly what the signed artifacts are supposed to rule out.
Evidence: npx vitest run --coverage on main @ c48bd6d → 99.83 % stmts / 98.35 % branch / 100 % funcs / 99.82 % lines; every residual src/ line is already claimed by a held PR. This is a contract-test gap in the release workflows, not a src/ coverage gap, so it is filed as regression-risk.
Recommendation
Add __tests__/releaseWorkflows.test.ts that parses both workflows with js-yaml (as workflows.test.ts does) and asserts:
both: WAYBILL_VERSION is vX.Y.Z, WAYBILL_SHA256 is 64 hex, and the install step runs sha256sum -c before tar -xzf
both: SBOM scan uses --exclude-path dist --exclude-scope dev,build,test --format spdx-2.3-json; cosign signs into the .bundle; attestation subjects are dist/index.js + the SBOM; the release attaches exactly the three artifacts with generate_release_notes: true
cross-file: identical waybill pin, identical action shas in the same order, byte-identical Install waybill / Generate SBOM / Sign SBOM / Rename provenance bundle run scripts, identical attest with:, identical verify job apart from the pre-release input check and the "before tagging/releasing" wording
release.yml: triggers on v[0-9]+.[0-9]+.[0-9]+* only; derives VERSION/MAJOR from the tag; prerelease: contains(ref, '-'); floating-tag step gated on !contains(ref, '-'); never creates a tag
pre-release.yml: single required version string input; validation is the first step; its grep -E pattern accepts 2.1.0-rc.1 / 3.0.0-alpha.0 / 10.20.30-beta.12 and rejects 2.1.0, v2.1.0-rc.1, 2.1.0-rc, 2.1.0-dev.1; creates v${VERSION} before publishing; prerelease: true; never touches MAJOR
docs/releasing.md links both workflows, names the three artifact suffixes, states the X.Y.Z-(rc|alpha|beta).N form and shows the cosign verify-blob / gh attestation verify commands
No change to the workflow files themselves is needed; the test only reads them.
Priority
Impact: medium — a drift between the two files changes what a signed release ships without any check turning red
Effort: low — one new test file, no production or workflow change
Finding
.github/workflows/release.yml(tag push) and.github/workflows/pre-release.yml(workflow_dispatch) are two hand-maintained copies of one pipeline. Onmain@ c48bd6d both carry, line for line:env.WAYBILL_VERSION/env.WAYBILL_SHA256pin (v0.2.0, sha256-verified download);verifyjob (npm ci,npm run build && lint && test, thenpm run pack+git status --porcelain dist/drift gate);Install waybill,Generate SBOM (SPDX 2.3),Sign SBOM,Attest build provenanceandRename provenance bundlesteps;softprops/action-gh-releaseinputs attachingprow-github-actions-<version>.spdx.json,.spdx.json.bundleand.intoto.jsonl;Prepend curated release notesstep.Nothing shares code between the two files and nothing tests that they agree.
__tests__/workflows.test.tsonly asserts that everyuses:is sha-pinned with a version comment; held #234 only asserts thesetup-nodesteps readnode-version-file: package.json.docs/releasing.mdpromises that both workflows "run the same verification and SBOM pipeline", lists the three artifact names, states theX.Y.Z-(rc|alpha|beta).Ninput form, and says pre-releases never move the floating major tag — none of that is checked either.The failure mode is silent: a waybill bump, a
--exclude-scopechange, a renamed artifact or a new attestation subject landing in one file and not the other does not breakbuild-test; it shows up as a release and a release candidate with different SBOM contents or different provenance subjects, which is exactly what the signed artifacts are supposed to rule out.Evidence:
npx vitest run --coverageonmain@ c48bd6d → 99.83 % stmts / 98.35 % branch / 100 % funcs / 99.82 % lines; every residualsrc/line is already claimed by a held PR. This is a contract-test gap in the release workflows, not asrc/coverage gap, so it is filed asregression-risk.Recommendation
Add
__tests__/releaseWorkflows.test.tsthat parses both workflows withjs-yaml(asworkflows.test.tsdoes) and asserts:verify→github-releasewithneeds: verify; top-levelpermissions: contents: read; release jobcontents: write, id-token: write, attestations: writeexactlyWAYBILL_VERSIONisvX.Y.Z,WAYBILL_SHA256is 64 hex, and the install step runssha256sum -cbeforetar -xzf--exclude-path dist --exclude-scope dev,build,test --format spdx-2.3-json; cosign signs into the.bundle; attestation subjects aredist/index.js+ the SBOM; the release attaches exactly the three artifacts withgenerate_release_notes: trueInstall waybill/Generate SBOM/Sign SBOM/Rename provenance bundlerun scripts, identical attestwith:, identicalverifyjob apart from the pre-release input check and the "before tagging/releasing" wordingrelease.yml: triggers onv[0-9]+.[0-9]+.[0-9]+*only; derivesVERSION/MAJORfrom the tag;prerelease: contains(ref, '-'); floating-tag step gated on!contains(ref, '-'); never creates a tagpre-release.yml: single requiredversionstring input; validation is the first step; itsgrep -Epattern accepts2.1.0-rc.1/3.0.0-alpha.0/10.20.30-beta.12and rejects2.1.0,v2.1.0-rc.1,2.1.0-rc,2.1.0-dev.1; createsv${VERSION}before publishing;prerelease: true; never touchesMAJORdocs/releasing.mdlinks both workflows, names the three artifact suffixes, states theX.Y.Z-(rc|alpha|beta).Nform and shows thecosign verify-blob/gh attestation verifycommandsNo change to the workflow files themselves is needed; the test only reads them.
Priority
Filed by quality agent (hold-gated mode)
🐝 Hive Agent:
quality| Instance:hosted-available-lke648397-260827-5q9t| SHA:c48bd6d— hive: agent=quality backend=copilot model=claude-fable-5.1 copilot=1.0.88