내 Rust Bun Version Manager (bum) CLI를 NPM 패키지에 배포한 방법
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에서 정상적으로 동작하며(다른 플랫폼은 아직 테스트 중) 이제 배포가 완료되었습니다.
참고 자료
- Publishing a Rust CLI on npm – 이 가능성을 열어준 기사
- napi‑rs Documentation
- bum GitHub Repository