Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,26 @@ jobs:
MACOS_SIGN_PASSWORD: ${{ secrets.MACOS_SIGN_PASSWORD }}
MACOS_SIGN_TEAM_ID: ${{ vars.MACOS_SIGN_TEAM_ID }}
run: python scripts/release.py settings
- name: Install the pinned Artifact Signing client
timeout-minutes: 5
if: steps.saved.outputs.restored != 'true'
shell: pwsh
run: ./scripts/install-signing-client.ps1
- name: Sign in to Azure with OIDC
if: steps.saved.outputs.restored != 'true'
uses: azure/login@8216e11d8cd9b42fe925c852af8e76311ff067ac # v2
with:
client-id: ${{ vars.AZURE_CLIENT_ID }}
tenant-id: ${{ vars.AZURE_TENANT_ID }}
subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
- name: Install the Artifact Signing client and sign the installers
- name: Sign the Windows installers
if: steps.saved.outputs.restored != 'true'
shell: pwsh
env:
ARTIFACT_SIGNING_ENDPOINT: ${{ vars.ARTIFACT_SIGNING_ENDPOINT }}
ARTIFACT_SIGNING_ACCOUNT_NAME: ${{ vars.ARTIFACT_SIGNING_ACCOUNT_NAME }}
ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME: ${{ vars.ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME }}
run: |
Install-Module -Name ArtifactSigning -RequiredVersion 0.1.8 -Scope CurrentUser -Force -Repository PSGallery
python scripts/release.py prepare-windows
if ($LASTEXITCODE -ne 0) { throw 'Installer preparation failed.' }
Get-ChildItem packaging/*.ps1 | ForEach-Object { ./scripts/sign-windows.ps1 -Path $_.FullName }
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ on:
permissions:
contents: read
jobs:
signing-client:
runs-on: windows-2025
timeout-minutes: 15
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: Recover the signing client from a missing package repository
shell: pwsh
run: ./scripts/test-signing-client.ps1
saved-release:
if: github.event_name == 'workflow_dispatch' && inputs.release_run_id != ''
permissions:
Expand Down
31 changes: 31 additions & 0 deletions scripts/install-signing-client.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#Requires -Version 7.2
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest

for ($attempt = 1; $attempt -le 3; $attempt++) {
try {
$gallery = @(Get-PSRepository -ErrorAction Stop | Where-Object Name -eq 'PSGallery')
if ($gallery.Count -eq 0) {
Register-PSRepository -Default -ErrorAction Stop
$gallery = @(Get-PSRepository -Name PSGallery -ErrorAction Stop)
}
if ($gallery.Count -ne 1 -or $gallery[0].SourceLocation.TrimEnd('/') -cne 'https://www.powershellgallery.com/api/v2') {
throw [System.Security.SecurityException]::new('PSGallery must use the official HTTPS package source.')
}
Install-Module -Name ArtifactSigning -RequiredVersion 0.1.8 -Scope CurrentUser -Force -Repository PSGallery -ErrorAction Stop
$module = Import-Module ArtifactSigning -RequiredVersion 0.1.8 -Force -PassThru -ErrorAction Stop
if ($module.Version -ne [version]'0.1.8' -or -not $module.ExportedCommands.ContainsKey('Invoke-ArtifactSigning')) {
throw 'The pinned Artifact Signing client did not load.'
}
Write-Output 'ArtifactSigning 0.1.8 is installed and ready.'
# PackageManagement can leave a native NuGet error code after recovery.
# Report success only after the exact module and command have loaded.
exit 0
} catch [System.Security.SecurityException] {
throw
} catch {
if ($attempt -eq 3) { throw }
Write-Warning "Artifact Signing setup attempt $attempt failed: $($_.Exception.Message). Retrying."
Start-Sleep -Seconds (5 * $attempt)
}
}
49 changes: 49 additions & 0 deletions scripts/test-signing-client.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#Requires -Version 7.2
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest
if ($env:GITHUB_ACTIONS -ne 'true' -or $env:RUNNER_OS -ne 'Windows') {
throw 'Run this test only on a disposable Windows Actions runner.'
}
$setup = Join-Path $PSScriptRoot 'install-signing-client.ps1'

# Reproduce the failed release with no registered PSGallery, then use the real service.
if (Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue) {
Unregister-PSRepository -Name PSGallery -ErrorAction Stop
}
if (Get-PSRepository -Name PSGallery -ErrorAction SilentlyContinue) { throw 'PSGallery was not removed for the test.' }
$global:LASTEXITCODE = 23
& $setup
if ($LASTEXITCODE -ne 0) { throw 'Successful setup kept a stale native process exit code.' }
$module = Get-Module -ListAvailable ArtifactSigning | Where-Object Version -eq ([version]'0.1.8')
if (-not $module) { throw 'The pinned module was not installed.' }
& $setup
Write-Output 'Real missing-repository recovery, module import, and repeated setup passed.'

# Check bounded retries without waiting or making extra network requests.
& {
$state = @{ Attempts=0; Sleeps=0; Failures=1; Source='https://www.powershellgallery.com/api/v2' }
function Get-PSRepository {
[CmdletBinding()] param([string]$Name)
[pscustomobject]@{ Name='PSGallery'; SourceLocation=$state.Source }
}
function Install-Module {
[CmdletBinding()] param($Name, $RequiredVersion, $Scope, [switch]$Force, $Repository)
if ($Name -ne 'ArtifactSigning' -or $RequiredVersion -ne '0.1.8' -or $Scope -ne 'CurrentUser' -or $Repository -ne 'PSGallery') {
throw 'The installer changed its pinned module or repository.'
}
$state.Attempts++
if ($state.Attempts -le $state.Failures) { throw 'Simulated package service failure.' }
}
function Start-Sleep { param($Seconds) $state.Sleeps++ }
& $setup
if ($state.Attempts -ne 2 -or $state.Sleeps -ne 1) { throw 'Transient failure recovery failed.' }
$state.Attempts=0; $state.Sleeps=0; $state.Failures=3
$failed=$false
try { & $setup } catch { $failed=$true }
if (-not $failed -or $state.Attempts -ne 3 -or $state.Sleeps -ne 2) { throw 'Persistent failures did not stop after three attempts.' }
$state.Attempts=0; $state.Sleeps=0; $state.Source='https://packages.example.invalid/api/v2'
$failed=$false
try { & $setup } catch [System.Security.SecurityException] { $failed=$true }
if (-not $failed -or $state.Attempts -ne 0 -or $state.Sleeps -ne 0) { throw 'An unexpected package source was not rejected.' }
}
Write-Output 'Retry limits and package source checks passed.'
Loading