Development
Release Guide
Last updated 2026-04-13
Release Guide
This document defines the current release shape for MustardScript and the commands
maintainers should run before publishing anything.
Current Release Shape
- The primary release artifact is the npm package
mustardscript. - The published npm install path is prebuilt-only. If no matching optional native binding package is installed for the current platform, the root package fails closed at runtime instead of compiling locally.
- Optional prebuilt
.nodebinaries now define the npm release path for the documented target matrix. - The Rust crates are implementation crates first. A separate
cargo publishflow is optional and is not required for the npm release path.
Release Prerequisites
- Start from a clean checkout or an isolated release worktree.
- Confirm the version in
package.jsonand the Cargo workspace metadata is the release candidate you intend to publish. - Wait for the normal CI matrix to pass on Linux, macOS, and Windows before starting the release checklist.
Release Verification Checklist
Run these commands from the repository root.
Canonical verification command
npm run verify:release
That command runs the same release verification flow used by the manual GitHub
Actions workflow in .github/workflows/release-verify.yml. It now verifies the
scoped package name, the packed file list, and the default npm publish --dry-run path.
1. Build, test, and lint the release candidate
npm install
cargo test --workspace
npm test
npm run lint
This covers the current build path, the Rust workspace tests, the Node API tests, the typecheck, and the package smoke tests.
2. Verify the optional prebuilt path if you intend to publish it
npm run verify:prebuilt
That command runs the prebuilt smoke coverage in
tests/package-smoke.test.js. It verifies the configured @napi-rs/cli
target metadata, stages a host-matching release binary into a generated npm
package directory, proves the root package declares the expected
optionalDependencies, installs the root tarball in a consumer with a local
override for the matching prebuilt package, and then proves the root package
loads successfully only when the matching binding package is present. The
loader accepts only validated .node artifacts from the expected optional
package layout; JavaScript main fallbacks, generic local binary fallbacks,
and arbitrary override module resolution are rejected.
3. Verify the npm package shape
npm pack --dry-run
npm pack
Check the dry-run output before keeping the generated tarball:
- The tarball should include the public JS and type entrypoints plus the native
loader for the prebuilt-only package contract:
dist/index.js,index.d.ts,mustard.d.ts, anddist/native-loader.js. - The tarball should include
LICENSEandSECURITY.md. - The tarball should not include Rust sources, local build products,
.github/, tests, planning documents, or a platform-specific.nodebinary from a maintainer machine.
4. Install smoke test from the packed tarball
repo_root="$PWD"
tmpdir="$(mktemp -d)"
tarball="$(npm pack --silent)"
mkdir "$tmpdir/consumer"
cd "$tmpdir/consumer"
npm init -y
npm install "$repo_root/$tarball"
node - <<'EOF'
const { Mustard } = require('mustardscript');
async function main() {
const runtime = new Mustard('let total = 1; total = total + 41; total;');
const value = await runtime.run();
if (value !== 42) {
throw new Error(`expected 42, got ${value}`);
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
EOF
This validates the exact release tarball with optional dependencies omitted, proving the package fails closed when no matching prebuilt binding is present.
5. Matching binding-package smoke test
Install the root tarball with an override for the current platform binding
package and rerun the smoke program. The repository now automates this in
tests/package-smoke.test.js, which stages a host-matching binding package,
installs the root tarball with an override to that binding tarball, and then
proves guest code runs successfully from the consumer.
npm install "$repo_root/$tarball"
node - <<'EOF'
const { Mustard } = require('mustardscript');
async function main() {
const runtime = new Mustard('let total = 40; total = total + 2; total;');
const value = await runtime.run();
if (value !== 42) {
throw new Error(`expected 42, got ${value}`);
}
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});
EOF
There is not yet a previously published binding package to upgrade from in the repository-local smoke path. Once real published versions exist, add an install-from-previous-version then upgrade-to-candidate flow if upgrade behavior needs explicit coverage.
6. Verify Rust package readiness if a crate release is being considered
The default release path does not publish a Rust crate. If maintainers decide to publish one later, the current verifiable flow for the core crate is:
cargo publish --dry-run --allow-dirty -p mustard
For the addon and sidecar crates, maintain dry-run packaging checks:
cargo package -p mustard-node --allow-dirty --list
cargo package -p mustard-sidecar --allow-dirty --list
Interpretation:
mustardis the only crate that currently makes sense as a future standalone Rust artifact.mustard-nodeandmustard-sidecarare packaging checks for completeness, not a recommendation to publish those crates independently.- If maintainers later decide to publish more than the core crate, add any remaining metadata and remove the current path dependencies before attempting an actual publish.
npm Namespace And Registry Access
As verified on April 11, 2026, npm view mustard resolves to an unrelated
public package at version 1.1.12, so this repository now targets the scoped
package name mustardscript.
Current state:
npm view mustardscriptreturns404 Not Found, which is compatible with a first publish for this package name.npm publish --dry-runis the command shape the automated release verification now checks.- An actual public publish still requires npm Trusted Publishing to be configured for this repository and workflow on the npm side. That trust relationship cannot be proven from repository-local verification alone.
Publishing The npm Package
Once the checklist passes:
npm publish
The manual GitHub Actions release workflow now uses npm Trusted Publishing via
GitHub OIDC instead of an NPM_TOKEN secret. The publish job therefore depends
on the existing id-token: write permission and the npm package trust
relationship staying configured for this repository/workflow pair.
Recommended follow-up:
- tag the release commit in git
- attach release notes that summarize the supported subset and the prebuilt target matrix
- link to
README.md,docs/LANGUAGE.md,docs/HOST_API.md, anddocs/SECURITY_MODEL.md
If maintainers ever move away from mustardscript, update the package name,
smoke tests, release verification script, and this document together.
Optional Prebuilt Binary Flow
The optional prebuilt flow is now the npm release path. It exists to ship the explicitly supported target matrix while failing closed on unsupported hosts.
Current prebuilt target matrix:
x86_64-unknown-linux-gnu->@mustardscript/binding-linux-x64-gnuaarch64-apple-darwin->@mustardscript/binding-darwin-arm64x86_64-apple-darwin->@mustardscript/binding-darwin-x64x86_64-pc-windows-msvc->@mustardscript/binding-win32-x64-msvc
Current mechanics:
package.jsoncarries the target list in thenapi.targetsfield so release automation can validate the supported target matrix.scripts/generate-prebuilt-packages.tscreates the per-target binding package directories undernpm/with scoped@mustardscript/binding-*package names.dist/native-loader.jsaccepts explicit native override paths for development and security testing, then loads only the exact expected local dev artifact names or the matching optional binding package..github/workflows/release.ymlis the manual, explicit prebuilt workflow. It builds the configured targets, stages them withscripts/generate-prebuilt-packages.tsplusnapi artifacts, runsnpm run verify:prebuilt, and only publishes whenworkflow_dispatchis invoked withpublish=true.- The publish job uses npm Trusted Publishing through GitHub Actions OIDC
rather than an
NPM_TOKENsecret. - The publish job now publishes each generated
npm/<target>/package withnpm publishdirectly before publishing the root package, rather than routing throughnapi pre-publish.
Bootstrap helper for first-time scoped package setup:
scripts/bootstrap-binding-stubs.sh --output-dir /tmp/ms-binding-bootstrap
That helper writes minimal placeholder packages for the current
@mustardscript/binding-* names so maintainers can do an initial manual
npm publish --access public for each package before switching the package
settings to npm Trusted Publishing. Use a placeholder version such as
0.0.0-bootstrap that the root package will never reference.
Local verification hook:
npm run verify:prebuilt
That local hook verifies the host-matching prebuilt install path. Cross-platform artifact builds remain a GitHub Actions concern because they require the corresponding runner environments.
External blocker for a real prebuilt publish:
- The workflow requires npm Trusted Publishing to stay configured for
mustardscriptand its optional prebuilt packages before the per-packagenpm publishsteps and the final rootnpm publishcan succeed through GitHub OIDC. Repository-local verification cannot prove that registry-side trust configuration.