Skip to content

Commit e1d6115

Browse files
authored
Merge pull request #191 from yyuneu/feat/cli-lib-entry
feat: npm 배포 (N-API 브리지)
2 parents faf788f + 398b0fa commit e1d6115

26 files changed

Lines changed: 1114 additions & 191 deletions
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"changes": {"crates/vespertide-cli/Cargo.toml": "Minor", "bridge/node/package.json": "Minor"}, "note": "vespertide-cli를 lib + bin으로 나누고(run / main / run_cli 진입점) 그 위에 N-API 브리지 bridge/node를 추가해 CLI를 npm 패키지 vespertide(플랫폼 패키지 @vespertide/cli-*)로 배포한다. cli는 다른 크레이트가 핀하지 않는 leaf라 단독 Minor로 올리고, npm 매니페스트는 updateOn으로 매 크레이트 웨이브에 Patch로 따라오며, 이번처럼 같은 단계로 올릴 때는 명시한다.", "date": "2026-09-08T09:00:00.0000000Z"}

‎.changepacks/config.json‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33
"**",
44
"!crates/**",
55
"crates/vespertide-schema-gen/Cargo.toml",
6-
"!apps/vscode-extension/package.json"
6+
"!apps/vscode-extension/package.json",
7+
"!bridge/node/package.json"
78
],
89
"baseBranch": "main",
910
"latestPackage": "crates/vespertide/Cargo.toml",
1011
"publish": {
11-
"apps/vscode-extension/package.json": "echo skip npm publish for vscode-extension - shipped via vsce in the vscode-release job"
12+
"apps/vscode-extension/package.json": "echo skip npm publish for vscode-extension - shipped via vsce in the vscode-release job",
13+
"bridge/node/package.json": "echo skip npm publish here - published by the node-publish job"
14+
},
15+
"updateOn": {
16+
"crates/*": [
17+
"bridge/node/package.json"
18+
]
1219
}
1320
}

‎.github/workflows/CI.yml‎

Lines changed: 140 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ jobs:
157157
echo "Computed cargo-semver-checks release-type: '${RT:-<derive-from-version>}'"
158158
- uses: obi1kenobi/cargo-semver-checks-action@v2
159159
with:
160-
# Only check published crates; skip cli (binary) and schema-gen (publish=false)
160+
# Only check published crates; skip schema-gen (publish=false) and cli, whose
161+
# lib target is new — add it once a release with the lib is on crates.io
161162
package: vespertide,vespertide-core,vespertide-config,vespertide-loader,vespertide-naming,vespertide-planner,vespertide-query,vespertide-exporter,vespertide-macro
162163
feature-group: default-features
163164
# Empty => action derives strictly from the Cargo.toml version.
@@ -564,6 +565,144 @@ jobs:
564565
- name: Check schema drift
565566
run: git diff --no-index --exit-code -- schemas _tmp_schemas
566567

568+
# npm: the CLI as a Node addon (bridge/node, napi-rs). Fires only when the
569+
# npm manifest is in the changepacks wave. Mirrors lsp-release's target
570+
# table; `cross` is used for the same single aarch64-linux cell.
571+
node-build:
572+
name: npm Build (${{ matrix.target }})
573+
needs: changepacks
574+
if: ${{ contains(needs.changepacks.outputs.changepacks, 'bridge/node/package.json') }}
575+
runs-on: ${{ matrix.runs-on }}
576+
strategy:
577+
fail-fast: false
578+
matrix:
579+
include:
580+
- target: x86_64-unknown-linux-gnu
581+
runs-on: ubuntu-latest
582+
use_cross: false
583+
- target: aarch64-unknown-linux-gnu
584+
runs-on: ubuntu-latest
585+
use_cross: true
586+
- target: x86_64-apple-darwin
587+
# Cross-arch on the ARM runner, as in lsp-release.
588+
runs-on: macos-latest
589+
use_cross: false
590+
- target: aarch64-apple-darwin
591+
runs-on: macos-latest
592+
use_cross: false
593+
- target: x86_64-pc-windows-msvc
594+
runs-on: windows-latest
595+
use_cross: false
596+
steps:
597+
- uses: actions/checkout@v7
598+
599+
- uses: actions-rust-lang/setup-rust-toolchain@v1
600+
with:
601+
target: ${{ matrix.target }}
602+
603+
- uses: oven-sh/setup-bun@v2
604+
with:
605+
bun-version: latest
606+
607+
- name: Install cross (aarch64-unknown-linux-gnu only)
608+
if: matrix.use_cross
609+
uses: taiki-e/install-action@v2.86.3
610+
with:
611+
tool: cross
612+
613+
- name: Install
614+
working-directory: bridge/node
615+
run: bun install --frozen-lockfile
616+
617+
- name: Build addon (native)
618+
if: ${{ !matrix.use_cross }}
619+
working-directory: bridge/node
620+
run: bun run build --target ${{ matrix.target }}
621+
622+
- name: Build addon (cross)
623+
if: matrix.use_cross
624+
working-directory: bridge/node
625+
run: bun run build --target ${{ matrix.target }} --use-cross
626+
627+
# The addon only loads where it was built for; the two cross-arch
628+
# cells skip the smoke test.
629+
- name: Smoke test
630+
if: ${{ matrix.target != 'aarch64-unknown-linux-gnu' && matrix.target != 'x86_64-apple-darwin' }}
631+
working-directory: bridge/node
632+
run: bun test
633+
634+
- uses: actions/upload-artifact@v7
635+
with:
636+
name: bindings-${{ matrix.target }}
637+
path: bridge/node/vespertide.*.node
638+
if-no-files-found: error
639+
retention-days: 1
640+
641+
node-publish:
642+
name: npm Publish
643+
needs: [changepacks, node-build]
644+
if: ${{ contains(needs.changepacks.outputs.changepacks, 'bridge/node/package.json') }}
645+
runs-on: ubuntu-latest
646+
permissions:
647+
contents: write
648+
steps:
649+
- uses: actions/checkout@v7
650+
651+
- uses: oven-sh/setup-bun@v2
652+
with:
653+
bun-version: latest
654+
655+
# npm (not bun) performs the publish so the registry auth from
656+
# setup-node applies; `npm publish` runs `prepublishOnly`, which pushes
657+
# the five platform packages before the root one.
658+
- uses: actions/setup-node@v7
659+
with:
660+
node-version: '24'
661+
registry-url: 'https://registry.npmjs.org'
662+
663+
- uses: actions-rust-lang/setup-rust-toolchain@v1
664+
665+
- name: Install
666+
working-directory: bridge/node
667+
run: bun install --frozen-lockfile
668+
669+
# `index.js` / `index.d.ts` are build outputs (gitignored); only
670+
# `napi build` writes them, so the publish host builds once for
671+
# its own platform before assembling the packages.
672+
- name: Build addon (regenerates the JS loader)
673+
working-directory: bridge/node
674+
run: bun run build
675+
676+
- name: Create platform package dirs
677+
working-directory: bridge/node
678+
run: bunx napi create-npm-dirs
679+
680+
- uses: actions/download-artifact@v8
681+
with:
682+
path: bridge/node/artifacts
683+
684+
- name: Move addons into platform packages
685+
working-directory: bridge/node
686+
run: bun run artifacts
687+
688+
- name: Upload addons to changepacks release
689+
uses: owjs3901/upload-github-release-asset@main
690+
with:
691+
upload_url: ${{ fromJson(needs.changepacks.outputs.release_assets_urls)['bridge/node/package.json'] }}
692+
asset_path: bridge/node/npm/*/vespertide.*.node
693+
694+
- name: Publish to npm
695+
working-directory: bridge/node
696+
env:
697+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
698+
run: |
699+
if [ -z "$NODE_AUTH_TOKEN" ]; then
700+
echo "::warning::NPM_TOKEN not set — skipping npm publish"
701+
else
702+
npm publish --access public
703+
fi
704+
shell: bash
705+
567706
insta-pending:
568707
name: insta-pending
569708
runs-on: ubuntu-latest

‎AGENTS.md‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ vespertide/
2626
│ ├── vespertide-naming/ # Naming convention utilities
2727
│ ├── vespertide-schema-gen/# JSON Schema generation
2828
│ └── vespertide/ # Re-export crate (user-facing API)
29+
├── bridge/node/ # N-API bridge: the CLI as the `vespertide` npm package (+ @vespertide/cli-* platform packages)
2930
├── examples/app/ # Example project with models/migrations (out-of-workspace)
3031
├── tools/lsp-profile/ # LSP synthetic / realistic workload + latency profiler (out-of-workspace)
3132
├── fuzz/ # cargo-fuzz targets (4 targets, see FUZZING section)
@@ -522,6 +523,13 @@ There is no separate `lsp-release.yml` or `vscode-release.yml`.
522523
`crates/vespertide-lsp/Cargo.toml` is in the wave. Builds the
523524
`vespertide-lsp` binary natively + cross + windows, packages `tar.gz`/`zip`
524525
with `sha256`, uploads to the changepacks release.
526+
- **`node-build`** (matrix × 5 targets) + **`node-publish`** — fire only when
527+
`bridge/node/package.json` is in the wave. Build the napi addon per
528+
target, rebuild once on the publish host (only `napi build` writes the
529+
gitignored `index.js` loader), assemble the `@vespertide/cli-*` platform packages with
530+
`napi create-npm-dirs` / `napi artifacts`, upload the `.node` files to
531+
the changepacks release, then `npm publish` (skipped with a warning
532+
when `NPM_TOKEN` is unset).
525533
- **`vscode-release`** (matrix × 5 vsce targets) — fires only when
526534
`apps/vscode-extension/package.json` is in the wave. Pulls the matching
527535
LSP binary (just-released if LSP is also in the wave, otherwise the latest
@@ -530,10 +538,13 @@ There is no separate `lsp-release.yml` or `vscode-release.yml`.
530538

531539
### Configuration
532540
- `.changepacks/config.json` — tracks `crates/**/Cargo.toml` (except
533-
`vespertide-schema-gen` which is `publish=false`) and
534-
`apps/vscode-extension/package.json`. `apps/landing`, `apps/zed-extension`,
541+
`vespertide-schema-gen` which is `publish=false`),
542+
`apps/vscode-extension/package.json` and `bridge/node/package.json`.
543+
`updateOn` adds the npm manifest to every crate wave as a **Patch** bump so
544+
`node-publish` always fires; list it explicitly in the descriptor when the
545+
npm version should move at the same level as the crates. `apps/landing`, `apps/zed-extension`,
535546
`tools/`, and `tests/` are intentionally not tracked.
536-
- Required secrets: `CARGO_REGISTRY_TOKEN`, `VSCE_PAT`, `OVSX_PAT`.
547+
- Required secrets: `CARGO_REGISTRY_TOKEN`, `VSCE_PAT`, `OVSX_PAT`, `NPM_TOKEN`.
537548
- `.changepacks/changepack_log_*.json` files are the **committed bump
538549
descriptors** (written by `bunx @changepacks/cli`, consumed by
539550
`changepacks/action` on merge — analogous to changesets' `.changeset/*.md`),

‎Cargo.lock‎

Lines changed: 107 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Cargo.toml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# cargo test --manifest-path tests/runtime-sqlite/Cargo.toml
1313
# cargo build --manifest-path examples/app/Cargo.toml
1414
# cargo run --release --manifest-path tools/lsp-profile/Cargo.toml -- --workload synthetic
15-
members = ["crates/*", "fuzz"]
15+
members = ["crates/*", "fuzz", "bridge/*"]
1616
exclude = ["apps/zed-extension", "examples/app", "tests/runtime-sqlite", "tools/lsp-profile"]
1717
default-members = [
1818
"crates/vespertide",
@@ -95,6 +95,7 @@ vespertide-planner = { path = "crates/vespertide-planner", version = "=0.3.0" }
9595
vespertide-query = { path = "crates/vespertide-query", version = "=0.3.0" }
9696
vespertide-exporter = { path = "crates/vespertide-exporter", version = "=0.3.0" }
9797
vespertide-lsp = { path = "crates/vespertide-lsp", version = "=0.3.0" }
98+
vespertide-cli = { path = "crates/vespertide-cli", version = "=0.3.0" }
9899

99100
[profile.dev]
100101
debug = 1 # Line tables only — faster DWARF generation for large codegen output

0 commit comments

Comments
 (0)