Skip to content

feat: add release infrastructure with cross-compiled binaries - #31

Closed
wyattjoh wants to merge 44 commits into
mainfrom
wyattjoh/compile
Closed

feat: add release infrastructure with cross-compiled binaries#31
wyattjoh wants to merge 44 commits into
mainfrom
wyattjoh/compile

Conversation

@wyattjoh

@wyattjoh wyattjoh commented Mar 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Monorepo restructure: Migrated from flat src/ layout to a Bun workspace monorepo with two packages — clerk (public npm wrapper) and @clerk/cli-core (private, core implementation)
  • Cross-compiled binary distribution: Added a GitHub Actions release workflow that compiles standalone Bun binaries for 8 platform targets (macOS arm64/x64, Linux arm64/x64 glibc+musl, Windows arm64/x64), runs native smoke tests, and publishes platform-specific npm packages (@clerk/cli-{target})
  • Release automation: Integrated release-please for conventional-commit-based versioning, added a releaser script (scripts/releaser/) that generates platform packages with correct os/cpu/libc fields and publishes them to npm with provenance
  • Canary and snapshot channels: Added canary (from main, subset smoke-tested) and snapshot (from PR branches via !snapshot comment) release workflows for pre-release testing
  • Smart npm shim: packages/cli/bin/clerk detects platform/arch/libc at runtime and resolves the correct native binary via optionalDependencies, so npx clerk just works without requiring Node.js at runtime
  • Unscoped wrapper package: The wrapper publishes as clerk (unscoped) on npm, while platform-specific binary packages remain scoped under @clerk/cli-{target}
  • Safety guardrails: Binary format verification at build time, smoke tests on native runners before stable publishing, idempotent publish checks, prepublishOnly guards, and OIDC trusted publishing (no stored npm tokens)
  • Added MIT license

Test plan

  • Verify bun install resolves correctly from root
  • Run bun test from root — all tests should pass
  • Verify bun run dev still works for local development
  • Run bun run lint and bun run format:check — should pass clean
  • Spot-check release workflow YAML against the target matrix in scripts/releaser/targets.ts
  • Review the npm shim (packages/cli/bin/clerk) for platform detection edge cases
  • Review canary (release.yml) and snapshot (snapshot.yml) workflow triggers
  • Verify releaser publishes wrapper as clerk and platform packages as @clerk/cli-{target}

Summary by CodeRabbit

Release Notes

  • New Features

    • Added pre-compiled binary distribution for multiple platforms (macOS ARM64/x64, Linux ARM64/x64 with glibc and musl variants, Windows ARM64/x64)
    • Introduced canary and snapshot release channels for early access and testing
    • Added help [command] command to display command-specific help
    • Added --verbose global flag for detailed error output
  • Chores

    • Implemented automated release pipeline with versioning and changelog management
    • Enhanced build and deployment infrastructure

@wyattjoh
wyattjoh force-pushed the wyattjoh/compile branch 2 times, most recently from dd1aedc to 4ca8ddc Compare March 13, 2026 23:34
@wyattjoh
wyattjoh marked this pull request as ready for review March 16, 2026 17:42
@wyattjoh
wyattjoh force-pushed the wyattjoh/compile branch 2 times, most recently from 78e805e to 463b486 Compare March 16, 2026 18:44
@coderabbitai

coderabbitai Bot commented Mar 19, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request restructures the Clerk CLI from a single package into a Bun-based monorepo with separate @clerk/cli-core and @clerk/cli packages. New GitHub Actions workflows are introduced for building binaries, running smoke tests, and managing stable/canary/snapshot releases. TypeScript import specifiers throughout the codebase are updated to include explicit .ts file extensions. Build and release automation scripts are added using Bun and Node.js. Configuration files including tsconfig.json, package.json manifests, Changesets config, and documentation are added or updated to reflect the new monorepo structure and release process.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat: add release infrastructure with cross-compiled binaries' directly and accurately describes the main change: introducing release infrastructure including cross-compilation support for multiple binary targets.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

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

Tip

You can customize the high-level summary generated by CodeRabbit.

Configure the reviews.high_level_summary_instructions setting to provide custom instructions for generating the high-level summary.

@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: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 0ca8332 and 3b0dbf9.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • packages/cli-core/mocks/bun.lock is 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.json
  • CLAUDE.md
  • LICENSE
  • README.md
  • docs/releasing.md
  • package.json
  • packages/cli-core/mocks/index.html
  • packages/cli-core/mocks/package.json
  • packages/cli-core/mocks/src/main.ts
  • packages/cli-core/mocks/src/style.css
  • packages/cli-core/mocks/tsconfig.json
  • packages/cli-core/package.json
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/cli.ts
  • packages/cli-core/src/commands/api/README.md
  • packages/cli-core/src/commands/api/bapi.test.ts
  • packages/cli-core/src/commands/api/bapi.ts
  • packages/cli-core/src/commands/api/catalog.test.ts
  • packages/cli-core/src/commands/api/catalog.ts
  • packages/cli-core/src/commands/api/index.test.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/commands/api/interactive.test.ts
  • packages/cli-core/src/commands/api/interactive.ts
  • packages/cli-core/src/commands/api/ls.test.ts
  • packages/cli-core/src/commands/api/ls.ts
  • packages/cli-core/src/commands/auth/README.md
  • packages/cli-core/src/commands/auth/login.test.ts
  • packages/cli-core/src/commands/auth/login.ts
  • packages/cli-core/src/commands/auth/logout.test.ts
  • packages/cli-core/src/commands/auth/logout.ts
  • packages/cli-core/src/commands/config/README.md
  • packages/cli-core/src/commands/config/pull.test.ts
  • packages/cli-core/src/commands/config/pull.ts
  • packages/cli-core/src/commands/config/push.test.ts
  • packages/cli-core/src/commands/config/push.ts
  • packages/cli-core/src/commands/config/schema.test.ts
  • packages/cli-core/src/commands/config/schema.ts
  • packages/cli-core/src/commands/deploy/README.md
  • packages/cli-core/src/commands/deploy/index.test.ts
  • packages/cli-core/src/commands/deploy/index.ts
  • packages/cli-core/src/commands/doctor/README.md
  • packages/cli-core/src/commands/doctor/checks.ts
  • packages/cli-core/src/commands/doctor/context.test.ts
  • packages/cli-core/src/commands/doctor/context.ts
  • packages/cli-core/src/commands/doctor/doctor.test.ts
  • packages/cli-core/src/commands/doctor/format.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/doctor/types.ts
  • packages/cli-core/src/commands/env/README.md
  • packages/cli-core/src/commands/env/pull.test.ts
  • packages/cli-core/src/commands/env/pull.ts
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/index.ts
  • packages/cli-core/src/commands/link/README.md
  • packages/cli-core/src/commands/link/index.test.ts
  • packages/cli-core/src/commands/link/index.ts
  • packages/cli-core/src/commands/unlink/README.md
  • packages/cli-core/src/commands/unlink/index.test.ts
  • packages/cli-core/src/commands/unlink/index.ts
  • packages/cli-core/src/commands/whoami/README.md
  • packages/cli-core/src/commands/whoami/index.test.ts
  • packages/cli-core/src/commands/whoami/index.ts
  • packages/cli-core/src/globals.d.ts
  • packages/cli-core/src/lib/auth-server.test.ts
  • packages/cli-core/src/lib/auth-server.ts
  • packages/cli-core/src/lib/autolink.test.ts
  • packages/cli-core/src/lib/autolink.ts
  • packages/cli-core/src/lib/color.ts
  • packages/cli-core/src/lib/config.test.ts
  • packages/cli-core/src/lib/config.ts
  • packages/cli-core/src/lib/constants.ts
  • packages/cli-core/src/lib/credential-store.test.ts
  • packages/cli-core/src/lib/credential-store.ts
  • packages/cli-core/src/lib/dotenv.test.ts
  • packages/cli-core/src/lib/dotenv.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/lib/framework.test.ts
  • packages/cli-core/src/lib/framework.ts
  • packages/cli-core/src/lib/git.test.ts
  • packages/cli-core/src/lib/git.ts
  • packages/cli-core/src/lib/pkce.test.ts
  • packages/cli-core/src/lib/pkce.ts
  • packages/cli-core/src/lib/plapi.test.ts
  • packages/cli-core/src/lib/plapi.ts
  • packages/cli-core/src/lib/token-exchange.test.ts
  • packages/cli-core/src/lib/token-exchange.ts
  • packages/cli-core/src/mode.test.ts
  • packages/cli-core/src/mode.ts
  • packages/cli-core/src/test/integration/agent-mode.test.ts
  • packages/cli-core/src/test/integration/api-queries.test.ts
  • packages/cli-core/src/test/integration/auth-lifecycle.test.ts
  • packages/cli-core/src/test/integration/config-management.test.ts
  • packages/cli-core/src/test/integration/config-put.test.ts
  • packages/cli-core/src/test/integration/deploy-to-prod.test.ts
  • packages/cli-core/src/test/integration/dry-run.test.ts
  • packages/cli-core/src/test/integration/env-merge.test.ts
  • packages/cli-core/src/test/integration/error-recovery.test.ts
  • packages/cli-core/src/test/integration/onboard.test.ts
  • packages/cli-core/src/test/integration/switch-apps.test.ts
  • packages/cli-core/src/test/lib/http.ts
  • packages/cli-core/src/test/lib/setup.ts
  • packages/cli-core/src/test/stubs.ts
  • packages/cli-core/tsconfig.json
  • packages/cli/LICENSE
  • packages/cli/bin/clerk
  • packages/cli/package.json
  • release-please-config.json
  • scripts/releaser/index.ts
  • scripts/releaser/targets.ts

Comment thread .github/workflows/release.yml
Comment thread .github/workflows/smoke-test.yml Outdated
Comment thread .github/workflows/snapshot.yml Outdated

@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

♻️ Duplicate comments (1)
.github/workflows/snapshot.yml (1)

136-149: ⚠️ Potential issue | 🔴 Critical

Heredoc 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 <<-EOF to 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

📥 Commits

Reviewing files that changed from the base of the PR and between 3b0dbf9 and aa9e75c.

📒 Files selected for processing (3)
  • .github/workflows/release.yml
  • .github/workflows/smoke-test.yml
  • .github/workflows/snapshot.yml

Comment thread .github/workflows/snapshot.yml Outdated
@wyattjoh
wyattjoh requested review from kylemac and rafa-thayto March 19, 2026 17:51
type: string
description: "Prefix for artifact names (e.g. 'clerk', 'clerk-canary', 'clerk-snapshot')"

jobs:

@brkalow brkalow Mar 19, 2026

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.

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.

Comment thread docs/releasing.md
Comment thread docs/releasing.md Outdated
Comment thread docs/releasing.md Outdated
Comment thread docs/releasing.md
wyattjoh added 13 commits March 19, 2026 16:39
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.
wyattjoh added 18 commits March 19, 2026 16:39
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
@wyattjoh
wyattjoh requested a review from brkalow March 19, 2026 22:43

@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 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

📥 Commits

Reviewing files that changed from the base of the PR and between aa9e75c and 0847f26.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • packages/cli-core/mocks/bun.lock is 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.json
  • CLAUDE.md
  • LICENSE
  • README.md
  • docs/releasing.md
  • package.json
  • packages/cli-core/mocks/index.html
  • packages/cli-core/mocks/package.json
  • packages/cli-core/mocks/src/main.ts
  • packages/cli-core/mocks/src/style.css
  • packages/cli-core/mocks/tsconfig.json
  • packages/cli-core/package.json
  • packages/cli-core/src/cli-program.ts
  • packages/cli-core/src/cli.ts
  • packages/cli-core/src/commands/api/README.md
  • packages/cli-core/src/commands/api/bapi.test.ts
  • packages/cli-core/src/commands/api/bapi.ts
  • packages/cli-core/src/commands/api/catalog.test.ts
  • packages/cli-core/src/commands/api/catalog.ts
  • packages/cli-core/src/commands/api/index.test.ts
  • packages/cli-core/src/commands/api/index.ts
  • packages/cli-core/src/commands/api/interactive.test.ts
  • packages/cli-core/src/commands/api/interactive.ts
  • packages/cli-core/src/commands/api/ls.test.ts
  • packages/cli-core/src/commands/api/ls.ts
  • packages/cli-core/src/commands/auth/README.md
  • packages/cli-core/src/commands/auth/login.test.ts
  • packages/cli-core/src/commands/auth/login.ts
  • packages/cli-core/src/commands/auth/logout.test.ts
  • packages/cli-core/src/commands/auth/logout.ts
  • packages/cli-core/src/commands/config/README.md
  • packages/cli-core/src/commands/config/pull.test.ts
  • packages/cli-core/src/commands/config/pull.ts
  • packages/cli-core/src/commands/config/push.test.ts
  • packages/cli-core/src/commands/config/push.ts
  • packages/cli-core/src/commands/config/schema.test.ts
  • packages/cli-core/src/commands/config/schema.ts
  • packages/cli-core/src/commands/deploy/README.md
  • packages/cli-core/src/commands/deploy/index.test.ts
  • packages/cli-core/src/commands/deploy/index.ts
  • packages/cli-core/src/commands/doctor/README.md
  • packages/cli-core/src/commands/doctor/checks.ts
  • packages/cli-core/src/commands/doctor/context.test.ts
  • packages/cli-core/src/commands/doctor/context.ts
  • packages/cli-core/src/commands/doctor/doctor.test.ts
  • packages/cli-core/src/commands/doctor/format.ts
  • packages/cli-core/src/commands/doctor/index.ts
  • packages/cli-core/src/commands/doctor/types.ts
  • packages/cli-core/src/commands/env/README.md
  • packages/cli-core/src/commands/env/pull.test.ts
  • packages/cli-core/src/commands/env/pull.ts
  • packages/cli-core/src/commands/init/README.md
  • packages/cli-core/src/commands/init/index.ts
  • packages/cli-core/src/commands/link/README.md
  • packages/cli-core/src/commands/link/index.test.ts
  • packages/cli-core/src/commands/link/index.ts
  • packages/cli-core/src/commands/unlink/README.md
  • packages/cli-core/src/commands/unlink/index.test.ts
  • packages/cli-core/src/commands/unlink/index.ts
  • packages/cli-core/src/commands/whoami/README.md
  • packages/cli-core/src/commands/whoami/index.test.ts
  • packages/cli-core/src/commands/whoami/index.ts
  • packages/cli-core/src/globals.d.ts
  • packages/cli-core/src/lib/auth-server.test.ts
  • packages/cli-core/src/lib/auth-server.ts
  • packages/cli-core/src/lib/autolink.test.ts
  • packages/cli-core/src/lib/autolink.ts
  • packages/cli-core/src/lib/color.ts
  • packages/cli-core/src/lib/config.test.ts
  • packages/cli-core/src/lib/config.ts
  • packages/cli-core/src/lib/constants.ts
  • packages/cli-core/src/lib/credential-store.test.ts
  • packages/cli-core/src/lib/credential-store.ts
  • packages/cli-core/src/lib/dotenv.test.ts
  • packages/cli-core/src/lib/dotenv.ts
  • packages/cli-core/src/lib/errors.ts
  • packages/cli-core/src/lib/framework.test.ts
  • packages/cli-core/src/lib/framework.ts
  • packages/cli-core/src/lib/git.test.ts
  • packages/cli-core/src/lib/git.ts
  • packages/cli-core/src/lib/pkce.test.ts
  • packages/cli-core/src/lib/pkce.ts
  • packages/cli-core/src/lib/plapi.test.ts
  • packages/cli-core/src/lib/plapi.ts
  • packages/cli-core/src/lib/token-exchange.test.ts
  • packages/cli-core/src/lib/token-exchange.ts
  • packages/cli-core/src/mode.test.ts
  • packages/cli-core/src/mode.ts
  • packages/cli-core/src/test/integration/agent-mode.test.ts
  • packages/cli-core/src/test/integration/api-queries.test.ts
  • packages/cli-core/src/test/integration/auth-lifecycle.test.ts
  • packages/cli-core/src/test/integration/config-management.test.ts
  • packages/cli-core/src/test/integration/config-put.test.ts
  • packages/cli-core/src/test/integration/deploy-to-prod.test.ts
  • packages/cli-core/src/test/integration/dry-run.test.ts
  • packages/cli-core/src/test/integration/env-merge.test.ts
  • packages/cli-core/src/test/integration/error-recovery.test.ts
  • packages/cli-core/src/test/integration/onboard.test.ts
  • packages/cli-core/src/test/integration/switch-apps.test.ts
  • packages/cli-core/src/test/lib/http.ts
  • packages/cli-core/src/test/lib/setup.ts
  • packages/cli-core/src/test/stubs.ts
  • packages/cli-core/tsconfig.json
  • packages/cli/LICENSE
  • packages/cli/bin/clerk
  • packages/cli/package.json
  • scripts/build.ts
  • scripts/check-release.ts
  • scripts/lib/npm.ts
  • scripts/releaser/index.ts
  • scripts/releaser/targets.ts
  • scripts/snapshot.ts

Comment on lines +26 to +31
- run: bun install --frozen-lockfile

- name: Build all targets
env:
CLI_VERSION: ${{ inputs.version }}
run: bun run scripts/build.ts --version="${CLI_VERSION}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, find and examine the build script
find . -name "build.ts" -o -name "build.js" | head -20

Repository: 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 -50

Repository: 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.ts

Repository: clerk/cli-new

Length of output: 2701


🏁 Script executed:

cat -n ./packages/cli-core/package.json | head -40

Repository: clerk/cli-new

Length of output: 1105


🏁 Script executed:

cat -n ./scripts/releaser/targets.ts

Repository: clerk/cli-new

Length of output: 2532


🏁 Script executed:

grep -r "@napi-rs/keyring" ./packages/cli-core/src/ --include="*.ts" -n

Repository: 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)
  • Runtime loader (platform selection): index.js detects process.platform + process.arch, and on Linux additionally detects musl vs glibc (using process.report when available, otherwise inspecting ldd). It then loads either a local .node file (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-gnu is the x86_64-unknown-linux-gnu binary). (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:


🌐 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 .node files into the produced executable, but it’s essentially embedding/packaging: you still need a .node binary built for the same OS/arch as the executable target. [2]
  • Make sure the addon is directly required, e.g.:
    const addon = require("./addon.node");
    Bun warns that if the addon is loaded indirectly via tools like @mapbox/node-pre-gyp, it may not bundle correctly unless the .node is directly required. [2]

Practical cross-compilation workflow for native addons

  1. 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 for bun-windows-x64, etc.).
  2. Ensure your dependency install selects the right platform artifacts when needed:
    bun install --os=linux --cpu=x64
    This tells Bun to select packages for that platform (useful for cross-platform builds). [3]
  3. 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:


🏁 Script executed:

grep -r "credential-store\|from.*lib" ./packages/cli-core/src/cli.ts -n | head -20

Repository: clerk/cli-new

Length of output: 103


🏁 Script executed:

grep -r "credential-store" ./packages/cli-core/src/ --include="*.ts" | grep -v "\.test\.ts" -c

Repository: clerk/cli-new

Length of output: 58


🏁 Script executed:

rg "credential-store" ./packages/cli-core/src/ --type ts --files-with-matches | grep -v test

Repository: clerk/cli-new

Length of output: 335


🏁 Script executed:

grep -A 2 "credential-store" ./packages/cli-core/src/commands/auth/login.ts | head -10

Repository: 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.

@wyattjoh

Copy link
Copy Markdown
Contributor Author

Superseded by a 4-PR stack for reviewability:

PR Description
1 #43 Monorepo restructure
2 #44 Cross-compile build + smoke tests
3 #45 Release infrastructure (changesets)
4 #46 Release documentation + LICENSE

Review in parallel, merge sequentially 1→2→3→4.

@wyattjoh wyattjoh closed this Mar 20, 2026
@wyattjoh
wyattjoh deleted the wyattjoh/compile branch April 13, 2026 23:03
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.

2 participants