Repository navigation
fix: make action builds reproducible and drop license sidecars - #96
Conversation
When build.ignore was set, the ignore stub went to a random mkdtemp dir whose path rspack embedded in the bundle, so the same source built twice produced byte-different output. The stub now uses a deterministic node_modules/.cache path, so repeated builds are byte-identical. Bundled third-party license banners are kept inline via output.legalComments instead of being extracted to *.LICENSE.txt sidecar files, removing the extra committed file while preserving attribution. Closes #94 Signed-off-by: C. Spencer Beggs <spencer@savvyweb.systems>
There was a problem hiding this comment.
Code Review
Current Commit: 3e8e612
Summary
Clean, well-scoped fix for both issues in #94. The root cause diagnosis is correct: rspack embeds the stub path verbatim as the module id, so mkdtemp made the bundle non-deterministic. Moving to a fixed node_modules/.cache path is the right call — project-local (preserving the #81 symlink-safety property), deterministic, and the writeFile helper already creates parent directories with mkdirSync({ recursive: true }), so no extra scaffolding is needed. The legalComments: "inline" addition is minimal and correct.
The integration tests are well-structured: isolated mkdtempSync copies prevent fixture contamination, and the two-build hash comparison is a reliable regression gate.
Two items to clean up before merging:
Issues
.gitignore — duplicate entry and wrong comment
See inline comment on .gitignore lines 34–38. .claude/settings.local.json is listed twice, and the section comment ("Temporary api-extractor files created during build") is a copy-paste artifact that describes none of the entries under it.
idempotent-build.int.test.ts — debug writes left in
See inline comment on lines 76–78. The writeFileSync calls to /tmp/issue94-*.js were useful while debugging but should be removed before merge. The writeFileSync import at line 9 becomes unused once they're gone.
Checks
All five validation checks passed: PR Title Validation, Conventional Commits, Code Quality, Markdown, Tests ✓
| # Temporary api-extractor files created during build | ||
| .claude/settings.local.json | ||
| .claude/scheduled_tasks.lock | ||
| .claude/plans | ||
| .claude/cache |
There was a problem hiding this comment.
Two problems with this block:
-
Duplicate entry —
.claude/settings.local.jsonalready appears on line 33 (under# Claude AI files). The new entry on line 35 is an exact duplicate and can be dropped. -
Misleading comment —
# Temporary api-extractor files created during builddescribes none of the entries below it. These are Claude AI runtime files. The comment should either be removed or corrected (e.g.# Claude AI runtime files).
| // Dump both bundles for out-of-band diffing while diagnosing the bug. | ||
| writeFileSync("/tmp/issue94-a.js", first); | ||
| writeFileSync("/tmp/issue94-b.js", second); |
There was a problem hiding this comment.
These three lines are debug diagnostic artifacts — the comment even says "while diagnosing the bug." They should be removed before merging. The writes to /tmp are harmless but the writeFileSync import at line 9 becomes unused once they're gone, so that import should be cleaned up too.
Drop the duplicate .claude/settings.local.json entry and the misleading api-extractor comment from .gitignore, and remove the leftover /tmp debug dumps and now-unused writeFileSync import from the idempotency test. Signed-off-by: C. Spencer Beggs <spencer@savvyweb.systems>
Fixes both issues reported in #94.
Non-idempotent builds — when
build.ignorewas set, the ignore stub was written to a randommkdtempdirectory whose absolute path rspack embedded in the bundle as a module id, so building the same source twice produced byte-different output and noisy diffs in committed action code. The stub now lives at a deterministicnode_modules/.cache/github-action-builder/ignore-stub.mjspath (project-local, preserving the symlink-safety property from the #81 design), so repeated builds are byte-identical.License sidecars — bundled third-party license banners are kept inline via
output.legalComments: "inline"instead of being extracted to*.LICENSE.txtsidecar files, removing the extra committed file while preserving attribution.Adds integration regression tests: one builds a fixture twice and asserts byte-identical
main.js; one asserts no*.LICENSE.txtand that the notice stays inline. Full unit + integration suite green, typecheck green.Closes #94
Signed-off-by: C. Spencer Beggs spencer@savvyweb.systems