feat: add release infrastructure with cross-compiled binaries - #31
feat: add release infrastructure with cross-compiled binaries#31wyattjoh wants to merge 44 commits into
Conversation
dd1aedc to
4ca8ddc
Compare
78e805e to
463b486
Compare
e977238 to
3b0dbf9
Compare
📝 WalkthroughWalkthroughThis pull request restructures the Clerk CLI from a single package into a Bun-based monorepo with separate 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📝 Coding Plan
Comment Tip You can customize the high-level summary generated by CodeRabbit.Configure the |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/release.yml:
- Around line 11-15: The workflow-level permissions currently grant contents:
write, pull-requests: write, and id-token: write to all jobs; change the default
permissions block so the workflow-level permissions are read-only (e.g.,
contents: read, pull-requests: read, id-token: read) and move any write scopes
down into only the specific job definitions that require them (for example add
contents: write or id-token: write to the release-please and canary-version job
permission blocks only), and apply the same narrowing change to snapshot.yml by
removing workflow-level id-token: write and pull-requests: write and granting
those write scopes only on the job(s) that actually need them.
In @.github/workflows/smoke-test.yml:
- Around line 28-62: The script currently writes the matrix variable with a
simple echo which truncates at the first newline; change the final write to use
the GitHub Actions multiline output heredoc form for $GITHUB_OUTPUT: emit the
header line using the output name and a unique delimiter, then write the full
contents of the matrix variable, then close with the delimiter so the entire
JSON (including embedded newlines) is preserved for
fromJSON(needs.resolve-matrix.outputs.matrix). Specifically, replace the simple
echo of matrix with a three-step heredoc-style write that uses the matrix
variable name and a delimiter (e.g., EOF) so the full multiline JSON is appended
correctly to $GITHUB_OUTPUT.
In @.github/workflows/snapshot.yml:
- Around line 137-150: The heredoc used in the gh pr comment step is using
"<<EOF" which requires the terminator to be at column 1 and breaks YAML
indentation; change the heredoc opener to "<<-EOF" so the body may be indented
within the YAML block and also ensure the closing "EOF" terminator is dedented
consistently (or left at column 1) to match "<<-EOF"; update the gh pr comment
invocation that constructs the message (the command invoking gh pr comment "${{
github.event.issue.number }}" --repo "${GH_REPO}" --body "$(cat <<EOF ... EOF
)") to use "<<-EOF" and adjust the terminator indentation accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: bfb3a452-88aa-460d-a027-906f3e31221b
⛔ Files ignored due to path filters (2)
bun.lockis excluded by!**/*.lockpackages/cli-core/mocks/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (116)
.github/workflows/build-binaries.yml.github/workflows/ci.yml.github/workflows/release.yml.github/workflows/smoke-test.yml.github/workflows/snapshot.yml.gitignore.oxlintrc.json.release-please-manifest.jsonCLAUDE.mdLICENSEREADME.mddocs/releasing.mdpackage.jsonpackages/cli-core/mocks/index.htmlpackages/cli-core/mocks/package.jsonpackages/cli-core/mocks/src/main.tspackages/cli-core/mocks/src/style.csspackages/cli-core/mocks/tsconfig.jsonpackages/cli-core/package.jsonpackages/cli-core/src/cli-program.tspackages/cli-core/src/cli.tspackages/cli-core/src/commands/api/README.mdpackages/cli-core/src/commands/api/bapi.test.tspackages/cli-core/src/commands/api/bapi.tspackages/cli-core/src/commands/api/catalog.test.tspackages/cli-core/src/commands/api/catalog.tspackages/cli-core/src/commands/api/index.test.tspackages/cli-core/src/commands/api/index.tspackages/cli-core/src/commands/api/interactive.test.tspackages/cli-core/src/commands/api/interactive.tspackages/cli-core/src/commands/api/ls.test.tspackages/cli-core/src/commands/api/ls.tspackages/cli-core/src/commands/auth/README.mdpackages/cli-core/src/commands/auth/login.test.tspackages/cli-core/src/commands/auth/login.tspackages/cli-core/src/commands/auth/logout.test.tspackages/cli-core/src/commands/auth/logout.tspackages/cli-core/src/commands/config/README.mdpackages/cli-core/src/commands/config/pull.test.tspackages/cli-core/src/commands/config/pull.tspackages/cli-core/src/commands/config/push.test.tspackages/cli-core/src/commands/config/push.tspackages/cli-core/src/commands/config/schema.test.tspackages/cli-core/src/commands/config/schema.tspackages/cli-core/src/commands/deploy/README.mdpackages/cli-core/src/commands/deploy/index.test.tspackages/cli-core/src/commands/deploy/index.tspackages/cli-core/src/commands/doctor/README.mdpackages/cli-core/src/commands/doctor/checks.tspackages/cli-core/src/commands/doctor/context.test.tspackages/cli-core/src/commands/doctor/context.tspackages/cli-core/src/commands/doctor/doctor.test.tspackages/cli-core/src/commands/doctor/format.tspackages/cli-core/src/commands/doctor/index.tspackages/cli-core/src/commands/doctor/types.tspackages/cli-core/src/commands/env/README.mdpackages/cli-core/src/commands/env/pull.test.tspackages/cli-core/src/commands/env/pull.tspackages/cli-core/src/commands/init/README.mdpackages/cli-core/src/commands/init/index.tspackages/cli-core/src/commands/link/README.mdpackages/cli-core/src/commands/link/index.test.tspackages/cli-core/src/commands/link/index.tspackages/cli-core/src/commands/unlink/README.mdpackages/cli-core/src/commands/unlink/index.test.tspackages/cli-core/src/commands/unlink/index.tspackages/cli-core/src/commands/whoami/README.mdpackages/cli-core/src/commands/whoami/index.test.tspackages/cli-core/src/commands/whoami/index.tspackages/cli-core/src/globals.d.tspackages/cli-core/src/lib/auth-server.test.tspackages/cli-core/src/lib/auth-server.tspackages/cli-core/src/lib/autolink.test.tspackages/cli-core/src/lib/autolink.tspackages/cli-core/src/lib/color.tspackages/cli-core/src/lib/config.test.tspackages/cli-core/src/lib/config.tspackages/cli-core/src/lib/constants.tspackages/cli-core/src/lib/credential-store.test.tspackages/cli-core/src/lib/credential-store.tspackages/cli-core/src/lib/dotenv.test.tspackages/cli-core/src/lib/dotenv.tspackages/cli-core/src/lib/errors.tspackages/cli-core/src/lib/framework.test.tspackages/cli-core/src/lib/framework.tspackages/cli-core/src/lib/git.test.tspackages/cli-core/src/lib/git.tspackages/cli-core/src/lib/pkce.test.tspackages/cli-core/src/lib/pkce.tspackages/cli-core/src/lib/plapi.test.tspackages/cli-core/src/lib/plapi.tspackages/cli-core/src/lib/token-exchange.test.tspackages/cli-core/src/lib/token-exchange.tspackages/cli-core/src/mode.test.tspackages/cli-core/src/mode.tspackages/cli-core/src/test/integration/agent-mode.test.tspackages/cli-core/src/test/integration/api-queries.test.tspackages/cli-core/src/test/integration/auth-lifecycle.test.tspackages/cli-core/src/test/integration/config-management.test.tspackages/cli-core/src/test/integration/config-put.test.tspackages/cli-core/src/test/integration/deploy-to-prod.test.tspackages/cli-core/src/test/integration/dry-run.test.tspackages/cli-core/src/test/integration/env-merge.test.tspackages/cli-core/src/test/integration/error-recovery.test.tspackages/cli-core/src/test/integration/onboard.test.tspackages/cli-core/src/test/integration/switch-apps.test.tspackages/cli-core/src/test/lib/http.tspackages/cli-core/src/test/lib/setup.tspackages/cli-core/src/test/stubs.tspackages/cli-core/tsconfig.jsonpackages/cli/LICENSEpackages/cli/bin/clerkpackages/cli/package.jsonrelease-please-config.jsonscripts/releaser/index.tsscripts/releaser/targets.ts
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
.github/workflows/snapshot.yml (1)
136-149:⚠️ Potential issue | 🔴 CriticalHeredoc breaks YAML block scalar parsing.
The heredoc content at lines 137-148 is flush-left (no indentation), which violates YAML block scalar requirements. The YAML parser fails at line 140 because it interprets
npm install -g clerk@...as YAML syntax due to the colon.Use
<<-EOF(with hyphen) which strips leading tabs, allowing the heredoc body to be indented within the YAML block:Suggested fix
- gh pr comment "${{ github.event.issue.number }}" --repo "${GH_REPO}" --body "$(cat <<EOF -## Snapshot published - -\`\`\`sh -npm install -g clerk@${SNAPSHOT_VERSION} -\`\`\` - -| Package | Version | -|---------|---------| -| \`clerk\` | \`${SNAPSHOT_VERSION}\` | - -> Published from ${short_sha} -EOF - )" + gh pr comment "${{ github.event.issue.number }}" --repo "${GH_REPO}" --body "$(cat <<-EOF + ## Snapshot published + + \`\`\`sh + npm install -g clerk@${SNAPSHOT_VERSION} + \`\`\` + + | Package | Version | + |---------|---------| + | \`clerk\` | \`${SNAPSHOT_VERSION}\` | + + > Published from ${short_sha} + EOF + )"Note: The indentation in the heredoc body must use tabs (not spaces) for
<<-EOFto strip them correctly.,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.github/workflows/snapshot.yml around lines 136 - 149, The YAML block scalar fails because the heredoc body is flush-left; change the heredoc opener from "EOF" to "<<-EOF" for the gh pr comment invocation so leading tabs can be stripped, and then indent the heredoc body using tabs (not spaces) so the YAML parser treats the block correctly; update the heredoc delimiter "EOF" accordingly in the gh pr comment command surrounding the npm/clerk table and short_sha content.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/snapshot.yml:
- Around line 173-176: The PR comment body passed to the gh CLI (the gh pr
comment invocation) currently includes unintended leading spaces because of the
line continuation; update the body string so the "The snapshot publish workflow
failed..." line has no leading spaces relative to the header (either put the
entire message on a single quoted line or use a here-doc/explicit newline
without leading spaces) to ensure the comment renders without the extra
indentation.
---
Duplicate comments:
In @.github/workflows/snapshot.yml:
- Around line 136-149: The YAML block scalar fails because the heredoc body is
flush-left; change the heredoc opener from "EOF" to "<<-EOF" for the gh pr
comment invocation so leading tabs can be stripped, and then indent the heredoc
body using tabs (not spaces) so the YAML parser treats the block correctly;
update the heredoc delimiter "EOF" accordingly in the gh pr comment command
surrounding the npm/clerk table and short_sha content.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 17777c0a-8cfe-4ee8-b1c0-6542c9168329
📒 Files selected for processing (3)
.github/workflows/release.yml.github/workflows/smoke-test.yml.github/workflows/snapshot.yml
| type: string | ||
| description: "Prefix for artifact names (e.g. 'clerk', 'clerk-canary', 'clerk-snapshot')" | ||
|
|
||
| jobs: |
There was a problem hiding this comment.
Is there a practical benefit to parallelizing these? For a larger project I could see the argument, but these builds should be really quick.
I expect the overhead of the GH actions setup here to build / upload / download the artifacts might end up taking longer than doing everything inline.
Restructure into bun workspace monorepo, add npm wrapper package with platform binary shim, release-please configuration, cross-compilation release workflow, native smoke tests, musl/Alpine support, and MIT license.
Add canary releases on every push to main (when no stable release is created) and snapshot releases triggered by `!snapshot` PR comments. The releaser script now accepts --tag and --version flags to support publishing pre-release versions to npm. Also fixes pre-existing lint failures: oxlintrc override globs and dead variable declarations in link tests.
Bun on Linux treats extensionless imports (e.g., "../../lib/config") and
explicit imports ("../../lib/config.ts") as separate module instances.
This caused _setConfigDir() called in tests to modify a different module
instance than the one used by production code, making resolveProfile()
always return undefined in CI.
Bun on Linux treats different import specifiers for the same file (e.g., "./mode.js", "./mode", "./mode.ts") as separate module instances. This broke mock.module() in tests and _setConfigDir() state sharing, causing 86 test failures in CI. Normalize all relative imports across production and test code to consistently use .ts extensions.
Bun's mock.module() leaks between test files when run in a single process (oven-sh/bun#12823), causing 89 failures on Linux with fresh node_modules. Run each test file in its own bun test invocation.
- Fix shell injection in snapshot workflow by using env var instead of direct interpolation of comment body - Fix docs URL check in CliError (docs.clerk.com -> clerk.com/docs) - Deduplicate build matrix into reusable build-binaries.yml workflow - Use specific paths in .oxlintrc.json override instead of ** globs - Fix heredoc indentation in snapshot PR comment - Remove unnecessary async wrapper on login action - Use path.join() instead of template literal for path construction - Add musl detection fallback warning in CLI launcher - Rename root package from cli-new to @clerk/cli-workspace
- Use NPM_CONFIG_PROVENANCE env var instead of --provenance flag for trusted publishing, matching the JS monorepo pattern - Add explicit id-token: write permissions to all publish jobs - Switch snapshot versions from SHA-based to datetime-based format (x.y.z-name.v<YYYYMMDDHHmmss>) for monotonic semver ordering - Add bun run build to CI for pre-merge bundle validation - Update release documentation to reflect all changes
Update releasing.md to reflect the build-binaries.yml reusable workflow extraction and document musl Docker-based smoke testing. Add reference comments to the bin/clerk shim explaining the musl detection approach.
- Regenerate bun.lock with correct workspace name - Add canary smoke test (linux-x64) before publish - Add explicit files field to platform packages - Add root-level build:compile script
- Add linux-x64 smoke test to snapshot workflow before publishing - Guard npm shim signal forwarding with `exited` flag to prevent double-kill after child process exits - Fix targets.ts comment to reference build-binaries.yml (not release.yml) - Document win32-arm64 as best-effort (untested) in workflow and releasing docs
Switch publish jobs from Blacksmith to GitHub-hosted runners (ubuntu-latest) to enable npm OIDC trusted publishing. This eliminates the NPM_TOKEN secret and authenticates via GitHub's OIDC provider instead. - Move publish-npm, canary-publish, snapshot publish, and upload-github-assets jobs to ubuntu-latest - Add npm upgrade step (>= 11.5.1 required for OIDC) - Remove NODE_AUTH_TOKEN and NPM_CONFIG_PROVENANCE env vars - Keep Blacksmith for build, CI, and compute-heavy jobs - Update releasing docs with OIDC details and first-publish caveat
Replaces the async spawn with signal forwarding, event handlers, and exited guard with a single spawnSync call. The child process receives terminal signals directly via the shared process group.
The heredoc patterns used for PR comments placed content at column 1 inside YAML literal block scalars, which is invalid YAML per the spec. While GitHub Actions' Go-based parser tolerated this, strict parsers (PyYAML, yaml npm package) reject it. Switch to echo statements writing to a temp file with --body-file to avoid YAML/shell quoting conflicts entirely.
- check-release.ts: distinguish npm E404 from network errors - canary.ts/snapshot.ts: throw on failure instead of stdout success=0 - release.yml/snapshot.yml: simplify version steps (scripts exit non-zero) - targets.ts: update stale comment referencing removed matrix
d08433f to
0847f26
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/build-binaries.yml:
- Around line 26-31: The workflow currently runs a single bun install on Linux
and then cross-compiles all targets with bun run scripts/build.ts (using
CLI_VERSION), causing platform-specific optionalDependencies like
`@napi-rs/keyring-`* to be missing for non-Linux targets; change the job to
install and build each target on a matching runner (create a matrix of targets
mapped to appropriate runner OS/arch), run bun install --frozen-lockfile on that
runner before invoking bun run scripts/build.ts
--version="${CLI_VERSION}"/--target=<target> so the platform-specific native
addons are present and embedded during build; ensure the build step references
the same CLI_VERSION env and that each matrix entry runs its own install+build
pair rather than reusing a single install.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 3a78472f-b86e-4bd1-9aef-48cc8079220f
⛔ Files ignored due to path filters (2)
bun.lockis excluded by!**/*.lockpackages/cli-core/mocks/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (120)
.changeset/README.md.changeset/config.json.github/workflows/build-binaries.yml.github/workflows/ci.yml.github/workflows/release.yml.github/workflows/smoke-test.yml.github/workflows/snapshot.yml.gitignore.oxlintrc.jsonCLAUDE.mdLICENSEREADME.mddocs/releasing.mdpackage.jsonpackages/cli-core/mocks/index.htmlpackages/cli-core/mocks/package.jsonpackages/cli-core/mocks/src/main.tspackages/cli-core/mocks/src/style.csspackages/cli-core/mocks/tsconfig.jsonpackages/cli-core/package.jsonpackages/cli-core/src/cli-program.tspackages/cli-core/src/cli.tspackages/cli-core/src/commands/api/README.mdpackages/cli-core/src/commands/api/bapi.test.tspackages/cli-core/src/commands/api/bapi.tspackages/cli-core/src/commands/api/catalog.test.tspackages/cli-core/src/commands/api/catalog.tspackages/cli-core/src/commands/api/index.test.tspackages/cli-core/src/commands/api/index.tspackages/cli-core/src/commands/api/interactive.test.tspackages/cli-core/src/commands/api/interactive.tspackages/cli-core/src/commands/api/ls.test.tspackages/cli-core/src/commands/api/ls.tspackages/cli-core/src/commands/auth/README.mdpackages/cli-core/src/commands/auth/login.test.tspackages/cli-core/src/commands/auth/login.tspackages/cli-core/src/commands/auth/logout.test.tspackages/cli-core/src/commands/auth/logout.tspackages/cli-core/src/commands/config/README.mdpackages/cli-core/src/commands/config/pull.test.tspackages/cli-core/src/commands/config/pull.tspackages/cli-core/src/commands/config/push.test.tspackages/cli-core/src/commands/config/push.tspackages/cli-core/src/commands/config/schema.test.tspackages/cli-core/src/commands/config/schema.tspackages/cli-core/src/commands/deploy/README.mdpackages/cli-core/src/commands/deploy/index.test.tspackages/cli-core/src/commands/deploy/index.tspackages/cli-core/src/commands/doctor/README.mdpackages/cli-core/src/commands/doctor/checks.tspackages/cli-core/src/commands/doctor/context.test.tspackages/cli-core/src/commands/doctor/context.tspackages/cli-core/src/commands/doctor/doctor.test.tspackages/cli-core/src/commands/doctor/format.tspackages/cli-core/src/commands/doctor/index.tspackages/cli-core/src/commands/doctor/types.tspackages/cli-core/src/commands/env/README.mdpackages/cli-core/src/commands/env/pull.test.tspackages/cli-core/src/commands/env/pull.tspackages/cli-core/src/commands/init/README.mdpackages/cli-core/src/commands/init/index.tspackages/cli-core/src/commands/link/README.mdpackages/cli-core/src/commands/link/index.test.tspackages/cli-core/src/commands/link/index.tspackages/cli-core/src/commands/unlink/README.mdpackages/cli-core/src/commands/unlink/index.test.tspackages/cli-core/src/commands/unlink/index.tspackages/cli-core/src/commands/whoami/README.mdpackages/cli-core/src/commands/whoami/index.test.tspackages/cli-core/src/commands/whoami/index.tspackages/cli-core/src/globals.d.tspackages/cli-core/src/lib/auth-server.test.tspackages/cli-core/src/lib/auth-server.tspackages/cli-core/src/lib/autolink.test.tspackages/cli-core/src/lib/autolink.tspackages/cli-core/src/lib/color.tspackages/cli-core/src/lib/config.test.tspackages/cli-core/src/lib/config.tspackages/cli-core/src/lib/constants.tspackages/cli-core/src/lib/credential-store.test.tspackages/cli-core/src/lib/credential-store.tspackages/cli-core/src/lib/dotenv.test.tspackages/cli-core/src/lib/dotenv.tspackages/cli-core/src/lib/errors.tspackages/cli-core/src/lib/framework.test.tspackages/cli-core/src/lib/framework.tspackages/cli-core/src/lib/git.test.tspackages/cli-core/src/lib/git.tspackages/cli-core/src/lib/pkce.test.tspackages/cli-core/src/lib/pkce.tspackages/cli-core/src/lib/plapi.test.tspackages/cli-core/src/lib/plapi.tspackages/cli-core/src/lib/token-exchange.test.tspackages/cli-core/src/lib/token-exchange.tspackages/cli-core/src/mode.test.tspackages/cli-core/src/mode.tspackages/cli-core/src/test/integration/agent-mode.test.tspackages/cli-core/src/test/integration/api-queries.test.tspackages/cli-core/src/test/integration/auth-lifecycle.test.tspackages/cli-core/src/test/integration/config-management.test.tspackages/cli-core/src/test/integration/config-put.test.tspackages/cli-core/src/test/integration/deploy-to-prod.test.tspackages/cli-core/src/test/integration/dry-run.test.tspackages/cli-core/src/test/integration/env-merge.test.tspackages/cli-core/src/test/integration/error-recovery.test.tspackages/cli-core/src/test/integration/onboard.test.tspackages/cli-core/src/test/integration/switch-apps.test.tspackages/cli-core/src/test/lib/http.tspackages/cli-core/src/test/lib/setup.tspackages/cli-core/src/test/stubs.tspackages/cli-core/tsconfig.jsonpackages/cli/LICENSEpackages/cli/bin/clerkpackages/cli/package.jsonscripts/build.tsscripts/check-release.tsscripts/lib/npm.tsscripts/releaser/index.tsscripts/releaser/targets.tsscripts/snapshot.ts
| - run: bun install --frozen-lockfile | ||
|
|
||
| - name: Build all targets | ||
| env: | ||
| CLI_VERSION: ${{ inputs.version }} | ||
| run: bun run scripts/build.ts --version="${CLI_VERSION}" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find and examine the build script
find . -name "build.ts" -o -name "build.js" | head -20Repository: clerk/cli-new
Length of output: 75
🏁 Script executed:
# Check the workflow file structure to understand how targets are built
cat -n .github/workflows/build-binaries.yml | head -50Repository: clerk/cli-new
Length of output: 1722
🏁 Script executed:
# Find and examine the cli-core package.json
find . -path "*/packages/cli-core/package.json"Repository: clerk/cli-new
Length of output: 89
🏁 Script executed:
cat -n ./scripts/build.tsRepository: clerk/cli-new
Length of output: 2701
🏁 Script executed:
cat -n ./packages/cli-core/package.json | head -40Repository: clerk/cli-new
Length of output: 1105
🏁 Script executed:
cat -n ./scripts/releaser/targets.tsRepository: clerk/cli-new
Length of output: 2532
🏁 Script executed:
grep -r "@napi-rs/keyring" ./packages/cli-core/src/ --include="*.ts" -nRepository: clerk/cli-new
Length of output: 799
🌐 Web query:
@napi-rs/keyring package structure platform-specific
💡 Result:
@napi-rs/keyring follows the common napi-rs “meta package + per-platform binary packages” layout:
-
Main (cross-platform) package:
@napi-rs/keyring- Ships JS/TS files (
index.js,index.d.ts, etc.). - Declares platform-specific packages under
optionalDependencies, including (example list from v1.1.3):
@napi-rs/keyring-darwin-x64,@napi-rs/keyring-darwin-arm64,@napi-rs/keyring-win32-x64-msvc,@napi-rs/keyring-win32-ia32-msvc,@napi-rs/keyring-win32-arm64-msvc,@napi-rs/keyring-linux-x64-gnu,@napi-rs/keyring-linux-x64-musl,@napi-rs/keyring-linux-arm64-gnu,@napi-rs/keyring-linux-arm64-musl,@napi-rs/keyring-linux-arm-gnueabihf,@napi-rs/keyring-freebsd-x64. (app.unpkg.com)
- Ships JS/TS files (
-
Runtime loader (platform selection):
index.jsdetectsprocess.platform+process.arch, and on Linux additionally detects musl vs glibc (usingprocess.reportwhen available, otherwise inspectingldd). It then loads either a local.nodefile (if present) or requires the matching platform package such as@napi-rs/keyring-linux-x64-gnu/@napi-rs/keyring-linux-x64-musl. (app.unpkg.com) -
Per-platform packages: each
@napi-rs/keyring-<platform>package is essentially “just the native binary for that target” (e.g.@napi-rs/keyring-linux-x64-gnuis thex86_64-unknown-linux-gnubinary). (socket.dev) -
Install behavior: because those binaries are in
optionalDependencies, package managers will typically only install the one that matches the current OS/CPU (and libc variant on Linux). (napi.rs)
Citations:
- 1: https://app.unpkg.com/%40napi-rs/keyring%401.1.3/files/package.json
- 2: https://app.unpkg.com/%40napi-rs/keyring%401.1.0/files/index.js
- 3: https://socket.dev/npm/package/%40napi-rs/keyring-linux-x64-gnu
- 4: https://napi.rs/blog/announce-v3?utm_source=openai
🌐 Web query:
Bun build --compile --target cross-compilation native modules
💡 Result:
To cross-compile a single-file executable with Bun, you use bun build --compile plus a Bun target triple:
bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp
# examples: bun-linux-arm64, bun-windows-x64, bun-darwin-arm64, etc.Bun documents these --target values and shows cross-compiling from one OS/arch to another. [1]
Native modules (.node / N-API addons) with --compile
- Bun can embed
.nodefiles into the produced executable, but it’s essentially embedding/packaging: you still need a.nodebinary built for the same OS/arch as the executable target. [2] - Make sure the addon is directly required, e.g.:
Bun warns that if the addon is loaded indirectly via tools like
const addon = require("./addon.node");
@mapbox/node-pre-gyp, it may not bundle correctly unless the.nodeis directly required. [2]
Practical cross-compilation workflow for native addons
- Build/obtain the addon for the target (e.g., Linux x64):
- Prefer packages that publish prebuilt binaries for your target.
- Or build the addon in a target environment (common: Linux Docker for
bun-linux-x64, Windows runner forbun-windows-x64, etc.).
- Ensure your dependency install selects the right platform artifacts when needed:
This tells Bun to select packages for that platform (useful for cross-platform builds). [3]
bun install --os=linux --cpu=x64
- Run the compile step with the same target:
bun build --compile --target=bun-linux-x64 ./index.ts --outfile myapp
Key point: --target cross-compiles the Bun executable, but your native addon must already be built for that target (and ideally be directly required so Bun can embed it). [1][2]
Sources:
[1] Bun docs: single-file executable + cross-compilation targets (--compile, --target=...) (bun.sh)
[2] Bun docs: “Embed N-API Addons” / embedding .node files + node-pre-gyp caveat (bun.sh)
[3] Bun docs: bun install --cpu/--os for platform-specific package selection (bun.sh)
Citations:
- 1: https://bun.sh/docs/bundler/executables
- 2: https://bun.sh/docs/bundler/executables
- 3: https://bun.sh/docs/cli/install
🏁 Script executed:
grep -r "credential-store\|from.*lib" ./packages/cli-core/src/cli.ts -n | head -20Repository: clerk/cli-new
Length of output: 103
🏁 Script executed:
grep -r "credential-store" ./packages/cli-core/src/ --include="*.ts" | grep -v "\.test\.ts" -cRepository: clerk/cli-new
Length of output: 58
🏁 Script executed:
rg "credential-store" ./packages/cli-core/src/ --type ts --files-with-matches | grep -v testRepository: clerk/cli-new
Length of output: 335
🏁 Script executed:
grep -A 2 "credential-store" ./packages/cli-core/src/commands/auth/login.ts | head -10Repository: clerk/cli-new
Length of output: 255
🏁 Script executed:
head -30 ./packages/cli-core/src/commands/auth/login.ts | grep -A 2 "credential-store"Repository: clerk/cli-new
Length of output: 255
Build each target from a matching install and runner.
The workflow runs bun install --frozen-lockfile once on a Linux host (line 26), then cross-compiles all 8 targets (darwin-arm64, darwin-x64, linux-, win32-) from that single install (line 31). @napi-rs/keyring declares platform-specific binaries as optionalDependencies (e.g., @napi-rs/keyring-darwin-arm64, @napi-rs/keyring-linux-x64-gnu); during bun install on Linux, only Linux variants are installed. When bun build --compile --target=bun-darwin-arm64 cross-compiles, the binary lacks the correct native keyring addon, and at runtime credential-store falls back to plaintext file storage instead. The current file/--version checks pass because they verify only binary format, not functionality. Build each target on its matching runner before publishing to ensure native addons are available for embedding.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/build-binaries.yml around lines 26 - 31, The workflow
currently runs a single bun install on Linux and then cross-compiles all targets
with bun run scripts/build.ts (using CLI_VERSION), causing platform-specific
optionalDependencies like `@napi-rs/keyring-`* to be missing for non-Linux
targets; change the job to install and build each target on a matching runner
(create a matrix of targets mapped to appropriate runner OS/arch), run bun
install --frozen-lockfile on that runner before invoking bun run
scripts/build.ts --version="${CLI_VERSION}"/--target=<target> so the
platform-specific native addons are present and embedded during build; ensure
the build step references the same CLI_VERSION env and that each matrix entry
runs its own install+build pair rather than reusing a single install.
Summary
src/layout to a Bun workspace monorepo with two packages —clerk(public npm wrapper) and@clerk/cli-core(private, core implementation)@clerk/cli-{target})scripts/releaser/) that generates platform packages with correctos/cpu/libcfields and publishes them to npm with provenancecanary(from main, subset smoke-tested) andsnapshot(from PR branches via!snapshotcomment) release workflows for pre-release testingpackages/cli/bin/clerkdetects platform/arch/libc at runtime and resolves the correct native binary viaoptionalDependencies, sonpx clerkjust works without requiring Node.js at runtimeclerk(unscoped) on npm, while platform-specific binary packages remain scoped under@clerk/cli-{target}prepublishOnlyguards, and OIDC trusted publishing (no stored npm tokens)Test plan
bun installresolves correctly from rootbun testfrom root — all tests should passbun run devstill works for local developmentbun run lintandbun run format:check— should pass cleanscripts/releaser/targets.tspackages/cli/bin/clerk) for platform detection edge casesrelease.yml) and snapshot (snapshot.yml) workflow triggersclerkand platform packages as@clerk/cli-{target}Summary by CodeRabbit
Release Notes
New Features
help [command]command to display command-specific help--verboseglobal flag for detailed error outputChores