Skip to content

ci: assert every @chestnutlabs tarball lists LICENSE and NOTICE.md before publish - #438

Merged
sobechestnut-dev merged 1 commit into
devfrom
a08/pack-license-guard
Oct 26, 2026
Merged

sobechestnut-dev merged 1 commit into
devfrom
a08/pack-license-guard

Conversation

@sobechestnut-dev

Copy link
Copy Markdown
Contributor

What

CI gate for ChestnutLabs/AnyBridge#2765 (owner ruling 6a): before any publish, every @chestnutlabs/* tarball must list LICENSE and NOTICE.md.

The 0.20.1 tarballs shipped with files: ["dist"] and carried no MIT notice for the inherited remcoder/gcode-preview code. This PR makes the fix and the guard that keeps it. Nothing is published: no npm publish, no tag, no version bump. The 0.20.2 republish waits for go-public.

  • Each of the 14 packages now holds LICENSE and NOTICE.md (byte copies of the repo root's) and names both in files.
  • New tools/pack-license-check.mjs (npm run pack:license-check), per publishable workspace:
    1. npm pack --dry-run --json -w <pkg> must list LICENSE and NOTICE.md;
    2. the manifest's files must name both (npm auto-adds a package-local LICENSE, so the packed list alone would not notice LICENSE leaving files);
    3. the package's copies must equal the root files byte for byte (no stale or empty placeholder).
      It needs no build and runs with --ignore-scripts. It reads both npm JSON shapes (array on npm 10/11, object keyed by name on npm 12).
  • Wired in as the first step after npm ci in run-tests.yml (build job) and in release-publish.yml, before the build and before release:publish.
  • pack-check.mjs allows NOTICE.md at the tarball root; pack-check-snapshots.json gains LICENSE + NOTICE.md for all 14 (+28 lines, nothing reordered).
  • release-process.md step 5 names the new gate.

RED-check (the guard fails when it should)

Unit and CLI tests (tools/pack-license-check.test.mjs, 17 tests) drive the real npm pack --dry-run against throwaway two-package repos. Mutants run on the WIP-committed tree, no-op control green first:

  • Script mutants (18 total over two rounds): drop NOTICE.md / LICENSE from the required set, unreported package passes, byte compare off, substring match, private package not skipped, missing copy ok, empty root ok, CLI always exits 0, no-packages passes, array-only npm parse, manifest check skipped / LICENSE-only / NOTICE-only / missing files ok / files not read: all caught. One mutant (backslash normalisation of listed paths) survived because the code was dead for these names; the code was deleted, not tested.
  • Real-tree runs on packages/gcode-parser (the actual repo, CLI exit code):
Break Result
none (control) OK, rc 0
files drops NOTICE.md FAIL (tarball + manifest)
files drops LICENSE FAIL (manifest; the tarball still lists it because npm auto-adds a package-local LICENSE)
files back to ["dist"] (the 0.20.1 shape) FAIL (3 findings)
package LICENSE deleted FAIL (tarball + missing copy)
package NOTICE.md drifted FAIL (differs from root)

Gate

Local, fresh npm ci --ignore-scripts plus a build of toolpath-core (the prepare step CI runs): lint (0 errors, 79 pre-existing warnings), typeCheck, test (16 files, 309 tests), test:packages (61), license:check, docs:links, prettier --check ., test:consumer-vue, and pack:check (14 snapshots, publint, attw: OK) under npm 10.9.2.

pack:check fails on this PC's npm 12 (parsed[0] on an object-shaped --json result). That is pre-existing, unrelated to this PR, and CI uses npm 10/11. Flagged below as a follow-up.

Notes for the reviewer

  • NOTICE.md ships verbatim. Its relative links (docs/...) will not resolve inside a tarball; LICENSE does. Left as is: it is the file the upstream notice requires.
  • The copies are committed, not generated at pack time, so the diff shows exactly what ships and the drift check keeps them equal to the root. A prepack copy step is the alternative; it hides the files from review.
  • No package source changed, so no changeset is needed (the changeset check only looks at packages/*/src).

Follow-ups (each with a recommendation)

  • [P3] pack-check.mjs reads parsed[0].files, which breaks on npm 12's object-shaped --json. Recommendation: follow-up (CI is on npm 10/11 today; fix before the runners move).
  • [P3] One-step-further: have tools/release/publish.mjs (and release:dry-run) call the same check, so a local rehearsal fails too. Recommendation: adopt later; the workflow already runs it before publish.
  • [P3] One-step-further: also assert the root LICENSE still has the upstream "Remco Veldkamp" copyright line. Recommendation: skip; the owner has not decided whether LICENSE also carries the Chestnut line.

🤖 Generated with Claude Code

…fore publish

The 0.20.1 tarballs shipped with files: ["dist"], so none carried the MIT
notice for the inherited code. Each package now holds LICENSE and NOTICE.md
(copies of the root files) and names both in "files". A new
tools/pack-license-check.mjs runs npm pack --dry-run --json -w <pkg> for
every publishable workspace and fails when LICENSE or NOTICE.md is not
listed, when "files" does not name them, or when a package copy differs
from the root file. It runs first in the test gate and in the publish
workflow, before anything uploads. Pack snapshots updated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@sobechestnut-dev
sobechestnut-dev marked this pull request as ready for review October 26, 2026 03:27
@sobechestnut-dev
sobechestnut-dev merged commit bb82b64 into dev Oct 26, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants