diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml new file mode 100644 index 0000000..2322a4f --- /dev/null +++ b/.github/actions/build/action.yml @@ -0,0 +1,48 @@ +name: "Build" +description: "Sets up environment and builds the WASM package" + +inputs: + node-version: + description: "Node.js version to install" + required: false + default: "24" + setup-npm-registry: + description: "Setup NPM registry URL" + required: false + default: "false" + +runs: + using: "composite" + steps: + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + with: + node-version: ${{ inputs.node-version }} + registry-url: ${{ inputs.setup-npm-registry == 'true' && 'https://registry.npmjs.org' || '' }} + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + + - name: Install cargo-binstall + uses: cargo-bins/cargo-binstall@main + + - name: Install wasm-pack + shell: bash + run: cargo binstall wasm-pack -y + + - name: Install just + uses: extractions/setup-just@v2 + + - name: Install npm dependencies + shell: bash + working-directory: ./wasm + run: npm install + + - name: Build WASM + shell: bash + run: just build-wasm diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 0000000..0d4379e --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,93 @@ +# Publishes @bulletxyz/sdk-wasm to npm. Triggered by the `v` git tag +# release-plz creates after a release PR is merged (see release-plz.yml). +# release-plz.toml pins this to a single workspace-wide `v` tag rather +# than per-crate tags, so both crates ship under one release. +# +# The version is already baked into wasm/Cargo.toml and wasm/package.json at this +# point, so this workflow only builds and publishes — no version bump, no commits. +# +# Authentication uses npm OIDC Trusted Publishers; configure at: +# https://www.npmjs.com/package/@bulletxyz/sdk-wasm/access +# See: https://docs.npmjs.com/generating-provenance-statements#publishing-packages-with-provenance-via-github-actions + +name: NPM Publish + +on: + push: + tags: + - "v*" + workflow_dispatch: + inputs: + tag: + description: "Existing git tag to publish (e.g. v0.0.14)" + required: true + type: string + +env: + CARGO_TERM_COLOR: always + CARGO_NET_GIT_FETCH_WITH_CLI: true + CI: 1 + +concurrency: + group: npm-publish + cancel-in-progress: false + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Resolve ref + id: ref + env: + INPUT_TAG: ${{ inputs.tag }} + PUSH_REF: ${{ github.ref }} + run: | + if [ -n "${INPUT_TAG:-}" ]; then + echo "ref=${INPUT_TAG}" >> "$GITHUB_OUTPUT" + else + echo "ref=${PUSH_REF}" >> "$GITHUB_OUTPUT" + fi + + - name: Checkout tag + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + ref: ${{ steps.ref.outputs.ref }} + persist-credentials: false + fetch-depth: 1 + + - name: Verify tag matches Cargo + package.json versions + id: version + env: + REF: ${{ steps.ref.outputs.ref }} + run: | + set -euo pipefail + TAG="${REF#refs/tags/}" + V="${TAG#v}" + if ! echo "$V" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$'; then + echo "::error::Tag '$TAG' is not a valid semver tag" >&2 + exit 1 + fi + CARGO_V=$(grep -m1 '^version' wasm/Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/') + PKG_V=$(node -p "require('./wasm/package.json').version") + if [ "$V" != "$CARGO_V" ] || [ "$V" != "$PKG_V" ]; then + echo "::error::Version mismatch — tag=$V wasm/Cargo.toml=$CARGO_V wasm/package.json=$PKG_V" >&2 + exit 1 + fi + echo "version=$V" >> "$GITHUB_OUTPUT" + + - name: Build + uses: ./.github/actions/build + with: + setup-npm-registry: "true" + + - name: Publish to npm + working-directory: wasm + env: + VERSION: ${{ steps.version.outputs.version }} + run: | + set -euo pipefail + TAG=$(echo "$VERSION" | grep -q '-' && echo "rc" || echo "latest") + npm publish --provenance --tag "$TAG" --access public diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml index 7fdc033..ac6e01e 100644 --- a/.github/workflows/release-plz.yml +++ b/.github/workflows/release-plz.yml @@ -31,7 +31,7 @@ jobs: with: fetch-depth: 0 token: ${{ steps.app-token.outputs.token }} - persist-credentials: false + persist-credentials: true - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable @@ -39,7 +39,50 @@ jobs: id: cargo-auth uses: rust-lang/crates-io-auth-action@bbd81622f20ce9e2dd9622e3218b975523e45bbe # v1.0.4 - - uses: release-plz/action@1528104d2ca23787631a1c1f022abb64b34c1e11 # v0.5.128 + - name: Run release-plz + id: release-plz + uses: release-plz/action@1528104d2ca23787631a1c1f022abb64b34c1e11 # v0.5.128 env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} CARGO_REGISTRY_TOKEN: ${{ steps.cargo-auth.outputs.token }} + + # release-plz bumps Rust crate versions in Cargo.toml, but doesn't know about + # wasm/package.json. Mirror the new wasm/Cargo.toml version onto the same + # release PR so the Rust crate and the @bulletxyz/sdk-wasm npm package stay + # in lockstep. The actual `npm publish` is handled by npm-publish.yml, + # which is triggered by the v tag release-plz creates after merge. + - name: Sync wasm/package.json to release PR + if: ${{ steps.release-plz.outputs.pr != '' }} + env: + PR_JSON: ${{ steps.release-plz.outputs.pr }} + run: | + set -euo pipefail + + BRANCH=$(echo "$PR_JSON" | jq -r '.head_branch') + if [ -z "$BRANCH" ] || [ "$BRANCH" = "null" ]; then + echo "No head_branch in release PR output; skipping package.json sync" + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + git fetch origin "$BRANCH" + git checkout "$BRANCH" + + # cargo pkgid outputs "path+file:///…/wasm#bullet-rust-sdk-wasm@0.0.14" on + # modern Cargo, or "…#0.0.14" on older versions. Take whatever follows the + # final '#' or '@'. + V=$(cargo pkgid -p bullet-rust-sdk-wasm | sed -E 's/^.*[#@]([^#@]+)$/\1/') + echo "Syncing wasm/package.json to version $V" + + (cd wasm && npm version "$V" --no-git-tag-version --allow-same-version) + + if git diff --quiet -- wasm/package.json; then + echo "wasm/package.json already at $V; nothing to commit" + exit 0 + fi + + git add wasm/package.json + git commit -m "chore: sync wasm/package.json to $V" + git push origin "$BRANCH" diff --git a/Cargo.toml b/Cargo.toml index b05f4f9..b9321a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,11 @@ [workspace] members = ["rust", "wasm"] -resolver = "2" +resolver = "3" + +[workspace.package] +edition = "2024" +license = "MIT" +repository = "https://github.com/bulletxyz/bullet-rust-sdk" [workspace.dependencies] bon = "3.9.0" diff --git a/flake.nix b/flake.nix index 80e61de..4d03f23 100644 --- a/flake.nix +++ b/flake.nix @@ -87,6 +87,7 @@ packages = [ rust pkgs.cargo-nextest + pkgs.cargo-edit pkgs.just (makeWasmPack pkgs) pkgs.pkg-config diff --git a/release-plz.toml b/release-plz.toml index 670e3c3..a4df6b2 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -17,10 +17,21 @@ commit_parsers = [ { message = "^build", skip = true }, ] -# wasm crate ships to npm as @bulletxyz/sdk-wasm via a separate workflow_dispatch -# flow; release-plz only manages the Rust crate. -# `publish = false` mirrors wasm/Cargo.toml so release-plz's consistency check passes. +# Force a single shared `v` tag for the whole release. By default +# release-plz uses per-package `-v` tags in multi-package +# workspaces; we override that on the Rust crate and disable tag/release +# creation for the wasm crate entirely. The wasm crate still gets its version +# bumped in lockstep with the Rust crate (it path-depends on it) and its +# `npm publish` is driven from the same `v` tag by npm-publish.yml. +[[package]] +name = "bullet-rust-sdk" +git_tag_name = "v{{ version }}" + +# wasm crate ships to npm as @bulletxyz/sdk-wasm (not crates.io). Skip +# crates.io publish, and skip the separate git tag / GitHub release so we +# don't get a duplicate `bullet-rust-sdk-wasm-v` tag. [[package]] name = "bullet-rust-sdk-wasm" -release = false publish = false +git_tag_enable = false +git_release_enable = false diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 2651b5f..76f9ae6 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "bullet-rust-sdk" version = "0.0.13" -edition = "2024" -license = "MIT" +edition.workspace = true +license.workspace = true +repository.workspace = true description = "Rust SDK for the Bullet trading platform" homepage = "https://www.bullet.xyz" -repository = "https://github.com/bulletxyz/bullet-rust-sdk" readme = "../README.md" keywords = ["bullet", "trading", "exchange", "sdk"] categories = ["api-bindings", "cryptography::cryptocurrencies"] diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index 2fe7da8..4265340 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "bullet-rust-sdk-wasm" version = "0.0.13" -edition = "2024" -license = "MIT" +edition.workspace = true +license.workspace = true +repository.workspace = true description = "WebAssembly bindings for the Bullet trading SDK" -repository = "https://github.com/bulletxyz/bullet-rust-sdk" publish = false # ships to npm as @bulletxyz/sdk-wasm, not crates.io [lib] diff --git a/wasm/package.json b/wasm/package.json index 3c70200..8f969fa 100644 --- a/wasm/package.json +++ b/wasm/package.json @@ -21,7 +21,9 @@ ], "type": "module", "scripts": { - "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" + "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", + "build": "cd .. && just build-wasm", + "prepublishOnly": "[ -n \"$CI\" ] || npm run build" }, "devDependencies": { "@jest/globals": "30.3.0",