ci(book): fix publish #359
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| env: | |
| DEBUG: napi:* | |
| APP_NAME: spider-rs | |
| MACOSX_DEPLOYMENT_TARGET: "10.13" | |
| # Ensure provenance is always on (OIDC trusted publisher expects this) | |
| NPM_CONFIG_PROVENANCE: "true" | |
| permissions: | |
| contents: write | |
| id-token: write | |
| on: | |
| push: | |
| branches: [main] | |
| tags-ignore: ["**"] | |
| paths-ignore: | |
| - "**/*.md" | |
| - LICENSE | |
| - "**/*.gitignore" | |
| - .editorconfig | |
| - docs/** | |
| pull_request: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| name: stable - ${{ matrix.settings.target }} - node@20 | |
| runs-on: ${{ matrix.settings.host }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| settings: | |
| - host: macos-latest | |
| target: x86_64-apple-darwin | |
| build: yarn build --target x86_64-apple-darwin | |
| - host: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| build: yarn build --target x86_64-pc-windows-msvc | |
| - host: windows-latest | |
| target: i686-pc-windows-msvc | |
| build: | | |
| choco install openssl.light -y | |
| set OPENSSL_LIB_DIR=C:\Program Files\OpenSSL\lib | |
| set OPENSSL_INCLUDE_DIR=C:\Program Files\OpenSSL\include | |
| yarn build --target i686-pc-windows-msvc | |
| - host: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| setup: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| gcc build-essential cmake pkg-config perl libc6 \ | |
| ca-certificates openssl libssl-dev | |
| build: yarn build --target x86_64-unknown-linux-gnu | |
| - host: macos-latest | |
| target: aarch64-apple-darwin | |
| build: yarn build --target aarch64-apple-darwin | |
| # TEMP DISABLED (failing OpenSSL cross / android toolchain) | |
| # - host: ubuntu-latest | |
| # target: aarch64-unknown-linux-gnu | |
| # docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 | |
| # build: yarn build --target aarch64-unknown-linux-gnu | |
| # TEMP DISABLED (Android OpenSSL) | |
| # - host: ubuntu-latest | |
| # target: aarch64-linux-android | |
| # setup: | | |
| # sudo apt-get update | |
| # sudo apt-get install -y --no-install-recommends \ | |
| # ca-certificates curl git \ | |
| # build-essential cmake perl pkg-config clang \ | |
| # unzip zip | |
| # build: yarn build --target aarch64-linux-android | |
| # TEMP DISABLED (Android OpenSSL) | |
| # - host: ubuntu-latest | |
| # target: armv7-linux-androideabi | |
| # setup: | | |
| # sudo apt-get update | |
| # sudo apt-get install -y --no-install-recommends \ | |
| # ca-certificates curl git \ | |
| # build-essential cmake perl pkg-config clang \ | |
| # unzip zip | |
| # build: yarn build --target armv7-linux-androideabi | |
| - host: windows-latest | |
| target: aarch64-pc-windows-msvc | |
| build: | | |
| choco install openssl.light -y | |
| set OPENSSL_LIB_DIR=C:\Program Files\OpenSSL-Win64\lib | |
| set OPENSSL_INCLUDE_DIR=C:\Program Files\OpenSSL-Win64\include | |
| yarn build --target aarch64-pc-windows-msvc | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup node | |
| uses: actions/setup-node@v4 | |
| if: ${{ !matrix.settings.docker }} | |
| with: | |
| node-version: 20 | |
| cache: yarn | |
| - name: Install Rust (latest stable) | |
| uses: dtolnay/rust-toolchain@stable | |
| if: ${{ !matrix.settings.docker }} | |
| with: | |
| toolchain: stable | |
| targets: ${{ matrix.settings.target }} | |
| - name: Rust version (non-docker) | |
| if: ${{ !matrix.settings.docker }} | |
| run: | | |
| rustc -Vv | |
| cargo -Vv | |
| shell: bash | |
| - name: Cache cargo | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| .cargo-cache | |
| target/ | |
| key: ${{ matrix.settings.target }}-cargo-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }} | |
| - name: Setup toolchain deps | |
| if: ${{ matrix.settings.setup && !matrix.settings.docker }} | |
| run: ${{ matrix.settings.setup }} | |
| shell: bash | |
| - name: Install dependencies | |
| if: ${{ !matrix.settings.docker }} | |
| run: yarn --no-immutable | |
| - name: Setup node x86 | |
| uses: actions/setup-node@v4 | |
| if: matrix.settings.target == 'i686-pc-windows-msvc' | |
| with: | |
| node-version: 20 | |
| cache: yarn | |
| architecture: x86 | |
| - name: Build | |
| if: ${{ !matrix.settings.docker }} | |
| run: ${{ matrix.settings.build }} | |
| shell: bash | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: bindings-${{ matrix.settings.target }} | |
| path: ${{ env.APP_NAME }}.*.node | |
| if-no-files-found: error | |
| test-macOS-windows-binding: | |
| name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }} | |
| needs: [build] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| settings: | |
| - host: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| architecture: x64 | |
| - host: macos-latest | |
| target: aarch64-apple-darwin | |
| architecture: arm64 | |
| - host: macos-latest | |
| target: x86_64-apple-darwin | |
| architecture: x64 | |
| node: ["18", "20"] | |
| runs-on: ${{ matrix.settings.host }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| cache: yarn | |
| architecture: ${{ matrix.settings.architecture }} | |
| - name: Install dependencies | |
| run: yarn --no-immutable | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: bindings-${{ matrix.settings.target }} | |
| path: . | |
| - name: List packages | |
| run: ls -R . | |
| shell: bash | |
| test-linux-x64-gnu-binding: | |
| name: Test bindings on Linux-x64-gnu - node@${{ matrix.node }} | |
| needs: [build] | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| node: ["18", "20"] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| cache: yarn | |
| - name: Install dependencies | |
| run: yarn --no-immutable | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: bindings-x86_64-unknown-linux-gnu | |
| path: . | |
| - name: List packages | |
| run: ls -R . | |
| shell: bash | |
| publish: | |
| name: Publish | |
| runs-on: ubuntu-latest | |
| needs: | |
| - test-macOS-windows-binding | |
| - test-linux-x64-gnu-binding | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: yarn | |
| - name: Install dependencies | |
| run: yarn --no-immutable | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: artifacts | |
| - name: Move artifacts | |
| run: yarn artifacts | |
| - name: List packages | |
| run: ls -R ./npm | |
| shell: bash | |
| - name: Detect release + version | |
| id: rel | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| MSG="$(git log -1 --pretty=%B | tr -d '\r')" | |
| echo "Commit message: $MSG" | |
| if echo "$MSG" | grep -Eq "^[0-9]+\.[0-9]+\.[0-9]+$"; then | |
| echo "release=true" >> "$GITHUB_OUTPUT" | |
| echo "tag=latest" >> "$GITHUB_OUTPUT" | |
| echo "version=$MSG" >> "$GITHUB_OUTPUT" | |
| elif echo "$MSG" | grep -Eq "^[0-9]+\.[0-9]+\.[0-9]+"; then | |
| VER="$(echo "$MSG" | head -n1 | awk '{print $1}')" | |
| echo "release=true" >> "$GITHUB_OUTPUT" | |
| echo "tag=next" >> "$GITHUB_OUTPUT" | |
| echo "version=$VER" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "release=false" >> "$GITHUB_OUTPUT" | |
| echo "tag=" >> "$GITHUB_OUTPUT" | |
| echo "version=" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Skip publish (not a release) | |
| if: steps.rel.outputs.release != 'true' | |
| run: echo "Not a release, skipping publish" | |
| # ✅ REQUIRED for "publish always": authenticate with NPM_TOKEN | |
| - name: Configure npm auth + provenance (fail-fast) | |
| if: steps.rel.outputs.release == 'true' | |
| shell: bash | |
| env: | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| run: | | |
| set -euxo pipefail | |
| if [ -z "${NPM_TOKEN:-}" ]; then | |
| echo "NPM_TOKEN secret is not set" | |
| exit 1 | |
| fi | |
| # Always-auth so scoped packages publish reliably | |
| printf "@spider-rs:registry=https://registry.npmjs.org/\n//registry.npmjs.org/:_authToken=%s\n" "$NPM_TOKEN" > ~/.npmrc | |
| npm config set provenance true | |
| # Hard check token validity | |
| npm ping | |
| npm whoami | |
| # ✅ Make GitHub release creation idempotent (optional, but prevents 422) | |
| - name: Ensure GitHub release exists (idempotent) | |
| if: steps.rel.outputs.release == 'true' | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ steps.rel.outputs.version }} | |
| run: | | |
| set -euxo pipefail | |
| TAG="v${VERSION}" | |
| if gh release view "$TAG" --repo spider-rs/spider-nodejs >/dev/null 2>&1; then | |
| echo "GitHub release $TAG already exists; skipping create." | |
| else | |
| gh release create "$TAG" --repo spider-rs/spider-nodejs --title "$TAG" --notes "" | |
| fi | |
| # ✅ Publish only packages matching this release version (prevents stale 0.0.162 folders) | |
| - name: Publish platform packages (version-gated) | |
| if: steps.rel.outputs.release == 'true' | |
| shell: bash | |
| env: | |
| TAG: ${{ steps.rel.outputs.tag }} | |
| VERSION: ${{ steps.rel.outputs.version }} | |
| run: | | |
| set -euxo pipefail | |
| # Allowlist only targets you build right now | |
| ALLOW_DIRS=( | |
| "npm/win32-x64-msvc" | |
| "npm/win32-ia32-msvc" | |
| "npm/win32-arm64-msvc" | |
| "npm/darwin-x64" | |
| "npm/darwin-arm64" | |
| "npm/linux-x64-gnu" | |
| ) | |
| publish_dir () { | |
| local dir="$1" | |
| local pj="$dir/package.json" | |
| local v | |
| v="$(node -p "require('./$pj').version")" | |
| # Only publish folders that match the release version | |
| if [ "$v" != "$VERSION" ]; then | |
| echo "Skipping $dir (package.json version=$v, expected=$VERSION)" | |
| return 0 | |
| fi | |
| echo "Publishing $dir (version=$v) ..." | |
| if [ "$TAG" = "latest" ]; then | |
| (cd "$dir" && npm publish --access public) | |
| else | |
| (cd "$dir" && npm publish --access public --tag "$TAG") | |
| fi | |
| } | |
| for dir in "${ALLOW_DIRS[@]}"; do | |
| if [ -d "$dir" ] && [ -f "$dir/package.json" ]; then | |
| publish_dir "$dir" | |
| else | |
| echo "Skipping missing dir: $dir" | |
| fi | |
| done | |
| - name: Publish root package (ignore scripts) | |
| if: steps.rel.outputs.release == 'true' | |
| shell: bash | |
| env: | |
| TAG: ${{ steps.rel.outputs.tag }} | |
| VERSION: ${{ steps.rel.outputs.version }} | |
| run: | | |
| set -euxo pipefail | |
| ROOT_VER="$(node -p "require('./package.json').version")" | |
| if [ "$ROOT_VER" != "$VERSION" ]; then | |
| echo "Root package version=$ROOT_VER does not match release version=$VERSION" | |
| exit 1 | |
| fi | |
| if [ "$TAG" = "latest" ]; then | |
| npm publish --access public --ignore-scripts | |
| else | |
| npm publish --access public --tag "$TAG" --ignore-scripts | |
| fi |