Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
232 changes: 232 additions & 0 deletions .github/workflows/npm_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
name: npm release

# Publishes 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 four packages.
#
# 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 four variants at the tag's version
workflow_dispatch:
inputs:
engine:
description: "Which variant to release"
type: choice
required: true
default: all
options: [all, hermes, jsc, quickjs, v8]
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
default: ""
rebuild_framework:
description: "Rebuild the shared framework (tools + dotnet-bridge) from source instead of shipping the copies committed under template/framework."
type: boolean
required: false
default: false

# Minimal default token permissions for every job; the publish job declares id-token:write locally.
permissions:
contents: read

# A variant is the same framework as @nativescript/windows — the WinUI 3 app template,
# dotnet-bridge and tools — with the engine's runtime DLL staged as framework/libs/x64/
# nativescript.dll. `template/build.ps1 -Engine <engine>` copies that shared scaffolding out of
# template/framework and builds the engine cdylib into it, so a release is:
#
# setup (resolve version + engine matrix) -> build (one Windows job per engine, npm pack)
# -> publish (npm, OIDC)
#
# Everything the shared framework needs is committed under template/framework, so the default build
# only compiles the engine. `rebuild_framework` regenerates that scaffolding from source instead.
jobs:
setup:
name: Resolve version
runs-on: ubuntu-latest
outputs:
npm_version: ${{ steps.out.outputs.NPM_VERSION }}
npm_tag: ${{ steps.out.outputs.NPM_TAG }}
build_matrix: ${{ steps.out.outputs.BUILD_MATRIX }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 22
# setup-node v6 enables dependency caching by default when it detects a package manager —
# a cache-poisoning vector, worst in the jobs that publish. Disabled on every setup-node
# step in this workflow (also keeps pre-v6 behavior).
package-manager-cache: false
- name: Compute version, dist-tag and engine matrix
id: out
env:
# env indirection keeps the dispatch inputs out of shell interpolation
INPUT_VERSION: ${{ inputs.version }}
INPUT_ENGINE: ${{ inputs.engine || 'all' }}
run: node ./scripts/resolve-release.mjs --version "$INPUT_VERSION" --engine "$INPUT_ENGINE"

build:
name: Build ${{ matrix.engine }}
runs-on: windows-latest
needs: setup
strategy:
fail-fast: false
# { include: [{ engine: hermes }, ...] } — the engines selected in `setup`.
matrix: ${{ fromJSON(needs.setup.outputs.build_matrix) }}
env:
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
ENGINE: ${{ matrix.engine }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false # disable to prevent leaking credentials to build scripts
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 22
package-manager-cache: false
- uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
with:
toolchain: stable
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
with:
# The engine packages are excluded from the root workspace (their C builds / prebuilt
# engine links must not run on a plain `cargo build`), so each has its own target dir.
workspaces: |
. -> target
packages/windows-${{ matrix.engine }} -> target
key: ${{ matrix.engine }}
# quickjs' napi shim uses clang/GCC C extensions that MSVC `cl` rejects; build.rs compiles it
# with clang-cl from this exact path and silently falls back to `cl` (which then fails deep in
# the C build) when it is absent. Fail early with the real reason instead.
- name: Ensure clang-cl (quickjs shim)
if: ${{ matrix.engine == 'quickjs' }}
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$clangCl = 'C:\Program Files\LLVM\bin\clang-cl.exe'
if (-not (Test-Path $clangCl)) {
Write-Host "clang-cl not found at $clangCl — installing LLVM."
choco install llvm --no-progress -y
}
if (-not (Test-Path $clangCl)) {
throw "clang-cl is required to compile the quickjs napi shim and is not at $clangCl."
}
& $clangCl --version
- name: Setup .NET (framework rebuild)
if: ${{ 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
if: ${{ 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.
# -SkipArm64: the variants are x64-only (`cpu: ["x64"]` in their package.json).
run: |
$ErrorActionPreference = 'Stop'
./template/build.ps1 -SkipArm64 -SkipRelease -SkipDevtools
- name: Set package version
shell: pwsh
working-directory: packages/windows-${{ matrix.engine }}
run: npm version $env:NPM_VERSION --no-git-tag-version --allow-same-version
- name: Build ${{ matrix.engine }} framework
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
# build.ps1 reports through Write-Host, so the log has to be teed off the information
# stream (`*>&1`) rather than stdout for the WARN check below to see anything.
$logPath = Join-Path $env:RUNNER_TEMP "build-$env:ENGINE.log"
./template/build.ps1 -Engine $env:ENGINE *>&1 | Tee-Object -FilePath $logPath
# A missing engine runtime DLL (hermes.dll, JavaScriptCore.dll, …) beside the cdylib is
# only a warning there, but it ships a framework that fails to load at app start.
if (Select-String -Path $logPath -Pattern 'expected engine DLL not found' -Quiet) {
throw "An engine runtime DLL was not staged — see the WARN above."
}
$dll = "packages/windows-$env:ENGINE/framework/libs/x64/nativescript.dll"
if (-not (Test-Path $dll)) { throw "Runtime DLL missing: $dll" }
- name: Pack
shell: pwsh
working-directory: packages/windows-${{ matrix.engine }}
run: |
$ErrorActionPreference = 'Stop'
# --pack-destination does not create the directory.
New-Item -ItemType Directory -Force -Path ../../dist | Out-Null
npm pack --pack-destination ../../dist
- name: Upload npm package artifact
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: npm-package-${{ matrix.engine }}
path: dist/nativescript-windows-${{ matrix.engine }}-${{ needs.setup.outputs.npm_version }}.tgz
if-no-files-found: error

publish:
name: Publish ${{ matrix.engine }}
runs-on: ubuntu-latest
# Part of the npm trusted-publisher match — see the header note before renaming.
environment: npm-publish
needs:
- setup
- build
permissions:
contents: read
id-token: write # OIDC trusted publishing + provenance
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.setup.outputs.build_matrix) }}
env:
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
NPM_TAG: ${{ needs.setup.outputs.npm_tag }}
ENGINE: ${{ matrix.engine }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: 22
package-manager-cache: false
registry-url: "https://registry.npmjs.org"
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: npm-package-${{ matrix.engine }}
path: dist
- name: Update npm (required for OIDC trusted publishing)
run: |
corepack enable npm
corepack install -g npm@11.5.1
test "$(npm --version)" = "11.5.1"
test "$(npx --version)" = "11.5.1"
- name: Publish package (OIDC trusted publishing)
if: ${{ vars.USE_NPM_TOKEN != 'true' }}
run: |
echo "Publishing @nativescript/windows-$ENGINE@$NPM_VERSION to NPM with tag $NPM_TAG via OIDC trusted publishing..."
unset NODE_AUTH_TOKEN
if [ -n "${NPM_CONFIG_USERCONFIG:-}" ]; then
rm -f "$NPM_CONFIG_USERCONFIG"
fi
npm publish "./dist/nativescript-windows-${ENGINE}-${NPM_VERSION}.tgz" --tag "$NPM_TAG" --access public --provenance
env:
NODE_AUTH_TOKEN: ""
- name: Publish package (granular token)
if: ${{ vars.USE_NPM_TOKEN == 'true' }}
run: |
echo "Publishing @nativescript/windows-$ENGINE@$NPM_VERSION to NPM with tag $NPM_TAG via granular token..."
npm publish "./dist/nativescript-windows-${ENGINE}-${NPM_VERSION}.tgz" --tag "$NPM_TAG" --access public --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
88 changes: 88 additions & 0 deletions .github/workflows/windows-napi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
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.
on:
push:
tags:
- 'windows-napi-v*'
pull_request:
paths:
- 'windows-napi/**'
- 'runtime/**'
- 'metadata/**'
- '.github/workflows/windows-napi.yml'
workflow_dispatch:

env:
CARGO_TERM_COLOR: always

jobs:
build:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
settings:
- target: x86_64-pc-windows-msvc
- target: aarch64-pc-windows-msvc
name: build ${{ matrix.settings.target }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.settings.target }}
- uses: Swatinem/rust-cache@v2
with:
key: ${{ matrix.settings.target }}
- name: Install npm dependencies
working-directory: windows-napi
run: npm ci
- name: Build .node
working-directory: windows-napi
run: npx napi build --platform --release --target ${{ matrix.settings.target }}
- name: Run test suite (x64 only — the runner cannot execute arm64 binaries)
if: matrix.settings.target == 'x86_64-pc-windows-msvc'
working-directory: windows-napi
# Server runner SKUs can lack optional feature packs some suites touch (e.g. Media
# Foundation for the MediaPlayer event test), so failures here warn instead of block;
# the authoritative run is the local one on a client SKU.
continue-on-error: true
run: npm test
- uses: actions/upload-artifact@v4
with:
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
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
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@v4
with:
path: windows-napi/artifacts
- name: Distribute artifacts into npm/<triple> sub-packages
working-directory: windows-napi
run: npx napi artifacts
- name: Publish
working-directory: windows-napi
# `napi prepublish` (prepublishOnly script) publishes each npm/<triple> sub-package,
# then the root package publishes with them as optionalDependencies.
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# Rust build artifacts (root + nested workspace/package crates)
/target
target/
/**/*.rs.bk
/Cargo.lock

# Node
node_modules/

# Logs
*.log

.DS_Store
.vs

Expand Down
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[workspace]
resolver = "2"
members = ["metadata", "playground", "runtime", "runtime-binding-gen", "sbg", "nativescript", "typings-generator", "integration-tests", "runtime-devtools", "metadata-generator","tools/dotnet-tool"]
members = ["metadata", "playground", "runtime", "runtime-binding-gen", "sbg", "nativescript", "typings-generator", "integration-tests", "runtime-devtools", "metadata-generator","tools/dotnet-tool", "windows-napi"]
# Excluded so their C builds / prebuilt-engine links don't run on normal `cargo` invocations.
exclude = ["packages/common", "packages/demo", "packages/windows-quickjs", "packages/windows-hermes", "packages/windows-jsc", "packages/windows-v8"]

[workspace.dependencies]
windows = "0.62.2"
Expand Down
Loading
Loading