Repository navigation
feat: publish @pact-foundation/pact-cli to npm - #171
Merged
Merged
Conversation
`npm/pact-cli` is the `@pact-foundation/pact-cli` npm package: plain CommonJS with no dependencies and no build step, for Node 16 and later. It keeps the seven bin commands that pact-js-cli users already have. The binary comes from an optional dependency on `@pact-foundation/pact-cli-<os>-<arch>`, the platform package names already on npm. The wrapper spawns it without a shell, forwards SIGINT, SIGTERM and SIGHUP to it, and exits the way the binary exited. A missing platform package names the package it expected, with no stack trace. npm cannot point a bin at a file in another package. Platform-package bins only appear through npm's hoisting, not under pnpm, Yarn 2+ or `npx`, so `bin/pact.js` is the minimum shim. `pact <command>` is the supported form, matching the PyPI wheel, which installs only `pact`. The hyphenated commands (`pact-broker`, `pact-mock-server`, `pact-stub-server`, `pact-plugin`, `pact-verifier`, `pactflow`) still run the matching subcommand, after a one-line deprecation notice on stderr. Removing them breaks `^18`, so they can go no earlier than crate 1.0.0. The tests cover argument and exit-status passthrough, signal forwarding in both directions and the missing-package error, all from a path with a space. The `npm` job in test.yml runs them on Linux and Windows, and checks that the wrapper loads on Node 16. Assisted-by: Claude Code:claude-opus-5-5
Each stable release now publishes the `pact` binary to npm as `@pact-foundation/pact-cli` and six platform packages. The package was built in pact-foundation/pact-js-cli, which trailed every release here by a separate update-and-release cycle. Publishing moves here so npm ships with each release. npm has no epoch and accepts only three-part versions, so crate `X.Y.Z` maps to npm `(18+X).Y.Z`: `v0.10.9` becomes `18.10.9`. That continues the pact-js-cli 18.x line above its last release, 18.1.2, so users on `^18` receive new releases without changing their range. git-cliff makes a crate minor bump additive, which suits a `^18` range. The new `publish-npm` post-announce job downloads the cargo-dist archives, verifies their checksums and repackages each binary with `.github/scripts/build-npm.js`, a dependency-free Node script. Node has no xz or zip support, so the script extracts with `tar` and `unzip`. The binaries are copied byte for byte; nothing is recompiled. Linux packages carry the static musl binary. The Windows arm64 package carries the x86_64 binary, which runs under Windows' x64 emulation, until the native target is re-enabled; the published 18.1.2 package for that platform has no binary. A smoke matrix on six runners, including windows-11-arm, installs the main tarball with the host's platform tarball and runs every bin. The publish job then uploads through npm trusted publishing with provenance, platform packages first and the main package last, and skips versions that already exist. npm moves `latest` to whatever is published last, so the job reads the current `latest` and publishes an older version under `backfill`. A registry error other than E404 fails the job, so an outage cannot pass for "not published". A final job installs the version from the registry and runs `pact --help`. Prereleases are skipped. `workflow_dispatch` takes a tag, for retries and backfills of tags that have cargo-dist archives. Maintainer setup: configure npm trusted publishing on all seven packages for release.yml and publish-npm.yml with environment `npm`, and create that environment in this repo. Assisted-by: Claude Code:claude-opus-5-5
JP-Ellis
force-pushed
the
feat/npm-publish
branch
from
October 21, 2026 04:46
a915ba4 to
a03357f
Compare
JP-Ellis
enabled auto-merge (rebase)
October 21, 2026 04:47
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.
Goal
Publish
@pact-foundation/pact-clito npm from this repo on every stable release, the same waypublish-pypi.ymlpublishespact-python-cli. Today pact-foundation/pact-js-cli repackages these binaries in a separate release cycle, so npm trails:18.1.2ships crate0.10.8while this repo is at0.10.9. pact-js-cli will be archived separately once a release from here is on npm.What ships
npm/pact-cli/: plain CommonJS wrappers for the seven existing bins (pact,pact-broker,pact-mock-server,pact-stub-server,pact-plugin,pact-verifier,pactflow).@pact-foundation/pact-cli-<os>-<arch>, the same layout and package names as today..github/scripts/build-npm.js: a dependency-free Node script. It extracts withtarandunzip, since Node has no xz or zip support. It copies the cargo-dist binaries byte for byte into six platform packages. Linux uses the static musl binary. Windows arm64 carries the x64 binary under emulation; the publishedpact-cli-windows-arm64@18.1.2has no binary at all..github/workflows/publish-npm.yml: a cargo-dist post-announce job (wired throughCargo.toml;release.ymlregenerated withdist generate) with four stages:windows-11-armandmacos-15-intel, thatnpm installs the main tarball with the host's platform tarball and runs every bin;pact --help.npmjob intest.ymlruns the wrapper tests on Linux, macOS and Windows, and checks that the wrapper loads on Node 16.Decisions
Versioning: crate
X.Y.Zmaps to npm(18+X).Y.Z, sov0.10.9becomes18.10.9. npm has no epoch and rejects four-part versions. A prerelease or build-metadata suffix would sort wrongly or drop out of^18. The offset keeps^18users on the line above18.1.2, and git-cliff makes crate minor bumps additive.The
latestguard: npm moveslatestto the most recent publish, not the highest version. The publish job reads the currentlatestand publishes an older version (a retry or backfill) underbackfill. Any registry error other than E404 fails the job, so an outage can't pass for "not published".Why no postinstall download: cargo-dist's own npm installer downloads the binary at install time. That approach breaks under
--ignore-scriptsand in offline CI, so the binaries ship in the packages.Signal forwarding: the first version used
spawnSync, the same as pact-js-cli. Review showed that a SIGTERM sent to the wrapper, as test harnesses,concurrentlyandtimeoutall send, leftpact mockrunning and holding its port. The wrapper now uses asyncspawnand forwards signals.Hyphenated commands are deprecated.
pact <command>is the supported form, matching the PyPI wheel, which installs onlypact. The six hyphenated commands keep working, but each printswarning: pact-broker is deprecated; use 'pact broker' instead(or its equivalent) on stderr. Removing them breaks^18, so they can go no earlier than crate 1.0.0 (npm 19.0.0).Why a JS shim at all: a wheel is selected per platform and drops
pactstraight ontoPATH. npm commands come from thebinof the installed package, which can't point into an optional dependency. Bins on the platform packages only work through npm's hoisting, not under pnpm, Yarn 2+ ornpx. Sobin/pact.jsis the minimum.Before the first release
release.ymlandpublish-npm.yml, with environmentnpm.npmenvironment in this repo.Testing
npm install. All seven bins run from the installed package.dist planare clean.