Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
1752057
Add experimental OPC UA data channel framing and services to the stack
marcschier Jul 28, 2026
1bdf1ac
Add the DataChannel Service Set, negotiation and the QUIC peer binding
marcschier Jul 28, 2026
d38aba9
Add the SequenceNumber budget, the opc.quic binding and a loopback suite
marcschier Jul 28, 2026
41a6c54
Implement the opc.quic transport end to end
marcschier Jul 28, 2026
217428d
Raise data channel and QUIC test coverage to 87.6%, fixing three defects
marcschier Jul 29, 2026
4d21c12
Fix a connection-credit deadlock and three conformance divergences
marcschier Jul 29, 2026
8c8ef90
Wire the obligations that existed only as tested, uncalled code
marcschier Jul 29, 2026
98552ab
Complete the end-to-end QUIC path and fix what it exposed
marcschier Jul 29, 2026
7d5552c
Bring the data channels notes up to date
marcschier Jul 29, 2026
fd35c5a
Fix the security review findings on the data channels feature
marcschier Jul 29, 2026
e63d427
Fix four security review findings in the data channel implementation
marcschier Jul 30, 2026
549384b
Fix out-of-order symmetric chunks and add a throughput benchmark
marcschier Jul 30, 2026
24b8ffe
Stop counting credit stalls where the transport owns flow control
marcschier Jul 30, 2026
067d3dc
Merge branch 'master' into data-channels-quic-experimental
marcschier Aug 11, 2026
6d57355
Fix five data channel specification-compliance defects
marcschier Aug 11, 2026
ec0fd8e
Stabilize data channel conformance reset checks
marcschier Aug 11, 2026
b28dd10
Add a message-extension seam to the SecureChannel
marcschier Aug 11, 2026
cfaa34f
Move the data channel engine into Opc.Ua.Core.Channels
marcschier Aug 11, 2026
3f84a1b
Fix data channel review findings: security, wiring, and coverage
marcschier Aug 11, 2026
706c7e9
Enforce the receiver-side PING rate bound (Part 6 errata 5.11)
marcschier Aug 11, 2026
33b691b
Fix the per-TFM CI build and simplify the message-extension seam
marcschier Aug 12, 2026
c8a8a09
Update the service inventory counts for the data channel Services
marcschier Aug 12, 2026
b6b7ccf
Add Opc.Ua.Core.Channels to the reference server Dockerfile restore l…
marcschier Aug 12, 2026
71fba92
Cover the data channel Service forwarders on the client sessions
marcschier Aug 12, 2026
18a3077
Make the message-extension seam internal to Opc.Ua.Core
marcschier Aug 12, 2026
d65d416
Move the data channel code back into Opc.Ua.Core and delete the seam
marcschier Aug 12, 2026
cd1ba90
Address the PR review: sample layout, audio sample, docs, logging and…
marcschier Aug 12, 2026
c33413b
Fix send-gate ticket leak and chunk-size overflow on the data channel…
marcschier Aug 12, 2026
d877e35
Bind the QUIC test listeners dual stack so they are reachable over IPv6
marcschier Aug 12, 2026
dc8e58d
Install msquic on the Linux test leg so the QUIC tests actually run
marcschier Aug 13, 2026
4e31dcb
Cover the engine edge paths the coverage gate flagged
marcschier Aug 13, 2026
e3b7ba1
Pin SSH.NET to the version that fixes GHSA-q939-rpr3-3284
marcschier Aug 13, 2026
7815216
Merge origin/master into data-channels-quic-experimental
marcschier Aug 17, 2026
1df49fb
Merge origin/master into data-channels-quic-experimental
marcschier Aug 18, 2026
0ac500a
Merge remote-tracking branch 'origin/master' into data-channels-quic-…
marcschier Aug 20, 2026
5969deb
Merge origin/master into data-channels-quic-experimental
marcschier Aug 21, 2026
0ec487c
Merge origin/master into data-channels-quic-experimental
marcschier Aug 29, 2026
b310bb3
Merge origin/master into data-channels-quic-experimental
marcschier Aug 29, 2026
5a34978
Merge remote-tracking branch 'origin/master' into data-channels-quic-…
marcschier Aug 30, 2026
e7e841e
Merge remote-tracking branch 'origin/master' into data-channels-quic-…
marcschier Aug 31, 2026
467b369
Merge origin/master into data-channels-quic-experimental
marcschier Aug 31, 2026
fc49d12
Merge remote-tracking branch 'origin/master' into data-channels-quic-…
marcschier Aug 31, 2026
4a0d125
Merge origin/master into data-channels-quic-experimental
marcschier Aug 31, 2026
d47bf26
Merge origin/master into data-channels-quic-experimental
marcschier Sep 1, 2026
99edb81
Merge origin/master into data-channels-quic-experimental
marcschier Sep 2, 2026
19aa6b1
Merge origin/master into data-channels-quic-experimental
marcschier Sep 3, 2026
2505d96
Merge origin/master into data-channels-quic-experimental
marcschier Sep 4, 2026
2f2ea52
Merge origin/master into data-channels-quic-experimental
marcschier Sep 4, 2026
92f1b2f
Merge origin/master into data-channels-quic-experimental
marcschier Sep 4, 2026
0321a9d
Merge origin/master into data-channels-quic-experimental
marcschier Sep 5, 2026
5dad34d
Merge branch 'master' into data-channels-quic-experimental
marcschier Sep 7, 2026
d52e33f
Merge branch 'master' into data-channels-quic-experimental
marcschier Sep 8, 2026
9d568bd
Merge remote-tracking branch 'origin/master' into data-channels-quic-…
Copilot Sep 10, 2026
9c3168e
Merge branch 'master' into data-channels-quic-experimental
marcschier Sep 11, 2026
4514409
Merge origin/master into data-channels-quic-experimental
marcschier Sep 15, 2026
f177038
Merge origin/master into data-channels-quic-experimental
marcschier Sep 24, 2026
9c043d2
Merge origin/master into data-channels-quic-experimental
marcschier Sep 29, 2026
f2c634b
Merge origin/master into data-channels-quic-experimental
marcschier Sep 29, 2026
1f88957
Fix data-channel cleanup after hosted server shutdown
marcschier Sep 29, 2026
89875dc
Merge remote-tracking branch 'origin/master' into data-channels-quic-…
Copilot Oct 2, 2026
0fc76af
Release send gate ticket in key derivation probe after merge
Copilot Oct 2, 2026
a2bc883
Merge branch 'master' into data-channels-quic-experimental
marcschier Oct 2, 2026
3c8df2a
Merge remote-tracking branch 'origin/master' into data-channels-quic-…
Copilot Oct 3, 2026
f3ee553
Merge branch 'master' into data-channels-quic-experimental
marcschier Oct 3, 2026
a951f69
Merge remote-tracking branch 'origin/master' into data-channels-quic-…
Copilot Oct 5, 2026
134ad35
Merge branch 'master' into data-channels-quic-experimental
marcschier Oct 5, 2026
8e7ada4
Merge branch 'master' into data-channels-quic-experimental
marcschier Oct 6, 2026
ca9f9a0
Merge branch 'master' into data-channels-quic-experimental
marcschier Oct 7, 2026
4add732
Merge remote-tracking branch 'origin/master' into data-channels-quic-…
Copilot Oct 9, 2026
2043c73
Merge origin/master into data-channels-quic-experimental
marcschier Oct 10, 2026
75f074e
Make sidecar identifier regression independent of build metadata
marcschier Oct 10, 2026
a1af791
Merge remote-tracking branch 'origin/master' into data-channels-quic-…
Copilot Oct 10, 2026
aae0a26
Merge master ordered-writer changes into data channels and QUIC
marcschier Oct 16, 2026
5ffb6f9
Merge master Machinery inspection and transfer updates
marcschier Oct 16, 2026
a2e47f0
Merge master exact-NodeSet source generation option
marcschier Oct 16, 2026
526f876
Implement OPC UA Data Channels
marcschier Oct 18, 2026
7c8c7e7
Fix Data Channels XML summary formatting
marcschier Oct 18, 2026
60ad14f
Merge master into data-channels-quic-experimental
marcschier Oct 19, 2026
99dca35
Keep native validation packages out of standard publish
marcschier Oct 19, 2026
1648ccf
Report actual native tool pin mismatches
marcschier Oct 19, 2026
09fd4dd
Report all native tool pin mismatches
marcschier Oct 19, 2026
ee504fb
Update native Windows CRT pins for runner image
marcschier Oct 19, 2026
e08befd
Correct pinned MSVC runtime hash
marcschier Oct 19, 2026
e873bbb
Add native lane failure diagnostics
marcschier Oct 19, 2026
0de303c
Disambiguate WoT event field lookup
marcschier Oct 19, 2026
690ea3d
Fix flaky data channel budget assertions
marcschier Oct 19, 2026
4a17ecb
Preserve WebTransport reliable prefix on STOP_SENDING
marcschier Oct 19, 2026
3788921
Update QUIC STOP regression expectations
marcschier Oct 19, 2026
cc9f0ee
Fix Linux native verifier path
marcschier Oct 19, 2026
3ed28c6
Quote Linux Go test arguments in PowerShell
marcschier Oct 19, 2026
ad0190c
Merge origin/master into data-channels-quic-experimental
marcschier Oct 19, 2026
c343005
Align native workflow with pinned .NET SDK
marcschier Oct 19, 2026
8f45120
Await NUnit 5 async assertions
marcschier Oct 19, 2026
7207353
Fix native QUIC NUnit 5 diagnostics
marcschier Oct 19, 2026
a36fd6c
Await NUnit 5 async assertions in transport tests
marcschier Oct 19, 2026
b930f4f
Fix CI asset integrity and resource counter assertions
marcschier Oct 19, 2026
4115ef6
Merge origin/master and preserve data-channel lifecycle guards
marcschier Oct 20, 2026
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
  •  
  •  
  •  
6 changes: 6 additions & 0 deletions .azurepipelines/expected-packages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
# `dotnet pack` output only; the hand-authored `nuget/*.nuspec` packages are packed
# in a later pipeline step and are not part of this check.
#
# Native QUIC/HTTP3 packages are validated and produced by the dedicated native
# workflow; they require native build receipts and are not part of signed preview
# package publication.
#
# To update: run `dotnet pack preview-pack.slnx` and reconcile the produced
# package ids with this file in the same pull request that changes them.

Expand All @@ -25,6 +29,8 @@ OPCFoundation.NetStandard.Opc.Ua.AutoID
OPCFoundation.NetStandard.Opc.Ua.AutoID.Client
OPCFoundation.NetStandard.Opc.Ua.AutoID.Server
OPCFoundation.NetStandard.Opc.Ua.Bindings.Https
OPCFoundation.NetStandard.Opc.Ua.Bindings.Quic
OPCFoundation.NetStandard.Opc.Ua.Bindings.WebTransport
OPCFoundation.NetStandard.Opc.Ua.Client
OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes
OPCFoundation.NetStandard.Opc.Ua.Configuration
Expand Down
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ src/Opc.Ua.AMB/Model/Opc.Ua.AMB.NodeSet2.xml text eol=lf
# SHA-256 hashes are pinned in src/Opc.Ua.AutoID/Model/provenance.json and
# tests/Opc.Ua.AutoID.Tests/AutoIdModelAssetTests.cs, so the checked-out bytes must be
# the upstream bytes on every platform.
src/Opc.Ua.AutoID/Model/Opc.Ua.AutoID.NodeSet2.xml text eol=lf
# The upstream license banner includes trailing spaces covered by the pinned hash.
src/Opc.Ua.AutoID/Model/Opc.Ua.AutoID.NodeSet2.xml text eol=lf whitespace=-blank-at-eol
src/Opc.Ua.AutoID/Model/Opc.Ua.AutoID.NodeIds.csv text eol=lf

# Denote all files that should not have line endings normalized, should not be
Expand Down
6 changes: 6 additions & 0 deletions .github/actions/run-dotnet-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ inputs:
description: Collect Cobertura coverage ('true' or 'false').
required: false
default: 'false'
require-clean-exit:
description: Reject non-zero test-host exits even when the recorded counters all pass.
required: false
default: 'false'
artifact-name:
description: Name of the artifact that receives the results of this batch.
required: true
Expand Down Expand Up @@ -223,6 +227,7 @@ runs:
CI_HANG_TIMEOUT: ${{ inputs.hang-timeout }}
CI_PER_PROJECT_TIMEOUT: ${{ inputs.per-project-timeout }}
CI_COVERAGE: ${{ inputs.coverage }}
CI_REQUIRE_CLEAN_EXIT: ${{ inputs.require-clean-exit }}
run: |
# The core_pattern set by 'Enable native core dumps' only takes effect for
# a process whose RLIMIT_CORE is non-zero, and the limit is inherited, not
Expand All @@ -249,6 +254,7 @@ runs:
-HangTimeout $env:CI_HANG_TIMEOUT `
-PerProjectTimeoutMinutes ([int]$env:CI_PER_PROJECT_TIMEOUT) `
-ResultsDirectory 'TestResults' `
-RequireCleanExit:($env:CI_REQUIRE_CLEAN_EXIT -eq 'true') `
-Coverage:($env:CI_COVERAGE -eq 'true')

# Apple Crash Reporter writes .crash (legacy) and .ips (JSON, macOS 12+)
Expand Down
11 changes: 10 additions & 1 deletion .github/scripts/get-ci-matrix.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,20 @@ $DefaultFilter = 'TestCategory!=LongRunning&TestCategory!=Stress'
# validates, so a legacy leg fails with NETSDK1005
# before the no-op shell can take effect. It is covered
# by the fuzz-public-drop job instead.
# Native flow backends - require verified x64 native assets and run in the
# and bindings required native-data-channels reusable workflow.
# The HTTP3 test host also uses TUnit rather than VSTest.
$MainlineExclusions = @(
'Opc.Ua.Aot.Tests.csproj',
'Opc.Ua.Stress.Tests.csproj',
'Opc.Ua.Subscriptions.Durable.Tests.csproj',
'Opc.Ua.OneFuzz.Validator.Tests.csproj')
'Opc.Ua.OneFuzz.Validator.Tests.csproj',
'Opc.Ua.Native.QuicGo.Tests.csproj',
'Opc.Ua.Native.Http3.Tests.csproj',
'Opc.Ua.Bindings.Quic.Tests.csproj',
'Opc.Ua.Bindings.WebTransport.Tests.csproj',
'Opc.Ua.DataChannel.ClientServer.Tests.csproj',
'Opc.Ua.DataChannel.Samples.Tests.csproj')

$DurableProject = 'Opc.Ua.Subscriptions.Durable.Tests.csproj'

Expand Down
28 changes: 20 additions & 8 deletions .github/scripts/get-test-verdict.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@
TRX parsing it consumes, in their own file lets both be tested without
building or running anything.

The emitted TRX decides the verdict, not the 'dotnet test' exit code. A
non-zero exit is tolerated when - and only when - the results record at least
one *passing* test, no failure of any kind, and no test fixture whose setup or
teardown failed, which means the host died during process exit after the last
test and every teardown had already run. A host that dies mid-run leaves a
non-zero failed/aborted/passedButRunAborted counter and is rejected, and a
suite whose tests were all skipped is rejected as well.
The emitted TRX is the primary verdict. By default, the existing at-exit
tolerance accepts a non-zero exit only with passing results, no failure
counters, and no failed fixture setup or teardown. Counters alone cannot
distinguish every mid-run crash: VSTest can omit aborted counters after a
host dies. RequireCleanExit disables that tolerance for native qualification.
A suite whose tests were all skipped is always rejected.
#>

<#
Expand Down Expand Up @@ -48,6 +47,11 @@
.PARAMETER ExitCode
Exit code of the 'dotnet test' process.

.PARAMETER RequireCleanExit
Reject any non-zero host exit, even when the partial TRX contains only
passing counters. Native qualification uses this stricter policy without
changing the default tolerance for existing mainline test jobs.

.PARAMETER TimedOut
Whether the executor killed the process at its per-project ceiling.

Expand All @@ -70,7 +74,8 @@ function Get-TestRunVerdict
[Parameter(Mandatory = $true)] [int] $ExitCode,
[Parameter(Mandatory = $true)] [bool] $TimedOut,
[Parameter(Mandatory = $false)] [int] $TimeoutMinutes = 0,
[Parameter(Mandatory = $false)] [string[]] $FixtureFailures = @()
[Parameter(Mandatory = $false)] [string[]] $FixtureFailures = @(),
[Parameter(Mandatory = $false)] [switch] $RequireCleanExit
)

# A timeout is never benign: the executor killed a process that was still
Expand Down Expand Up @@ -141,6 +146,13 @@ function Get-TestRunVerdict
}

if ($ExitCode -ne 0) {
if ($RequireCleanExit) {
return [pscustomobject]@{
Passed = $false
Tolerated = $false
Reason = "A clean host exit is required, but the test host exited with code $ExitCode."
}
}
return [pscustomobject]@{
Passed = $true
Tolerated = $true
Expand Down
120 changes: 120 additions & 0 deletions .github/scripts/prepare-native-http3.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Copyright (c) OPC Foundation. Licensed under the MIT License.
<#
.SYNOPSIS
Resolves and verifies the public Rust toolchain used by the native HTTP3 build.
.DESCRIPTION
An existing isolated toolchain can be supplied without downloading anything.
Otherwise rustup installs the exact public release into build-private homes.
Executed compiler and runtime files are checked against the native package's
accepted public-input manifest. No default or system toolchain is changed.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)][string] $ToolRoot,
[string] $ExistingToolchainRoot = ''
)

$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
if (-not $IsWindows) {
throw 'This prepares Windows tools. Linux uses the separately pinned public Rust container.'
}
$repository = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..'))
$pins = Get-Content -LiteralPath (
Join-Path $repository 'src\Opc.Ua.Native.Http3\build\public-inputs.json') -Raw | ConvertFrom-Json
$ToolRoot = [IO.Path]::GetFullPath($ToolRoot)

if (-not [string]::IsNullOrWhiteSpace($ExistingToolchainRoot)) {
$toolchain = (Resolve-Path -LiteralPath $ExistingToolchainRoot).Path
}
else {
$rustup = (Get-Command rustup -CommandType Application -ErrorAction Stop).Source
New-Item -ItemType Directory -Path $ToolRoot -Force | Out-Null
$manifest = Join-Path $ToolRoot 'public-release-manifest.toml'
Invoke-WebRequest -Uri $pins.release_manifest_url -OutFile $manifest -TimeoutSec 120
if ((Get-FileHash -LiteralPath $manifest -Algorithm SHA256).Hash -ne $pins.release_manifest_sha256) {
throw 'The public Rust release manifest does not match the accepted hash.'
}

$environment = @{
RUSTUP_HOME = Join-Path $ToolRoot 'rustup'
CARGO_HOME = Join-Path $ToolRoot 'cargo'
RUSTUP_DIST_SERVER = 'https://static.rust-lang.org'
RUSTUP_UPDATE_ROOT = 'https://static.rust-lang.org/rustup'
RUSTUP_TOOLCHAIN = ''
}
$previous = @{}
foreach ($entry in $environment.GetEnumerator()) {
$previous[$entry.Key] = [Environment]::GetEnvironmentVariable($entry.Key, 'Process')
[Environment]::SetEnvironmentVariable($entry.Key, $entry.Value, 'Process')
}
try {
$selected = "$($pins.rust_version)-$($pins.windows.target)"
& $rustup toolchain install $selected --no-self-update --profile minimal `
--component rustfmt --component clippy --component rust-src | Out-Host
if ($LASTEXITCODE -ne 0) {
throw 'Could not install the exact public Rust release into the private build home.'
}
$toolchain = & $rustup run $selected rustc --print sysroot
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($toolchain)) {
throw 'The explicitly selected Rust toolchain did not report its root.'
}
$toolchain = [IO.Path]::GetFullPath($toolchain.Trim())
}
finally {
foreach ($entry in $previous.GetEnumerator()) {
[Environment]::SetEnvironmentVariable($entry.Key, $entry.Value, 'Process')
}
}
}

foreach ($file in $pins.windows.files) {
$path = Join-Path $toolchain $file.path
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) {
throw "The selected public Rust toolchain is missing '$($file.path)'."
}
if ((Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash -ne $file.sha256) {
throw "The selected public Rust toolchain differs at '$($file.path)'."
}
}

$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
if (-not (Test-Path -LiteralPath $vswhere -PathType Leaf)) {
throw 'Visual Studio discovery is unavailable; the pinned MSVC/SDK prerequisites must be installed.'
}
$installations = @(& $vswhere -all -products '*' -property installationPath)
if ($LASTEXITCODE -ne 0) {
throw 'Visual Studio discovery failed.'
}
$visualStudio = $installations | Where-Object {
Test-Path -LiteralPath (Join-Path $_ "VC\Tools\MSVC\$($pins.windows.msvc_directory)") -PathType Container
} | Select-Object -First 1
if ([string]::IsNullOrWhiteSpace($visualStudio)) {
throw "The accepted MSVC $($pins.windows.msvc_directory) toolset is not installed."
}
$windowsSdk = Join-Path ${env:ProgramFiles(x86)} 'Windows Kits\10'
$msvc = Join-Path $visualStudio "VC\Tools\MSVC\$($pins.windows.msvc_directory)"
$nativeInputMismatches = [Collections.Generic.List[string]]::new()
foreach ($file in $pins.windows.native_files) {
$base = switch ($file.base) {
'msvc' { $msvc }
'sdk' { $windowsSdk }
default { throw "Unknown native-tool input base '$($file.base)'." }
}
$path = Join-Path $base $file.path
$actualHash = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash
if ($actualHash -ne $file.sha256) {
$nativeInputMismatches.Add(
"'$($file.path)' expected '$($file.sha256)', found '$actualHash'.")
}
}
if ($nativeInputMismatches.Count -gt 0) {
throw "The accepted MSVC/SDK inputs differ: $($nativeInputMismatches -join ' ')"
}

[pscustomobject]@{
ToolchainRoot = $toolchain
VisualStudioRoot = $visualStudio
WindowsSdkRoot = $windowsSdk
RustVersion = $pins.rust_version
}
141 changes: 141 additions & 0 deletions .github/scripts/prepare-native-quic.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Copyright (c) OPC Foundation. Licensed under the MIT License.
<#
.SYNOPSIS
Prepares the pinned, private-to-this-build Windows tools and QUIC source.
.DESCRIPTION
Reuses verified tools when present. New archives are checked before extraction,
and a mismatched existing installation is refused rather than overwritten.
The source copy stays outside GOMODCACHE so Go can apply the reviewed overlay.
No system installation or PATH configuration is changed.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)][string] $ToolRoot,
[Parameter(Mandatory)][string] $CacheRoot,
[Parameter(Mandatory)][string] $QuicSourceRoot,
[Parameter(Mandatory)][string] $VerificationRoot
)

$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
if (-not $IsWindows) {
throw 'The accepted cross-compilation toolchain runs on Windows and produces both x64 RIDs.'
}

$repository = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..'))
$project = Join-Path $repository 'src\Opc.Ua.Native.QuicGo'
$pins = Get-Content -LiteralPath (Join-Path $project 'build\native-pins.json') -Raw | ConvertFrom-Json
$ToolRoot = [IO.Path]::GetFullPath($ToolRoot)
$CacheRoot = [IO.Path]::GetFullPath($CacheRoot)
$QuicSourceRoot = [IO.Path]::GetFullPath($QuicSourceRoot)
$VerificationRoot = [IO.Path]::GetFullPath($VerificationRoot)

function Assert-FileHash([string] $Path, [string] $Expected)
{
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
throw "Required pinned file is missing: $Path"
}
$actual = (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash
if (-not [string]::Equals($actual, $Expected, [StringComparison]::OrdinalIgnoreCase)) {
throw "Pinned SHA256 mismatch for '$Path'; existing files were not overwritten."
}
}

function Install-PinnedTool(
[string] $Name,
[string] $ArchiveDirectory,
[string] $Executable,
[psobject] $Pin)
{
$destination = Join-Path $ToolRoot $Name
$executablePath = Join-Path $destination $Executable
if (Test-Path -LiteralPath $destination) {
Assert-FileHash $executablePath $Pin.executable_sha256
return $executablePath
}

$archives = Join-Path $ToolRoot 'archives'
New-Item -ItemType Directory -Path $archives -Force | Out-Null
$archive = Join-Path $archives ([IO.Path]::GetFileName(([Uri] $Pin.archive_url).AbsolutePath))
if (-not (Test-Path -LiteralPath $archive)) {
$partial = "$archive.$([Guid]::NewGuid().ToString('N')).partial"
try {
Invoke-WebRequest -Uri $Pin.archive_url -OutFile $partial -TimeoutSec 180
Assert-FileHash $partial $Pin.archive_sha256
Move-Item -LiteralPath $partial -Destination $archive
}
finally {
if (Test-Path -LiteralPath $partial -PathType Leaf) {
Remove-Item -LiteralPath $partial -Force
}
}
}
Assert-FileHash $archive $Pin.archive_sha256

$stage = Join-Path $ToolRoot ".$Name-stage-$([Guid]::NewGuid().ToString('N'))"
New-Item -ItemType Directory -Path $stage | Out-Null
Expand-Archive -LiteralPath $archive -DestinationPath $stage
$extracted = Join-Path $stage $ArchiveDirectory
Assert-FileHash (Join-Path $extracted $Executable) $Pin.executable_sha256
Move-Item -LiteralPath $extracted -Destination $destination
# A successful extraction moves its only top-level directory. Never recursively
# delete a caller's tool root, including when extraction or validation fails.
Remove-Item -LiteralPath $stage
return $executablePath
}

New-Item -ItemType Directory -Path $ToolRoot,$CacheRoot,$VerificationRoot -Force | Out-Null
$go = Install-PinnedTool 'go' 'go' 'bin\go.exe' $pins.go
$zig = Install-PinnedTool 'zig' "zig-x86_64-windows-$($pins.zig.version)" 'zig.exe' $pins.zig

$savedEnvironment = @{}
$buildEnvironment = @{
GOROOT = Join-Path $ToolRoot 'go'
GOPATH = Join-Path $CacheRoot 'gopath'
GOMODCACHE = Join-Path $CacheRoot 'modules'
GOCACHE = Join-Path $CacheRoot 'compiled'
GOENV = 'off'
GOTOOLCHAIN = 'local'
GOTELEMETRY = 'off'
GOFLAGS = ''
}
foreach ($entry in $buildEnvironment.GetEnumerator()) {
$savedEnvironment[$entry.Key] = [Environment]::GetEnvironmentVariable($entry.Key, 'Process')
[Environment]::SetEnvironmentVariable($entry.Key, $entry.Value, 'Process')
}
Push-Location (Join-Path $project 'native')
try {
$moduleJson = & $go mod download -json "$($pins.module)@$($pins.version)"
if ($LASTEXITCODE -ne 0) {
throw 'Could not acquire the checksum-pinned QUIC module.'
}
$module = $moduleJson | ConvertFrom-Json
if ($module.Sum -ne $pins.module_sum -or $module.GoModSum -ne $pins.module_mod_sum) {
throw 'The acquired QUIC module does not match the accepted module checksums.'
}
if (-not (Test-Path -LiteralPath $QuicSourceRoot)) {
$parent = Split-Path -Parent $QuicSourceRoot
New-Item -ItemType Directory -Path $parent -Force | Out-Null
Copy-Item -LiteralPath $module.Dir -Destination $QuicSourceRoot -Recurse
}
& python (Join-Path $project 'build\prepare_overlay.py') `
--source $QuicSourceRoot --verified-module $module.Dir --output $VerificationRoot
if ($LASTEXITCODE -ne 0) {
throw 'The source inventory or accepted QUIC overlays do not match.'
}
}
finally {
Pop-Location
foreach ($entry in $savedEnvironment.GetEnumerator()) {
[Environment]::SetEnvironmentVariable($entry.Key, $entry.Value, 'Process')
}
}

[pscustomobject]@{
ToolRoot = $ToolRoot
CacheRoot = $CacheRoot
QuicSourceRoot = $QuicSourceRoot
VerificationRoot = $VerificationRoot
Go = $go
Zig = $zig
}
Loading
Loading