내 Rust Bun Version Manager (bum) CLI를 NPM 패키지에 배포한 방법

발행: (2025년 12월 7일 오후 01:49 GMT+9)
4 min read
원문: Dev.to

Source: Dev.to

배경

저는 bum이라는 CLI를 만들었습니다 – Rust로 작성된 빠른 Bun 버전 관리자입니다. 로컬에서는 잘 동작하지만, 누구나 다음과 같이 실행할 수 있으면 좋겠다고 생각했습니다:

npx @owenizedd/bum use 1.3.3

Rust를 설치하거나 컴파일할 필요 없이 말이죠. 생각보다 완전히 가능했습니다! 이 방법은 lekoarts.de에서 발표한 “Rust CLI를 npm에 배포하기”라는 글을 통해 napi‑rs를 사용한다는 것을 알게 되면서 배웠습니다.

설정

Rust 프로젝트에 napi-rs 추가

# Cargo.toml
[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
napi = "2.16"
napi-derive = "2.16"

[build-dependencies]
napi-build = "2.1"

CLI를 napi 함수로 감싸기

// src/lib.rs
use napi_derive::napi;

#[napi]
pub fn run(args: Vec) -> napi::Result {
    let rt = tokio::runtime::Runtime::new()
        .map_err(|e| napi::Error::from_reason(format!("Failed to create runtime: {}", e)))?;

    // Your CLI logic here
    rt.block_on(async {
        // run_commands(args).await
    });

    Ok(())
}

간단한 bin.js 만들기

#!/usr/bin/env node

const { run } = require("./index");
const args = process.argv.slice(2);

try {
  run(args);
} catch (e) {
  console.error(e);
  process.exit(1);
}

package.json 설정

{
  "name": "@owenizedd/bum",
  "bin": "bin.js",
  "napi": {
    "name": "bum",
    "triples": {
      "defaults": true,
      "additional": [
        "aarch64-apple-darwin",
        "x86_64-apple-darwin",
        "x86_64-unknown-linux-musl",
        "aarch64-unknown-linux-gnu"
      ]
    }
  }
}

OpenSSL 교차 컴파일 지옥

reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
zip = { version = "1.1", default-features = false, features = ["deflate"] }

각 플랫폼(예: darwin-arm64, linux-x64-gnu)은 별도의 npm 패키지로 배포됩니다. 처음에는 이들의 package.json 버전을 업데이트하지 않아 npm이 패키지를 찾지 못하는 문제가 있었습니다.

해결 방법: CI에서 플랫폼 패키지 버전 동기화

- name: Sync platform package versions
  run: |
    VERSION=$(node -p "require('./package.json').version")
    for dir in npm/*/; do
      node -e "
        const fs = require('fs');
        const pkg = JSON.parse(fs.readFileSync('$dir/package.json'));
        pkg.version = '$VERSION';
        fs.writeFileSync('$dir/package.json', JSON.stringify(pkg, null, 2));
      "
    done

번들링 함정

처음에는 bun build bin.ts로 TypeScript를 컴파일하려 했습니다. Bun이 require('./index')를 인라인하고 해시가 포함된 .node 파일명을 고정시켜 깨진 코드를 만들었습니다:

// Broken bundling result
nativeBinding = require("./bum.darwin-arm64-7xvffnqw.node");

올바른(작동하는) import는 다음과 같아야 합니다:

// Correct import
nativeBinding = require("./bum.darwin-arm64.node");

해결책: bin.js는 순수 JavaScript로 작성하고 번들링을 피합니다.

결과

이제 누구나 다음 명령을 실행할 수 있습니다:

npx @owenizedd/bum use 1.3.3

macOS, Linux, Windows에서 정상적으로 동작하며(다른 플랫폼은 아직 테스트 중) 이제 배포가 완료되었습니다.

참고 자료

Back to Blog

관련 글

더 보기 »

커널 Rust 실험의 끝

기사 URL: https://lwn.net/Articles/1049831/ 댓글 URL: https://news.ycombinator.com/item?id=46213585 점수: 66 댓글: 22