Distribute Stripe CLI via npm (npx @stripe/cli)#1595
Open
johno-stripe wants to merge 3 commits into
Open
Conversation
…npm packages, enabling npx @stripe/cli login
…s complete, as it was staying open for a bit
22bfe50 to
84be4b5
Compare
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.
Reviewers
r? @
cc @stripe/developer-products
Summary
Adds npm as an install channel for the Stripe CLI. After this ships, developers with Node >= 18 can run:
This doesn't replace existing install paths (Homebrew, apt, Scoop, Docker) — it adds a fast path for the majority of the CLI's audience who already have Node installed.
Motivation
The Stripe CLI's primary audience is web developers integrating a payments API. Most already have Node.js. Today, installing the CLI on Windows requires Scoop (a package manager most developers haven't heard of), and every platform requires a separate tool.
npx @stripe/cliis a universal one-liner.What's included
@stripescope: a wrapper (@stripe/cli) and 5 platform-specific binary packages (darwin-arm64, darwin-x64, linux-x64, linux-arm64, win32-x64)publish-npmjob in the release workflow, gated on all three platform builds completing--no-optional: the wrapper's postinstall downloads directly from GitHub Releases and verifies SHA256 checksumsNo changes to Go code. No changes to goreleaser configs. The existing release artifacts are reused as-is. The README will only be updated to reflect this install path once this has been merged and published.
Prerequisites before merging
NPM_TOKENrepo secret (npm automation token scoped to@stripeorg)Test plan
node --checkpasses on all JS filesnode npm/scripts/stamp-version.js 1.41.1stamps all package.json files correctlyVERSION=1.41.1 GITHUB_TOKEN=... node npm/scripts/fetch-binaries.jsdownloads, verifies, and extracts all 5 platform binariesnode npm/wrapper/bin/shim.js --versionvia local symlink)npm install -g @stripe/cli && stripe --versionon all 3 platforms (covered by new install-test jobs)npx --yes @stripe/cli --versionexits successfullynpm install -g @stripe/cli --no-optional && stripe --versionexercises fallback path### How it works
For anyone unfamiliar with the npm mechanisms at play:
Platform selection via
optionalDependencies+os/cpunpm's
package.jsonsupports two fields that declare platform requirements:{ "os": ["darwin"], "cpu": ["arm64"] }When a package declares these, npm will skip installing it on non-matching hosts. The wrapper package (
@stripe/cli) lists all 5 platform packages asoptionalDependencies:optionalDependenciesmeans "install these if you can, don't fail if you can't." Combined with theos/cpuguards, npm installs only the one matching the user's machine (typically 15-50 MB). The other 4 are silently skipped.The
binfield and the shimThe wrapper declares
"bin": { "stripe": "bin/shim.js" }. When installed globally (npm install -g), npm symlinksstripeinto the user's PATH pointing atshim.js. The shim is 23 lines: it detects the current platform, finds the installed binary package viarequire.resolve(), and spawns it with all arguments forwarded andstdio: 'inherit'(transparent passthrough).The
postinstallfallbackIf npm was run with
--no-optional(or a corporate policy strips optional deps), no platform package is installed. The wrapper's"scripts": { "postinstall": "node scripts/postinstall.js" }detects this, downloads the correct archive from GitHub Releases, verifies the SHA256 checksum against goreleaser's published checksum files, and extracts the binary to avendor/bin/directory. The shim falls back to this path whenrequire.resolve()fails.Why platform packages have a
binfield tooEach platform package (e.g.
@stripe/cli-darwin-arm64) also declares"bin": { "stripe": "bin/stripe" }. This is a safety net: if someone installs a platform package directly, the binary still gets symlinked correctly. It also allows npm to set the executable bit during installation on systems that require it.