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
31 changes: 31 additions & 0 deletions .github/workflows/authority-installer-retention.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Authority installer retention

on:
pull_request:
paths:
- 'authority-host/windows/install-release.ps1'
- 'tests/windows/authority-release-retention.ps1'
- '.github/workflows/authority-installer-retention.yml'

permissions:
contents: read

jobs:
retention:
name: Prune obsolete Authority versions
runs-on: windows-latest
timeout-minutes: 20
steps:
- name: Check out repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Set up .NET
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json

- name: Run Authority installer retention regression
shell: pwsh
run: ./tests/windows/authority-release-retention.ps1
13 changes: 13 additions & 0 deletions authority-host/windows/install-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,19 @@ try {
[IO.File]::WriteAllText($recordTemp, $recordJson, $utf8NoBom)
Move-Item -Force $recordTemp $recordPath

# Version directories are release-owned and self-contained. Once the new
# version is active, remove obsolete release versions so updates do not
# accumulate full runtimes. Root state and unknown app content are preserved.
$targetName = 'v' + $ExpectedVersion
$obsoleteVersionDirs = @(
Get-ChildItem -LiteralPath $appRoot -Directory -Force | Where-Object {
$_.Name -match '^v\d+\.\d+\.\d+$' -and $_.Name -ne $targetName
}
)
foreach ($obsoleteVersionDir in $obsoleteVersionDirs) {
Remove-Item -LiteralPath $obsoleteVersionDir.FullName -Recurse -Force
}

# Remove only the obsolete root-level launcher from the legacy layout. State
# (`authority.db`, trust-store.json) and unknown user files are deliberately preserved.
$legacyExe = Join-Path $InstallDir 'GitHubDeliveryAuthority.exe'
Expand Down
75 changes: 75 additions & 0 deletions tests/windows/authority-release-retention.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
[CmdletBinding()]
param()

$ErrorActionPreference = 'Stop'
$repoRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..'))
$project = Join-Path $repoRoot 'authority-host\windows\GitHubDeliveryAuthority\GitHubDeliveryAuthority.csproj'
$installer = Join-Path $repoRoot 'authority-host\windows\install-release.ps1'
$workspace = Join-Path $env:RUNNER_TEMP ('authority-retention-' + [guid]::NewGuid().ToString('N'))
$publish = Join-Path $workspace 'publish'
$installDir = Join-Path $workspace 'install'
$utf8NoBom = New-Object System.Text.UTF8Encoding($false)

try {
New-Item -ItemType Directory -Force -Path $workspace | Out-Null

& dotnet restore $project --locked-mode
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
& dotnet publish $project --configuration Release --runtime win-x64 --self-contained true --no-restore --output $publish
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

$version = [string](Get-Content (Join-Path $repoRoot 'package.json') -Raw | ConvertFrom-Json).version
$sourceCommit = (& git -C $repoRoot rev-parse HEAD | Select-Object -First 1).Trim().ToLowerInvariant()
if ($sourceCommit -notmatch '^[0-9a-f]{40}$') { throw 'Could not resolve source commit for installer retention test.' }

$versionInfo = [ordered]@{
schemaVersion = 1
kind = 'github-delivery/authority-host-version'
version = $version
sourceCommit = $sourceCommit
platform = 'win32'
arch = 'x64'
}
[IO.File]::WriteAllText(
(Join-Path $publish 'authority-host-version.json'),
(($versionInfo | ConvertTo-Json) + [Environment]::NewLine),
$utf8NoBom
)

$appRoot = Join-Path $installDir 'app'
$oldA = Join-Path $appRoot 'v0.0.1'
$oldB = Join-Path $appRoot 'v0.0.2'
New-Item -ItemType Directory -Force -Path $oldA | Out-Null
New-Item -ItemType Directory -Force -Path $oldB | Out-Null
[IO.File]::WriteAllText((Join-Path $oldA 'old-a.bin'), 'old-a', $utf8NoBom)
[IO.File]::WriteAllText((Join-Path $oldB 'old-b.bin'), 'old-b', $utf8NoBom)

New-Item -ItemType Directory -Force -Path $installDir | Out-Null
$dbPath = Join-Path $installDir 'authority.db'
$trustPath = Join-Path $installDir 'trust-store.json'
[IO.File]::WriteAllText($dbPath, 'persistent-db', $utf8NoBom)
[IO.File]::WriteAllText($trustPath, '{"persistent":true}', $utf8NoBom)

& powershell.exe -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Bypass `
-File $installer `
-SourceDir $publish `
-ExpectedVersion $version `
-ExpectedSourceCommit $sourceCommit `
-InstallDir $installDir `
-SkipStart
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

$targetName = 'v' + $version
$releaseDirs = @(
Get-ChildItem $appRoot -Directory | Where-Object { $_.Name -match '^v\d+\.\d+\.\d+$' }
)
if ($releaseDirs.Count -ne 1 -or $releaseDirs[0].Name -ne $targetName) {
$found = ($releaseDirs | ForEach-Object Name) -join ', '
throw "Expected only $targetName under app after update, found: $found"
}
if ((Get-Content $dbPath -Raw) -ne 'persistent-db') { throw 'authority.db was not preserved.' }
if ((Get-Content $trustPath -Raw) -ne '{"persistent":true}') { throw 'trust-store.json was not preserved.' }
}
finally {
Remove-Item $workspace -Recurse -Force -ErrorAction SilentlyContinue
}
Loading