diff --git a/.github/workflows/npm_release.yml b/.github/workflows/npm_release.yml index 19d47f4..abe953b 100644 --- a/.github/workflows/npm_release.yml +++ b/.github/workflows/npm_release.yml @@ -1,17 +1,18 @@ name: npm release -# Publishes the classic runtime — @nativescript/windows — and the four engine variants — -# @nativescript/windows-{hermes,jsc,quickjs,v8} — to npm via OIDC trusted publishing. npm matches -# a trusted publisher on repository + workflow FILENAME + environment, so `npm_release.yml` and the -# `npm-publish` environment below are part of the credential: renaming either breaks publishing for -# all five packages, and every package needs its own trusted-publisher entry on npmjs.com. +# Publishes the classic runtime — @nativescript/windows — the four engine variants — +# @nativescript/windows-{hermes,jsc,quickjs,v8} — and the Node-API addon — @nativescript/windows-napi +# — to npm via OIDC trusted publishing. npm matches a trusted publisher on repository + workflow +# FILENAME + environment, so `npm_release.yml` and the `npm-publish` environment below are part of +# the credential: renaming either breaks publishing for all six packages, and every package needs +# its own trusted-publisher entry on npmjs.com. # # NOTE: `paths` filters deliberately do NOT accompany the tag trigger — a tag push can have an # empty changed-file set, and paths+tags together silently skip the run. on: push: tags: - - "v*" # -> all five packages at the tag's version + - "v*" # -> all six packages at the tag's version workflow_dispatch: inputs: engine: @@ -19,7 +20,7 @@ on: type: choice required: true default: all - options: [all, classic, hermes, jsc, quickjs, v8] + options: [all, classic, hermes, jsc, quickjs, v8, napi] version: description: "Release version to cut, e.g. 0.1.0-alpha.2 (dist-tag follows the prerelease id; a plain version publishes 'latest'). Leave empty for a rolling 'next' build." required: false @@ -30,7 +31,7 @@ on: required: false default: false skip_arm64: - description: "Classic only: build x64-only, skipping the arm64 cross-compiles (the engine variants are x64-only regardless)." + description: "Classic and napi: build x64-only, skipping the arm64 cross-compiles (the engine variants are x64-only regardless)." type: boolean required: false default: false @@ -50,6 +51,10 @@ permissions: # template/framework and builds the engine cdylib into packages/windows-/framework # as framework/libs/x64/nativescript.dll (x64 only, no devtools). # +# napi `napi build --target ` for each of `rust_targets` puts a +# windows-napi/windows..node per arch into the one package; its loader picks +# the one for the running CPU. +# # A release is: setup (resolve version + package matrix) -> build (one Windows job per package, # npm pack) -> publish (npm, OIDC). Everything the variants need is committed under # template/framework, so a variant build only compiles the engine; `rebuild_framework` regenerates @@ -118,7 +123,7 @@ jobs: - uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable with: toolchain: stable - # classic cross-compiles the arm64 runtime and tools; the variants build for the host. + # classic and napi cross-compile arm64 as well; the variants build for the host. targets: ${{ matrix.rust_targets }} - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: @@ -134,6 +139,7 @@ jobs: # profile dir is target/release for a host build and target// for the # classic cross builds, so every v8 build dir under the target roots is checked. - name: Re-fetch rusty_v8's static lib when the cache pruned it + if: ${{ matrix.engine != 'napi' }} shell: pwsh run: | foreach ($root in @('target', (Join-Path $env:PACKAGE_DIR 'target'))) { @@ -169,14 +175,14 @@ jobs: # The classic build publishes ManifestMerger and dotnet-typings-gen with `dotnet publish` # every time; a variant only needs the SDK when it regenerates the shared framework. - name: Setup .NET - if: ${{ matrix.engine == 'classic' || github.event_name == 'push' || inputs.rebuild_framework }} + if: ${{ matrix.engine == 'classic' || (matrix.engine != 'napi' && (github.event_name == 'push' || inputs.rebuild_framework)) }} uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 with: # dotnet-typings-gen targets net10.0; ManifestMerger is netstandard2.0 and builds on it. dotnet-version: "10.x" - name: Rebuild shared framework # Tag releases always rebuild, so a published variant never ships binaries built off CI. - if: ${{ matrix.engine != 'classic' && (github.event_name == 'push' || inputs.rebuild_framework) }} + if: ${{ matrix.engine != 'classic' && matrix.engine != 'napi' && (github.event_name == 'push' || inputs.rebuild_framework) }} shell: pwsh # -SkipRelease -SkipDevtools: the classic V8 nativescript.dll is not staged into a variant # (the engine cdylib takes its place), so building it here would be thrown away. @@ -210,7 +216,27 @@ jobs: shell: pwsh working-directory: ${{ matrix.dir }} run: npm version $env:NPM_VERSION --no-git-tag-version --allow-same-version + - name: Build windows-napi + if: ${{ matrix.engine == 'napi' }} + shell: pwsh + working-directory: windows-napi + env: + RUST_TARGETS: ${{ matrix.rust_targets }} + run: | + $ErrorActionPreference = 'Stop' + npm ci + if ($LASTEXITCODE -ne 0) { throw "npm ci failed" } + foreach ($target in $env:RUST_TARGETS.Split(',')) { + npx napi build --platform --release --target $target + if ($LASTEXITCODE -ne 0) { throw "napi build failed for $target" } + } + $nodes = @(Get-ChildItem windows.*.node) + if ($nodes.Count -ne $env:RUST_TARGETS.Split(',').Count) { + throw "Expected one .node per target, found: $($nodes.Name -join ', ')" + } + $nodes | Format-Table Name, Length - name: Build ${{ matrix.engine }} framework + if: ${{ matrix.engine != 'napi' }} shell: pwsh run: | $ErrorActionPreference = 'Stop' diff --git a/.github/workflows/windows-napi.yml b/.github/workflows/windows-napi.yml index f90431f..154262f 100644 --- a/.github/workflows/windows-napi.yml +++ b/.github/workflows/windows-napi.yml @@ -1,11 +1,7 @@ name: windows-napi -# NOTE: `paths` filters deliberately do NOT accompany the tag trigger — a tag push can have an -# empty changed-file set, and paths+tags together silently skip the run. +# Builds and tests the addon on pull requests. Releases publish it from npm_release.yml. on: - push: - tags: - - 'windows-napi-v*' pull_request: paths: - 'windows-napi/**' @@ -60,37 +56,3 @@ jobs: name: bindings-${{ matrix.settings.target }} path: windows-napi/windows.*.node if-no-files-found: error - - publish: - name: publish to npm - if: startsWith(github.ref, 'refs/tags/windows-napi-v') - needs: build - runs-on: windows-latest - permissions: - contents: read - id-token: write # npm provenance - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0 - with: - node-version: 20 - registry-url: https://registry.npmjs.org - - name: Install npm dependencies - working-directory: windows-napi - run: npm ci - - name: Download all binding artifacts - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - path: windows-napi/artifacts - - name: Distribute artifacts into npm/ sub-packages - working-directory: windows-napi - run: npx napi artifacts - - name: Publish - working-directory: windows-napi - # `napi prepublish` (prepublishOnly script) publishes each npm/ sub-package, - # then the root package publishes with them as optionalDependencies. - run: npm publish --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - # Applies to the sub-package publishes `napi prepublish` runs as well. - NPM_CONFIG_PROVENANCE: "true" diff --git a/packages/README.md b/packages/README.md index a4279cf..92c9159 100644 --- a/packages/README.md +++ b/packages/README.md @@ -142,14 +142,10 @@ for the engine (above), then `npm publish` the package (each is `os:["win32"], c classic package also ships arm64 + devtools). The CLI picks one, like android's `jsEngine` — and because the packages are interchangeable, switching is a dependency swap. -**`@nativescript/windows-napi` — the Node/Bun/Deno `.node`** (`../windows-napi`): -napi-rs flow, **implemented in `.github/workflows/windows-napi.yml`**: a build matrix per triple -(`x86_64-pc-windows-msvc`, `aarch64-pc-windows-msvc`) runs `napi build --platform --release ---target ` → `windows..node` artifacts (the x64 job also runs the 366-check test -suite, non-blocking on server SKUs); pushing a **`windows-napi-v*` tag** triggers the publish job: -`napi artifacts` distributes the .node files into the committed `windows-napi/npm//` -sub-packages, `napi prepublish` (the `prepublishOnly` script) publishes those, then the root -package publishes with them as `optionalDependencies`. Needs the `NPM_TOKEN` repo secret. -`index.js`/`index.d.ts`/`package-lock.json` are generated but committed (the publish job doesn't -build). If arm64 proves unbuildable (rusty_v8/libffi cross), drop the triple from the workflow -matrix, `napi.triples`, `optionalDependencies`, and `npm/win32-arm64-msvc/`. +**`@nativescript/windows-napi` — the Node/Bun/Deno `.node`** (`../windows-napi`): released by +`.github/workflows/npm_release.yml` with the packages above (`engine: napi`, also part of `all`). +The build runs `napi build --platform --release --target ` for `x86_64-pc-windows-msvc` +and `aarch64-pc-windows-msvc` and packs both `windows..node` files into the one package; +the generated loader picks the one for the running CPU. `.github/workflows/windows-napi.yml` +builds both triples and runs the test suite on pull requests. `index.js`/`index.d.ts`/ +`package-lock.json` are generated but committed. diff --git a/scripts/resolve-release.mjs b/scripts/resolve-release.mjs index cd0466f..c184cda 100644 --- a/scripts/resolve-release.mjs +++ b/scripts/resolve-release.mjs @@ -11,7 +11,7 @@ // // One version covers every package in a run: the classic runtime and the four engine variants are // the same framework built from the same commit and differ only in the runtime DLL, so they -// release in lockstep. +// release in lockstep. The windows-napi addon is built from the same runtime crate and follows. // // The repo has no root package.json, so this stays dependency-free — no semver/dayjs, and no // `npm install` step in front of it. @@ -32,6 +32,8 @@ const PACKAGES = { jsc: { dir: "packages/windows-jsc", name: "@nativescript/windows-jsc" }, quickjs: { dir: "packages/windows-quickjs", name: "@nativescript/windows-quickjs" }, v8: { dir: "packages/windows-v8", name: "@nativescript/windows-v8" }, + // The Node-API addon: one package carrying a `windows..node` per target. + napi: { dir: "windows-napi", name: "@nativescript/windows-napi" }, }; const ENGINES = Object.keys(PACKAGES); @@ -40,7 +42,8 @@ const USAGE = `Usage: node scripts/resolve-release.mjs [--version ] [-- --version release version to cut (leading "v" allowed); empty or omitted resolves from GITHUB_REF (v* tag) or falls back to a rolling "next" prerelease --engine all (default) | ${ENGINES.join(" | ")} - --skip-arm64 build the classic package x64-only (the engine variants are x64-only regardless) + --skip-arm64 build classic and windows-napi x64-only (the engine variants are x64-only + regardless) -h, --help show this help`; let values; @@ -141,24 +144,24 @@ const tag = npmTag(parseVersion(version, "Release version"), version); // dir where package.json lives (npm version / npm pack run there) // npm_name the published name // tarball what `npm pack` emits ("@scope/name" -> "scope-name-.tgz") -// rust_targets extra Rust targets the toolchain must install (classic cross-compiles arm64) +// rust_targets Rust targets to install and build: classic and napi also build arm64, and +// napi builds one `.node` per target // rust_workspaces the Swatinem/rust-cache `workspaces` mapping; the engine packages are excluded // from the root workspace and build into their own target/ dir +const allArchs = values["skip-arm64"] + ? "x86_64-pc-windows-msvc" + : "x86_64-pc-windows-msvc,aarch64-pc-windows-msvc"; const matrix = { include: engines.map((e) => { const pkg = PACKAGES[e]; - const classic = e === "classic"; + const inRootWorkspace = e === "classic" || e === "napi"; return { engine: e, dir: pkg.dir, npm_name: pkg.name, tarball: `${pkg.name.replace(/^@/, "").replace("/", "-")}-${version}.tgz`, - rust_targets: classic - ? values["skip-arm64"] - ? "x86_64-pc-windows-msvc" - : "x86_64-pc-windows-msvc,aarch64-pc-windows-msvc" - : "", - rust_workspaces: classic ? ". -> target" : `. -> target\n${pkg.dir} -> target`, + rust_targets: inRootWorkspace ? allArchs : "", + rust_workspaces: inRootWorkspace ? ". -> target" : `. -> target\n${pkg.dir} -> target`, }; }), }; diff --git a/windows-napi/.gitignore b/windows-napi/.gitignore index 93648bb..71c31c5 100644 --- a/windows-napi/.gitignore +++ b/windows-napi/.gitignore @@ -1,5 +1,5 @@ node_modules/ -# napi-rs binary outputs (CI builds per-triple; npm// receives them at publish time) +# napi-rs binary outputs (the release build packs one per triple) *.node artifacts/ # index.js / index.d.ts / package-lock.json are generated but COMMITTED: the publish CI job diff --git a/windows-napi/npm/win32-arm64-msvc/README.md b/windows-napi/npm/win32-arm64-msvc/README.md deleted file mode 100644 index 37a6d7d..0000000 --- a/windows-napi/npm/win32-arm64-msvc/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `@nativescript/windows-napi-win32-arm64-msvc` - -This is the **aarch64-pc-windows-msvc** binary for `@nativescript/windows-napi` diff --git a/windows-napi/npm/win32-arm64-msvc/package.json b/windows-napi/npm/win32-arm64-msvc/package.json deleted file mode 100644 index 5b4bc57..0000000 --- a/windows-napi/npm/win32-arm64-msvc/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "@nativescript/windows-napi-win32-arm64-msvc", - "version": "0.1.0", - "os": [ - "win32" - ], - "cpu": [ - "arm64" - ], - "main": "windows.win32-arm64-msvc.node", - "files": [ - "windows.win32-arm64-msvc.node" - ], - "description": "Node-API WinRT interop for Windows.", - "engines": { - "node": ">= 18" - } -} \ No newline at end of file diff --git a/windows-napi/npm/win32-x64-msvc/README.md b/windows-napi/npm/win32-x64-msvc/README.md deleted file mode 100644 index 0859399..0000000 --- a/windows-napi/npm/win32-x64-msvc/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# `@nativescript/windows-napi-win32-x64-msvc` - -This is the **x86_64-pc-windows-msvc** binary for `@nativescript/windows-napi` diff --git a/windows-napi/npm/win32-x64-msvc/package.json b/windows-napi/npm/win32-x64-msvc/package.json deleted file mode 100644 index eb6fe67..0000000 --- a/windows-napi/npm/win32-x64-msvc/package.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "name": "@nativescript/windows-napi-win32-x64-msvc", - "version": "0.1.0", - "os": [ - "win32" - ], - "cpu": [ - "x64" - ], - "main": "windows.win32-x64-msvc.node", - "files": [ - "windows.win32-x64-msvc.node" - ], - "description": "Node-API WinRT interop for Windows.", - "engines": { - "node": ">= 18" - } -} \ No newline at end of file diff --git a/windows-napi/package-lock.json b/windows-napi/package-lock.json index c483153..8be08dd 100644 --- a/windows-napi/package-lock.json +++ b/windows-napi/package-lock.json @@ -1,11 +1,11 @@ { - "name": "@nativescript/windows", + "name": "@nativescript/windows-napi", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@nativescript/windows", + "name": "@nativescript/windows-napi", "version": "0.1.0", "os": [ "win32" diff --git a/windows-napi/package.json b/windows-napi/package.json index c151c21..5878bdb 100644 --- a/windows-napi/package.json +++ b/windows-napi/package.json @@ -34,20 +34,14 @@ "scripts": { "build": "napi build --platform --release", "build:debug": "napi build --platform", - "artifacts": "napi artifacts", - "prepublishOnly": "napi prepublish -t npm --skip-gh-release", "test": "node test/run-tests.js" }, "repository": { "type": "git", - "url": "https://github.com/triniwiz/windows-runtime.git", + "url": "https://github.com/NativeScript/windows", "directory": "windows-napi" }, "devDependencies": { "@napi-rs/cli": "^2.18.0" - }, - "optionalDependencies": { - "@nativescript/windows-napi-win32-x64-msvc": "0.1.0", - "@nativescript/windows-napi-win32-arm64-msvc": "0.1.0" } }