Skip to content

Isolate the NodeIterator stress fixture #752

Isolate the NodeIterator stress fixture

Isolate the NodeIterator stress fixture #752

name: NuGet packages
on:
workflow_dispatch:
inputs:
publish:
description: Publish the verified repository version to NuGet.org
required: true
default: false
type: boolean
push:
branches:
- main
tags:
- 'v*'
pull_request:
branches:
- main
- release/*
paths:
- '.github/workflows/native-runtime-packages.yml'
- '.github/actions/graphics-sdk/**'
- 'eng/graphics/**'
- 'experiments/WebScene.NativeEngine.Probe/**'
- 'packaging/WebScene.NativeEngine.Runtime/**'
- 'scripts/build-native-engine-runtime.sh'
- 'scripts/build-native-engine-runtime-linux-container.sh'
- 'scripts/build-native-engine-runtime.ps1'
- 'scripts/V8WindowsEnvironment.psm1'
- 'scripts/pack-packages.sh'
- 'scripts/verify-cross-rid-compatibility.py'
- 'scripts/tests/test_native_runtime_workflow_policy.py'
- 'scripts/tests/test_verify_cross_rid_compatibility.py'
- 'scripts/tests/test_v8_windows_environment.ps1'
- 'scripts/verify-release-packages.py'
- 'src/WebScene.Backend.Abstractions/**'
- 'src/WebScene.Backend.Avalonia/**'
- 'src/WebScene.Backend.Uno/**'
- 'src/WebScene.Sdk.Avalonia/**'
- 'src/WebScene.Sdk.NativeHost.Shared/**'
- 'src/WebScene.Sdk.Uno/**'
- 'src/WebScene.Diagnostics.Cdp/**'
- 'tests/WebPlatformSubset/**'
- 'tests/WebScene.Runtime.PackageSmoke/**'
- 'tests/Shared/**'
- 'tests/WebScene.Uno.PackageSmoke/**'
- 'third-party/v8-patches/**'
permissions:
actions: read
contents: read
jobs:
metadata:
runs-on: [self-hosted, Linux, X64]
outputs:
package-version: ${{ steps.version.outputs.package-version }}
publish: ${{ steps.version.outputs.publish }}
steps:
- uses: actions/checkout@v4
- name: Test cross-RID evidence verifier
run: |
python3 -m unittest \
scripts/tests/test_native_runtime_workflow_policy.py \
scripts/tests/test_verify_cross_rid_compatibility.py
- name: Setup .NET
uses: actions/setup-dotnet@v5
env:
DOTNET_INSTALL_DIR: ${{ runner.temp }}/dotnet
with:
dotnet-version: 8.0.x
global-json-file: global.json
- id: version
name: Resolve and validate package version
shell: bash
env:
REQUESTED_PUBLISH: ${{ inputs.publish }}
run: |
version="$(
dotnet msbuild src/WebScene.Core/WebScene.Core.csproj \
-getProperty:PackageVersion -nologo |
tail -n 1 | tr -d '\r'
)"
publish="${REQUESTED_PUBLISH:-false}"
if [[ "$GITHUB_REF_TYPE" == tag ]]; then
tag_version="${GITHUB_REF_NAME#v}"
if [[ "$tag_version" != "$version" ]]; then
echo "Tag version '$tag_version' does not match repository PackageVersion '$version'." >&2
exit 1
fi
publish=true
fi
if [[ -z "$version" ]]; then
echo "Unable to resolve the repository package version." >&2
exit 1
fi
echo "package-version=$version" >> "$GITHUB_OUTPUT"
echo "publish=$publish" >> "$GITHUB_OUTPUT"
release-ci-gate:
name: Require successful CI for release commit
needs: metadata
if: needs.metadata.outputs.publish == 'true'
runs-on: [self-hosted, Linux, X64]
steps:
- name: Require successful CI for the exact commit
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
IFS=$'\t' read -r conclusion url <<< "$(
gh run list \
--repo "$GITHUB_REPOSITORY" \
--workflow CI.yml \
--commit "$GITHUB_SHA" \
--event push \
--status completed \
--limit 20 \
--json conclusion,url,databaseId \
--jq '(sort_by(.databaseId) | last // {}) | [(.conclusion // "missing"), (.url // "")] | @tsv'
)"
if [[ "$conclusion" != success ]]; then
echo "The exact release commit '$GITHUB_SHA' has no successful completed CI push run (latest conclusion: '$conclusion')." >&2
exit 1
fi
echo "Release CI gate passed: $url"
packages:
name: Pack .NET packages
needs: metadata
runs-on: [self-hosted, Linux, X64]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
env:
DOTNET_INSTALL_DIR: ${{ runner.temp }}/dotnet
with:
dotnet-version: 8.0.x
global-json-file: global.json
- name: Pack and verify .NET packages
shell: bash
run: |
scripts/pack-packages.sh \
--package-version '${{ needs.metadata.outputs.package-version }}' \
--output "$GITHUB_WORKSPACE/artifacts/nuget-packages"
- name: Upload .NET packages
uses: actions/upload-artifact@v4
with:
name: packages-${{ needs.metadata.outputs.package-version }}
path: |
artifacts/nuget-packages/*.nupkg
artifacts/nuget-packages/*.snupkg
artifacts/nuget-packages/packages.json
if-no-files-found: error
native:
name: Build ${{ matrix.rid }}
needs: metadata
strategy:
fail-fast: false
matrix:
include:
- os: [self-hosted, macOS, ARM64]
rid: osx-arm64
cpu: arm64
monolith: libv8_monolith.a
script: unix
v8_revision: 15.3.10
partition_alloc: true
v8_configuration: ReleasePartitionAlloc
v8_cache_generation: v9-v8-15.3.10-pa-no-process-shim-macos-inspector
v8_cache_script: scripts/build-native-engine-runtime.sh
v8_cache_patch: packaging/WebScene.NativeEngine.Runtime/patches/V8PartitionAllocMacVisibilityPatch.txt
- os: [self-hosted, Linux, X64]
rid: linux-x64
cpu: x64
monolith: libv8_monolith.a
script: unix
v8_revision: 15.3.10
partition_alloc: true
v8_configuration: ReleasePartitionAlloc
v8_cache_generation: v12-v8-15.3.10-pa-no-process-shim-ubuntu22-gcc12-lld-no-crel-shared-inspector
v8_cache_script: scripts/build-native-engine-runtime.sh
v8_cache_patch: packaging/WebScene.NativeEngine.Runtime/patches/V8BuildNoCrelPatch.txt
- os: windows-2022
rid: win-x64
cpu: x64
monolith: v8_monolith.lib
script: windows
v8_revision: 15.3.10
partition_alloc: true
v8_configuration: ReleasePartitionAlloc
v8_cache_generation: v9-v8-15.3.10-pa-windows-compat-inspector
v8_cache_script: scripts/build-native-engine-runtime.ps1
v8_cache_patch: packaging/WebScene.NativeEngine.Runtime/patches/V8WindowsCompatibilityPatch.txt
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
env:
DOTNET_INSTALL_DIR: ${{ runner.temp }}/dotnet
with:
dotnet-version: 8.0.x
global-json-file: global.json
- name: Test V8 Windows child environment
if: matrix.rid == 'win-x64'
shell: pwsh
run: ./scripts/tests/test_v8_windows_environment.ps1
- name: Expose the self-hosted Rust toolchain
if: matrix.rid == 'osx-arm64'
shell: bash
run: |
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
"$HOME/.cargo/bin/cargo" --version
- name: Build Ubuntu 22.04 Linux runtime image
if: matrix.rid == 'linux-x64'
shell: bash
run: |
docker build \
--platform linux/amd64 \
--file packaging/WebScene.NativeEngine.Runtime/Dockerfile.linux-x64 \
--tag webscene-native-linux-builder:ubuntu-22.04 \
packaging/WebScene.NativeEngine.Runtime
- id: v8-cache-key
name: Resolve pinned V8 SDK cache identity
shell: bash
run: |
echo "image-version=${ImageVersion:-unknown}" >> "$GITHUB_OUTPUT"
- id: v8-cache
name: Restore pinned V8 SDK
uses: actions/cache/restore@v4
with:
path: |
artifacts/native-engine-v8/${{ matrix.rid }}/v8/include
artifacts/native-engine-v8/${{ matrix.rid }}/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/args.gn
artifacts/native-engine-v8/${{ matrix.rid }}/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/obj/${{ matrix.monolith }}
artifacts/native-engine-v8/${{ matrix.rid }}/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/icudtl.dat
artifacts/native-engine-v8/${{ matrix.rid }}/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/gen/third_party/partition_alloc/src
artifacts/native-engine-v8/${{ matrix.rid }}/v8/LICENSE
artifacts/native-engine-v8/${{ matrix.rid }}/v8/third_party/icu/LICENSE
artifacts/native-engine-v8/${{ matrix.rid }}/v8/third_party/partition_alloc/src
key: webscene-v8-sdk-${{ matrix.v8_cache_generation }}-${{ matrix.rid }}-${{ hashFiles('third-party/v8-patches/**') }}-${{ steps.v8-cache-key.outputs.image-version }}-${{ hashFiles(matrix.v8_cache_script, matrix.v8_cache_patch, 'scripts/V8WindowsEnvironment.psm1') }}
restore-keys: |
${{ matrix.rid != 'win-x64' && format('webscene-v8-sdk-{0}-{1}-{2}-', matrix.v8_cache_generation, matrix.rid, hashFiles('third-party/v8-patches/**')) || '' }}
${{ matrix.rid != 'win-x64' && format('webscene-v8-sdk-{0}-{1}-', matrix.v8_cache_generation, matrix.rid) || '' }}
- id: restored-v8-sdk
name: Validate restored V8 SDK compatibility
shell: bash
run: |
root="artifacts/native-engine-v8/${{ matrix.rid }}/v8"
args="$root/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/args.gn"
monolith="$root/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/obj/${{ matrix.monolith }}"
if [[ '${{ matrix.rid }}' == 'win-x64' \
&& '${{ steps.v8-cache.outputs.cache-hit }}' != 'true' ]]; then
echo "The exact Windows V8 cache identity was not restored; forcing a clean child build."
rm -rf "$root"
echo "ready=false" >> "$GITHUB_OUTPUT"
echo "cold-build=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "cold-build=false" >> "$GITHUB_OUTPUT"
partition_alloc_is_compatible() {
[[ '${{ matrix.partition_alloc }}' != 'true' ]] \
|| { [[ -d "$root/third_party/partition_alloc/src" ]] \
&& [[ -f "$root/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/gen/third_party/partition_alloc/src/partition_alloc/buildflags.h" ]]; }
}
args_are_compatible() {
grep -Eq '^v8_enable_pointer_compression *= *true$' "$args" \
&& grep -Eq '^v8_enable_pointer_compression_shared_cage *= *true$' "$args" \
&& grep -Eq '^v8_enable_partition_alloc *= *${{ matrix.partition_alloc }}$' "$args" \
&& { [[ '${{ matrix.partition_alloc }}' != 'true' ]] \
|| [[ '${{ matrix.rid }}' == 'win-x64' ]] \
|| { grep -Eq '^use_allocator_shim *= *false$' "$args" \
&& grep -Eq '^use_partition_alloc_as_malloc *= *false$' "$args"; }; } \
&& { [[ '${{ matrix.rid }}' != 'linux-x64' ]] \
|| { grep -Eq '^use_lld *= *true$' "$args" \
&& grep -Eq '^use_sysroot *= *false$' "$args" \
&& grep -Eq '^v8_monolithic_for_shared_library *= *true$' "$args"; }; }
}
if [[ -f "$root/include/v8.h" \
&& -f "$root/include/v8-inspector.h" \
&& $(grep -c 'virtual void consoleAPICalled' "$root/include/v8-inspector.h") -gt 0 \
&& -f "$args" \
&& -f "$monolith" \
&& -f "$root/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/icudtl.dat" \
&& -f "$root/LICENSE" \
&& -f "$root/third_party/icu/LICENSE" ]] \
&& args_are_compatible \
&& partition_alloc_is_compatible; then
echo "ready=true" >> "$GITHUB_OUTPUT"
else
echo "ready=false" >> "$GITHUB_OUTPUT"
if [[ -d "$root" ]]; then
echo "Discarding an incomplete or incompatible restored V8 SDK at '$root'."
rm -rf "$root"
fi
fi
- name: Prepare pinned Dawn runtime dependency
if: matrix.rid != 'linux-x64'
uses: ./.github/actions/graphics-sdk
with:
rid: ${{ matrix.rid }}
component: dawn
- name: Prepare pinned Windows ANGLE composition dependency
if: matrix.rid == 'win-x64'
uses: ./.github/actions/graphics-sdk
with:
rid: ${{ matrix.rid }}
component: angle
- name: Build, pack, and test macOS runtime
env:
WEBSCENE_NATIVE_SKIP_HARDWARE_TESTS: '1'
if: matrix.rid == 'osx-arm64'
shell: bash
run: |
v8_root=
restored_v8="$GITHUB_WORKSPACE/artifacts/native-engine-v8/${{ matrix.rid }}/v8"
if [[ '${{ steps.restored-v8-sdk.outputs.ready }}' == 'true' ]]; then
v8_root="$restored_v8"
fi
scripts/build-native-engine-runtime.sh \
--rid '${{ matrix.rid }}' \
--graphics-sdk "$GITHUB_WORKSPACE/artifacts/graphics-sdk/${{ matrix.rid }}" \
--package-version '${{ needs.metadata.outputs.package-version }}' \
--v8-root "$v8_root" \
--v8-revision '${{ matrix.v8_revision }}' \
--upstream-v8 \
--partition-alloc \
--output "$GITHUB_WORKSPACE/artifacts/nuget-packages"
- name: Build, pack, and test Linux runtime
if: matrix.rid == 'linux-x64'
shell: bash
run: |
v8_root=
if [[ '${{ steps.restored-v8-sdk.outputs.ready }}' == 'true' ]]; then
v8_root="/workspace/artifacts/native-engine-v8/${{ matrix.rid }}/v8"
fi
docker run --rm \
--platform linux/amd64 \
--user "$(id -u):$(id -g)" \
--env HOME=/tmp/webscene-home \
--env DOTNET_CLI_HOME=/tmp/webscene-home \
--env CARGO_HOME=/tmp/webscene-home/.cargo \
--env NUGET_PACKAGES=/tmp/webscene-home/.nuget/packages \
--volume "$GITHUB_WORKSPACE:/workspace" \
--workdir /workspace \
webscene-native-linux-builder:ubuntu-22.04 \
bash -lc "
mkdir -p \"\$HOME\"
mkdir -p \"\$CARGO_HOME\"
scripts/build-native-engine-runtime-linux-container.sh \
--rid '${{ matrix.rid }}' \
--package-version '${{ needs.metadata.outputs.package-version }}' \
--v8-root '$v8_root' \
--v8-revision '${{ matrix.v8_revision }}' \
--upstream-v8 \
--partition-alloc \
--output /workspace/artifacts/nuget-packages
"
- name: Build, pack, and test Windows runtime
env:
WEBSCENE_NATIVE_SKIP_HARDWARE_TESTS: '1'
if: matrix.script == 'windows'
shell: pwsh
run: |
$v8Root = ""
$restoredV8 = "$env:GITHUB_WORKSPACE/artifacts/native-engine-v8/${{ matrix.rid }}/v8"
if ('${{ steps.restored-v8-sdk.outputs.ready }}' -eq 'true') {
$v8Root = $restoredV8
}
./scripts/build-native-engine-runtime.ps1 `
-Rid '${{ matrix.rid }}' `
-GraphicsSdk "$env:GITHUB_WORKSPACE/artifacts/graphics-sdk/${{ matrix.rid }}" `
-PackageVersion '${{ needs.metadata.outputs.package-version }}' `
-V8Root $v8Root `
-V8Revision '${{ matrix.v8_revision }}' `
-UpstreamV8 `
-PartitionAlloc `
-Output "$env:GITHUB_WORKSPACE/artifacts/nuget-packages"
- name: Verify fresh Windows V8 child build
if: matrix.rid == 'win-x64' && steps.v8-cache.outputs.cache-hit != 'true'
shell: pwsh
run: |
if ('${{ steps.restored-v8-sdk.outputs.ready }}' -ne 'false' `
-or '${{ steps.restored-v8-sdk.outputs.cold-build }}' -ne 'true') {
throw "Windows V8 qualification did not record a cold build."
}
$root = "$env:GITHUB_WORKSPACE/artifacts/native-engine-v8/${{ matrix.rid }}"
$required = @(
"$root/depot_tools/.git",
"$root/v8/.git",
"$root/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/args.gn",
"$root/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/obj/${{ matrix.monolith }}",
"$root/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/icudtl.dat"
)
foreach ($path in $required) {
if (-not (Test-Path $path)) {
throw "Fresh Windows V8 child output is missing: $path"
}
}
Write-Host "Fresh Windows V8 checkout, GN generation, and Ninja output verified."
- name: Run candidate compatibility discovery
continue-on-error: true
shell: bash
run: |
case '${{ matrix.rid }}' in
osx-arm64) native_name=libwebscene_native_engine.dylib ;;
linux-x64) native_name=libwebscene_native_engine.so ;;
win-x64) native_name=webscene_native_engine.dll ;;
*) echo "Unsupported discovery RID '${{ matrix.rid }}'." >&2; exit 1 ;;
esac
native_path="$(
find artifacts/native-engine-runtime-build \
-path "*/package-smoke/runtimes/${{ matrix.rid }}/native/$native_name" \
-print -quit
)"
if [[ -z "$native_path" ]]; then
echo "Unable to locate the packaged ${{ matrix.rid }} native runtime." >&2
exit 1
fi
build_dir="${native_path%%/package-smoke/runtimes/*}"
dotnet run \
--project tests/WebPlatformSubset/runner/WebScene.WebPlatformSubset.Runner.csproj \
-c Release --no-build -- \
--selection candidate \
--native-library "$native_path" \
--native-cache-directory "$build_dir/code-cache" \
--output "$build_dir/wpt-candidate-results"
- id: v8-sdk-ready
name: Validate completed V8 SDK for caching
if: always() && steps.v8-cache.outputs.cache-hit != 'true'
shell: bash
run: |
root="artifacts/native-engine-v8/${{ matrix.rid }}/v8"
args="$root/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/args.gn"
monolith="$root/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/obj/${{ matrix.monolith }}"
partition_alloc_is_compatible() {
[[ '${{ matrix.partition_alloc }}' != 'true' ]] \
|| { [[ -d "$root/third_party/partition_alloc/src" ]] \
&& [[ -f "$root/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/gen/third_party/partition_alloc/src/partition_alloc/buildflags.h" ]]; }
}
args_are_compatible() {
grep -Eq '^v8_enable_pointer_compression *= *true$' "$args" \
&& grep -Eq '^v8_enable_pointer_compression_shared_cage *= *true$' "$args" \
&& grep -Eq '^v8_enable_partition_alloc *= *${{ matrix.partition_alloc }}$' "$args" \
&& { [[ '${{ matrix.rid }}' != 'linux-x64' ]] \
|| { grep -Eq '^use_lld *= *true$' "$args" \
&& grep -Eq '^use_sysroot *= *false$' "$args" \
&& grep -Eq '^v8_monolithic_for_shared_library *= *true$' "$args" \
&& grep -Eq '^use_allocator_shim *= *false$' "$args" \
&& grep -Eq '^use_partition_alloc_as_malloc *= *false$' "$args"; }; }
}
if [[ -f "$root/include/v8.h" \
&& -f "$root/include/v8-inspector.h" \
&& $(grep -c 'virtual void consoleAPICalled' "$root/include/v8-inspector.h") -gt 0 \
&& -f "$args" \
&& -f "$monolith" \
&& -f "$root/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/icudtl.dat" \
&& -f "$root/LICENSE" \
&& -f "$root/third_party/icu/LICENSE" ]] \
&& args_are_compatible \
&& partition_alloc_is_compatible; then
echo "ready=true" >> "$GITHUB_OUTPUT"
else
echo "ready=false" >> "$GITHUB_OUTPUT"
fi
- name: Save completed V8 SDK
if: always() && steps.v8-sdk-ready.outputs.ready == 'true'
uses: actions/cache/save@v4
with:
path: |
artifacts/native-engine-v8/${{ matrix.rid }}/v8/include
artifacts/native-engine-v8/${{ matrix.rid }}/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/args.gn
artifacts/native-engine-v8/${{ matrix.rid }}/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/obj/${{ matrix.monolith }}
artifacts/native-engine-v8/${{ matrix.rid }}/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/icudtl.dat
artifacts/native-engine-v8/${{ matrix.rid }}/v8/out/${{ matrix.cpu }}/${{ matrix.v8_configuration }}/gen/third_party/partition_alloc/src
artifacts/native-engine-v8/${{ matrix.rid }}/v8/LICENSE
artifacts/native-engine-v8/${{ matrix.rid }}/v8/third_party/icu/LICENSE
artifacts/native-engine-v8/${{ matrix.rid }}/v8/third_party/partition_alloc/src
key: webscene-v8-sdk-${{ matrix.v8_cache_generation }}-${{ matrix.rid }}-${{ hashFiles('third-party/v8-patches/**') }}-${{ steps.v8-cache-key.outputs.image-version }}-${{ hashFiles(matrix.v8_cache_script, matrix.v8_cache_patch, 'scripts/V8WindowsEnvironment.psm1') }}
- name: Upload required compatibility evidence
if: success()
uses: actions/upload-artifact@v4
with:
name: compatibility-required-${{ matrix.rid }}-${{ needs.metadata.outputs.package-version }}
path: artifacts/native-engine-runtime-build/**/wpt-results/**
if-no-files-found: error
- name: Upload failed compatibility evidence
if: failure()
uses: actions/upload-artifact@v4
with:
name: compatibility-required-${{ matrix.rid }}-${{ needs.metadata.outputs.package-version }}
path: artifacts/native-engine-runtime-build/**/wpt-results/**
if-no-files-found: warn
- name: Upload candidate compatibility evidence
if: always()
uses: actions/upload-artifact@v4
with:
name: compatibility-candidate-${{ matrix.rid }}-${{ needs.metadata.outputs.package-version }}
path: artifacts/native-engine-runtime-build/**/wpt-candidate-results/**
if-no-files-found: warn
- name: Upload verified RID package
uses: actions/upload-artifact@v4
with:
name: native-${{ matrix.rid }}-${{ needs.metadata.outputs.package-version }}
path: artifacts/nuget-packages/*.nupkg
if-no-files-found: error
required-evidence:
name: Verify cross-RID required evidence
needs: [metadata, native]
runs-on: [self-hosted, Linux, X64]
steps:
- uses: actions/checkout@v4
- name: Download required compatibility evidence
uses: actions/download-artifact@v4
with:
pattern: compatibility-required-*
path: artifacts/required-compatibility
- name: Verify exact required profile across released RIDs
run: |
python3 scripts/verify-cross-rid-compatibility.py \
--input-root artifacts/required-compatibility \
--profile tests/WebPlatformSubset/webscene-component-profile.json \
--selection required \
--expected-rid osx-arm64 \
--expected-rid linux-x64 \
--expected-rid win-x64 \
--output artifacts/required-compatibility/cross-rid-summary.json
- name: Upload cross-RID required summary
uses: actions/upload-artifact@v4
with:
name: compatibility-required-summary-${{ needs.metadata.outputs.package-version }}
path: artifacts/required-compatibility/cross-rid-summary.json
if-no-files-found: error
package-set:
name: Verify release package set
needs: [metadata, packages, native, required-evidence]
runs-on: [self-hosted, Linux, X64]
steps:
- uses: actions/checkout@v4
- name: Download .NET packages
uses: actions/download-artifact@v4
with:
name: packages-${{ needs.metadata.outputs.package-version }}
path: artifacts/nuget-packages
- name: Download native packages
uses: actions/download-artifact@v4
with:
pattern: native-*-${{ needs.metadata.outputs.package-version }}
path: artifacts/nuget-packages
merge-multiple: true
- name: Verify versions, dependencies, symbols, and package inventory
shell: bash
run: |
python3 scripts/verify-release-packages.py \
artifacts/nuget-packages \
--version '${{ needs.metadata.outputs.package-version }}' \
--native-rid osx-arm64 \
--native-rid linux-x64 \
--native-rid win-x64 \
--output artifacts/nuget-packages/release-packages.json
shasum -a 256 \
artifacts/nuget-packages/*.nupkg \
artifacts/nuget-packages/*.snupkg \
> artifacts/nuget-packages/packages.sha256
- name: Upload complete verified package set
uses: actions/upload-artifact@v4
with:
name: release-${{ needs.metadata.outputs.package-version }}
path: |
artifacts/nuget-packages/*.nupkg
artifacts/nuget-packages/*.snupkg
artifacts/nuget-packages/*.json
artifacts/nuget-packages/packages.sha256
if-no-files-found: error
candidate-evidence:
name: Verify cross-RID candidate evidence
needs: [metadata, native]
if: always() && needs.metadata.result == 'success'
runs-on: [self-hosted, Linux, X64]
steps:
- uses: actions/checkout@v4
- name: Download candidate compatibility evidence
continue-on-error: true
uses: actions/download-artifact@v4
with:
pattern: compatibility-candidate-*
path: artifacts/candidate-compatibility
- name: Verify exact candidate profile across released RIDs
if: always()
run: |
python3 scripts/verify-cross-rid-compatibility.py \
--input-root artifacts/candidate-compatibility \
--profile tests/WebPlatformSubset/webscene-component-profile.json \
--selection candidate \
--advisory-test-failures \
--expected-rid osx-arm64 \
--expected-rid linux-x64 \
--expected-rid win-x64 \
--output artifacts/candidate-compatibility/cross-rid-summary.json
- name: Upload cross-RID candidate summary
if: always()
uses: actions/upload-artifact@v4
with:
name: compatibility-candidate-summary-${{ needs.metadata.outputs.package-version }}
path: artifacts/candidate-compatibility/cross-rid-summary.json
if-no-files-found: warn
consumer:
name: Package consumer ${{ matrix.rid }}
needs: [metadata, package-set]
strategy:
fail-fast: false
matrix:
include:
- os: [self-hosted, macOS, ARM64]
rid: osx-arm64
- os: [self-hosted, Linux, X64]
rid: linux-x64
- os: windows-2022
rid: win-x64
runs-on: ${{ matrix.os }}
env:
# PR builds intentionally reuse package versions. Keep their extracted
# packages isolated so a self-hosted runner cannot consume an older build
# of the same version from its persistent global NuGet cache. runner.temp
# is unavailable while GitHub evaluates a job-level environment.
NUGET_PACKAGES: ${{ github.workspace }}/artifacts/nuget-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.rid }}
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v5
env:
DOTNET_INSTALL_DIR: ${{ runner.temp }}/dotnet
with:
dotnet-version: 8.0.x
global-json-file: global.json
- name: Download verified package set
uses: actions/download-artifact@v4
with:
name: release-${{ needs.metadata.outputs.package-version }}
path: artifacts/nuget-packages
- name: Restore clean package consumer
shell: bash
run: |
dotnet restore tests/WebScene.Runtime.PackageSmoke/WebScene.Runtime.PackageSmoke.csproj \
-p:RuntimeIdentifier='${{ matrix.rid }}' \
-p:WebScenePackageVersion='${{ needs.metadata.outputs.package-version }}' \
--configfile tests/WebScene.Runtime.PackageSmoke/NuGet.config
dotnet restore tests/WebScene.Uno.PackageSmoke/WebScene.Uno.PackageSmoke.csproj \
-p:RuntimeIdentifier='${{ matrix.rid }}' \
-p:WebScenePackageVersion='${{ needs.metadata.outputs.package-version }}' \
--configfile tests/WebScene.Uno.PackageSmoke/NuGet.config
- name: Build and run clean package consumer
shell: bash
run: |
dotnet run \
--project tests/WebScene.Runtime.PackageSmoke/WebScene.Runtime.PackageSmoke.csproj \
-c Release \
-r '${{ matrix.rid }}' \
--no-restore \
-p:WebScenePackageVersion='${{ needs.metadata.outputs.package-version }}'
dotnet run \
--project tests/WebScene.Uno.PackageSmoke/WebScene.Uno.PackageSmoke.csproj \
-c Release \
-r '${{ matrix.rid }}' \
--no-restore \
-p:WebScenePackageVersion='${{ needs.metadata.outputs.package-version }}'
publish:
name: Publish to NuGet.org
needs: [metadata, consumer, release-ci-gate]
if: needs.metadata.outputs.publish == 'true'
runs-on: [self-hosted, Linux, X64]
environment: nuget.org
permissions:
contents: read
id-token: write
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
NUGET_USER: ${{ secrets.NUGET_USER }}
steps:
- uses: actions/checkout@v4
- name: Download verified package set
uses: actions/download-artifact@v4
with:
name: release-${{ needs.metadata.outputs.package-version }}
path: artifacts/nuget-packages
- name: Setup .NET
uses: actions/setup-dotnet@v5
env:
DOTNET_INSTALL_DIR: ${{ runner.temp }}/dotnet
with:
dotnet-version: 8.0.x
global-json-file: global.json
- id: trusted-publishing
name: Exchange GitHub identity for a temporary NuGet API key
if: env.NUGET_API_KEY == '' && env.NUGET_USER != ''
uses: NuGet/login@v1
with:
user: ${{ env.NUGET_USER }}
- name: Publish packages and symbols
env:
TRUSTED_PUBLISHING_API_KEY: ${{ steps.trusted-publishing.outputs.NUGET_API_KEY }}
shell: bash
run: |
api_key="${NUGET_API_KEY:-$TRUSTED_PUBLISHING_API_KEY}"
if [[ -z "$api_key" ]]; then
echo "Configure either NUGET_API_KEY or NUGET_USER in the nuget.org environment." >&2
exit 1
fi
for package in artifacts/nuget-packages/*.nupkg; do
dotnet nuget push "$package" \
--api-key "$api_key" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate
done