Release mechanics for v0.2.0, and drop a dependency that never ran - #34
Merged
Conversation
Cutting the release becomes a tag push. release.yml builds the npm tarball, five static agentwall-verify binaries, a CycloneDX SBOM, and checksums from the tagged tree, attests them with SLSA provenance, and publishes to npm over Trusted Publishing so no long-lived token exists to leak. workflow_dispatch runs the same build and provenance and stops before publishing, so the pipeline is proven while no tag exists. Three failures the workflow now catches instead of shipping: - The tag and package.json can disagree. They are compared before anything is built, because every downstream artifact would otherwise carry a different number than the release page. - verifierVersion was a const, and the linker silently ignores -X on anything that is not a string var. It is now a var, a test holds it in step with package.json, and the workflow asks the built binary what it reports. - cyclonedx-npm omits a package that is not hoisted to the top level while keeping that package's own dependencies, which produced an SBOM with pino missing and pino's dependency tail present. The SBOM is now checked against npm's view of the production tree and a missing name fails the release. Packaging: name becomes @reesebuilt/agentwall (the unscoped name belongs to an unrelated project), version 0.2.0, engines floor 22.12.0, and the repository, homepage and bugs fields several tools read. The files list stops shipping 2.4 MB of PNGs that nothing in the repository references. Removed the direct pino dependency. Nothing imports it; the only occurrence in src/ was a word in a comment. Fastify owns the logger and depends on pino itself, and our declaration only pinned a second unused copy of pino 8 beside it. Runtime dependencies are three, and a clean install drops 11 transitive packages. @types/node tracks the supported floor at ^22 rather than the newest release, since types describing APIs the runtime lacks compile and then crash. Test Suites: 34 passed, 34 total Tests: 300 passed, 300 total
The generator's upload-assets job owns that permission and is guarded by `if: inputs.upload-assets`, which is false here, so the caller granting it was privilege the run never uses. The release job attaches the provenance.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step 4.2a: release workflow and packaging metadata
Branch
agent/4.2a-release, rebased onto f5e62b9. Cutting v0.2.0 becomes a tag pushand nothing else.
Conformance:
26 cases, typescript and go: 22 agreed, 4 declared divergence(s), 0 failure(s).node scripts/check-workflow-pins.jsafter the rebase, so release.yml is judged by thechecker that now gates merges rather than assumed to satisfy it:
The one exempt reference is the SLSA generator, matched by the allowlist entry 4.1 seeded.
What the workflow produces on a tag
buildresolves the version, refuses to continue when the tag and package.jsondisagree, runs lint/build/test/conformance, then emits the npm tarball, a CycloneDX
SBOM, five static
agentwall-verifybinaries, andchecksums.txt.provenancegenerates SLSA3 attestation over those subjects.
releaseattaches everything withnotes taken verbatim from the CHANGELOG plus a Verify block.
npm-publishpublishesthe exact packed tarball over Trusted Publishing.
dockerbuilds, pushes andcosign-signs the image digest.
workflow_dispatchruns build and provenance and stops before publishing, so thepipeline is provable while no tag exists.
Proven locally, not asserted
Every workflow step below was extracted from the YAML with a parser and executed
verbatim, not paraphrased.
Cross-compiled with
CGO_ENABLED=0 ... -trimpath -ldflags "-s -w -X main.verifierVersion=0.2.0"on go1.22.12:
sha256sum -c checksums.txtreports OK on all seven. Sizes: linux/amd64 2652.1 KB,linux/arm64 2560.1 KB, darwin/amd64 2733.3 KB, darwin/arm64 2613.9 KB,
windows/amd64 2788.5 KB, tarball 552.5 KB, SBOM 100.6 KB.
Rebuilding linux/amd64 and linux/arm64 from scratch produced byte-identical files
(
cmpclean), so the reproducibility claim holds.Three failures the workflow now catches
verifierVersionwas aconst, andthe Go linker ignores
-Xon anything that is not a string var, so the ldflags inthe plan would have been a no-op and a v0.3.0 build would have reported 0.2.0. It is
now a
var,TestVersionMatchesPackageJSONholds it in step with package.json(falsified: desyncing package.json to 0.3.0 fails the test with a readable message),
and the workflow asks the built binary what it reports.
the top level while keeping that package's own dependencies. The generated SBOM
listed
@pinojs/redact,atomic-sleep,split2and the rest of pino's tail with nopinoentry. The workflow now cross-checks the SBOM againstnpm ls --omit=dev --alland fails when a name is absent entirely (falsified: deleting pino from the SBOM
exits 1). A package installed at two versions is reported, not fatal, because the
name is still present.
A fourth was caught by running the step rather than reading it: staging the file list
in
dist-release/.manifestput that file into its own checksum list, because a shellredirection creates its target before the command on the left runs.
sha256sum -cwould then have failed on a correct download. Fixed with
mapfile.Packaging metadata
@reesebuilt/agentwall0.2.0, bin stillagentwall,engines.node >=22.12.0,publishConfigaccess public and provenance true,prepackbuilds because dist isuntracked, plus the
repository,homepageandbugsfields that were absent.npm pack --dry-run: 227 files, 567.5 kB packed, 1.7 MB unpacked. Contents aredist/,public/,examples/, README, LICENSE, NOTICE, CHANGELOG, SECURITY,package.json. No
src/,tests/,scripts/,.github/,docs/,verifier/,lockfile, dotfile, key or config with a secret in it.
filesgains!public/assets/*.png. Those three PNGs are 2.4 MB and nothing in therepository references them: the dashboard loads only the brand icons and three
tutorial webps, and the README hero points at
docs/assets/, a different copy. Theyare still in git; only the tarball stops carrying them.
Two dependency decisions
@types/node pinned to ^22.20.1, not 26. Agreed with the reasoning as given. Types
must track the minimum supported runtime:
@types/node26 describes APIs absent onNode 22, so the compiler accepts code that crashes on the floor we advertise. Typecheck
passes on ^22.
pino removed. Verified before acting: zero
from "pino"orrequire("pino")inany tracked file, and the only occurrence in
src/is the word inside a comment infile-sink.ts. BothLoggerLiketypes are locally defined structural types(
Pick<Console, "error" | "warn">and a local interface), not pino types. The onlyFastify logger option we pass is
{ level }, which is stable across pino 7 through 10.The decisive evidence: the lockfile resolved our declared
pino@8.21.0to top-levelnode_modules/pinowhile Fastify used its own nestedpino@9.14.0. The dependency wedeclared was not even the logger that ran. Bumping it to 10.3.1 would have changed
nothing at runtime.
Removing it and regenerating the lockfile drops 11 transitive packages (the browserify
shim tail pino 8 carried:
buffer,readable-stream,safe-buffer,string_decoder,events,process, and friends) and lets Fastify's pino resolve to 10.3.1, which isalso what makes the SBOM correct. README and CONTRIBUTING both claimed "deliberately
four" runtime dependencies; both now say three and name Fastify as pino's owner.
Not mine, but blocking a tag
The
dockerjob references./Dockerfile, which Step 4.2b creates. 4.2b must mergebefore any tag is pushed or that job fails. Job spec agreed with Step42bDocker over IRC:
linux/amd64 only,
provenance: true, signsteps.push.outputs.digestrather than thetag.
Per Step 4.1:
slsa-framework/slsa-github-generatoris referenced by tag, not SHA, andneeds the allowlist entry in
scripts/check-workflow-pins.js. Step41Scorecard isseeding it. Every other action is pinned by 40-hex SHA with a version comment; every job
declares a permissions block; the workflow-level default is
contents: read.Operator prerequisites
See the agent report. In short: npm Trusted Publishing and the
reesebuiltscope, areleaseGitHub environment, and one greenworkflow_dispatchdry run.