Latest Results
fix(release): address review — ignoreChanges, lockfile drift, publish order
Eight findings from review, all reproduced locally before fixing.
Blocking:
1. lerna.json's command.publish.ignoreChanges was dropped. VersionCommand
declares publish as an other-command config, so `lerna version` read it —
which is why docs-only commits released nothing. version.mjs fell through to
patch for any commit, so the docs commit already on main would have shipped
eight versions whose changelogs read only "Version bump only for package".
commitsSince now drops a commit whose every path matches the ignore globs.
Verified: with only a README-touching commit outstanding, "Nothing to
release"; a commit touching both a README and a source file still releases.
2. pnpm records each importer's specifier, so version.mjs rewriting
dicom-codec's six sibling ranges stranded pnpm-lock.yaml and the next
--frozen-lockfile install failed with ERR_PNPM_OUTDATED_LOCKFILE. yarn 1's
lockfile had no workspace-local entries, so this was new. The release now
runs `pnpm install --lockfile-only --ignore-scripts` and commits the lock.
Verified by reproducing the failure and then the clean install.
3. libjpeg-turbo-12bit is the only package with no bench script, so a
single-package bench scope made pnpm exit 1 with
ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT and fail the blocking gate; lerna exited 0.
It now has an explicit no-op bench script.
4. The publish loop was alphabetical, so dicom-codec published third, carrying
freshly-rewritten ranges on four siblings that had not been published yet.
New tools/release/publish-order.mjs emits packages in dependency order.
Should fix:
5. `read` returns non-zero at EOF, so under `set -e` the publish loop died at
the first private manifest and its `continue` guard was unreachable. The
helper emits one stream of complete lines, so there is no such edge.
6. libjpeg-turbo-12bit has no vitest config, so the release's test gate never
touches it; combined with if-no-files-found: ignore, a dropped artifact
could publish a package with an empty dist. The helper refuses to emit a
package that ships dist/ but has none.
7. tools/release/ was absent from TOOLCHAIN_PATHS, so version.mjs would first
execute during a live release. It is now a toolchain path, and pr-checks
runs both scripts as a dry-run step (the test job now checks out full
history and tags for it).
8. The lockfile's settings block records only autoInstallPeers and
excludeLinksFromLockfile, so changing nodeLinker/allowBuilds/
linkWorkspacePackages left it byte-identical and CI would restore a cache
built against the old layout. All five cache keys now hash
pnpm-workspace.yaml too.ci/pnpm-trusted-publishing fix(release): address review — ignoreChanges, lockfile drift, publish order
Eight findings from review, all reproduced locally before fixing.
Blocking:
1. lerna.json's command.publish.ignoreChanges was dropped. VersionCommand
declares publish as an other-command config, so `lerna version` read it —
which is why docs-only commits released nothing. version.mjs fell through to
patch for any commit, so the docs commit already on main would have shipped
eight versions whose changelogs read only "Version bump only for package".
commitsSince now drops a commit whose every path matches the ignore globs.
Verified: with only a README-touching commit outstanding, "Nothing to
release"; a commit touching both a README and a source file still releases.
2. pnpm records each importer's specifier, so version.mjs rewriting
dicom-codec's six sibling ranges stranded pnpm-lock.yaml and the next
--frozen-lockfile install failed with ERR_PNPM_OUTDATED_LOCKFILE. yarn 1's
lockfile had no workspace-local entries, so this was new. The release now
runs `pnpm install --lockfile-only --ignore-scripts` and commits the lock.
Verified by reproducing the failure and then the clean install.
3. libjpeg-turbo-12bit is the only package with no bench script, so a
single-package bench scope made pnpm exit 1 with
ERR_PNPM_RECURSIVE_RUN_NO_SCRIPT and fail the blocking gate; lerna exited 0.
It now has an explicit no-op bench script.
4. The publish loop was alphabetical, so dicom-codec published third, carrying
freshly-rewritten ranges on four siblings that had not been published yet.
New tools/release/publish-order.mjs emits packages in dependency order.
Should fix:
5. `read` returns non-zero at EOF, so under `set -e` the publish loop died at
the first private manifest and its `continue` guard was unreachable. The
helper emits one stream of complete lines, so there is no such edge.
6. libjpeg-turbo-12bit has no vitest config, so the release's test gate never
touches it; combined with if-no-files-found: ignore, a dropped artifact
could publish a package with an empty dist. The helper refuses to emit a
package that ships dist/ but has none.
7. tools/release/ was absent from TOOLCHAIN_PATHS, so version.mjs would first
execute during a live release. It is now a toolchain path, and pr-checks
runs both scripts as a dry-run step (the test job now checks out full
history and tags for it).
8. The lockfile's settings block records only autoInstallPeers and
excludeLinksFromLockfile, so changing nodeLinker/allowBuilds/
linkWorkspacePackages left it byte-identical and CI would restore a cache
built against the old layout. All five cache keys now hash
pnpm-workspace.yaml too.ci/pnpm-trusted-publishing fix(docker:build): start from a clean build/ and dist/
A docker:build of libjpeg-turbo-8bit produced artifacts that failed the CSP
check with Function constructors. The cause was not the toolchain: cmake had
reused packages/libjpeg-turbo-8bit/build/CMakeCache.txt dated 2023-10-31 and
referencing emsdk's node 16, so the -sDYNAMIC_EXECUTION=0/-sEMBIND_AOT=1 link
flags added in 042be30 were never applied. A cached configure is silently
authoritative over flags it has never seen.
The packages disagree about cleaning: charls clears build/ and dist/, openjpeg
clears build/, libjpeg-turbo-12bit clears dist/, and libjpeg-turbo-8bit and
openjphjs clear neither. CI is immune either way because its runners check out
fresh, which is exactly the environment this script exists to reproduce — so it
now clears both itself rather than depending on which package it is building.
dist/ matters as much as build/: artifacts the current emsdk no longer emits
(the .js.mem files) otherwise linger forever, and dist is in these packages'
"files" array, so a local publish would ship them.
CODECS_KEEP_BUILD=1 opts out for iteration. Verified by rebuilding
libjpeg-turbo-8bit: CSP check passes, all 12 dist-size measurements are
identical to tools/dist-size/baseline.json, the two orphaned .js.mem files are
gone, and the package's test suite passes against the result.ci/pnpm-trusted-publishing fix(docker:build): start from a clean build/ and dist/
A docker:build of libjpeg-turbo-8bit produced artifacts that failed the CSP
check with Function constructors. The cause was not the toolchain: cmake had
reused packages/libjpeg-turbo-8bit/build/CMakeCache.txt dated 2023-10-31 and
referencing emsdk's node 16, so the -sDYNAMIC_EXECUTION=0/-sEMBIND_AOT=1 link
flags added in 042be30 were never applied. A cached configure is silently
authoritative over flags it has never seen.
The packages disagree about cleaning: charls clears build/ and dist/, openjpeg
clears build/, libjpeg-turbo-12bit clears dist/, and libjpeg-turbo-8bit and
openjphjs clear neither. CI is immune either way because its runners check out
fresh, which is exactly the environment this script exists to reproduce — so it
now clears both itself rather than depending on which package it is building.
dist/ matters as much as build/: artifacts the current emsdk no longer emits
(the .js.mem files) otherwise linger forever, and dist is in these packages'
"files" array, so a local publish would ship them.
CODECS_KEEP_BUILD=1 opts out for iteration. Verified by rebuilding
libjpeg-turbo-8bit: CSP check passes, all 12 dist-size measurements are
identical to tools/dist-size/baseline.json, the two orphaned .js.mem files are
gone, and the package's test suite passes against the result.ci/pnpm-trusted-publishing ci: replace lerna + yarn + CircleCI with pnpm and npm trusted publishing
The release was carried by two long-lived personal credentials: an NPM_TOKEN
in CircleCI, and a maintainer's personal SSH key, which was the only reason
`lerna version` could push the version commit past main's branch protection.
Both are now gone.
- pnpm replaces yarn + lerna as the workspace driver. lerna.json and yarn.lock
are deleted, pnpm-workspace.yaml pins the flat (hoisted) node_modules layout
the packages were built against, and `lerna run --scope` becomes
`pnpm --filter` throughout pr-checks.yml and bench.yml.
- tools/release/version.mjs replaces `lerna version`, reproducing the same
independent conventional-commit bumps, per-package tags, dependent range
cascade and CHANGELOG format. It only mutates files and emits a plan; all
git writes live in the workflow, so `--dry-run` is a safe local preview.
- .github/workflows/release.yml replaces the CircleCI NPM_PUBLISH job. npm
auth is OIDC trusted publishing (short-lived, scoped to this workflow file);
git auth is the built-in GITHUB_TOKEN. Every step is idempotent, so a re-run
after a partial failure finishes rather than double-publishes.
- Trusted publishing forces provenance generation, which requires each
package.json's repository.url to match this repo. Only openjphjs was
correct; charls pointed at chafey/charls-js, openjpeg at https://localhost,
and five packages had no repository field at all.
tools/release/README.md documents the flow and the two one-time setup scripts
(npm trusted publishers, and migrating main to a ruleset so the Actions bot can
push the version commit).ci/pnpm-trusted-publishing ci: replace lerna + yarn + CircleCI with pnpm and npm trusted publishing
The release was carried by two long-lived personal credentials: an NPM_TOKEN
in CircleCI, and a maintainer's personal SSH key, which was the only reason
`lerna version` could push the version commit past main's branch protection.
Both are now gone.
- pnpm replaces yarn + lerna as the workspace driver. lerna.json and yarn.lock
are deleted, pnpm-workspace.yaml pins the flat (hoisted) node_modules layout
the packages were built against, and `lerna run --scope` becomes
`pnpm --filter` throughout pr-checks.yml and bench.yml.
- tools/release/version.mjs replaces `lerna version`, reproducing the same
independent conventional-commit bumps, per-package tags, dependent range
cascade and CHANGELOG format. It only mutates files and emits a plan; all
git writes live in the workflow, so `--dry-run` is a safe local preview.
- .github/workflows/release.yml replaces the CircleCI NPM_PUBLISH job. npm
auth is OIDC trusted publishing (short-lived, scoped to this workflow file);
git auth is the built-in GITHUB_TOKEN. Every step is idempotent, so a re-run
after a partial failure finishes rather than double-publishes.
- Trusted publishing forces provenance generation, which requires each
package.json's repository.url to match this repo. Only openjphjs was
correct; charls pointed at chafey/charls-js, openjpeg at https://localhost,
and five packages had no repository field at all.
tools/release/README.md documents the flow and the two one-time setup scripts
(npm trusted publishers, and migrating main to a ruleset so the Actions bot can
push the version commit).ci/pnpm-trusted-publishing Latest Branches
-25%
ci/pnpm-trusted-publishing -17%
agent/refresh-codec-readmes +8%
© 2026 CodSpeed Technology