Everything belonging to the v4 TypeScript engine moves to the master-v4
branch: apps/, packages/, build-utils/, changesets, pnpm workspace, turbo,
the Node.js CI workflow and the ts-* release jobs. What remains is the
Rust workspace under rust/, the npm wrapper and platform packages, the
GitHub Action, Docker image, flake, skills, benchmark and fixtures/.
- rust.yml gains a smoke job that runs the release binary against
fixtures/ (json, sarif, html) and asserts clones and reporters
- release.yml keeps only the Rust jobs; pnpm removed from every workflow;
audit.yml keeps cargo audit + cargo deny; dependabot drops the root npm entry
- root package.json becomes a thin private package depending on the exact
jscpd@5 version so the pre-commit hook keeps installing the binary;
sync-version.mjs keeps it in step
- .pre-commit-hooks.yaml used v4-only flags that the v5 CLI rejects; fixed
- FORMATS.md is now generated from the Rust tokenizer (224 formats)
- README, docs, CONTRIBUTING, SECURITY, package and crate descriptions,
benchmark and skills rewritten for v5 on its own terms: no v5-vs-v4 speed
multipliers anywhere, a single "Looking for v4?" pointer to master-v4
Rust / crates.io
- report the invoked binary name in --version/--help (jscpd vs cpd)
- add repository, documentation, keywords, categories to all crates
- exclude tests/ from the jscpd crate package; add cargo-binstall metadata
- expand the crates.io README and render it on docs.rs
npm
- add funding field to jscpd, cpd and jscpd@4 packages
- add jscpd-linux-arm64-musl platform package and clearer unsupported-platform error
- enable provenance for the v4 changeset publish
Pipelines
- rust.yml: windows-latest in the matrix, drop dead schedule guard
- release.yml: cosign-sign release assets, floating v5 tag, LICENSE in tarballs,
invoke the new docker.yml (GHCR multi-arch image) after each release
- audit.yml: nightly cargo audit, cargo deny and pnpm audit
- Dockerfile: multi-stage image from release binaries (distroless), mcp target for Glama
Docs / community
- README: shields badges, single primary install per OS, GitHub Action snippet,
link to jscpd.dev, v5.1 features, 15 reporters, 224 formats, 8 platforms
- docs: @v5 action tag, Docker usage, baseline section, current crate versions
- YAML issue forms with engine selector, config.yml, CODEOWNERS
- remove stale .github/CONTRIBUTING.md stub, custom.md, TODO.md; untrack rust/jscpd-report.json
- examples: mark as v4 API, add a Rust cpd-finder example
The workspace declared rust-version = "1.87", set once when the Rust
workspace was added (0ddf611, June 2026) and never revisited. crates.io
publishes that field verbatim, so the jscpd crate page advertised a floor
the crate cannot actually build on:
$ cargo +1.93 check -p jscpd
error: rustc 1.93.1 is not supported by the following packages:
oxc_allocator@0.147.0 requires rustc 1.96.0
...
The real floor is 1.96.0, forced by the 13 oxc_* crates that reach the
published crate through the mandatory oxc_parser -> cpd-tokenizer -> jscpd
chain. ignore, globset and askama independently require 1.88. The number
was probably accurate at the time — oxc was pinned to 0.133 then — and
drifted silently as oxc raised its own MSRV across later bumps.
Nothing caught the drift because every workflow pins the toolchain to
1.97.1, so no job ever built at the declared MSRV. This adds an `msrv` job
that reads rust-version straight out of Cargo.toml and runs cargo check
against exactly that toolchain, so the next oxc bump fails loudly instead
of quietly falsifying published metadata.
The check uses `cargo +<version>` rather than relying on the toolchain
installed by the action: rust-toolchain.toml pins 1.97.1 for development
and would otherwise take precedence, making the job test the wrong
compiler.
rust/README.md claimed 1.87+ and pointed at rust-toolchain.toml, which
actually pins 1.97.1 — the two never agreed. It now states the MSRV and
explains what the pin is for.
Verified: cargo +1.96 check --workspace --all-targets --locked passes, so
1.96 is achievable including dev-dependencies.
Pinning rust-toolchain.toml to 1.97.1 made rustup auto-install a bare
1.97.1 toolchain in CI instead of using the dtolnay-installed 1.97 one
that carried clippy and rustfmt, failing the Clippy step. Declare the
components in the toolchain file so any auto-install includes them, and
align the workflow toolchain pins to 1.97.1 so CI installs the exact
toolchain cargo resolves.
Claude-Session: https://claude.ai/code/session_01VvWzWfEK6oV4HTe6EoGkff
- pin every GitHub Action to a full commit SHA (with version comments);
add github-actions ecosystem to Dependabot to keep the pins fresh
- follow least privilege for workflow tokens: top-level contents:read
everywhere, checks/id-token/contents write grants scoped to the jobs
that need them (test-reporter, npm/crates trusted publishing,
gh release create)
- add SECURITY.md with private disclosure channels and supported versions
Also enabled outside the repo: master branch ruleset (no force-push or
deletion) and GitHub private vulnerability reporting.
dorny/test-reporter@v1 calls the GitHub check-runs write API which
is restricted when running from a fork PR. fail-on-error: false only
guards against test-result failures, not API errors. Adding
continue-on-error: true at the step level prevents the 403
HttpError from failing the job on fork PRs while keeping the
reporter fully functional on pushes to master.
actions/setup-node@v5 enables package-manager-cache by default, which tries
to save the pnpm store in post-cleanup. When pnpm install is never run
(e.g. idempotent publish skip), the store path doesn't exist, causing
'Path Validation Error: Path(s) specified for caching do not exist'.
Also added cache-on-failure: true to all Swatinem/rust-cache steps to
ensure Cargo cache is saved even on failed builds.
nextest writes JUnit to target/nextest/<profile>/<path>, not to
a workspace-root-relative path. Update nextest.toml path from
'test-results/nextest-junit.xml' to 'nextest.xml' and update all
three Rust workflows (rust.yml, crates-publish.yml,
npm-rust-publish.yml) to reference the correct location:
rust/target/nextest/ci/nextest.xml
- add cargo-nextest test steps to all Rust pipelines (rust.yml,
crates-publish.yml, npm-rust-publish.yml) with JUnit report upload
via dorny/test-reporter
- add Vitest JUnit reporter to Node.js pipelines (nodejs.yml,
release.yml) with test report display
- add rust/.config/nextest.toml with ci profile for JUnit output
- move platform-published check before build in npm-rust-publish.yml
so already-published targets skip the entire build chain
- make each cargo publish step idempotent (skip if version exists)
- add master to rust.yml push/pull_request branch triggers
- publish-packages: remove changeset version (run separately)