fix(releases): bootstrap verified Artifact Signing dependency #44

Merged
KofTwentyTwo merged 1 commits from fix/artifact-signing-bootstrap into main 2026-10-12 23:22:03 +00:00
2 changed files with 99 additions and 0 deletions

No files matched your search

+29
View File
@@ -366,6 +366,35 @@ jobs:
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
# The pinned official action installs this tool with --add-source, which
# conflicts with NuGet package source mapping. Stage its verified package
# in the action's own dependency directory without changing caller policy.
- name: Prepare pinned Artifact Signing CLI dependency
if: inputs.sign
shell: pwsh
run: |
param(
[string]$PackageDirectory = $env:RUNNER_TEMP,
[string]$InstallRoot = $env:LOCALAPPDATA
)
$ErrorActionPreference = 'Stop'
$version = '0.9.1-beta.26227.3'
$package = Join-Path $PackageDirectory "sign.$version.nupkg"
$destination = Join-Path $InstallRoot "ArtifactSigning/sign/sign.$version"
if (Test-Path -LiteralPath $destination) { throw 'Artifact Signing CLI directory already exists; use a fresh release runner.' }
Invoke-WebRequest -Uri "https://api.nuget.org/v3-flatcontainer/sign/$version/sign.$version.nupkg" -OutFile $package
if ((Get-FileHash -LiteralPath $package -Algorithm SHA256).Hash -ne '34FD0D4AEABDBC363A48883881865B4DD65E6C11BA916028082FA23F3E1B1BA1')
{
throw 'Artifact Signing CLI package checksum mismatch.'
}
dotnet nuget verify $package --all
if ($LASTEXITCODE -ne 0) { throw 'Artifact Signing CLI NuGet signature verification failed.' }
[IO.Compression.ZipFile]::ExtractToDirectory($package, $destination)
if (-not (Test-Path -LiteralPath (Join-Path $destination 'tools/net8.0/any/sign.dll') -PathType Leaf))
{
throw 'Artifact Signing CLI package is missing its executable.'
}
# Signs the app's own executables and DLLs (and the CLI) before packing, so the
# binaries inside the installer, the portable zip, and the update packages are
# all signed (K22-REL-05).
+70
View File
@@ -14,6 +14,13 @@ BeforeAll `
if($start -lt 0 -or $end -ge $lines.Count) { throw 'Cannot locate the actual Velopack signing wrapper.' }
$script:Wrapper = Join-Path $TestDrive 'sign-one.ps1'
[IO.File]::WriteAllLines($script:Wrapper, @($lines[($start + 1)..($end - 1)] | ForEach-Object { $_.Substring(10) }))
$dependencyStart = [Array]::IndexOf($lines, ' - name: Prepare pinned Artifact Signing CLI dependency')
if($dependencyStart -lt 0) { throw 'Cannot locate the actual signing dependency bootstrap.' }
$dependencyStart += 4
$dependencyEnd = $dependencyStart
while($dependencyEnd -lt $lines.Count -and $lines[$dependencyEnd].StartsWith(' ')) { $dependencyEnd++ }
$script:Bootstrap = Join-Path $TestDrive 'sign-dependency.ps1'
[IO.File]::WriteAllLines($script:Bootstrap, @($lines[$dependencyStart..($dependencyEnd - 1)] | ForEach-Object { $_.Substring(10) }))
$script:OldTool = $env:K22_VPK_SIGN_TOOL
$env:K22_VPK_SIGN_TOOL = 'Invoke-WrapperTool'
@@ -31,6 +38,69 @@ BeforeAll `
}
}
Describe 'The actual Artifact Signing dependency bootstrap' `
{
BeforeEach `
{
$script:InstallRoot = Join-Path $TestDrive ([guid]::NewGuid().ToString('N'))
$script:PackageDirectory = Join-Path $script:InstallRoot 'downloads'
[void][IO.Directory]::CreateDirectory($script:PackageDirectory)
$script:Destination = Join-Path $script:InstallRoot 'ArtifactSigning/sign/sign.0.9.1-beta.26227.3'
Mock Invoke-WebRequest `
{
$fixture = Join-Path (Split-Path -Parent $OutFile) 'fixture'
[void][IO.Directory]::CreateDirectory((Join-Path $fixture 'tools/net8.0/any'))
[IO.File]::WriteAllText((Join-Path $fixture 'tools/net8.0/any/sign.dll'), 'fixture')
[IO.Compression.ZipFile]::CreateFromDirectory($fixture, $OutFile)
}
Mock Get-FileHash { [pscustomobject]@{ Hash = '34FD0D4AEABDBC363A48883881865B4DD65E6C11BA916028082FA23F3E1B1BA1' } }
Mock dotnet { $global:LASTEXITCODE = 0 }
}
It 'stages the verified tool where the official action finds it' `
{
& $script:Bootstrap -InstallRoot $script:InstallRoot -PackageDirectory $script:PackageDirectory
Get-Content -LiteralPath (Join-Path $script:Destination 'tools/net8.0/any/sign.dll') | Should -Be 'fixture'
Should -Invoke dotnet -Exactly 1
}
It 'rejects a changed download before verification or extraction' `
{
Mock Get-FileHash { [pscustomobject]@{ Hash = 'bad' } }
{ & $script:Bootstrap -InstallRoot $script:InstallRoot -PackageDirectory $script:PackageDirectory } | Should -Throw '*checksum mismatch*'
Test-Path -LiteralPath $script:Destination | Should -BeFalse
Should -Invoke dotnet -Exactly 0
}
It 'rejects an invalid NuGet signature before extraction' `
{
Mock dotnet { $global:LASTEXITCODE = 1 }
{ & $script:Bootstrap -InstallRoot $script:InstallRoot -PackageDirectory $script:PackageDirectory } | Should -Throw '*signature verification failed*'
Test-Path -LiteralPath $script:Destination | Should -BeFalse
}
It 'preserves a preexisting dependency directory' `
{
[void][IO.Directory]::CreateDirectory($script:Destination)
$marker = Join-Path $script:Destination 'marker.txt'
[IO.File]::WriteAllText($marker, 'preserve')
{ & $script:Bootstrap -InstallRoot $script:InstallRoot -PackageDirectory $script:PackageDirectory } | Should -Throw '*already exists*'
Get-Content -LiteralPath $marker | Should -Be 'preserve'
Should -Invoke Invoke-WebRequest -Exactly 0
}
It 'rejects a package without the expected executable' `
{
Mock Invoke-WebRequest `
{
$emptyFixture = Join-Path (Split-Path -Parent $OutFile) 'empty'
[void][IO.Directory]::CreateDirectory($emptyFixture)
[IO.Compression.ZipFile]::CreateFromDirectory($emptyFixture, $OutFile)
}
{ & $script:Bootstrap -InstallRoot $script:InstallRoot -PackageDirectory $script:PackageDirectory } | Should -Throw '*missing its executable*'
}
}
AfterAll `
{
$env:K22_VPK_SIGN_TOOL = $script:OldTool