name: publish on: # Automated schedule - canary releases from master schedule: - cron: "0 3 * * 2-6" # Tuesdays - Saturdays, at 3am UTC # Manual trigger - PR releases or dry-runs (based on workflow inputs) workflow_dispatch: inputs: pr: description: "PR Number - If set, a real release will be created for the branch associated with the given PR number. If blank, a dry-run of the currently selected branch will be performed." required: false type: number release: types: [ published ] # Dynamically generate the display name for the GitHub UI based on the event type and inputs run-name: ${{ github.event.inputs.pr && format('PR Release for {0}', github.event.inputs.pr) || github.event_name == 'schedule' && 'Canary Release' || github.event_name == 'workflow_dispatch' && !github.event.inputs.pr && 'Release Dry-Run' || github.ref_name }} env: DEBUG: napi:* NX_RUN_GROUP: ${{ github.run_id }}-${{ github.run_attempt }} CYPRESS_INSTALL_BINARY: 0 NODE_VERSION: 20.19.0 PNPM_VERSION: 10.11.1 # Aligned with root package.json (pnpm/action-setup will helpfully error if out of sync) jobs: # We first need to determine the version we are releasing, and if we need a custom repo or ref to use for the git checkout in subsequent steps. # These values depend upon the event type that triggered the workflow: # # - schedule: # - We are running a canary release which always comes from the master branch, we can use default ref resolution # in actions/checkout. The exact version will be generated within scripts/nx-release.ts. # # - release: # - We are running a full release which is based on the tag that triggered the release event, we can use default # ref resolution in actions/checkout. The exact version will be generated within scripts/nx-release.ts. # # - workflow_dispatch: # - We are either running a dry-run on the current branch, in which case the version will be statica and we can use # default ref resolution in actions/checkout, or we are creating a PR release for the given PR number, in which case # we should generate an applicable version number within publish-resolve-data.js and use a custom ref of the PR branch name. resolve-required-data: name: Resolve Required Data if: ${{ github.repository_owner == 'nrwl' }} runs-on: ubuntu-latest outputs: version: ${{ steps.script.outputs.version }} dry_run_flag: ${{ steps.script.outputs.dry_run_flag }} success_comment: ${{ steps.script.outputs.success_comment }} publish_branch: ${{ steps.script.outputs.publish_branch }} ref: ${{ steps.script.outputs.ref }} repo: ${{ steps.script.outputs.repo }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} steps: # Default checkout on the triggering branch so that the latest publish-resolve-data.js script is available - uses: actions/checkout@v4 # Set up pnpm and node so that we can verify our setup and that the NPM_TOKEN secret will work later - uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - name: Setup node uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} registry-url: 'https://registry.npmjs.org' check-latest: true # Ensure that the NPM_TOKEN secret is still valid before wasting any time deriving data or building projects - name: Check NPM Credentials run: npm whoami && echo "NPM credentials are valid" || (echo "NPM credentials are invalid or have expired." && exit 1) - name: Resolve and set checkout and version data to use for release id: script uses: actions/github-script@v7 env: PR_NUMBER: ${{ github.event.inputs.pr }} with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const script = require('${{ github.workspace }}/scripts/publish-resolve-data.js'); await script({ github, context, core }); - name: (PR Release Only) Check out latest master if: ${{ steps.script.outputs.ref != '' }} uses: actions/checkout@v4 with: # Check out the latest master branch to get its copy of nx-release.ts repository: nrwl/nx ref: master path: latest-master-checkout - name: (PR Release Only) Check out PR branch if: ${{ steps.script.outputs.ref != '' }} uses: actions/checkout@v4 with: # Check out the PR branch to get its copy of nx-release.ts repository: ${{ steps.script.outputs.repo }} ref: ${{ steps.script.outputs.ref }} path: pr-branch-checkout - name: (PR Release Only) Ensure that nx-release.ts has not changed in the PR being released if: ${{ steps.script.outputs.ref != '' }} env: FILE_TO_COMPARE: "scripts/nx-release.ts" run: | if ! cmp -s "latest-master-checkout/${{ env.FILE_TO_COMPARE }}" "pr-branch-checkout/${{ env.FILE_TO_COMPARE }}"; then echo "🛑 Error: The file ${{ env.FILE_TO_COMPARE }} is different on the ${{ steps.script.outputs.ref }} branch on ${{ steps.script.outputs.repo }} vs latest master on nrwl/nx, cancelling workflow. If you did not modify the file, then you likely just need to rebase/merge latest master." exit 1 else echo "✅ The file ${{ env.FILE_TO_COMPARE }} is identical between the ${{ steps.script.outputs.ref }} branch on ${{ steps.script.outputs.repo }} and latest master on nrwl/nx." fi build: needs: [ resolve-required-data ] if: ${{ github.repository_owner == 'nrwl' }} strategy: fail-fast: false matrix: settings: - host: macos-13 target: x86_64-apple-darwin setup: |- rustup target add aarch64-apple-darwin build: | pnpm nx run-many --target=build-native -- --target=x86_64-apple-darwin - host: windows-latest setup: |- rustup target add aarch64-pc-windows-msvc build: pnpm nx run-many --target=build-native -- --target=x86_64-pc-windows-msvc target: x86_64-pc-windows-msvc # Windows 32bit (not needed) # - host: windows-latest # build: | # yarn nx -- run-many --target=build-native -- --target=i686-pc-windows-msvc # target: i686-pc-windows-msvc - host: ubuntu-latest target: x86_64-unknown-linux-gnu docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian build: |- set -e && npm i -g pnpm@10.11.1 --force && pnpm --version && pnpm install --frozen-lockfile && rustup target add aarch64-unknown-linux-gnu && pnpm nx run-many --verbose --target=build-native -- --target=x86_64-unknown-linux-gnu - host: ubuntu-latest target: x86_64-unknown-linux-musl docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine build: |- set -e && npm i -g pnpm@10.11.1 --force && pnpm --version && pnpm install --frozen-lockfile && rustup target add x86_64-unknown-linux-musl && pnpm nx run-many --verbose --target=build-native -- --target=x86_64-unknown-linux-musl - host: macos-13 target: aarch64-apple-darwin setup: |- rustup target add aarch64-apple-darwin build: | sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*; export CC=$(xcrun -f clang); export CXX=$(xcrun -f clang++); SYSROOT=$(xcrun --sdk macosx --show-sdk-path); export CFLAGS="-isysroot $SYSROOT -isystem $SYSROOT"; pnpm nx run-many --target=build-native -- --target=aarch64-apple-darwin - host: ubuntu-latest target: aarch64-unknown-linux-gnu docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 build: |- set -e && npm i -g pnpm@10.11.1 --force && pnpm --version && pnpm install --frozen-lockfile && rustup target add aarch64-unknown-linux-gnu && pnpm nx run-many --verbose --target=build-native -- --target=aarch64-unknown-linux-gnu - host: ubuntu-latest target: armv7-unknown-linux-gnueabihf setup: | sudo apt-get update sudo apt-get install gcc-arm-linux-gnueabihf -y rustup target add armv7-unknown-linux-gnueabihf build: | CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=/usr/bin/arm-linux-gnueabihf-gcc pnpm nx run-many --target=build-native -- --target=armv7-unknown-linux-gnueabihf # Android (not needed) # - host: ubuntu-latest # target: aarch64-linux-android # build: | # pnpm nx run-many --target=build-native -- --target=aarch64-linux-android # - host: ubuntu-latest # target: armv7-linux-androideabi # build: | # pnpm nx run-many --target=build-native -- --target=armv7-linux-androideabi - host: ubuntu-latest target: aarch64-unknown-linux-musl docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine build: |- set -e && rustup target add aarch64-unknown-linux-musl && npm i -g pnpm@10.11.1 --force && pnpm --version && pnpm install --frozen-lockfile && pnpm nx run-many --verbose --target=build-native -- --target=aarch64-unknown-linux-musl - host: windows-latest target: aarch64-pc-windows-msvc setup: |- rustup target add aarch64-pc-windows-msvc build: pnpm nx run-many --target=build-native -- --target=aarch64-pc-windows-msvc name: stable - ${{ matrix.settings.target }} - node@18 runs-on: ${{ matrix.settings.host }} steps: - uses: actions/checkout@v4 with: repository: ${{ needs.resolve-required-data.outputs.repo }} ref: ${{ needs.resolve-required-data.outputs.ref }} - uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - name: Setup node uses: actions/setup-node@v4 if: ${{ !matrix.settings.docker }} with: node-version: ${{ env.NODE_VERSION }} check-latest: true cache: 'pnpm' - name: Install uses: dtolnay/rust-toolchain@stable if: ${{ !matrix.settings.docker }} with: targets: ${{ matrix.settings.target }} - 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-registry - uses: goto-bus-stop/setup-zig@v2 if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' }} with: version: 0.10.0 - name: Setup toolchain run: ${{ matrix.settings.setup }} if: ${{ matrix.settings.setup }} shell: bash - name: Setup node x86 if: matrix.settings.target == 'i686-pc-windows-msvc' run: yarn config set supportedArchitectures.cpu "ia32" shell: bash - name: Install dependencies if: ${{ !matrix.settings.docker }} run: pnpm install --frozen-lockfile timeout-minutes: 30 - name: Setup node x86 uses: actions/setup-node@v4 if: matrix.settings.target == 'i686-pc-windows-msvc' with: node-version: ${{ env.NODE_VERSION }} check-latest: true cache: pnpm architecture: x86 - name: Build in docker uses: addnab/docker-run-action@v3 if: ${{ matrix.settings.docker }} with: image: ${{ matrix.settings.docker }} options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build run: ${{ matrix.settings.build }} - name: Build run: ${{ matrix.settings.build }} if: ${{ !matrix.settings.docker }} shell: bash - name: Upload artifact uses: actions/upload-artifact@v4 with: name: bindings-${{ matrix.settings.target }} path: | packages/**/*.node packages/**/*.wasm if-no-files-found: error build-freebsd: needs: [ resolve-required-data ] if: ${{ github.repository_owner == 'nrwl' }} runs-on: ubuntu-latest name: Build FreeBSD timeout-minutes: 45 steps: - uses: actions/checkout@v4 with: repository: ${{ needs.resolve-required-data.outputs.repo }} ref: ${{ needs.resolve-required-data.outputs.ref }} - name: Build id: build uses: cross-platform-actions/action@v0.25.0 env: DEBUG: napi:* RUSTUP_IO_THREADS: 1 NX_PREFER_TS_NODE: true PLAYWRIGHT_BROWSERS_PATH: 0 with: operating_system: freebsd version: '14.0' architecture: x86-64 environment_variables: DEBUG RUSTUP_IO_THREADS CI NX_PREFER_TS_NODE PLAYWRIGHT_BROWSERS_PATH shell: bash run: | env whoami sudo pkg install -y -f node libnghttp2 www/npm git sudo npm install --location=global --ignore-scripts pnpm@10.11.1 curl https://sh.rustup.rs -sSf --output rustup.sh sh rustup.sh -y --profile minimal --default-toolchain stable source "$HOME/.cargo/env" echo "~~~~ rustc --version ~~~~" rustc --version echo "~~~~ node -v ~~~~" node -v echo "~~~~ pnpm --version ~~~~" pnpm --version pwd ls -lah whoami env freebsd-version pnpm install --frozen-lockfile --ignore-scripts pnpm nx run-many --verbose --outputStyle stream --target=build-native -- --target=x86_64-unknown-freebsd pnpm nx reset rm -rf node_modules rm -rf dist echo "KILL ALL NODE PROCESSES" killall node || true echo "COMPLETE" - name: Upload artifact uses: actions/upload-artifact@v4 with: name: bindings-freebsd path: packages/**/*.node if-no-files-found: error publish: if: ${{ github.repository_owner == 'nrwl' }} name: Publish runs-on: ubuntu-latest permissions: id-token: write contents: write pull-requests: write needs: - resolve-required-data - build-freebsd - build env: GH_TOKEN: ${{ github.token }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NPM_CONFIG_PROVENANCE: true steps: - uses: actions/checkout@v4 with: repository: ${{ needs.resolve-required-data.outputs.repo }} ref: ${{ needs.resolve-required-data.outputs.ref }} - uses: pnpm/action-setup@v4 with: version: ${{ env.PNPM_VERSION }} - name: Setup node uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} registry-url: 'https://registry.npmjs.org' check-latest: true cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts # This command will appropriately fail if no artifacts are available - name: List artifacts run: ls -R artifacts shell: bash - name: Build Wasm run: | wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-23/wasi-sdk-23.0-x86_64-linux.tar.gz tar -xvf wasi-sdk-23.0-x86_64-linux.tar.gz pnpm build:wasm - name: Publish env: VERSION: ${{ needs.resolve-required-data.outputs.version }} DRY_RUN: ${{ needs.resolve-required-data.outputs.dry_run_flag }} PUBLISH_BRANCH: ${{ needs.resolve-required-data.outputs.publish_branch }} NX_VERBOSE_LOGGING: true run: | echo "" # Create and check out the publish branch git checkout -b $PUBLISH_BRANCH echo "" echo "Version set to: $VERSION" echo "DRY_RUN set to: $DRY_RUN" echo "" pnpm nx-release --local=false $VERSION $DRY_RUN - name: (Stable Release Only) Trigger Docs Release # Publish docs only on a full release if: ${{ !github.event.release.prerelease && github.event_name == 'release' }} run: npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/release-docs.ts - name: (PR Release Only) Create comment for successful PR release if: success() && github.event.inputs.pr uses: actions/github-script@v7 env: SUCCESS_COMMENT: ${{ needs.resolve-required-data.outputs.success_comment }} with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const successComment = JSON.parse(process.env.SUCCESS_COMMENT); await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: ${{ github.event.inputs.pr }}, body: successComment }); pr_failure_comment: # Run this job if it is a PR release, running on the nrwl origin, and any of the required jobs failed if: ${{ github.repository_owner == 'nrwl' && github.event.inputs.pr && always() && contains(needs.*.result, 'failure') }} needs: [ resolve-required-data, build, build-freebsd, publish ] name: (PR Release Failure Only) Create comment for failed PR release runs-on: ubuntu-latest steps: - name: Create comment for failed PR release uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} # This script is intentionally kept inline (and e.g. not generated in publish-resolve-data.js) # to ensure that an error within the data generation itself is not missed. script: | const message = ` Failed to publish a PR release of this pull request, triggered by @${{ github.triggering_actor }}. See the failed workflow run at: https://github.com/nrwl/nx/actions/runs/${{ github.run_id }} `; await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: ${{ github.event.inputs.pr }}, body: message });