Skip to content

Bump napi from 3.11.0 to 3.12.0 in /lib #85

Bump napi from 3.11.0 to 3.12.0 in /lib

Bump napi from 3.11.0 to 3.12.0 in /lib #85

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: |
frontend/package-lock.json
app/package-lock.json
mastervault-mcp-server/package-lock.json
- name: Install frontend dependencies
working-directory: frontend
run: npm ci
- name: Install app dependencies
working-directory: app
run: npm ci
- name: Install mastervault-mcp-server dependencies
working-directory: mastervault-mcp-server
run: npm ci
- name: Lint frontend
working-directory: frontend
run: npm run lint
- name: Typecheck frontend
working-directory: frontend
run: npx tsc -b
- name: Typecheck app
working-directory: app
run: npx tsc -p tsconfig.json --noEmit
- name: Test frontend
working-directory: frontend
run: npm test
- name: Test app
working-directory: app
run: npm test
- name: Build frontend
working-directory: frontend
run: npm run build
- name: Build app
working-directory: app
run: npm run build
- name: Build bundled mastervault-mcp-server (the copy ModelForge packages)
working-directory: mastervault-mcp-server
run: npm run build:bundled
- name: Smoke-test the bundled mastervault-mcp-server over stdio
working-directory: mastervault-mcp-server
run: |
set -euo pipefail
mkdir -p /tmp/mastervault-ci-vault
echo '# CI test vault' > /tmp/mastervault-ci-vault/_orientation.md
printf '%s\n%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"ci","version":"1"}}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' \
| node dist-bundled/index.js /tmp/mastervault-ci-vault > worker-output.json
cat worker-output.json
test "$(grep -o 'mastervault_orient' worker-output.json | wc -l)" -ge 1
e2e:
# Electron E2E tests spin up a real Electron process and a Vite preview
# server per test, which is slower and flakier than the unit-test job
# above — kept as its own job so a flaky e2e run never blocks/hides a
# unit-test failure, and so it can be retried or skipped independently.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: |
frontend/package-lock.json
app/package-lock.json
e2e/package-lock.json
- name: Install frontend dependencies
working-directory: frontend
run: npm ci
- name: Install app dependencies
working-directory: app
run: npm ci
- name: Install e2e dependencies
working-directory: e2e
run: npm ci
- name: Install Playwright browsers
working-directory: e2e
run: npx playwright install --with-deps chromium
- name: Build frontend
working-directory: frontend
run: npm run build
- name: Build app
working-directory: app
run: npm run build
- name: Run e2e suite
working-directory: e2e
# Electron needs a real (or virtual) display even headless-ish, hence
# xvfb — there's no way to launch a BrowserWindow without one on Linux.
run: xvfb-run --auto-servernum npx playwright test
- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: e2e/playwright-report
retention-days: 7
rust:
# lib/ (modelforge-native) is the napi-rs addon backing GGUF downloads
# and, more recently, the JSON datastore/audit-hashing primitives (see
# docs/ARCHITECTURE.md). fmt/clippy/cargo test only prove the Rust side
# is correct on whichever OS runs them — they say nothing about whether
# the resulting `.node` binary actually loads under Node on that OS,
# which is a materially different failure mode (wrong ABI, missing
# platform target, packaging mismatch). Previously this job only ran on
# ubuntu-latest, so nothing in CI ever built or loaded the addon on
# Windows or macOS despite installers shipping for both — see
# docs/RUST_MIGRATION_ASSESSMENT.md's platform-matrix section. The
# matrix below and the "napi build + require() smoke test" step close
# that gap.
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: lib
steps:
- uses: actions/checkout@v4
- name: Install rustfmt and clippy
run: rustup component add rustfmt clippy
- uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
lib/target
key: ${{ runner.os }}-cargo-${{ hashFiles('lib/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-
# fmt/clippy only need to run once — Rust source and formatting are
# identical across OSes, so repeating this on all three runners would
# just burn CI minutes for the same answer three times.
- name: Format check
if: matrix.os == 'ubuntu-latest'
run: cargo fmt --check
- name: Clippy
if: matrix.os == 'ubuntu-latest'
run: cargo clippy --all-targets -- -D warnings
- name: Build
run: cargo build --release
- name: Test
run: cargo test
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: lib/package-lock.json
- name: Install napi CLI
run: npm ci
- name: napi build (produces the real platform-specific .node binary)
run: npm run build:debug
# The actual gap this job exists to close: proves Node on *this* OS
# can load the binary napi build just produced, not just that Cargo
# compiled it. A wrong target triple, an ABI mismatch, or a packaging
# error would fail cargo build's cross-compile silently-succeed case
# but fail right here.
- name: Load the built addon under Node and verify its exports
shell: bash
run: |
node -e "
const addon = require('../app/native');
const expected = ['downloadGgufFile', 'DownloadManager', 'readJsonFileNative', 'writeJsonFileAtomicNative', 'sha256HexNative', 'appendJsonArrayElementNative'];
const missing = expected.filter((name) => !(name in addon));
if (missing.length > 0) {
console.error('Native addon loaded but is missing expected exports:', missing);
process.exit(1);
}
console.log('Native addon loaded successfully on ${{ matrix.os }} with all expected exports.');
"
sbom:
# Generates a CycloneDX SBOM per npm workspace so every release has an
# auditable, machine-readable dependency manifest — required groundwork
# for the supply-chain/provenance controls flagged in
# docs/ENTERPRISE_READINESS_ASSESSMENT.md, and useful on its own for
# answering "are we affected by CVE-X" without re-deriving the dependency
# tree by hand. Runs independently of the test job so a flaky SBOM
# generation never blocks the build/test signal.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: |
frontend/package-lock.json
app/package-lock.json
mastervault-mcp-server/package-lock.json
- name: Install frontend dependencies
working-directory: frontend
run: npm ci
- name: Install app dependencies
working-directory: app
run: npm ci
- name: Install mastervault-mcp-server dependencies
working-directory: mastervault-mcp-server
run: npm ci
- name: Generate SBOM (frontend)
working-directory: frontend
run: npx --yes @cyclonedx/cyclonedx-npm --output-file ../sbom-frontend.cdx.json
- name: Generate SBOM (app)
working-directory: app
run: npx --yes @cyclonedx/cyclonedx-npm --output-file ../sbom-app.cdx.json
- name: Generate SBOM (mastervault-mcp-server)
working-directory: mastervault-mcp-server
run: npx --yes @cyclonedx/cyclonedx-npm --output-file ../sbom-mastervault-mcp-server.cdx.json
- uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom-*.cdx.json
retention-days: 90
python-recommender:
# ml/hardware-recommender/ is a standalone project (its own venv, own
# train/download-dataset scripts) that isn't part of the app's build —
# only its exported ONNX model ships with the app
# (app/python/artifacts/hardware_recommender.onnx), loaded at runtime by
# app/python/recommender_worker.py. This job is deliberately scoped to
# what release publishing actually depends on: the recommender package's
# own unit tests (data/feature encoding logic, no dataset or GPU needed)
# and a smoke test that the packaged worker script can actually load the
# shipped artifact and answer a request — never train.py or
# download_dataset.py, which need a real (large) dataset pull.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: ml/hardware-recommender/requirements.txt
- name: Install dependencies (CPU-only torch for ML tests)
working-directory: ml/hardware-recommender
# torch's CPU wheels live on a separate index from the rest of
# PyPI. Install it separately to keep the training/test stack CPU-only
# and avoid a multi-gigabyte CUDA wheel download on a GPU-less runner;
# the packaged app uses ONNX Runtime for inference and does not ship
# torch in its managed runtime.
run: |
python -m pip install --upgrade pip
python -m pip install --only-binary=:all: --index-url https://download.pytorch.org/whl/cpu torch==2.13.0
# pip parses comments and blank lines correctly. Passing the file
# through shell command substitution turned the first comment into
# a literal `#` requirement and failed the job.
python -m pip install --only-binary=:all: -r requirements.txt
- name: Run hardware-recommender unit tests
working-directory: ml/hardware-recommender
run: python -m pytest tests/ -q
- name: Verify the packaged model artifacts exist
run: |
test -f app/python/artifacts/hardware_recommender.onnx
test -f app/python/artifacts/hardware_recommender.onnx.sha256
test -f app/python/artifacts/hardware_recommender.meta.json
- name: Smoke-test the packaged recommender worker against the shipped artifact
working-directory: app/python
run: |
printf '%s\n%s\n' \
'{"protocol":1,"id":"1","method":"health","params":{}}' \
'{"protocol":1,"id":"2","method":"recommend","params":{"model_params_b":7,"ram_gb":16,"vram_gb":8,"cpu_cores":8,"platform":"linux","gpu_backend":"cuda"}}' \
| python recommender_worker.py > worker-output.json
cat worker-output.json
test "$(grep -o '"ok": *true' worker-output.json | wc -l)" -eq 2