From 371d338a8b02bcdf129558fd1f7dc25cf31e47cf Mon Sep 17 00:00:00 2001 From: kingchenc Date: Thu, 21 May 2026 21:06:42 +0200 Subject: [PATCH] Release 0.1.1: fix Node publish + bump everything v0.1.0 reached crates.io, PyPI, and the wickra-wasm npm package, but the wickra Node binding never published because the workflow called `napi create-npm-dirs` (plural). The actual napi CLI command is `create-npm-dir` (singular, per target). Changes that make the next tag's release actually finish: - workflow: replace the broken step with a loop that runs `napi create-npm-dir -t ` for each of our four build targets. - workflow: every registry publish step now treats "version already uploaded" as success. That makes re-runs (and partial-failure recoveries) safe instead of failing the whole job. - bindings/node/package.json: trim the napi.triples list to the four platforms we actually build (linux-x64-gnu, darwin-x64, darwin-arm64, win32-x64-msvc). The previous defaults+9-extras configuration would have made napi prepublish expect platform packages we never produced. - bindings/node/index.js: switch from local-only binary lookup to the proper napi loader that tries the local `.node` file first and falls back to the per-platform npm subpackage that's installed as an optional dependency in production. - Versions across all four published packages bumped to 0.1.1 so the Node binding can publish for the first time alongside refreshed cargo/pypi/wasm artefacts. --- .github/workflows/release.yml | 37 ++++++++++++++------ Cargo.lock | 12 +++---- Cargo.toml | 4 +-- bindings/node/index.js | 62 ++++++++++++++++++++++------------ bindings/node/package.json | 15 +++----- bindings/python/pyproject.toml | 2 +- 6 files changed, 82 insertions(+), 50 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6aae0419..d838ec58 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,24 +20,35 @@ jobs: - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - - name: Publish wickra-core - run: cargo publish -p wickra-core --token "$CARGO_REGISTRY_TOKEN" + # Idempotent publishing: if the version is already on crates.io we + # treat that as success so re-runs of the workflow don't fail. + - name: Publish wickra-core (idempotent) + run: | + out=$(cargo publish -p wickra-core --token "$CARGO_REGISTRY_TOKEN" 2>&1) && echo "$out" \ + || (echo "$out" | grep -q "already uploaded\|already exists" && echo "skip: already published" \ + || (echo "$out"; exit 1)) env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - name: Wait for crates.io index to refresh run: sleep 45 - - name: Publish wickra-data - run: cargo publish -p wickra-data --token "$CARGO_REGISTRY_TOKEN" + - name: Publish wickra-data (idempotent) + run: | + out=$(cargo publish -p wickra-data --token "$CARGO_REGISTRY_TOKEN" 2>&1) && echo "$out" \ + || (echo "$out" | grep -q "already uploaded\|already exists" && echo "skip: already published" \ + || (echo "$out"; exit 1)) env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - name: Wait for crates.io index to refresh run: sleep 45 - - name: Publish wickra - run: cargo publish -p wickra --token "$CARGO_REGISTRY_TOKEN" + - name: Publish wickra (idempotent) + run: | + out=$(cargo publish -p wickra --token "$CARGO_REGISTRY_TOKEN" 2>&1) && echo "$out" \ + || (echo "$out" | grep -q "already uploaded\|already exists" && echo "skip: already published" \ + || (echo "$out"; exit 1)) env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} @@ -170,9 +181,12 @@ jobs: path: bindings/node/artifacts pattern: bindings-* - - name: Create npm// scaffolding + - name: Create npm// scaffolding (one per target) working-directory: bindings/node - run: npx napi create-npm-dirs + run: | + for t in x86_64-unknown-linux-gnu x86_64-apple-darwin aarch64-apple-darwin x86_64-pc-windows-msvc; do + npx napi create-npm-dir -t "$t" . + done - name: Move binaries into platform package layout working-directory: bindings/node @@ -228,8 +242,11 @@ jobs: fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2)); " - - name: Publish wickra-wasm to npm + - name: Publish wickra-wasm to npm (idempotent) working-directory: bindings/wasm/pkg - run: npm publish --access public + run: | + out=$(npm publish --access public 2>&1) && echo "$out" \ + || (echo "$out" | grep -q "You cannot publish over" && echo "skip: version already on npm" \ + || (echo "$out"; exit 1)) env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/Cargo.lock b/Cargo.lock index 5cff9121..837b9c13 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1883,7 +1883,7 @@ dependencies = [ [[package]] name = "wickra" -version = "0.1.0" +version = "0.1.1" dependencies = [ "approx", "criterion", @@ -1894,7 +1894,7 @@ dependencies = [ [[package]] name = "wickra-core" -version = "0.1.0" +version = "0.1.1" dependencies = [ "approx", "proptest", @@ -1904,7 +1904,7 @@ dependencies = [ [[package]] name = "wickra-data" -version = "0.1.0" +version = "0.1.1" dependencies = [ "approx", "csv", @@ -1922,7 +1922,7 @@ dependencies = [ [[package]] name = "wickra-node" -version = "0.1.0" +version = "0.1.1" dependencies = [ "napi", "napi-build", @@ -1932,7 +1932,7 @@ dependencies = [ [[package]] name = "wickra-python" -version = "0.1.0" +version = "0.1.1" dependencies = [ "numpy", "pyo3", @@ -1941,7 +1941,7 @@ dependencies = [ [[package]] name = "wickra-wasm" -version = "0.1.0" +version = "0.1.1" dependencies = [ "console_error_panic_hook", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 283cd64f..2e648109 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ members = [ exclude = [] [workspace.package] -version = "0.1.0" +version = "0.1.1" authors = ["Wickra Contributors"] edition = "2021" rust-version = "1.75" @@ -23,7 +23,7 @@ keywords = ["finance", "trading", "indicators", "technical-analysis", "ta"] categories = ["finance", "mathematics", "science"] [workspace.dependencies] -wickra-core = { path = "crates/wickra-core", version = "0.1.0" } +wickra-core = { path = "crates/wickra-core", version = "0.1.1" } thiserror = "2" rayon = "1.10" diff --git a/bindings/node/index.js b/bindings/node/index.js index 8faeeb46..306036fb 100644 --- a/bindings/node/index.js +++ b/bindings/node/index.js @@ -1,30 +1,50 @@ /* eslint-disable */ /* prettier-ignore */ -// This loader is generated by `napi build --platform` at publish time. For -// editable development just `require` the local debug binary that napi places -// at the package root. +// Platform-aware loader for the Wickra Node native binding. +// +// In production (after `npm install wickra`) the actual `.node` binary +// lives inside a per-platform subpackage that npm installs as an +// optional dependency — e.g. `wickra-linux-x64-gnu`. In development +// (after `napi build --platform`) the binary sits next to this file. +// We try the local path first and fall back to the subpackage so the +// same loader works in both modes. +const { platform, arch } = process; const { join } = require('node:path'); const { existsSync } = require('node:fs'); -const { platform, arch } = process; -function loadNative() { - // Try precompiled per-platform binary first (published wheels do this). - const candidates = [ - `./wickra.${platform}-${arch}.node`, - `./wickra.${platform}-${arch}-musl.node`, - './wickra.node', - ]; - for (const c of candidates) { - const p = join(__dirname, c); - if (existsSync(p)) { - return require(p); - } +const TARGETS = { + 'linux-x64': 'linux-x64-gnu', + 'darwin-x64': 'darwin-x64', + 'darwin-arm64': 'darwin-arm64', + 'win32-x64': 'win32-x64-msvc', +}; + +function load() { + const key = `${platform}-${arch}`; + const target = TARGETS[key]; + if (!target) { + throw new Error( + `wickra: this platform/architecture combination is not supported (${key}). ` + + 'Open an issue at https://github.com/kingchenc/wickra/issues with your platform details.' + ); + } + + const localBinary = join(__dirname, `wickra.${target}.node`); + if (existsSync(localBinary)) { + return require(localBinary); + } + + try { + return require(`wickra-${target}`); + } catch (err) { + throw new Error( + `wickra: failed to load the native binding for ${key}. ` + + `Expected either ${localBinary} or the wickra-${target} package to be installed. ` + + `Run \`npm install\` again, or build from source with \`npm run build\`. ` + + `Underlying error: ${err.message}` + ); } - throw new Error( - `Wickra: no precompiled binary found for ${platform}-${arch}. ` + - 'Build from source with `napi build --release` or install a platform-specific package.' - ); } -module.exports = loadNative(); +module.exports = load(); diff --git a/bindings/node/package.json b/bindings/node/package.json index c965f9f1..3959b916 100644 --- a/bindings/node/package.json +++ b/bindings/node/package.json @@ -1,6 +1,6 @@ { "name": "wickra", - "version": "0.1.0", + "version": "0.1.1", "description": "Streaming-first technical indicators: incremental, fast, install-free. Node bindings powered by Rust.", "author": "kingchenc ", "main": "index.js", @@ -32,17 +32,12 @@ "napi": { "name": "wickra", "triples": { - "defaults": true, + "defaults": false, "additional": [ - "x86_64-unknown-linux-musl", - "aarch64-unknown-linux-gnu", - "i686-pc-windows-msvc", - "armv7-unknown-linux-gnueabihf", + "x86_64-unknown-linux-gnu", + "x86_64-apple-darwin", "aarch64-apple-darwin", - "aarch64-linux-android", - "x86_64-unknown-freebsd", - "aarch64-unknown-linux-musl", - "aarch64-pc-windows-msvc" + "x86_64-pc-windows-msvc" ] } }, diff --git a/bindings/python/pyproject.toml b/bindings/python/pyproject.toml index 31f86f8d..bb23ce3e 100644 --- a/bindings/python/pyproject.toml +++ b/bindings/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "wickra" -version = "0.1.0" +version = "0.1.1" description = "Streaming-first technical indicators: incremental, fast, install-free." readme = "README.md" license = { text = "PolyForm-Noncommercial-1.0.0" }