Skip to content

fix: publish npm binaries as per-platform optionalDependencies#447

Merged
alnr merged 2 commits into
masterfrom
alnr/npm-platform-packages
Jul 22, 2026
Merged

fix: publish npm binaries as per-platform optionalDependencies#447
alnr merged 2 commits into
masterfrom
alnr/npm-platform-packages

Conversation

@alnr

@alnr alnr commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Problem

npm audit flags every project that installs @ory/cli (#410). The package used binwrap to download the CLI binary at install time; binwrap is unmaintained and hard-depends on the deprecated request package, pulling in vulnerable form-data, tough-cookie, qs, uuid, and tar versions with no fix available. As of today that's 8 findings (3 critical) in a fresh install. The overrides added in #409 didn't help consumers, since npm only honors overrides from the root package of an install.

Solution

Adopt the esbuild/rollup pattern of per-platform binary packages:

  • npm/run.js — dependency-free launcher that bin.ory points to. Maps process.platform/process.arch to the matching platform package, resolves the binary, and spawns it with inherited stdio, propagating exit codes and signals.
  • npm/publish.js — release-time script run by the npm-publish CI job. Downloads the goreleaser archives from the GitHub release, extracts each binary into a minimal @ory/cli-<platform>-<arch> package (os/cpu fields make npm install only the matching one), smoke-tests the host binary, publishes the six platform packages first, then injects exact-version optionalDependencies into package.json and publishes @ory/cli itself.
  • binwrap, the install/prepare scripts, and all runtime npm dependencies are removed. @ory/cli is now a 5.6 kB package with four files and zero install scripts.
  • Prereleases (e.g. v1.3.1-pre.0) now publish under the next dist-tag instead of latest.
  • Windows arm64 is now supported on npm (the release asset already existed).

The optionalDependencies map is deliberately not checked in: it is injected at publish time, because checked-in entries pointing at not-yet-published versions would 404 on every dev npm install.

Verification

  • Full --dry-run against the real v1.3.1 and v1.3.1-pre.0 GitHub releases: all six platform packages build, the host smoke test (ory version) passes, and dist-tags resolve to latest/next respectively.
  • Consumer simulation (packed tarball + local platform packages): npm installs only the matching platform package, npx ory version runs the real binary, stdio and exit codes propagate, and npm audit reports 0 vulnerabilities (vs. 8 with the published 1.2.0).

Notes for reviewers

  • The first release from this branch creates six new packages in the @ory npm scope — NPM_TOKEN_AENEASR must be allowed to create packages there (--access public is passed).
  • v1.3.1 was tagged on GitHub but never reached npm (latest is still 1.2.0; the old binwrap-based job failed). After merging, it can be backfilled manually with node npm/publish.js v1.3.1 and an npm token.

Closes #410

🤖 Generated with Claude Code

https://claude.ai/code/session_01NaL51TwfZAAEEWU2v5CNWT

Summary by CodeRabbit

  • New Features

    • Added platform-specific npm packages for the Ory CLI, supporting automatic binary selection across supported operating systems and CPU architectures.
    • Added release validation, smoke testing, prerelease tagging, and dry-run support for npm publishing.
  • Improvements

    • Updated the publishing workflow to use Node.js 22 and the new automated release process.
    • Simplified the package contents and command-line launcher configuration.

The @ory/cli npm package used binwrap to download the CLI binary at
install time. binwrap is unmaintained and depends on the deprecated
request package, whose transitive dependencies trigger unfixable
critical npm audit findings in every consuming project.

The npm package now follows the esbuild pattern: the release pipeline
publishes one package per platform (e.g. @ory/cli-linux-x64) containing
just the prebuilt binary, and @ory/cli itself ships a dependency-free
launcher plus exact-version optionalDependencies on those packages.
npm's os/cpu fields ensure only the binary matching the consumer's
platform is downloaded. Install scripts and all runtime npm
dependencies are gone, and npm audit reports zero vulnerabilities.

Prereleases are now published under the "next" dist-tag instead of
"latest", and Windows arm64 binaries are now published to npm.

Closes #410

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NaL51TwfZAAEEWU2v5CNWT
@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The package now uses a platform-specific optional dependency layout. A new launcher resolves and executes the matching binary, while npm/publish.js builds and publishes per-platform packages before publishing the root package. CI invokes this flow with Node.js 22.

Changes

Platform-specific npm distribution

Layer / File(s) Summary
Platform launcher and package metadata
npm/run.js, package.json, npm/index.js, .npmignore
The package exposes npm/run.js, resolves the matching platform binary, removes the previous binwrap export and dependency, and changes published package contents and scripts.
Platform package generation and publishing
npm/publish.js
Release versions are validated, platform archives are downloaded and packaged, binaries are smoke-tested, platform packages are published, and root optionalDependencies are updated.
CI release command
.github/workflows/ci.yaml
The publish job upgrades to Node.js 22, configures npm authentication, and runs npm/publish.js with the Git ref name.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

CLI launch flow

sequenceDiagram
  participant npm
  participant npm_run_js
  participant PlatformPackage
  npm->>npm_run_js: invoke CLI
  npm_run_js->>PlatformPackage: resolve matching binary
  npm_run_js->>PlatformPackage: spawn binary
  PlatformPackage-->>npm_run_js: return exit status or signal
Loading

Release publishing flow

sequenceDiagram
  participant CI
  participant publish_js
  participant GitHub_Releases
  participant npm_registry
  CI->>publish_js: pass release ref
  publish_js->>GitHub_Releases: download platform archive
  publish_js->>publish_js: build and smoke-test packages
  publish_js->>npm_registry: publish platform packages
  publish_js->>npm_registry: publish root package
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: replacing binwrap with per-platform optionalDependencies for npm publishing.
Description check ✅ Passed The description covers the problem, solution, verification, and linked issue; only the checklist section is not filled in.
Linked Issues check ✅ Passed The changes address #410 by removing binwrap and npm audit vulnerabilities through per-platform packages and a dependency-free launcher.
Out of Scope Changes check ✅ Passed The workflow, ignore rules, package metadata, and new scripts all support the npm packaging redesign and appear in scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch alnr/npm-platform-packages

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yaml:
- Around line 95-99: Update the publish step around npm/publish.js so
github.ref_name is passed via a dedicated environment variable rather than
interpolated directly into the shell script. Invoke node npm/publish.js using
that environment variable, preserving the existing NPM_TOKEN handling and
publish behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ed4e7db3-a4e0-4bab-9bf1-6dbb3c61ae22

📥 Commits

Reviewing files that changed from the base of the PR and between 5d23615 and a66a872.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • .github/workflows/ci.yaml
  • .npmignore
  • npm/index.js
  • npm/publish.js
  • npm/run.js
  • package.json
💤 Files with no reviewable changes (2)
  • .npmignore
  • npm/index.js

Comment thread .github/workflows/ci.yaml Outdated
Pass github.ref_name to npm/publish.js via an environment variable
instead of interpolating it into the run script, so a crafted tag name
cannot inject shell commands.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NaL51TwfZAAEEWU2v5CNWT

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/ci.yaml (1)

84-100: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Declare least-privilege permissions for the publish job.

This job publishes through NPM_TOKEN and does not appear to need GitHub write access, but it inherits the repository’s default GITHUB_TOKEN permissions. Add an explicit job-level permission block, such as contents: read, and grant anything additional only if ory/ci/checkout@master requires it.

🛡️ Proposed fix
   npm-publish:
     name: Publish to npm
     runs-on: ubuntu-latest
+    permissions:
+      contents: read
     if: ${{ github.ref_type == 'tag' }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yaml around lines 84 - 100, Update the npm-publish job
to declare an explicit least-privilege permissions block with contents: read for
checkout, and add no broader GitHub permissions unless required by
ory/ci/checkout@master. Keep the existing NPM_TOKEN publishing flow unchanged.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/ci.yaml:
- Around line 84-100: Update the npm-publish job to declare an explicit
least-privilege permissions block with contents: read for checkout, and add no
broader GitHub permissions unless required by ory/ci/checkout@master. Keep the
existing NPM_TOKEN publishing flow unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e35d0d59-0483-4d0f-bb3b-8cb6d16a2b6e

📥 Commits

Reviewing files that changed from the base of the PR and between a66a872 and 485e035.

📒 Files selected for processing (1)
  • .github/workflows/ci.yaml

@jonas-jonas jonas-jonas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty cool, much neater than binwrap. I like, let's try it.

@alnr
alnr merged commit 3e534d2 into master Jul 22, 2026
30 of 32 checks passed
@alnr
alnr deleted the alnr/npm-platform-packages branch July 22, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

@ory/cli and npm audit

2 participants