Skip to content

Comments

fix: embed vibe-native in compiled release binaries#355

Merged
kexi merged 5 commits intodevelopfrom
fix/embed-native-module-in-release-binary
Feb 13, 2026
Merged

fix: embed vibe-native in compiled release binaries#355
kexi merged 5 commits intodevelopfrom
fix/embed-native-module-in-release-binary

Conversation

@kexi
Copy link
Owner

@kexi kexi commented Feb 13, 2026

Summary

  • Add native module build step to release.yml and beta-release.yml workflows
  • Builds .node files before bun build --compile so Bun embeds them in the $bunfs virtual filesystem
  • Enables clonefile syscall for directory CoW cloning instead of falling back to cp -cR on Homebrew-distributed binaries

Design decisions

Item Decision
macOS both architectures Both arm64/x64 builds run on macos-latest (arm64), so Rust cross-compiles both .node files. Bun bundler embeds both, runtime selects correct one via process.arch
Linux current arch only x64 on ubuntu-latest, arm64 on ubuntu-24.04-arm — each builds natively
Windows skipped vibe-native supports macOS/Linux only
--js false Prevents regeneration of committed index.js

Test plan

  • CI passes on all matrix targets (macOS x64/arm64, Linux x64/arm64, Windows)
  • Beta release binary includes .node files (strings <binary> | grep 'vibe-native')
  • vibe start on macOS uses clonefile strategy with the compiled binary

🤖 Generated with Claude Code

kexi and others added 2 commits February 13, 2026 09:46
Regenerated index.js and index.d.ts with latest napi build output.
Adds moveToTrash/moveToTrashAsync exports and updates formatting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add native module build step to release and beta-release workflows
so that bun build --compile includes the .node files in the binary.
This enables clonefile syscall for directory CoW cloning instead of
falling back to cp -cR on macOS/Linux.

- macOS: cross-compile for both x86_64 and aarch64
- Linux: build for current architecture only
- Windows: skip (vibe-native is macOS/Linux only)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Feb 13, 2026

Code Review: PR #355 - Embed vibe-native in compiled release binaries

Summary

This PR adds native module build steps to the release workflows to ensure .node files are embedded in the compiled Bun binary. This is critical for enabling Copy-on-Write cloning functionality in Homebrew-distributed binaries.

✅ Strengths

1. Well-Structured Solution

  • The approach of building native modules before bun build --compile is correct for Bun's $bunfs virtual filesystem
  • Clear separation between macOS (cross-compile both architectures) and Linux (native build per architecture)
  • Proper conditional execution with if: runner.os != 'Windows' since vibe-native only supports macOS/Linux

2. Good Documentation

  • PR description clearly explains the design decisions
  • Comments in the workflow explain the rationale for each approach
  • Test plan is well-defined

3. Consistency

  • Changes are mirrored in both release.yml and beta-release.yml
  • Follows the existing workflow structure

4. License Compliance

All dependencies maintain Apache-2.0 or compatible licenses:

  • napi, napi-derive, napi-build: MIT
  • trash: MIT
  • thiserror: MIT/Apache-2.0
  • tokio: MIT
  • libc, nix: MIT/Apache-2.0

No GPL contamination detected.

🔍 Issues & Recommendations

1. Type Safety Regression in index.d.ts ⚠️

Before:

export declare function getPlatform(): "darwin" | "linux";

After:

export declare function getPlatform(): string;

This is a type safety regression. The function only returns "darwin" or "linux", so the more specific type should be retained.

Recommendation: Revert this change or patch the auto-generated file to maintain the specific return type.

2. Potential Build Artifact Resolution

The .gitignore excludes packages/native/*.node, which is correct for development. However, verify that Bun's bundler properly resolves packages/native/*.node files when using monorepo workspace references.

Current flow:

  1. pnpm install --ignore-scripts - doesn't build native modules
  2. Build native module step - creates .node files in packages/native/
  3. bun build --compile - must embed the .node files

Recommendation: Test that the binary actually includes the .node files.

3. Missing Embedded Module Verification

The PR's test plan mentions verifying with strings <binary> | grep 'vibe-native', but this isn't automated in CI.

Recommendation: Add a CI verification step in the workflows after building the binary.

4. Cross-Compilation on Linux ARM

For Linux ARM builds, consider adding explicit target specification for consistency:

pnpm exec napi build --platform --release --target aarch64-unknown-linux-gnu --js false

5. Documentation Gap

No README updates, but this is a significant architectural change.

Recommendation: Update packages/native/README.md to note that the module is embedded in release binaries.

🔒 Security Review

✅ No Security Concerns Found

  • ✓ No shell injection risks (proper YAML syntax, no user input)
  • ✓ No credential exposure
  • --js false prevents unwanted file regeneration
  • ✓ Rust dependencies are from trusted sources (crates.io)
  • ✓ Native modules are built locally, not downloaded
  • ✓ Build process follows principle of least privilege

Dependencies Security Check

napi@2.16 (MIT) - N-API bindings
trash@5.2 (MIT) - Cross-platform trash
thiserror@2.0 (MIT/Apache-2.0) - Error handling
tokio@1.43 (MIT) - Async runtime
libc@0.2.169 (MIT/Apache-2.0) - C bindings
nix@0.29.0 (MIT) - Unix system calls

All licenses are compatible with Apache-2.0. No GPL dependencies.

📊 Performance Considerations

Positive Impact:

  • Embedded native modules eliminate runtime module resolution overhead
  • Copy-on-Write cloning will significantly improve vibe start performance
  • Binary size increase: ~200-500KB (minimal)

Build Time Impact:

  • macOS builds: +2-3 minutes (cross-compilation overhead) - acceptable for release
  • Linux builds: +30-60 seconds (native compilation) - minimal impact

🎯 Overall Assessment

Quality: ⭐⭐⭐⭐☆ (4/5)

This is a well-designed solution to ensure Copy-on-Write functionality works in distributed binaries. The implementation is mostly correct with a few refinements needed.

Must Fix Before Merge:

  1. ⚠️ Revert getPlatform() return type in index.d.ts to preserve type safety

Should Fix:

  1. Add CI verification step to confirm native module embedding
  2. Test that Bun bundler correctly finds and embeds packages/native/*.node files
  3. Add explicit Linux target for consistency

Nice to Have:

  1. Update documentation
  2. Add auto-generated file warnings to index.js and index.d.ts

✅ Approval Recommendation

Approve with changes requested. The solution is fundamentally sound, but the type safety regression should be fixed. The embedded module verification should be added to CI to prevent regressions.

Once the index.d.ts type is fixed and testing confirms the native modules are properly embedded, this PR will significantly improve the user experience for Homebrew-distributed binaries.


Automated review by Claude Code

Run prettier --write on index.d.ts and index.js to match project
code style.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Feb 13, 2026

PR Review: Fix embedding vibe-native in compiled release binaries

Summary

This PR addresses an important issue where the native .node modules were not being embedded in the compiled release binaries, causing the CLI to fall back to slower copy methods instead of using native clonefile on macOS and FICLONE on Linux.


✅ Strengths

1. Well-designed build strategy

  • macOS: Cross-compiles both x86_64 and aarch64 targets on macos-latest (arm64), embedding both architectures so the bundler can select the correct one at runtime via process.arch
  • Linux: Builds natively for the current architecture only (x64 on ubuntu-latest, arm64 on ubuntu-24.04-arm)
  • Windows: Correctly skipped since vibe-native only supports macOS/Linux
  • Uses --js false to prevent regeneration of committed index.js

2. Correct workflow integration

  • Uses jdx/mise-action@v2 which already provides Rust toolchain from .mise.toml
  • Properly adds Rust targets with rustup target add before cross-compilation
  • Runs before bun build --compile step, ensuring .node files are embedded in $bunfs

3. Good commit hygiene

  • Clear commit messages following project conventions
  • Separate commits for different concerns (bindings update, workflow changes, formatting)

⚠️ Issues & Recommendations

1. Type definition regression

File: packages/native/index.d.ts:34

Issue: The type definition was made less specific from "darwin" | "linux" to string. This is a breaking change that removes type safety.

Recommendation: Revert this change. The original type is more accurate and provides better TypeScript type checking. If this was auto-generated by NAPI-RS, consider post-processing the generated file or configuring NAPI to preserve this type.


2. Missing verification of embedded .node files

The test plan mentions verifying that the binary includes .node files, but this isn't automated in CI.

Recommendation: Consider adding a verification step after building in both workflows.


3. Documentation updates needed

The following files reference clonefile or native module functionality:

  • README.md / README.ja.md
  • packages/native/README.md

Recommendation: Verify if any documentation needs to be updated to reflect that the compiled binary now includes native modules by default.


4. macOS build duplication (informational)

Both x86_64-apple-darwin and aarch64-apple-darwin matrix jobs run on macos-latest and both compile both architectures. This is intentional since each binary needs both architectures, but consider adding a clarifying comment in the workflow.


5. License compatibility check

Checked packages/native/Cargo.toml dependencies:

  • ✅ All dependencies use permissive licenses (MIT/Apache-2.0/BSD)
  • ✅ No GPL-licensed dependencies detected
  • ✅ Package itself is Apache-2.0 licensed

6. Security considerations

  • ✅ Uses pnpm install --ignore-scripts to prevent malicious install scripts
  • ✅ Native module built from source in CI
  • ✅ Rust dependencies are locked in Cargo.lock
  • ✅ No shell injection risks

📋 Test Coverage

Current: The PR relies on manual testing based on the test plan

Recommendation: Add a simple smoke test after building to verify the compiled binary runs correctly.


🎯 Performance Considerations

Positive impact: This fix enables significantly faster directory cloning on macOS and Linux by using native CoW operations instead of falling back to cp -cR


📝 Summary

Critical Issues (must fix before merge):

  1. Type definition regression - Revert getPlatform(): string to getPlatform(): "darwin" | "linux"

Recommended Improvements:

  1. Add automated verification that .node files are embedded in binary
  2. Update documentation (README.md, README.ja.md) if needed
  3. Add clarifying comment about macOS building both architectures
  4. Consider adding basic smoke test for compiled binary

Already Good:

✅ Build strategy is sound
✅ No license conflicts
✅ No security issues
✅ Follows CI/CD best practices (using mise)
✅ Good commit structure


Verdict

Approve with requested changes. The core implementation is solid and solves an important problem. The type regression should be fixed before merge, but other recommendations are nice-to-haves that can be addressed in follow-up PRs if needed.


Note: This review was performed by Claude Code. Human review is still recommended for final approval.

NAPI-RS auto-generation widened the return type to string.
Restore the specific union type for type safety.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Feb 13, 2026

PR Review: Embed vibe-native in compiled release binaries

Summary

This PR successfully addresses the issue of native modules not being embedded in compiled binaries by adding a build step for .node files before the bun build --compile step. The approach is well-designed and follows platform-specific strategies.


✅ Code Quality & Best Practices

Strengths:

  • Clear separation of macOS (universal build) vs Linux (native build) strategies
  • Inline comments explain the rationale for architectural decisions
  • Proper conditional execution with if: runner.os != 'Windows' to skip unsupported platforms
  • Consistent implementation across both release.yml and beta-release.yml

Suggestions:

  1. Error handling: Consider adding error checking after the rustup target add command to handle cases where target installation fails:

    - name: Build native module
      if: runner.os != 'Windows'
      run: |
        cd packages/native
        if [ "\${{ runner.os }}" = "macOS" ]; then
          rustup target add x86_64-apple-darwin aarch64-apple-darwin || {
            echo "Failed to add Rust targets"
            exit 1
          }
          # ... rest of build
  2. Workflow efficiency: The rustup target add command is idempotent but runs on every build. While not a critical issue, you could cache Rust toolchains to speed up builds slightly (though mise may already handle this).


✅ Security Concerns

No security issues identified:

  • Native module build uses official napi tooling
  • No new dependencies introduced in workflows
  • Rust dependencies (napi, tokio, trash, libc, nix) are all well-established, permissively licensed crates
  • The --js false flag correctly prevents regeneration of committed files, maintaining integrity

License Compatibility:
✅ All Rust dependencies use Apache-2.0 or MIT licenses (checked Cargo.toml):

  • napi, napi-derive, napi-build: MIT
  • thiserror: Apache-2.0 or MIT
  • tokio: MIT
  • trash: MIT
  • libc: Apache-2.0 or MIT
  • nix: MIT

No GPL-licensed dependencies detected.


✅ Performance Considerations

Positive impacts:

  • Enables clonefile syscall for directory CoW cloning on Homebrew-distributed binaries, which is significantly faster than cp -cR fallback
  • Release binary size will increase slightly (embedding .node files), but this is acceptable tradeoff for functionality
  • Build time increases slightly due to Rust compilation, but parallelizable across matrix jobs

Build strategy efficiency:

  • macOS: Cross-compiling both architectures on arm64 runners is efficient (avoids needing x64 runners)
  • Linux: Native builds on respective architectures (x64/arm64) is the right approach

⚠️ Test Coverage

Test plan items are appropriate, but I recommend adding verification steps:

  1. Automated verification: Add a step in CI to verify the .node files are actually embedded:

    - name: Verify native module embedding
      if: runner.os != 'Windows'
      run: |
        if ! strings ${{ matrix.artifact }} | grep -q 'vibe-native'; then
          echo "Error: Native module not embedded in binary"
          exit 1
        fi
  2. Functional test: Consider adding a smoke test that runs vibe start with the compiled binary to ensure CoW detection works:

    - name: Test CoW functionality
      if: runner.os == 'macOS'
      run: |
        ./${{ matrix.artifact }} --version
        # Test that CoW is detected (would require a test repository)

⚠️ Documentation Updates

Action required: The following documentation files mention CoW/clonefile functionality and should be reviewed for updates:

  • README.md (lines referencing clonefile)
  • README.ja.md (Japanese version - must be kept in sync per CLAUDE.md rules)
  • packages/native/README.md

Specific recommendation:
Update documentation to clarify that:

  1. Compiled binaries from GitHub releases now include native CoW support
  2. Previously, Homebrew binaries might fall back to cp -cR if native modules weren't embedded
  3. This fix ensures consistent CoW performance across all distribution methods

According to .claude/rules/markdown.md, both README.md and README.ja.md must be updated in sync.


🔍 Additional Observations

  1. index.js changes: The addition of moveToTrash and moveToTrashAsync exports is unrelated to the PR's stated purpose. While this appears to be a necessary update (these functions exist in the native binding but weren't exported), consider:

    • Mentioning this in the PR description
    • Or splitting this into a separate commit/PR for clarity
  2. Windows support: The PR correctly skips Windows builds since vibe-native only supports macOS/Linux (per package.json os field). This is clearly documented.

  3. CI/CD workflow consistency: ✅ Both workflows follow the CI/CD rules in .claude/rules/ci-cd-workflows.md by using mise instead of individual setup actions.


📋 Recommendations Summary

Before merging:

  1. ✅ CI passes on all matrix targets (verify in Actions tab)
  2. ⚠️ Update README.md and README.ja.md to document the fix
  3. 🔍 Consider adding automated verification step to CI
  4. 📝 Update PR description to mention moveToTrash exports addition

Nice to have:

  • Add error handling for rustup target add
  • Add smoke test for CoW functionality in compiled binaries

Verdict

Approve with minor documentation updates recommended. The core implementation is solid, well-designed, and addresses the issue effectively. The main action item is updating the README files per project conventions.

Great work on the comprehensive test plan and clear design decisions table in the PR description! 🎉

Add CI verification step to confirm native module embedding in release
binaries, clarify macOS cross-compilation comments in workflows, and
update documentation to reflect native clonefile() syscall usage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@claude
Copy link

claude bot commented Feb 13, 2026

PR Review: Embed vibe-native in compiled release binaries

Summary

This PR successfully addresses a critical issue where Homebrew-distributed binaries were falling back to cp -cR instead of using native clonefile() syscalls because the native modules weren't embedded. The solution builds .node files before bun build --compile, ensuring they're included in the \$bunfs virtual filesystem.


✅ Strengths

1. Excellent Build Strategy

  • macOS cross-compilation: Smart approach building both x64 and arm64 on the same runner, letting N-API select the correct architecture at runtime
  • Verification step: The strings check ensures native modules are embedded - great defensive programming
  • Clear comments: Workflow comments excellently explain the architecture-specific decisions

2. Security & Code Quality

  • Well-documented native code: lib.rs has comprehensive security documentation covering OWASP A01:2021, TOCTOU prevention, and file type validation
  • Safe syscall patterns: Immediate errno capture using __error() to prevent race conditions
  • No new security issues: Changes are build-only; runtime security remains strong

3. Documentation

  • README updates: Both English and Japanese READMs correctly updated to reflect native clonefile() usage
  • Synchronized docs: .ja.md changes properly mirror English version per project conventions
  • packages/native/README.md: Good addition explaining binary embedding

4. Proper Synchronization

  • Added moveToTrash/moveToTrashAsync exports to index.js to match the TypeScript definitions
  • Fixed getPlatform() return type to restore type safety ("darwin" | "linux" instead of generic string)

🔍 Observations & Suggestions

1. License Compatibility

Based on Cargo.toml review:

  • All dependencies use permissive licenses (Apache-2.0/MIT)
  • trash crate: MIT/Apache-2.0
  • nix crate: MIT
  • napi-rs: MIT
  • libc, thiserror, tokio: All MIT/Apache-2.0
  • No GPL contamination risk - all dependencies are compatible with Apache-2.0 license

2. CI/CD Best Practices

The workflow follows the project's .claude/rules/ci-cd-workflows.md by using jdx/mise-action@v2 for tool setup. However:

  • Rustup usage: The workflow adds Rust targets with rustup target add, which assumes Rust is available via mise
  • Verification: Ensure .mise.toml includes Rust configuration (it should already from native package development)

3. Test Coverage

From the PR description, the test plan includes:

  • ✅ CI passes on matrix targets
  • ✅ Beta release verification with strings
  • ⏳ Runtime verification with vibe start on macOS

Suggestion: Consider adding an integration test in CI that:

  1. Runs the compiled binary
  2. Executes vibe start on a test repo
  3. Verifies native module loaded successfully (could check debug logs or strategy selection)

4. Potential Edge Cases

a) Linux ARM64 Native Module

The workflow builds Linux ARM64 natively on ubuntu-24.04-arm. Verify that:

  • The runner has Rust/Cargo available (mise should handle this)
  • Build artifacts are correctly placed for Bun to find

b) Fallback Behavior

If the .node file is missing or fails to load, does the code gracefully fall back to cp -cR? The implementation in native-clone.ts checks this.nativeClone \!== null and throws if unavailable, which might be too strict for compiled binaries.

Recommendation: Consider a graceful fallback chain:

clonefile (native) → cp -cR → rsync → standard copy

5. Minor Improvements

a) Verification Script Enhancement

Current verification uses strings to check for 'vibe-native' in the binary.

Suggestion: Make it more specific to reduce false positives:

if \! strings ARTIFACT | grep -q 'vibe-native.*\.node'; then

b) Windows Handling

The PR correctly skips Windows for native module building. Consider adding a comment in the workflow explaining why (vibe-native is macOS/Linux only per Cargo.toml).


🐛 Potential Issues

1. --js false Flag Concern

The workflow uses --js false to prevent regeneration of index.js. However:

  • Commit 680da3f shows index.js was regenerated to add moveToTrash exports
  • If future native changes add exports, developers must remember to:
    1. Generate bindings with default napi build
    2. Commit the updated index.js
    3. CI uses --js false to avoid overwriting

Recommendation: Add a comment in the workflow or native package README about this workflow.

2. Cross-Platform File Paths

The workflow uses shell scripts with cd packages/native. On Windows runners, this would fail, but it's correctly guarded with if: runner.os \!= 'Windows'. Good defensive coding.


📊 Performance & Optimization

  • Build time: Building native modules for both architectures on macOS adds ~2-3 minutes to CI
  • Binary size: Embedding .node files increases binary size by ~500KB-1MB (acceptable tradeoff for native CoW)
  • Runtime performance: Native clonefile() is 10-100x faster than cp -cR for large directories - excellent ROI

🎯 Recommendations

High Priority

  1. Merge as-is - The core implementation is solid and fixes the critical Homebrew issue

Medium Priority (Follow-up PRs)

  1. Add integration test: Verify native module loads in compiled binary during CI
  2. Document --js false workflow: Add to packages/native/README.md
  3. Consider fallback gracefully: Instead of throwing when native unavailable, fall back to cp -cR

Low Priority

  1. Enhance verification regex to be more specific
  2. Add Windows-skip explanation comment

📝 Final Verdict

Recommendation: ✅ APPROVE with minor suggestions

This is a well-implemented fix for a critical issue. The PR:

  • ✅ Solves the stated problem (native modules not embedded in Homebrew binaries)
  • ✅ Follows project conventions (CLAUDE.md, CI/CD rules)
  • ✅ Maintains security standards (no new vulnerabilities)
  • ✅ Has proper documentation updates (README sync)
  • ✅ Includes verification steps (strings check)
  • ✅ No license compatibility issues

The native module implementation shows excellent security awareness with OWASP references, TOCTOU prevention, and file type validation.

Great work! 🚀


📚 References Checked

  • ✅ CLAUDE.md development commands and PR guidelines
  • .claude/rules/ci-cd-workflows.md (mise usage)
  • .claude/rules/markdown.md (README synchronization)
  • ✅ Security checklist compliance
  • ✅ License compatibility (Apache-2.0, no GPL dependencies)
  • ✅ Branch strategy (targeting develop branch)

Reviewed by Claude Code on 2026-02-13

@kexi kexi merged commit 52ced58 into develop Feb 13, 2026
21 checks passed
@kexi kexi deleted the fix/embed-native-module-in-release-binary branch February 13, 2026 03:31
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.

1 participant