225 lines
11 KiB
YAML
225 lines
11 KiB
YAML
name: Windows
|
||
|
||
# The Windows-port CI (docs/WINDOWS_PORT.md §13 M0, §14). Three legs:
|
||
#
|
||
# protocol (REQUIRED) — Swift toolchain pinned by windows/props/swift-version.txt on
|
||
# hosted x64 (windows-2025) + ARM64 (windows-11-arm): build NucleicProtocol and run
|
||
# NucleicProtocolTests. Runs with NUCLEIC_WINDOWS_PROTOCOL_ONLY=1 so `swift test`
|
||
# builds only the wire layer — NucleicCore's Windows shims land with §12 items 7–9.
|
||
#
|
||
# core — the same toolchain WITHOUT the flag, driving the full Windows manifest
|
||
# (NucleicCore + NucleicCarbonTests + nucleic-hostd + NucleicProtocolC). Two tiers,
|
||
# because the legs are proven to different depths (docs/WINDOWS_PORT.md §12 item 9):
|
||
# * the x64 `swift build --target NucleicCore` step is REQUIRED — it builds green on
|
||
# Windows 11 amd64 as of the item-9 shims, so a regression must turn CI red;
|
||
# * ARM64 is still `continue-on-error` (no ARM64 hardware has run it), and
|
||
# NucleicCarbonTests is a per-step probe on both arches, because `swift test`
|
||
# builds EVERY declared target — nucleic-hostd, NucleicProtocolC, nucleic-smoke —
|
||
# and those are still §12 items 10+ work. That step's log is what remains of the
|
||
# M0 "corelibs/toolchain gap list". Both tiers flip to required at M2.
|
||
#
|
||
# broker (REQUIRED) — the C# nucleic-brokerd contract tests against FakeWslc
|
||
# (windows/Nucleic.sln, no wslc NuGet — that path needs -p:UseWslc=true and the M1
|
||
# spike hardware).
|
||
#
|
||
# The container e2e suite (M2) needs nested virtualization → the self-hosted runner (D11),
|
||
# added later behind a runs-on label; hosted runners cover everything above.
|
||
#
|
||
# WHERE THESE RUN (D11). Every `runs-on` below reads a repository variable with the hosted
|
||
# runner as its fallback, so moving a leg on-prem is a settings change, not a PR:
|
||
#
|
||
# WINDOWS_X64_RUNNER → label of the on-prem x64 box (unset ⇒ windows-2025)
|
||
# WINDOWS_ARM64_RUNNER → label of an on-prem ARM64 box (unset ⇒ windows-11-arm)
|
||
#
|
||
# Set them to a UNIQUE label you assigned that runner (e.g. nucleic-win-x64), not to the
|
||
# generic `self-hosted`. Clearing a variable fails the leg straight back to GitHub-hosted,
|
||
# which is the escape hatch when the box is down. Self-hosted minutes are never billed —
|
||
# hosted Windows minutes bill at 2x, which is what makes this leg the expensive one.
|
||
# On-prem machine setup: docs/WINDOWS_PORT.md §14.1.
|
||
|
||
on:
|
||
push:
|
||
branches: [main, dev]
|
||
paths:
|
||
- Package.swift
|
||
- Package.resolved
|
||
- Sources/**
|
||
- Tests/**
|
||
- windows/**
|
||
- ios/NucleicRemote/NucleicRemote/Views/Transcript/**
|
||
- .github/workflows/windows.yml
|
||
pull_request:
|
||
paths:
|
||
- Package.swift
|
||
- Package.resolved
|
||
- Sources/**
|
||
- Tests/**
|
||
- windows/**
|
||
- ios/NucleicRemote/NucleicRemote/Views/Transcript/**
|
||
- .github/workflows/windows.yml
|
||
workflow_dispatch:
|
||
|
||
concurrency:
|
||
group: windows-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
swift-version:
|
||
name: Resolve pinned Swift
|
||
runs-on: ubuntu-latest
|
||
outputs:
|
||
swift-version: ${{ steps.pin.outputs.swift-version }}
|
||
swift-build: ${{ steps.pin.outputs.swift-build }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
with:
|
||
sparse-checkout: windows/props/swift-version.txt
|
||
sparse-checkout-cone-mode: false
|
||
- id: pin
|
||
run: |
|
||
# windows/props/swift-version.txt holds e.g. "swift-6.3.3-RELEASE". gha-setup-swift
|
||
# composes the download URL as
|
||
# download.swift.org/<swift-version>/windows10/swift-<swift-build>/...
|
||
# so it wants swift-version=swift-6.3.3-release and swift-build=6.3.3-RELEASE
|
||
# (no "swift-" prefix — the action adds it).
|
||
#
|
||
# The pin has a FLOOR set by the runner's MSVC: the VS 2026 STL (14.5x) hard-errors
|
||
# (STL1000) on clang older than 20, and Swift 6.2.x bundles clang 19. 6.3.x bundles
|
||
# clang 21. Keep this at or above the Linux pin in scripts/build-nucleicd-linux.sh.
|
||
TAG="$(tr -d '[:space:]' < windows/props/swift-version.txt)"
|
||
echo "swift-version=$(echo "$TAG" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
|
||
echo "swift-build=${TAG#swift-}" >> "$GITHUB_OUTPUT"
|
||
|
||
protocol:
|
||
name: Protocol tests (${{ matrix.arch }})
|
||
needs: swift-version
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
- arch: x64
|
||
runner: ${{ vars.WINDOWS_X64_RUNNER || 'windows-2025' }}
|
||
build_arch: amd64
|
||
- arch: arm64
|
||
runner: ${{ vars.WINDOWS_ARM64_RUNNER || 'windows-11-arm' }}
|
||
build_arch: arm64
|
||
runs-on: ${{ matrix.runner }}
|
||
env:
|
||
# Narrow the manifest to the wire layer (see Package.swift): `swift test` builds every
|
||
# declared target, and NucleicCore doesn't compile on Windows yet.
|
||
NUCLEIC_WINDOWS_PROTOCOL_ONLY: "1"
|
||
steps:
|
||
- name: Git line-ending + longpath config
|
||
# Agents run in Linux containers — the repo must stay LF (docs/WINDOWS_PORT.md §4.4);
|
||
# and SwiftPM checkouts nest deep enough to hit MAX_PATH without longpaths.
|
||
run: |
|
||
git config --global core.autocrlf false
|
||
git config --global core.longpaths true
|
||
- uses: actions/checkout@v4
|
||
- uses: compnerd/[email protected]
|
||
with:
|
||
swift-version: ${{ needs.swift-version.outputs.swift-version }}
|
||
swift-build: ${{ needs.swift-version.outputs.swift-build }}
|
||
build_arch: ${{ matrix.build_arch }}
|
||
cache: true
|
||
- name: Build NucleicProtocol
|
||
run: swift build --target NucleicProtocol
|
||
- name: Run NucleicProtocolTests
|
||
# /FORCE:MULTIPLE is TEMPORARY — the CCryptoBoringSSL / CNIOBoringSSL
|
||
# `p_thread_callback_boringssl` collision (docs/WINDOWS_PORT.md §14.1.1). It bites here
|
||
# even under NUCLEIC_WINDOWS_PROTOCOL_ONLY, because NucleicProtocol itself links both
|
||
# Crypto and NIOSSL on Windows. Drop it when one copy prefixes the symbol.
|
||
run: swift test --filter NucleicProtocolTests -Xlinker "/FORCE:MULTIPLE"
|
||
|
||
core:
|
||
name: Full core (${{ matrix.arch }})
|
||
needs: swift-version
|
||
# x64 is required (the NucleicCore build is green there); ARM64 has never run, so it
|
||
# stays advisory until a box confirms it. See the header note.
|
||
continue-on-error: ${{ matrix.experimental }}
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
include:
|
||
- arch: x64
|
||
runner: ${{ vars.WINDOWS_X64_RUNNER || 'windows-2025' }}
|
||
build_arch: amd64
|
||
vcpkg_triplet: x64-windows-static-md-nucleic
|
||
experimental: false
|
||
- arch: arm64
|
||
runner: ${{ vars.WINDOWS_ARM64_RUNNER || 'windows-11-arm' }}
|
||
build_arch: arm64
|
||
vcpkg_triplet: arm64-windows-static-md-nucleic
|
||
experimental: true
|
||
runs-on: ${{ matrix.runner }}
|
||
steps:
|
||
- name: Git line-ending + longpath config
|
||
run: |
|
||
git config --global core.autocrlf false
|
||
git config --global core.longpaths true
|
||
- uses: actions/checkout@v4
|
||
- uses: compnerd/[email protected]
|
||
with:
|
||
swift-version: ${{ needs.swift-version.outputs.swift-version }}
|
||
swift-build: ${{ needs.swift-version.outputs.swift-build }}
|
||
build_arch: ${{ matrix.build_arch }}
|
||
cache: true
|
||
- name: Provision SQLite for GRDB
|
||
# GRDB's GRDBSQLite is a .systemLibrary declaring only an apt provider, so nothing
|
||
# supplies sqlite3.h / sqlite3.lib on Windows — hence "including GRDB/SQLite linkage"
|
||
# in the §13 M0 exit criteria. The -static-md triplet links SQLite statically against
|
||
# the DYNAMIC CRT, matching what the Swift toolchain uses: the CRT agrees and there's
|
||
# no DLL to stage beside the test binaries. Stock vcpkg FEATURES are enough — nothing
|
||
# here uses FTS or rtree — but the stock BUILD is not: it compiles the snapshot API out,
|
||
# which GRDB's system-SQLite path needs, hence the overlay triplet below.
|
||
shell: pwsh
|
||
run: |
|
||
# Hosted images ship vcpkg and set VCPKG_INSTALLATION_ROOT; a self-hosted box does
|
||
# not. Fall back to a clone under RUNNER_TOOL_CACHE, which survives between jobs,
|
||
# so the bootstrap is a one-time cost on the on-prem runner rather than per-run.
|
||
$vcpkg = $env:VCPKG_INSTALLATION_ROOT
|
||
if (-not $vcpkg -or -not (Test-Path (Join-Path $vcpkg 'vcpkg.exe'))) {
|
||
$vcpkg = Join-Path $env:RUNNER_TOOL_CACHE 'vcpkg'
|
||
if (-not (Test-Path (Join-Path $vcpkg '.git'))) {
|
||
git clone --depth 1 https://github.com/microsoft/vcpkg $vcpkg
|
||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||
}
|
||
if (-not (Test-Path (Join-Path $vcpkg 'vcpkg.exe'))) {
|
||
& (Join-Path $vcpkg 'bootstrap-vcpkg.bat') -disableMetrics
|
||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||
}
|
||
}
|
||
# Overlay triplet = stock -static-md + SQLITE_ENABLE_SNAPSHOT, which GRDB's
|
||
# system-SQLite path needs and the stock port compiles out (§14.2).
|
||
& (Join-Path $vcpkg 'vcpkg.exe') install sqlite3:${{ matrix.vcpkg_triplet }} "--overlay-triplets=$env:GITHUB_WORKSPACE\windows\vcpkg-triplets"
|
||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||
$root = Join-Path $vcpkg 'installed\${{ matrix.vcpkg_triplet }}'
|
||
# Deliberately NOT %INCLUDE%/%LIB%: clang's MSVC driver stops auto-detecting the
|
||
# VC + Windows SDK header set as soon as %INCLUDE% names one existing directory,
|
||
# so appending here could silently strip the system headers. Pass them per-build
|
||
# as ordinary -I / -LIBPATH instead.
|
||
"SQLITE_INCLUDE=$root\include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||
"SQLITE_LIB=$root\lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||
- name: Build NucleicCore
|
||
# `/LIBPATH:`, not `-LIBPATH:` — a leading `-L` is eaten by the driver's GNU-style
|
||
# library-path flag and the linker ends up searching `IBPATH:C:\…` (§14.2).
|
||
run: swift build --target NucleicCore -Xcc "-I$env:SQLITE_INCLUDE" -Xlinker "/LIBPATH:$env:SQLITE_LIB"
|
||
- name: Run NucleicCarbonTests (gap probe)
|
||
# Advisory on BOTH arches, independently of the job tier: `swift test` builds every
|
||
# declared target, so this covers nucleic-hostd / NucleicProtocolC / nucleic-smoke —
|
||
# §12 items 10+ — not just the suite it names. Flips to required with those items.
|
||
continue-on-error: true
|
||
# /FORCE:MULTIPLE as in the protocol job — see §14.1.1.
|
||
run: swift test --filter NucleicCarbonTests -Xcc "-I$env:SQLITE_INCLUDE" -Xlinker "/LIBPATH:$env:SQLITE_LIB" -Xlinker "/FORCE:MULTIPLE"
|
||
|
||
broker:
|
||
name: Broker contract tests
|
||
runs-on: ${{ vars.WINDOWS_X64_RUNNER || 'windows-latest' }}
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
- uses: actions/setup-dotnet@v4
|
||
with:
|
||
dotnet-version: "9.0.x"
|
||
- name: Test windows/Nucleic.sln
|
||
run: dotnet test windows/Nucleic.sln --nologo
|