Skip to content

Commit 8a0348f

Browse files
committed
fix: ci for classic builds
1 parent c24db6f commit 8a0348f

2 files changed

Lines changed: 176 additions & 69 deletions

File tree

‎.github/workflows/npm_release.yml‎

Lines changed: 123 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
11
name: npm release
22

3-
# Publishes the four engine variants — @nativescript/windows-{hermes,jsc,quickjs,v8} — to npm via
4-
# OIDC trusted publishing. npm matches a trusted publisher on repository + workflow FILENAME +
5-
# environment, so `npm_release.yml` and the `npm-publish` environment below are part of the
6-
# credential: renaming either breaks publishing for all four packages.
3+
# Publishes the classic runtime — @nativescript/windows — and the four engine variants —
4+
# @nativescript/windows-{hermes,jsc,quickjs,v8} — to npm via OIDC trusted publishing. npm matches
5+
# a trusted publisher on repository + workflow FILENAME + environment, so `npm_release.yml` and the
6+
# `npm-publish` environment below are part of the credential: renaming either breaks publishing for
7+
# all five packages, and every package needs its own trusted-publisher entry on npmjs.com.
78
#
89
# NOTE: `paths` filters deliberately do NOT accompany the tag trigger — a tag push can have an
910
# empty changed-file set, and paths+tags together silently skip the run.
1011
on:
1112
push:
1213
tags:
13-
- "v*" # -> all four variants at the tag's version
14+
- "v*" # -> all five packages at the tag's version
1415
workflow_dispatch:
1516
inputs:
1617
engine:
17-
description: "Which variant to release"
18+
description: "Which package to release"
1819
type: choice
1920
required: true
2021
default: all
21-
options: [all, hermes, jsc, quickjs, v8]
22+
options: [all, classic, hermes, jsc, quickjs, v8]
2223
version:
2324
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."
2425
required: false
2526
default: ""
2627
rebuild_framework:
27-
description: "Rebuild the shared framework (tools + dotnet-bridge) from source instead of shipping the copies committed under template/framework."
28+
description: "Engine variants: rebuild the shared framework (tools + dotnet-bridge) from source instead of shipping the copies committed under template/framework. (The classic build always rebuilds it.)"
29+
type: boolean
30+
required: false
31+
default: false
32+
skip_arm64:
33+
description: "Classic only: build x64-only, skipping the arm64 cross-compiles (the engine variants are x64-only regardless)."
2834
type: boolean
2935
required: false
3036
default: false
@@ -33,16 +39,21 @@ on:
3339
permissions:
3440
contents: read
3541

36-
# A variant is the same framework as @nativescript/windows — the WinUI 3 app template,
37-
# dotnet-bridge and tools — with the engine's runtime DLL staged as framework/libs/x64/
38-
# nativescript.dll. `template/build.ps1 -Engine <engine>` copies that shared scaffolding out of
39-
# template/framework and builds the engine cdylib into it, so a release is:
42+
# Every package is the same framework — the WinUI 3 app template, dotnet-bridge and tools — with a
43+
# runtime DLL staged as framework/libs/<arch>/nativescript.dll:
4044
#
41-
# setup (resolve version + engine matrix) -> build (one Windows job per engine, npm pack)
42-
# -> publish (npm, OIDC)
45+
# classic `template/build.ps1` builds the workspace `nativescript` cdylib (rusty_v8) for x64 and
46+
# arm64, release and devtools, rebuilds the tools (dotnet-tool, nsbundle_pack, sbg,
47+
# typings-generator, ManifestMerger, dotnet-typings-gen) and stages it all under
48+
# template/framework. Published from template/ as @nativescript/windows.
49+
# variant `template/build.ps1 -Engine <engine>` copies the shared scaffolding out of
50+
# template/framework and builds the engine cdylib into packages/windows-<engine>/framework
51+
# as framework/libs/x64/nativescript.dll (x64 only, no devtools).
4352
#
44-
# Everything the shared framework needs is committed under template/framework, so the default build
45-
# only compiles the engine. `rebuild_framework` regenerates that scaffolding from source instead.
53+
# A release is: setup (resolve version + package matrix) -> build (one Windows job per package,
54+
# npm pack) -> publish (npm, OIDC). Everything the variants need is committed under
55+
# template/framework, so a variant build only compiles the engine; `rebuild_framework` regenerates
56+
# that scaffolding from source instead.
4657
jobs:
4758
setup:
4859
name: Resolve version
@@ -66,25 +77,32 @@ jobs:
6677
# a cache-poisoning vector, worst in the jobs that publish. Disabled on every setup-node
6778
# step in this workflow (also keeps pre-v6 behavior).
6879
package-manager-cache: false
69-
- name: Compute version, dist-tag and engine matrix
80+
- name: Compute version, dist-tag and package matrix
7081
id: out
7182
env:
7283
# env indirection keeps the dispatch inputs out of shell interpolation
7384
INPUT_VERSION: ${{ inputs.version }}
7485
INPUT_ENGINE: ${{ inputs.engine || 'all' }}
75-
run: node ./scripts/resolve-release.mjs --version "$INPUT_VERSION" --engine "$INPUT_ENGINE"
86+
INPUT_SKIP_ARM64: ${{ inputs.skip_arm64 && 'true' || 'false' }}
87+
run: |
88+
args=(--version "$INPUT_VERSION" --engine "$INPUT_ENGINE")
89+
if [ "$INPUT_SKIP_ARM64" = "true" ]; then args+=(--skip-arm64); fi
90+
node ./scripts/resolve-release.mjs "${args[@]}"
7691
7792
build:
7893
name: Build ${{ matrix.engine }}
7994
runs-on: windows-latest
8095
needs: setup
8196
strategy:
8297
fail-fast: false
83-
# { include: [{ engine: hermes }, ...] } — the engines selected in `setup`.
98+
# { include: [{ engine, dir, npm_name, tarball, rust_targets, rust_workspaces }, ...] } — the
99+
# packages selected in `setup` (see scripts/resolve-release.mjs).
84100
matrix: ${{ fromJSON(needs.setup.outputs.build_matrix) }}
85101
env:
86102
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
87103
ENGINE: ${{ matrix.engine }}
104+
PACKAGE_DIR: ${{ matrix.dir }}
105+
SKIP_ARM64: ${{ inputs.skip_arm64 && 'true' || 'false' }}
88106
steps:
89107
- name: Harden the runner (Audit all outbound calls)
90108
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
@@ -100,29 +118,36 @@ jobs:
100118
- uses: dtolnay/rust-toolchain@4cda84d5c5c54efe2404f9d843567869ab1699d4 # stable
101119
with:
102120
toolchain: stable
121+
# classic cross-compiles the arm64 runtime and tools; the variants build for the host.
122+
targets: ${{ matrix.rust_targets }}
103123
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
104124
with:
105125
# The engine packages are excluded from the root workspace (their C builds / prebuilt
106126
# engine links must not run on a plain `cargo build`), so each has its own target dir.
107-
workspaces: |
108-
. -> target
109-
packages/windows-${{ matrix.engine }} -> target
127+
workspaces: ${{ matrix.rust_workspaces }}
110128
key: ${{ matrix.engine }}
111-
# rusty_v8's build script downloads its prebuilt static lib to <target>/release/gn_out, which
129+
# rusty_v8's build script downloads its prebuilt static lib to <profile dir>/gn_out, which
112130
# rust-cache prunes (it keeps only build/, deps/ and .fingerprint/) while keeping the build
113131
# script's recorded output. When a lockfile change then recompiles the v8 crate, the script
114132
# counts as fresh, nothing downloads the lib again, and the link fails with "could not find
115-
# native static library `rusty_v8`". Drop the v8 build state so the script runs again.
133+
# native static library `rusty_v8`". Drop the v8 build state so the script runs again. The
134+
# profile dir is target/release for a host build and target/<triple>/<profile> for the
135+
# classic cross builds, so every v8 build dir under the target roots is checked.
116136
- name: Re-fetch rusty_v8's static lib when the cache pruned it
117137
shell: pwsh
118138
run: |
119-
foreach ($target in @('target', "packages/windows-$env:ENGINE/target")) {
120-
$release = Join-Path $target 'release'
121-
if ((Test-Path $release) -and -not (Test-Path (Join-Path $release 'gn_out/obj/rusty_v8.lib'))) {
122-
Write-Host "rusty_v8.lib missing under $release; resetting the v8 build script."
123-
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue `
124-
(Join-Path $release 'build/v8-*'), (Join-Path $release '.fingerprint/v8-*')
125-
}
139+
foreach ($root in @('target', (Join-Path $env:PACKAGE_DIR 'target'))) {
140+
if (-not (Test-Path $root)) { continue }
141+
Get-ChildItem -Path $root -Recurse -Directory -Filter 'v8-*' -ErrorAction SilentlyContinue |
142+
Where-Object { $_.Parent.Name -eq 'build' } |
143+
ForEach-Object {
144+
$profileDir = $_.Parent.Parent.FullName
145+
if (-not (Test-Path (Join-Path $profileDir 'gn_out/obj/rusty_v8.lib'))) {
146+
Write-Host "rusty_v8.lib missing under $profileDir; resetting the v8 build script."
147+
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue `
148+
(Join-Path $profileDir 'build/v8-*'), (Join-Path $profileDir '.fingerprint/v8-*')
149+
}
150+
}
126151
}
127152
# quickjs' napi shim uses clang/GCC C extensions that MSVC `cl` rejects; build.rs compiles it
128153
# with clang-cl from this exact path and silently falls back to `cl` (which then fails deep in
@@ -141,53 +166,98 @@ jobs:
141166
throw "clang-cl is required to compile the quickjs napi shim and is not at $clangCl."
142167
}
143168
& $clangCl --version
144-
- name: Setup .NET (framework rebuild)
145-
if: ${{ inputs.rebuild_framework }}
169+
# The classic build publishes ManifestMerger and dotnet-typings-gen with `dotnet publish`
170+
# every time; a variant only needs the SDK when it regenerates the shared framework.
171+
- name: Setup .NET
172+
if: ${{ matrix.engine == 'classic' || inputs.rebuild_framework }}
146173
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
147174
with:
148175
# dotnet-typings-gen targets net10.0; ManifestMerger is netstandard2.0 and builds on it.
149176
dotnet-version: "10.x"
150177
- name: Rebuild shared framework
151-
if: ${{ inputs.rebuild_framework }}
178+
if: ${{ matrix.engine != 'classic' && inputs.rebuild_framework }}
152179
shell: pwsh
153180
# -SkipRelease -SkipDevtools: the classic V8 nativescript.dll is not staged into a variant
154181
# (the engine cdylib takes its place), so building it here would be thrown away.
155182
# -SkipArm64: the variants are x64-only (`cpu: ["x64"]` in their package.json).
156183
run: |
157184
$ErrorActionPreference = 'Stop'
158185
./template/build.ps1 -SkipArm64 -SkipRelease -SkipDevtools
186+
# The runtime DLLs and tool binaries are committed under template/framework so the variants
187+
# can stage them without a classic build. A classic release must ship what THIS run built,
188+
# so clear them first: with the committed copies gone, the checks after the build can only
189+
# pass on fresh output, never on a stale binary a failed step left in place.
190+
- name: Clear committed runtime DLLs and tools (classic)
191+
if: ${{ matrix.engine == 'classic' }}
192+
shell: pwsh
193+
run: |
194+
$ErrorActionPreference = 'Stop'
195+
foreach ($dir in @('template/framework/libs/x64', 'template/framework/libs/arm64',
196+
'template/framework/libs/devtools', 'template/framework/tools')) {
197+
if (Test-Path $dir) { Remove-Item -Recurse -Force $dir }
198+
}
159199
- name: Set package version
160200
shell: pwsh
161-
working-directory: packages/windows-${{ matrix.engine }}
201+
working-directory: ${{ matrix.dir }}
162202
run: npm version $env:NPM_VERSION --no-git-tag-version --allow-same-version
163203
- name: Build ${{ matrix.engine }} framework
164204
shell: pwsh
165205
run: |
166206
$ErrorActionPreference = 'Stop'
207+
$buildArgs = @()
208+
if ($env:ENGINE -ne 'classic') {
209+
$buildArgs += @('-Engine', $env:ENGINE)
210+
} elseif ($env:SKIP_ARM64 -eq 'true') {
211+
$buildArgs += '-SkipArm64'
212+
}
167213
# build.ps1 reports through Write-Host, so the log has to be teed off the information
168-
# stream (`*>&1`) rather than stdout for the WARN check below to see anything.
214+
# stream (`*>&1`) rather than stdout for the log checks below to see anything.
169215
$logPath = Join-Path $env:RUNNER_TEMP "build-$env:ENGINE.log"
170-
./template/build.ps1 -Engine $env:ENGINE *>&1 | Tee-Object -FilePath $logPath
171-
# A missing engine runtime DLL (hermes.dll, JavaScriptCore.dll, …) beside the cdylib is
172-
# only a warning there, but it ships a framework that fails to load at app start.
173-
if (Select-String -Path $logPath -Pattern 'expected engine DLL not found' -Quiet) {
174-
throw "An engine runtime DLL was not staged — see the WARN above."
216+
./template/build.ps1 @buildArgs *>&1 | Tee-Object -FilePath $logPath
217+
218+
$libs = Join-Path $env:PACKAGE_DIR 'framework/libs'
219+
$tools = Join-Path $env:PACKAGE_DIR 'framework/tools'
220+
if ($env:ENGINE -eq 'classic') {
221+
# The tool builds only warn on failure and skip the copy; treat that as fatal here.
222+
$failure = 'failed for target|Expected build output not found|Skipping copy|dotnet publish for .* failed'
223+
if (Select-String -Path $logPath -Pattern $failure -Quiet) {
224+
throw "A tool build failed — see the log above."
225+
}
226+
$archs = @('x64')
227+
if ($env:SKIP_ARM64 -ne 'true') { $archs += 'arm64' }
228+
$expected = @('ManifestMerger/ManifestMerger.dll', 'dotnet-typings-gen/dotnet-typings-gen.dll') |
229+
ForEach-Object { Join-Path $tools $_ }
230+
foreach ($arch in $archs) {
231+
$expected += (Join-Path $libs "$arch/nativescript.dll")
232+
$expected += (Join-Path $libs "devtools/$arch/nativescript.dll")
233+
foreach ($tool in @('dotnet-tool', 'nsbundle_pack', 'sbg', 'typings-generator')) {
234+
$expected += (Join-Path $tools "$tool-$arch.exe")
235+
}
236+
}
237+
} else {
238+
# A missing engine runtime DLL (hermes.dll, JavaScriptCore.dll, …) beside the cdylib is
239+
# only a warning there, but it ships a framework that fails to load at app start.
240+
if (Select-String -Path $logPath -Pattern 'expected engine DLL not found' -Quiet) {
241+
throw "An engine runtime DLL was not staged — see the WARN above."
242+
}
243+
$expected = @(Join-Path $libs 'x64/nativescript.dll')
175244
}
176-
$dll = "packages/windows-$env:ENGINE/framework/libs/x64/nativescript.dll"
177-
if (-not (Test-Path $dll)) { throw "Runtime DLL missing: $dll" }
245+
$missing = $expected | Where-Object { -not (Test-Path $_) }
246+
if ($missing) { throw "Build output missing: $($missing -join ', ')" }
178247
- name: Pack
179248
shell: pwsh
180-
working-directory: packages/windows-${{ matrix.engine }}
249+
working-directory: ${{ matrix.dir }}
181250
run: |
182251
$ErrorActionPreference = 'Stop'
183252
# --pack-destination does not create the directory.
184-
New-Item -ItemType Directory -Force -Path ../../dist | Out-Null
185-
npm pack --pack-destination ../../dist
253+
$dist = Join-Path $env:GITHUB_WORKSPACE 'dist'
254+
New-Item -ItemType Directory -Force -Path $dist | Out-Null
255+
npm pack --pack-destination $dist
186256
- name: Upload npm package artifact
187257
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
188258
with:
189259
name: npm-package-${{ matrix.engine }}
190-
path: dist/nativescript-windows-${{ matrix.engine }}-${{ needs.setup.outputs.npm_version }}.tgz
260+
path: dist/${{ matrix.tarball }}
191261
if-no-files-found: error
192262

193263
publish:
@@ -207,7 +277,8 @@ jobs:
207277
env:
208278
NPM_VERSION: ${{ needs.setup.outputs.npm_version }}
209279
NPM_TAG: ${{ needs.setup.outputs.npm_tag }}
210-
ENGINE: ${{ matrix.engine }}
280+
NPM_NAME: ${{ matrix.npm_name }}
281+
TARBALL: ${{ matrix.tarball }}
211282
steps:
212283
- name: Harden the runner (Audit all outbound calls)
213284
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
@@ -231,18 +302,18 @@ jobs:
231302
- name: Publish package (OIDC trusted publishing)
232303
if: ${{ vars.USE_NPM_TOKEN != 'true' }}
233304
run: |
234-
echo "Publishing @nativescript/windows-$ENGINE@$NPM_VERSION to NPM with tag $NPM_TAG via OIDC trusted publishing..."
305+
echo "Publishing $NPM_NAME@$NPM_VERSION to NPM with tag $NPM_TAG via OIDC trusted publishing..."
235306
unset NODE_AUTH_TOKEN
236307
if [ -n "${NPM_CONFIG_USERCONFIG:-}" ]; then
237308
rm -f "$NPM_CONFIG_USERCONFIG"
238309
fi
239-
npm publish "./dist/nativescript-windows-${ENGINE}-${NPM_VERSION}.tgz" --tag "$NPM_TAG" --access public --provenance
310+
npm publish "./dist/$TARBALL" --tag "$NPM_TAG" --access public --provenance
240311
env:
241312
NODE_AUTH_TOKEN: ""
242313
- name: Publish package (granular token)
243314
if: ${{ vars.USE_NPM_TOKEN == 'true' }}
244315
run: |
245-
echo "Publishing @nativescript/windows-$ENGINE@$NPM_VERSION to NPM with tag $NPM_TAG via granular token..."
246-
npm publish "./dist/nativescript-windows-${ENGINE}-${NPM_VERSION}.tgz" --tag "$NPM_TAG" --access public --provenance
316+
echo "Publishing $NPM_NAME@$NPM_VERSION to NPM with tag $NPM_TAG via granular token..."
317+
npm publish "./dist/$TARBALL" --tag "$NPM_TAG" --access public --provenance
247318
env:
248319
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}

0 commit comments

Comments
 (0)