From f3b4e48b07125c8685fdaa33cbe2a77710d36622 Mon Sep 17 00:00:00 2001 From: Nico Ritschel Date: Sun, 13 Sep 2026 16:22:20 -0700 Subject: [PATCH 1/2] Qualify release artifacts on their target hosts --- .../workflows/duckdb-extension-release.yml | 9 +- .github/workflows/release-rust-binaries.yml | 5 - .github/workflows/rust-runtime-release.yml | 36 ++-- .github/workflows/sidemantic-rs-wheels.yml | 42 ++--- docs/rust-native-runtime-packaging.md | 158 +++++++----------- sidemantic-rs/tests/packaged_cli_smoke.py | 36 ++++ 6 files changed, 142 insertions(+), 144 deletions(-) create mode 100644 sidemantic-rs/tests/packaged_cli_smoke.py diff --git a/.github/workflows/duckdb-extension-release.yml b/.github/workflows/duckdb-extension-release.yml index 14402ae53..695837f79 100644 --- a/.github/workflows/duckdb-extension-release.yml +++ b/.github/workflows/duckdb-extension-release.yml @@ -17,6 +17,10 @@ on: tags: - "sidemantic-duckdb-v*" + pull_request: + paths: + - ".github/workflows/duckdb-extension-release.yml" + permissions: contents: read @@ -60,11 +64,6 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable - - name: Cache cargo - uses: Swatinem/rust-cache@v2 - with: - workspaces: sidemantic-rs - - name: Install build dependencies run: sudo apt-get update && sudo apt-get install -y ninja-build diff --git a/.github/workflows/release-rust-binaries.yml b/.github/workflows/release-rust-binaries.yml index b42556292..a59a18786 100644 --- a/.github/workflows/release-rust-binaries.yml +++ b/.github/workflows/release-rust-binaries.yml @@ -98,11 +98,6 @@ jobs: with: targets: ${{ matrix.target }} - - name: Cache cargo - uses: Swatinem/rust-cache@v2 - with: - workspaces: sidemantic-rs - - name: Build Rust binaries env: TARGET: ${{ matrix.target }} diff --git a/.github/workflows/rust-runtime-release.yml b/.github/workflows/rust-runtime-release.yml index 579e49744..731ee65ca 100644 --- a/.github/workflows/rust-runtime-release.yml +++ b/.github/workflows/rust-runtime-release.yml @@ -18,6 +18,11 @@ on: - "sidemantic-rs-v*" - "rust-runtime-v*" + pull_request: + paths: + - ".github/workflows/rust-runtime-release.yml" + - "sidemantic-rs/tests/packaged_cli_smoke.py" + permissions: contents: read @@ -38,11 +43,6 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable - - name: Cache cargo - uses: Swatinem/rust-cache@v2 - with: - workspaces: sidemantic-rs - - name: Run package metadata tests run: cargo test --locked --test package_metadata @@ -73,7 +73,7 @@ jobs: include: - os: ubuntu-latest artifact: linux-x86_64 - - os: macos-13 + - os: macos-15-intel artifact: macos-x86_64 - os: macos-14 artifact: macos-arm64 @@ -90,11 +90,6 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable - - name: Cache cargo - uses: Swatinem/rust-cache@v2 - with: - workspaces: sidemantic-rs - - name: Build CLI run: cargo build --release --locked --bin sidemantic @@ -104,12 +99,27 @@ jobs: mkdir -p dist if [ "$RUNNER_OS" = "Windows" ]; then - cp target/release/sidemantic.exe "dist/sidemantic-${{ matrix.artifact }}.exe" + cp ../target/release/sidemantic.exe "dist/sidemantic-${{ matrix.artifact }}.exe" else - cp target/release/sidemantic "dist/sidemantic-${{ matrix.artifact }}" + cp ../target/release/sidemantic "dist/sidemantic-${{ matrix.artifact }}" tar -czf "dist/sidemantic-${{ matrix.artifact }}.tar.gz" -C dist "sidemantic-${{ matrix.artifact }}" fi + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Smoke packaged CLI + shell: bash + run: | + if [ "$RUNNER_OS" = "Windows" ]; then + uv run --no-project tests/packaged_cli_smoke.py dist/sidemantic-${{ matrix.artifact }}.exe + else + mkdir -p dist/smoke + tar -xzf dist/sidemantic-${{ matrix.artifact }}.tar.gz -C dist/smoke + uv run --no-project tests/packaged_cli_smoke.py dist/smoke/sidemantic-${{ matrix.artifact }} + rm -r dist/smoke + fi + - name: Upload CLI artifact uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/sidemantic-rs-wheels.yml b/.github/workflows/sidemantic-rs-wheels.yml index 02e27b94c..24fff09bd 100644 --- a/.github/workflows/sidemantic-rs-wheels.yml +++ b/.github/workflows/sidemantic-rs-wheels.yml @@ -17,6 +17,12 @@ on: tags: - "sidemantic-rs-v*" + pull_request: + paths: + - ".github/workflows/sidemantic-rs-wheels.yml" + - "sidemantic-rs/pyproject.toml" + - "sidemantic-rs/tests/python_wheel_smoke.py" + permissions: contents: read @@ -33,9 +39,9 @@ jobs: include: - os: ubuntu-latest target: x86_64 - - os: ubuntu-latest + - os: ubuntu-24.04-arm target: aarch64 - - os: macos-13 + - os: macos-15-intel target: x86_64 - os: macos-14 target: aarch64 @@ -58,6 +64,13 @@ jobs: manylinux: auto target: ${{ matrix.target }} + - name: Install uv + uses: astral-sh/setup-uv@v5 + + - name: Smoke built wheel on its target host + shell: bash + run: uv run --no-project --with sidemantic-rs/dist/*.whl sidemantic-rs/tests/python_wheel_smoke.py + - name: Upload wheel uses: actions/upload-artifact@v4 with: @@ -93,36 +106,11 @@ jobs: path: sidemantic-rs/dist/*.tar.gz if-no-files-found: error - smoke-host-wheel: - name: Smoke host wheel - runs-on: ubuntu-latest - defaults: - run: - working-directory: sidemantic-rs - - steps: - - uses: actions/checkout@v4 - - - name: Install uv - uses: astral-sh/setup-uv@v5 - with: - enable-cache: true - - - name: Set up Python - run: uv python install 3.12 - - - name: Build host wheel - run: uvx maturin build --release --out dist - - - name: Smoke host wheel - run: uv run --no-project --with dist/*.whl tests/python_wheel_smoke.py - release: name: Publish or attach wheel artifacts needs: - build-wheels - build-sdist - - smoke-host-wheel if: ${{ startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && (inputs.publish_pypi == true || inputs.create_github_release == true)) }} runs-on: ubuntu-latest permissions: diff --git a/docs/rust-native-runtime-packaging.md b/docs/rust-native-runtime-packaging.md index f7f60e658..d335dd711 100644 --- a/docs/rust-native-runtime-packaging.md +++ b/docs/rust-native-runtime-packaging.md @@ -1,112 +1,82 @@ -# Rust Native Runtime Packaging +# Rust runtime packaging -The Rust native runtime is packaged separately from the main `sidemantic` Python package. It supports native Sidemantic YAML and SQL projects only. Python remains the importer and migration layer for external formats. +Runtime artifacts have separate release paths. The presence of a workflow or an +installation command below does not establish that a version is published. +Python remains the default engine; installing the Rust bindings does not promote it. -## Artifacts +| Artifact | Metadata | Release workflow | Qualification | +|---|---|---|---| +| Python `sidemantic` | Root `pyproject.toml` | `publish.yml` | Full suite, built wheel and DAX smoke; separate Pyodide CI | +| Python `sidemantic-rs` (`sidemantic_rs` import) | `sidemantic-rs/pyproject.toml` | `sidemantic-rs-wheels.yml` | Install and smoke each actual native wheel, plus sdist creation | +| Rust `sidemantic` crate and base CLI | `sidemantic-rs/Cargo.toml` | `rust-runtime-release.yml` | Crate package/dry-run, packaged CLI compilation smoke | +| Rust CLI and companion binaries | `sidemantic-rs/Cargo.toml` | `release-rust-binaries.yml` | Explicit existing release tag, matching crate version, binary smoke | +| Browser `sidemantic-wasm` | `sidemantic-wasm/package.json` | `wasm-package-release.yml` | WASM and consumer smoke tests | +| DuckDB extension | `sidemantic-duckdb/Makefile` | `duckdb-extension-release.yml` | Pinned DuckDB build and sqllogictests | -| Artifact | Package name | Current version | Release path | -|---|---|---:|---| -| Main Python package | `sidemantic` | `0.10.0` | `.github/workflows/publish.yml` | -| Rust runtime crate and CLI | `sidemantic` crate, `sidemantic` binary | `0.1.0` | `.github/workflows/rust-runtime-release.yml` | -| Python extension wheel | `sidemantic-rs`, module `sidemantic_rs` | `0.1.0` | `.github/workflows/sidemantic-rs-wheels.yml` | -| DuckDB extension | `sidemantic.duckdb_extension` | `0.1.0` source package | `.github/workflows/duckdb-extension-release.yml` | -| Native format | YAML and SQL contract | `1` | `docs/native-format.md` | +Workflow files live in `.github/workflows/`. Rust crate and wheel versions must +match; package metadata tests enforce this. Root Python and WASM versions are +separate. The DuckDB extension is tied to the DuckDB build version, currently +`v1.5.5`. Do not use a release intended for a different DuckDB ABI. -Keep the Rust crate version and `sidemantic-rs` Python extension version in lockstep. The package metadata test enforces this. +## Install from a checkout or downloaded artifact -## Rust CLI Install - -Until the crate is published, install from source: - -```bash -cargo install --path sidemantic-rs --locked -``` - -For a checked-out repository without installation: - -```bash -cargo run --manifest-path sidemantic-rs/Cargo.toml --bin sidemantic -- validate ./models -``` - -After the crate is published, the intended install path is: - -```bash -cargo install sidemantic --version 0.1.0 --locked -``` - -GitHub release CLI binaries are produced by the `Rust Runtime Release` workflow for Linux, macOS, and Windows. Treat those binaries as release artifacts for the Rust native runtime, not as replacements for the main Python CLI. - -## Rust Runtime Release - -Use `.github/workflows/rust-runtime-release.yml`. - -The workflow is safe to run as a check: - -- `cargo test --locked --test package_metadata` -- `cargo package --locked --no-verify` -- `cargo publish --locked --dry-run` -- release-mode CLI builds for Linux, macOS, and Windows - -Publishing to crates.io is opt-in. Run the workflow manually with `publish_crate: true`. If `CARGO_REGISTRY_TOKEN` is absent, the publish step exits successfully after printing a skip message. - -Tag pushes matching `sidemantic-rs-v*` or `rust-runtime-v*` build and attach CLI artifacts to a GitHub release. Manual runs can also set `create_github_release: true`. - -## Python Extension Wheels - -The Python extension package is named `sidemantic-rs` and imports as `sidemantic_rs`. - -Install from a published wheel: +The Python CLI remains the normal entry point: ```bash -uv add sidemantic-rs +uv tool install . +sidemantic validate ./models ``` -Build and smoke-test locally: +For the optional Rust binding, install a compatible downloaded wheel into the +same environment as Sidemantic. Source installation requires a Rust toolchain: ```bash -cd sidemantic-rs -uvx maturin build --release --out dist -uv run --no-project --with dist/*.whl tests/python_wheel_smoke.py +uv add ./sidemantic-rs +sidemantic validate ./models --engine rust --no-fallback ``` -Build the lightweight Python-only feature wheel: +The release wheel enables `python-adbc`. ADBC execution also requires an actual +database driver; a successful compiler smoke does not establish driver availability. +A compile-only binding can be built with +`uvx maturin build --manifest-path sidemantic-rs/Cargo.toml --no-default-features --features python`. +Core Python imports and Pyodide do not require either Rust wheel. -```bash -cd sidemantic-rs -uvx maturin build --no-default-features --features python --out dist-python -uv run --no-project --with dist-python/*.whl tests/python_wheel_python_smoke.py -``` - -Build the ADBC-enabled wheel: +For the standalone base Rust CLI: ```bash -cd sidemantic-rs -uvx maturin build --no-default-features --features python-adbc --out dist-adbc -uv run --no-project --with dist-adbc/*.whl tests/python_wheel_adbc_smoke.py +cargo install --path sidemantic-rs --locked +sidemantic compile --models ./models --metric orders.revenue ``` -Use `.github/workflows/sidemantic-rs-wheels.yml` for release wheels. It builds Linux, macOS, and Windows wheels plus an sdist, then smokes a host wheel. PyPI upload is opt-in with `publish_pypi: true`; if `MATURIN_PYPI_TOKEN` is absent, upload is skipped without failing the workflow. - -## DuckDB Extension Package - -The DuckDB extension is currently documented as a source-build path. Do not document `INSTALL sidemantic FROM community` as the primary path until community extension publication is complete. - -See `docs/duckdb-extension.md` for build and load commands. - -Use `.github/workflows/duckdb-extension-release.yml` to build a Linux extension artifact and run the sqllogictests. The repository currently supports DuckDB `v1.5.5` only, matching the vendored `extension-ci-tools` checkout. GitHub release upload is optional and controlled by `create_github_release`. - -Community extension publication remains a separate release step until repository signing, platform matrix, and DuckDB community registry metadata are finalized. - -## Version Compatibility - -| Python package | Rust runtime crate | `sidemantic-rs` wheel | Native format | DuckDB extension | DuckDB build target | -|---|---:|---:|---:|---:|---:| -| `0.10.0` | `0.1.0` | `0.1.0` | `1` | `0.1.0` source package | `1.4.2` | - -Compatibility rules: - -- Native format `1` is the contract shared by Python, Rust, WASM, and DuckDB extension paths. -- Rust parses native YAML and native SQL definitions directly. It does not parse external adapter formats. -- The Python extension wheel version must match the Rust crate version. -- DuckDB extension artifacts are tied to the DuckDB version used at build time. -- The main Python package should not depend on `sidemantic-rs` until Rust installation is reliable on supported platforms. +The Rust and Python CLIs both use the executable name `sidemantic`; choose an +isolated installation or invoke the desired binary by its full path. The base +Rust release uses default features and is a compiler/rewriter. It does not include +ADBC execution or the optional server, MCP, LSP, and workbench binaries. The +`release-rust-binaries.yml` workflow attaches a separate companion-binary bundle +to an explicitly selected existing release. Its service features still do not +enable ADBC. Neither artifact silently substitutes the Python CLI. + +Rust directly accepts native YAML/SQL, Cube YAML, and OSI YAML. Other source +formats continue through the Python import and migration path. See +[rust-engine-mode.md](rust-engine-mode.md) for the graph handoff and engine contract. + +For DuckDB build/load instructions, use [duckdb-extension.md](duckdb-extension.md). +The release workflow currently produces Linux amd64 artifacts only. Community +registry publication, signing, and additional platforms remain separate work; +`INSTALL sidemantic FROM community` is not an established installation path here. + +## Release checks and publication boundaries + +The Rust crate/CLI, wheel, and DuckDB workflows support manual qualification with +publication inputs left false. Changes to those workflows also run qualification +on pull requests. Wheel tests install the uploaded matrix outputs on matching +Linux x86_64/arm64, macOS x86_64/arm64, and Windows x86_64 hosts; they do not rebuild +a different wheel for acceptance. The CLI smoke compiles a model from a temporary +directory using the packaged executable. Production release jobs do not restore +or publish compiler caches. + +Publishing remains an explicit operational action. Existing tag triggers can +attach GitHub assets or publish the WASM package; manual publish/release flags +must not be enabled for qualification. Some workflows skip publishing when their +registry token is absent, so a green build alone does not prove publication. +Verify registry and release assets separately after any authorized release. diff --git a/sidemantic-rs/tests/packaged_cli_smoke.py b/sidemantic-rs/tests/packaged_cli_smoke.py new file mode 100644 index 000000000..c532bb63d --- /dev/null +++ b/sidemantic-rs/tests/packaged_cli_smoke.py @@ -0,0 +1,36 @@ +# /// script +# requires-python = ">=3.11" +# /// +"""Exercise a packaged CLI without depending on a repository checkout.""" + +import subprocess +import sys +import tempfile +from pathlib import Path + + +def main() -> None: + executable = Path(sys.argv[1]).resolve(strict=True) + with tempfile.TemporaryDirectory() as directory: + models = Path(directory) / "models.yaml" + models.write_text("""models: + - name: orders + table: orders + metrics: + - name: revenue + agg: sum + sql: amount +""") + result = subprocess.run( + [str(executable), "compile", "--models", str(models), "--metric", "orders.revenue"], + cwd=directory, + check=True, + text=True, + capture_output=True, + ) + if "SUM(" not in result.stdout.upper() or "amount" not in result.stdout: + raise AssertionError(f"packaged CLI did not compile the model: {result.stdout}") + + +if __name__ == "__main__": + main() From 47730c0ff85860f277536a1c3a89bbaf3470ac99 Mon Sep 17 00:00:00 2001 From: Nico Ritschel Date: Sun, 13 Sep 2026 16:21:14 -0700 Subject: [PATCH 2/2] Verify the packed WASM package before publishing --- .github/workflows/wasm-package-release.yml | 25 +++++--- sidemantic-wasm/README.md | 14 +++++ sidemantic-wasm/scripts/package_consumer.mjs | 65 ++++++++++++++++++++ sidemantic-wasm/scripts/smoke_package.mjs | 27 ++++++++ 4 files changed, 122 insertions(+), 9 deletions(-) create mode 100644 sidemantic-wasm/scripts/package_consumer.mjs create mode 100644 sidemantic-wasm/scripts/smoke_package.mjs diff --git a/.github/workflows/wasm-package-release.yml b/.github/workflows/wasm-package-release.yml index 9d36376dc..0b6c2e9b1 100644 --- a/.github/workflows/wasm-package-release.yml +++ b/.github/workflows/wasm-package-release.yml @@ -14,6 +14,9 @@ on: pull_request: paths: - "sidemantic-wasm/**" + - "sidemantic-rs/**" + - "Cargo.toml" + - "Cargo.lock" - ".github/workflows/wasm-package-release.yml" permissions: @@ -38,11 +41,6 @@ jobs: with: targets: wasm32-unknown-unknown - - name: Cache cargo - uses: Swatinem/rust-cache@v2 - with: - workspaces: . - - name: Install wasm-bindgen-cli matching the crate working-directory: . run: | @@ -75,8 +73,17 @@ jobs: bun run scripts/smoke_client.mjs bun run scripts/smoke_vite.mjs - - name: Check package contents - run: npm pack --dry-run + - name: Pack and smoke test shipped package + run: | + bun pm pack --ignore-scripts --filename sidemantic-wasm.tgz + bun run scripts/smoke_package.mjs sidemantic-wasm.tgz + + - name: Upload tested package + uses: actions/upload-artifact@v4 + with: + name: sidemantic-wasm-package + path: sidemantic-wasm/sidemantic-wasm.tgz + if-no-files-found: error - name: Set up npm registry if: ${{ (github.event_name == 'workflow_dispatch' && inputs.publish_npm == true) || startsWith(github.ref, 'refs/tags/sidemantic-wasm-v') }} @@ -94,5 +101,5 @@ jobs: echo "NPM_TOKEN not set; skipping publish." exit 0 fi - # wasm/ is already built above; --ignore-scripts avoids a redundant rebuild. - npm publish --access public --ignore-scripts + # Publish the exact tarball exercised by the isolated consumer above. + npm publish sidemantic-wasm.tgz --access public --ignore-scripts diff --git a/sidemantic-wasm/README.md b/sidemantic-wasm/README.md index 437bce796..4430b3229 100644 --- a/sidemantic-wasm/README.md +++ b/sidemantic-wasm/README.md @@ -107,6 +107,20 @@ The bundle is generated from the `sidemantic-rs` crate. Requires the Rust `wasm3 bun run build # runs scripts/build.sh -> wasm/ ``` +To check the distributable after building: + +```bash +bun pm pack --ignore-scripts --filename sidemantic-wasm.tgz +bun run scripts/smoke_package.mjs sidemantic-wasm.tgz +``` + +The release workflow installs this tarball into an isolated consumer with lifecycle +scripts disabled, imports the public entry points, checks bundled declaration and +WASM files, and exercises compilation, rewriting, client transport, and the installed +codegen CLI. It uploads the tested tarball and uses that same file for an authorized +npm publish. These Node checks cover package completeness; browser runtime parity +still requires the browser tests. + ## License AGPL-3.0-only. See [LICENSE](./LICENSE). diff --git a/sidemantic-wasm/scripts/package_consumer.mjs b/sidemantic-wasm/scripts/package_consumer.mjs new file mode 100644 index 000000000..a0f2ea641 --- /dev/null +++ b/sidemantic-wasm/scripts/package_consumer.mjs @@ -0,0 +1,65 @@ +// Copied outside the checkout by smoke_package.mjs. All runtime imports resolve +// through the installed tarball's public export map, including the WASM asset. +import assert from "node:assert/strict"; +import { readFileSync, writeFileSync } from "node:fs"; +import { execFileSync } from "node:child_process"; +import { createSidemanticRuntime } from "sidemantic-wasm"; +import { createClient } from "sidemantic-wasm/client"; +import { createWasmTransport } from "sidemantic-wasm/adapters/wasm"; +import { generateClientSchema } from "sidemantic-wasm/codegen"; + +const packageRoot = new URL("./", import.meta.resolve("sidemantic-wasm")); +const manifest = JSON.parse(readFileSync(new URL("package.json", packageRoot), "utf8")); +for (const [subpath, entry] of Object.entries(manifest.exports)) { + const specifier = subpath === "." ? manifest.name : manifest.name + subpath.slice(1); + if (typeof entry === "string") { + assert.ok(readFileSync(new URL(import.meta.resolve(specifier))).length > 0); + } else { + await import(specifier); + assert.ok(readFileSync(new URL(entry.types, packageRoot)).length > 0, `Missing types for ${specifier}`); + } +} + +const models = ` +models: + - name: orders + table: orders + primary_key: id + dimensions: + - {name: status, type: categorical, sql: status} + metrics: + - {name: revenue, agg: sum, sql: amount} +`; +writeFileSync("models.yml", models); +const wasmUrl = readFileSync(new URL(import.meta.resolve("sidemantic-wasm/sidemantic_bg.wasm"))); +const runtime = await createSidemanticRuntime({ wasmUrl }); +const query = { metrics: ["orders.revenue"], dimensions: ["orders.status"] }; +assert.deepEqual(runtime.validate(models, query), []); +assert.match(runtime.compile(models, query), /select/i); +assert.match(runtime.rewrite(models, "SELECT orders.revenue, orders.status FROM orders"), /select/i); +assert.ok(runtime.loadGraph(models)); +assert.match(await generateClientSchema(models, { wasmUrl }), /revenue/); + +let executedSql; +const transport = await createWasmTransport({ + models, + wasmUrl, + execute: async (sql) => { + executedSql = sql; + return [{ status: "complete", revenue: 12 }]; + }, +}); +const client = createClient({ + models: { orders: { + dimensions: { status: { kind: "categorical", ts: "string" } }, + metrics: { revenue: { agg: "sum", ts: "number" } }, + } }, + topMetrics: [], +}, { run: transport.run }); +assert.deepEqual(await client.query(query), [{ status: "complete", revenue: 12 }]); +assert.match(executedSql, /select/i); + +execFileSync("node_modules/.bin/sidemantic-codegen", [ + "types", "models.yml", "--no-yaml", "--out", "schema.ts", +], { stdio: "inherit" }); +assert.match(readFileSync("schema.ts", "utf8"), /revenue/); diff --git a/sidemantic-wasm/scripts/smoke_package.mjs b/sidemantic-wasm/scripts/smoke_package.mjs new file mode 100644 index 000000000..e1f4ba70d --- /dev/null +++ b/sidemantic-wasm/scripts/smoke_package.mjs @@ -0,0 +1,27 @@ +// Install a packed release into an isolated consumer without running lifecycle +// scripts. No source checkout or Rust toolchain is available to repair omissions. +// Run after packing: bun run scripts/smoke_package.mjs sidemantic-wasm.tgz +import { copyFileSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { spawnSync } from "node:child_process"; + +if (process.argv.length !== 3) throw new Error("Expected the package tarball path"); +const tarball = resolve(process.argv[2]); +const consumer = mkdtempSync(join(tmpdir(), "sidemantic-package-")); + +function run(command, args) { + const result = spawnSync(command, args, { cwd: consumer, stdio: "inherit" }); + if (result.error) throw result.error; + if (result.status !== 0) throw new Error(`${command} failed with status ${result.status}`); +} + +try { + writeFileSync(join(consumer, "package.json"), JSON.stringify({ private: true, type: "module" })); + copyFileSync(new URL("./package_consumer.mjs", import.meta.url), join(consumer, "consumer.mjs")); + run("bun", ["add", "--ignore-scripts", tarball]); + run("node", ["consumer.mjs"]); + console.log("SMOKE_PACKAGE_OK"); +} finally { + rmSync(consumer, { recursive: true, force: true }); +}