Drop Node.js 18 support (now Node 20+ only), modernize the dev toolchain, and increase test coverage to 100% - #1032
Open
yinzara wants to merge 2 commits into
Open
Conversation
yinzara
force-pushed
the
build/node-20-toolchain
branch
from
September 8, 2026 16:02
0c04d7e to
7d7cdde
Compare
Raise the supported Node.js baseline to >= 20 and bring the test, git-hook,
coverage and dependency toolchain up to currently-maintained versions.
Several dependency upgrades (notably glob 13 and pnpm 10+) surfaced latent
runtime bugs, which are fixed here as well.
Runtime fixes
-------------
* config discovery (src/configLoader/findup.js): switch to the named
`import { globSync } from 'glob'`. glob v9+ removed the default export, so
with glob 13 `glob` resolved to `undefined` and every config lookup threw
`TypeError: Cannot read properties of undefined (reading 'sync')`.
* `commitizen init --pnpm` (src/commitizen/init.js, src/commitizen/adapter.js):
- forward the `exact` option into `getInstallStringMappings()` and key the
pnpm `--save-exact` flag off `exact` instead of `saveExact` (which is never
set on the pnpm path). `commitizen init --pnpm --save-exact` now pins an
exact version instead of silently writing a caret range.
- pass `--ignore-scripts` to the pnpm install. pnpm >= 10 turns
"ignored build scripts" (ERR_PNPM_IGNORED_BUILDS) into a fatal error, which
aborted the install before `config.commitizen.path` was written. Commitizen
adapters never need their dependencies' lifecycle scripts.
Dependency upgrades
-------------------
* mocha 9.2.2 -> 12.0.0
* nyc 15.1.0 -> 18.0.0
* babel-plugin-istanbul 6.1.1 -> 7.0.1
* nodemon 2.0.22 -> 3.1.14
* sinon 12.0.1 -> 22.1.0
* uuid 8.3.2 -> 11.1.1
* conventional-changelog-conventionalcommits 4.6.3 -> 7.0.2
* glob 7.2.3 -> 13.0.6 (runtime dependency)
* fs-extra 9.1.0 -> 11.4.0 (runtime dependency)
* find-node-modules ^2.1.2 -> ^2.1.3 (runtime dependency)
Dependency removals and replacements
------------------------------------
* Move `cz-conventional-changelog` from `dependencies` to `devDependencies`.
Nothing in the runtime code (src/, bin/) requires it -- it only appears as
example text in `commitizen init` help output. Its real uses are this repo's
own `config.commitizen.path` (dogfooding commits) and the test suite. As a
runtime dependency it needlessly pulled itself and its `@commitlint/*`
subtree into every consumer of `commitizen`.
* ghooks -> husky 9. Drop the `config.ghooks` block, add `.husky/pre-commit`
(`npm run test && npm run check-coverage`), and run `husky` from the
`prepare` script. Replace the `in-publish` / `not-in-install` build-on-
publish hack with a standard `prepublishOnly` script; `in-publish` removed.
* Remove `codecov`. The npm `codecov` uploader is end-of-life and the
`report-coverage` script was unused: Azure Pipelines already publishes
coverage from `npm run write-coverage` (cobertura). Stale `codecov.io` and
`travis.ci` badges removed from the README.
* Remove `mocha-multi-reporters` and `mocha-junit-reporter`. Both are
unmaintained and throw "Class constructor Base cannot be invoked without
'new'" on mocha >= 11. Replaced with a small in-repo composite reporter,
test/reporters/multi-reporter.cjs, that drives mocha's built-in `spec` and
`xunit` reporters from a single run and fans `done()` out to flush the
JUnit XML file. `test/mochareporters.json` deleted.
Test harness
------------
* `test-actual` now runs `mocha --require @babel/register ...` directly, so it
works when invoked on its own and not only through the `nyc` wrapper. The
test files use extensionless ESM imports that only resolve once Babel
rewrites them to `require`.
* JUnit output is written to `./junit-testresults.xml` via
`--reporter-option output=`; added to `.gitignore`.
* test/tools/clean.js: the per-test artifact archive no longer copies
`node_modules`. It is large and slow, and on Windows pnpm's nested junctions
under `.pnpm` make a later recursive walk of `test/artifacts` (Azure's
PublishTestResults glob) fail with `EPERM: operation not permitted, stat`.
* babel.config.js: `@babel/preset-env` target bumped from node 10 to node 20.
CI
--
jobs/build.yml:
* Node matrix is now 20, 22, 24 and 26 (Node 18 removed); `maxParallel` 3 -> 4.
* Node install task `NodeTool@0` -> `UseNode@1`.
* pnpm bumped 9 -> 12.
* PublishTestResults@2 now points at the exact `junit-testresults.xml` under
`$(System.DefaultWorkingDirectory)` instead of the recursive `**/junit-*.xml`
glob, so it never walks the artifacts tree (which is what triggered the
Windows `EPERM: operation not permitted, stat` failure on pnpm's `.pnpm`
junctions).
* PublishCodeCoverageResults@1 -> @2; drop the removed `codeCoverageTool`
input and point `summaryFileLocation` at the single
`$(System.DefaultWorkingDirectory)/coverage/cobertura-coverage.xml` rather
than a recursive glob.
Housekeeping
------------
* .gitignore: add `.idea`, `.omc`, `junit-testresults.xml`.
* package-lock.json regenerated.
BREAKING CHANGE: Node.js 18 is no longer supported. The minimum supported
runtime is now Node.js 20 (`engines.node` is `>= 20`), and npm 9 or newer is
recommended. Installing or running commitizen on Node.js 18 or earlier will
fail the engine check. This is required because upgraded tooling and
transitive dependencies (mocha 12, nyc 18, glob 13's `minimatch`/`lru-cache`,
and the pnpm 10+ CLI used in CI) have themselves dropped Node.js 18.
Fixes commitizen#1020, fixes commitizen#1006
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
yinzara
force-pushed
the
build/node-20-toolchain
branch
from
September 8, 2026 16:23
7d7cdde to
39dc10d
Compare
yinzara
force-pushed
the
build/node-20-toolchain
branch
from
September 8, 2026 17:23
b2f34b5 to
af84ab8
Compare
`src/cli` was excluded from coverage entirely and several modules elsewhere
were only exercised incidentally, leaving overall coverage around 75%. This
adds focused unit tests (proxyquire + sinon) for the previously untested
code and tightens the gate to 100%.
New / expanded tests
--------------------
* test/tests/cli.js: exercises the whole CLI entry layer.
- cli/commitizen.js `bootstrap`: the `init` sub-command (calls `init()`
with cwd/name/parsed args, logs instead of throwing on failure, errors
when no adapter name is given), the usage-help path for every other
invocation, argv slicing and the `environment`/`argv` parameter defaults.
- cli/strategies/git.js: spawns `git commit` with the raw args and
inherited stdio, rethrows a child-process `error`, and only logs (never
spawns) in debug mode.
- cli/strategies/git-cz.js: `--amend` delegates to the plain git strategy,
the normal path wires inquirer/prompter/options into `commit()`,
`--retry` and `--hook` set the right flags, `-a`/`--all` stages all
files, a clean staging area throws unless `--allow-empty` is present,
and errors from the staging check and the commit callback are rethrown;
a successful commit exits 0.
* test/tests/git.js (new): git/log and git/whatChanged (success + throw on
git error); git/commit non-hook mode (spawn args, stdio, `error` event,
exit code 0 / 128-with-config-warning / other non-zero, single-callback
guard) and `--hook` mode (`w` write, the Windows `r+` fallback, write
failure, and fallback failure).
* test/tests/commitizen-commit.js (new): cache-dir creation failure is
logged and aborts, the normal prompter -> commit -> cache flow, an Error
from the adapter prompter is propagated without committing, and
`retryLastCommit` replays the cached attempt.
* test/tests/cache.js (new): create / merge / overwrite / read / missing-key
/ missing-file behaviour of the commitizen cache helpers.
* test/tests/adapter-unit.js (new): `getInstallStringMappings` for
npm/yarn/pnpm and the unknown-manager fallback (including the `exact` vs
`saveExact` distinction), `generateInstallAdapterCommand`, and
`addPathToAdapterConfig` indentation detection / two-space fallback / the
"already configured" guard branch.
* test/tests/init-unit.js (new): required-argument errors, an install
failure is logged rather than thrown, and `includeCommitizen` also runs
the commitizen install.
* test/tests/util.js: adds `getParsedJsonFromFile` /
`getParsedPackageJsonFromPath` (parse success, missing file, invalid
JSON) and `isInTest`.
* test/tests/configLoader.js: adds `loader` (explicit path, discovery,
nothing-found, default cwd), `findup` (walks up parent directories,
returns undefined at the root, honours the filter callback), and the
empty/missing config-path branches of `getContent` / `getNormalizedConfig`.
* test/tests/index.js: registers the new suites.
Source / config
---------------
* package.json: drop `src/cli` from `nyc.exclude` so the CLI layer is
measured, and raise `check-coverage` thresholds from 80 to 100 for
statements, branches, functions and lines.
* src/cli/parsers.js, src/cli/strategies.js: mark as
`/* istanbul ignore file */` -- they are pure re-export barrels, matching
src/git.js, src/commitizen.js and src/configLoader.js.
* src/configLoader/getNormalizedConfig.js: `/* istanbul ignore if */` on the
czConfig deprecation warning, which is deliberately suppressed while the
test globals are present and so cannot be reached from the suite.
Result: coverage goes from ~75% to 100% across statements, branches,
functions and lines; `npm test` and `npm run check-coverage` both pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
yinzara
force-pushed
the
build/node-20-toolchain
branch
from
September 8, 2026 17:29
af84ab8 to
505bfc5
Compare
This was referenced Sep 8, 2026
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.
Raise the supported Node.js baseline to >= 20 and bring the test, git-hook, coverage and dependency toolchain up to currently-maintained versions. Several dependency upgrades (notably glob 13 and pnpm 10+) surfaced latent runtime bugs, which are fixed here as well.
src/cliwas excluded from coverage entirely and several modules elsewhere were only exercised incidentally, leaving overall coverage around 75%. This PR adds focused unit tests (proxyquire + sinon) for the previously untested code and tightens the gate to 100%.Fixes #1020 and #1006
Runtime fixes
config discovery (src/configLoader/findup.js): switch to the named
import { globSync } from 'glob'. glob v9+ removed the default export, so with glob 13globresolved toundefinedand every config lookup threwTypeError: Cannot read properties of undefined (reading 'sync').commitizen init --pnpm(src/commitizen/init.js, src/commitizen/adapter.js):exactoption intogetInstallStringMappings()and key the pnpm--save-exactflag offexactinstead ofsaveExact(which is never set on the pnpm path).commitizen init --pnpm --save-exactnow pins an exact version instead of silently writing a caret range.--ignore-scriptsto the pnpm install. pnpm >= 10 turns "ignored build scripts" (ERR_PNPM_IGNORED_BUILDS) into a fatal error, which aborted the install beforeconfig.commitizen.pathwas written. Commitizen adapters never need their dependencies' lifecycle scripts.Dependency upgrades
Dependency removals and replacements
cz-conventional-changelogfromdependenciestodevDependencies. Nothing in the runtime code (src/, bin/) requires it -- it only appears as example text incommitizen inithelp output. Its real uses are this repo's ownconfig.commitizen.path(dogfooding commits) and the test suite. As a runtime dependency it needlessly pulled itself and its@commitlint/*subtree into every consumer ofcommitizen.config.ghooksblock, add.husky/pre-commit(npm run test && npm run check-coverage), and runhuskyfrom thepreparescript. Replace thein-publish/not-in-installbuild-on- publish hack with a standardprepublishOnlyscript;in-publishremoved.codecov. The npmcodecovuploader is end-of-life and thereport-coveragescript was unused: Azure Pipelines already publishes coverage fromnpm run write-coverage(cobertura). Stalecodecov.ioandtravis.cibadges removed from the README.mocha-multi-reportersandmocha-junit-reporter. Both are unmaintained and throw "Class constructor Base cannot be invoked without 'new'" on mocha >= 11. Replaced with a small in-repo composite reporter, test/reporters/multi-reporter.cjs, that drives mocha's built-inspecandxunitreporters from a single run and fansdone()out to flush the JUnit XML file.test/mochareporters.jsondeleted.Test harness
test-actualnow runsmocha --require @babel/register ...directly, so it works when invoked on its own and not only through thenycwrapper. The test files use extensionless ESM imports that only resolve once Babel rewrites them torequire../junit-testresults.xmlvia--reporter-option output=; Azure'sPublishTestResultsglob (**/junit-*.xml) still matches. Added to.gitignore.@babel/preset-envtarget bumped from node 10 to node 20.CI
jobs/build.yml:
maxParallel3 -> 4.NodeTool@0->UseNode@1.junit-testresults.xmlunder$(System.DefaultWorkingDirectory)instead of the recursive**/junit-*.xmlglob, so it never walks the artifacts tree (which is what triggered the
Windows
EPERM: operation not permitted, statfailure on pnpm's.pnpmjunctions).
codeCoverageToolinput and point
summaryFileLocationat the single$(System.DefaultWorkingDirectory)/coverage/cobertura-coverage.xmlratherthan a recursive glob.
New / expanded tests
bootstrap: theinitsub-command (callsinit()with cwd/name/parsed args, logs instead of throwing on failure, errors when no adapter name is given), the usage-help path for every other invocation, argv slicing and theenvironment/argvparameter defaults.git commitwith the raw args and inherited stdio, rethrows a child-processerror, and only logs (never spawns) in debug mode.--amenddelegates to the plain git strategy, the normal path wires inquirer/prompter/options intocommit(),--retryand--hookset the right flags,-a/--allstages all files, a clean staging area throws unless--allow-emptyis present, and errors from the staging check and the commit callback are rethrown; a successful commit exits 0.errorevent, exit code 0 / 128-with-config-warning / other non-zero, single-callback guard) and--hookmode (wwrite, the Windowsr+fallback, write failure, and fallback failure).retryLastCommitreplays the cached attempt.getInstallStringMappingsfor npm/yarn/pnpm and the unknown-manager fallback (including theexactvssaveExactdistinction),generateInstallAdapterCommand, andaddPathToAdapterConfigindentation detection / two-space fallback / the "already configured" guard branch.includeCommitizenalso runs the commitizen install.getParsedJsonFromFile/getParsedPackageJsonFromPath(parse success, missing file, invalid JSON) andisInTest.loader(explicit path, discovery, nothing-found, default cwd),findup(walks up parent directories, returns undefined at the root, honours the filter callback), and the empty/missing config-path branches ofgetContent/getNormalizedConfig.Source / config
src/clifromnyc.excludeso the CLI layer is measured, and raisecheck-coveragethresholds from 80 to 100 for statements, branches, functions and lines./* istanbul ignore file */-- they are pure re-export barrels, matching src/git.js, src/commitizen.js and src/configLoader.js./* istanbul ignore if */on the czConfig deprecation warning, which is deliberately suppressed while the test globals are present and so cannot be reached from the suite.Result: coverage goes from ~75% to 100% across statements, branches, functions and lines;
npm testandnpm run check-coverageboth pass.Housekeeping
.idea,.omc,junit-testresults.xml.BREAKING CHANGE: Node.js 18 is no longer supported. The minimum supported runtime is now Node.js 20 (
engines.nodeis>= 20), and npm 9 or newer is recommended. Installing or running commitizen on Node.js 18 or earlier will fail the engine check. This is required because upgraded tooling and transitive dependencies (mocha 12, nyc 18, glob 13'sminimatch/lru-cache, and the pnpm 10+ CLI used in CI) have themselves dropped Node.js 18.