302 lines
10 KiB
PowerShell
302 lines
10 KiB
PowerShell
#!/usr/bin/env pwsh
|
|
<#
|
|
.SYNOPSIS
|
|
Builds a local UniGetUI Chocolatey package.
|
|
|
|
.DESCRIPTION
|
|
Reproduces the Chocolatey packaging portion of Devolutions/release-notes CI.
|
|
By default it sources the installer and checksum from GitHub Releases instead
|
|
of OneDrive. It can also build a fresh local x64 installer from the current
|
|
checkout and embed that installer into the nupkg for local test-environment
|
|
debugging.
|
|
|
|
.PARAMETER Version
|
|
UniGetUI version to package, for example 2026.1.6. If omitted, the latest
|
|
GitHub release is used.
|
|
|
|
.PARAMETER OutputPath
|
|
Directory where the downloaded assets, generated package files, and final
|
|
.nupkg are written. Default: ./output/chocolatey
|
|
|
|
.PARAMETER BuildLocalInstaller
|
|
Build a fresh local x64 installer from the current checkout and package that
|
|
installer into the nupkg instead of using a GitHub release asset.
|
|
|
|
.PARAMETER InstallerPath
|
|
Use an existing local installer file instead of downloading from GitHub.
|
|
The installer is embedded into the generated nupkg.
|
|
|
|
.PARAMETER SkipLocalBuildTests
|
|
When BuildLocalInstaller is set, skip running tests during the local build.
|
|
|
|
.EXAMPLE
|
|
./scripts/build-chocolatey-package.ps1
|
|
|
|
.EXAMPLE
|
|
./scripts/build-chocolatey-package.ps1 -Version 2026.1.6
|
|
|
|
.EXAMPLE
|
|
./scripts/build-chocolatey-package.ps1 -BuildLocalInstaller -Version 2026.1.6 -SkipLocalBuildTests
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[string] $Version,
|
|
[string] $OutputPath = (Join-Path $PSScriptRoot ".." "output" "chocolatey"),
|
|
[switch] $BuildLocalInstaller,
|
|
[string] $InstallerPath,
|
|
[switch] $SkipLocalBuildTests
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
|
$TemplateDir = Join-Path $RepoRoot "package" "chocolatey" "unigetui"
|
|
$OutputPath = [System.IO.Path]::GetFullPath($OutputPath)
|
|
$DownloadDir = Join-Path $OutputPath "downloads"
|
|
$StagingRoot = Join-Path $OutputPath "staging"
|
|
$StagingDir = Join-Path $StagingRoot "unigetui"
|
|
|
|
function Invoke-GitHubApi {
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string] $Uri
|
|
)
|
|
|
|
$headers = @{
|
|
'Accept' = 'application/vnd.github+json'
|
|
'User-Agent' = 'UniGetUI-Chocolatey-Local-Pack'
|
|
}
|
|
|
|
if ($env:GITHUB_TOKEN) {
|
|
$headers['Authorization'] = "Bearer $($env:GITHUB_TOKEN)"
|
|
}
|
|
|
|
Invoke-RestMethod -Uri $Uri -Headers $headers
|
|
}
|
|
|
|
function Download-File {
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string] $Uri,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string] $DestinationPath
|
|
)
|
|
|
|
$headers = @{ 'User-Agent' = 'UniGetUI-Chocolatey-Local-Pack' }
|
|
if ($env:GITHUB_TOKEN) {
|
|
$headers['Authorization'] = "Bearer $($env:GITHUB_TOKEN)"
|
|
}
|
|
|
|
Invoke-WebRequest -Uri $Uri -Headers $headers -OutFile $DestinationPath
|
|
}
|
|
|
|
function Get-Release {
|
|
param(
|
|
[string] $RequestedVersion
|
|
)
|
|
|
|
if ([string]::IsNullOrWhiteSpace($RequestedVersion)) {
|
|
return Invoke-GitHubApi -Uri 'https://api.github.com/repos/Devolutions/UniGetUI/releases/latest'
|
|
}
|
|
|
|
$tag = if ($RequestedVersion.StartsWith('v')) { $RequestedVersion } else { "v$RequestedVersion" }
|
|
$escapedTag = [System.Uri]::EscapeDataString($tag)
|
|
return Invoke-GitHubApi -Uri "https://api.github.com/repos/Devolutions/UniGetUI/releases/tags/$escapedTag"
|
|
}
|
|
|
|
function Get-CurrentPackageVersion {
|
|
$assemblyInfoPath = Join-Path $RepoRoot 'src' 'SharedAssemblyInfo.cs'
|
|
$versionMatch = Select-String -Path $assemblyInfoPath -Pattern 'AssemblyInformationalVersion\("([^"]+)"\)'
|
|
if (-not $versionMatch) {
|
|
throw "Could not determine the current package version from $assemblyInfoPath"
|
|
}
|
|
|
|
return $versionMatch.Matches[0].Groups[1].Value
|
|
}
|
|
|
|
function Get-ChecksumFromFile {
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string] $ChecksumsPath,
|
|
|
|
[Parameter(Mandatory)]
|
|
[string] $AssetName
|
|
)
|
|
|
|
$pattern = "^(?<hash>[A-Fa-f0-9]{64})\s+$([regex]::Escape($AssetName))$"
|
|
$line = Select-String -Path $ChecksumsPath -Pattern $pattern | Select-Object -First 1
|
|
if (-not $line) {
|
|
throw "Could not find a SHA256 for '$AssetName' in $ChecksumsPath"
|
|
}
|
|
|
|
return $line.Matches[0].Groups['hash'].Value.ToUpperInvariant()
|
|
}
|
|
|
|
function Require-Command {
|
|
param(
|
|
[Parameter(Mandatory)]
|
|
[string] $Name
|
|
)
|
|
|
|
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
|
|
throw "Required command '$Name' was not found in PATH."
|
|
}
|
|
}
|
|
|
|
Require-Command -Name 'choco'
|
|
|
|
if (-not (Test-Path $TemplateDir)) {
|
|
throw "Chocolatey template folder not found: $TemplateDir"
|
|
}
|
|
|
|
if ($BuildLocalInstaller -and $InstallerPath) {
|
|
throw 'BuildLocalInstaller and InstallerPath cannot be used together.'
|
|
}
|
|
|
|
$useEmbeddedInstaller = $BuildLocalInstaller -or -not [string]::IsNullOrWhiteSpace($InstallerPath)
|
|
$installerAssetName = 'UniGetUI.Installer.x64.exe'
|
|
$installerUrl = $null
|
|
$release = $null
|
|
|
|
if ($BuildLocalInstaller) {
|
|
$localBuildOutputPath = Join-Path $OutputPath 'local-build'
|
|
$buildArgs = @{
|
|
Platform = 'x64'
|
|
OutputPath = $localBuildOutputPath
|
|
}
|
|
|
|
if (-not [string]::IsNullOrWhiteSpace($Version)) {
|
|
$buildArgs['Version'] = $Version
|
|
}
|
|
|
|
if ($SkipLocalBuildTests) {
|
|
$buildArgs['SkipTests'] = $true
|
|
}
|
|
|
|
Write-Host 'Building local UniGetUI installer from the current checkout'
|
|
& (Join-Path $PSScriptRoot 'build.ps1') @buildArgs
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "Local installer build failed with exit code $LASTEXITCODE"
|
|
}
|
|
|
|
$InstallerPath = Join-Path $localBuildOutputPath $installerAssetName
|
|
if (-not (Test-Path $InstallerPath)) {
|
|
throw "Local installer was not produced at $InstallerPath"
|
|
}
|
|
|
|
$packageVersion = if ([string]::IsNullOrWhiteSpace($Version)) { Get-CurrentPackageVersion } else { $Version }
|
|
Write-Host "Packaging UniGetUI Chocolatey package from local installer $InstallerPath"
|
|
}
|
|
elseif (-not [string]::IsNullOrWhiteSpace($InstallerPath)) {
|
|
$InstallerPath = [System.IO.Path]::GetFullPath($InstallerPath)
|
|
if (-not (Test-Path $InstallerPath)) {
|
|
throw "InstallerPath was not found: $InstallerPath"
|
|
}
|
|
|
|
$packageVersion = if ([string]::IsNullOrWhiteSpace($Version)) { Get-CurrentPackageVersion } else { $Version }
|
|
Write-Host "Packaging UniGetUI Chocolatey package from local installer $InstallerPath"
|
|
}
|
|
else {
|
|
$release = Get-Release -RequestedVersion $Version
|
|
$packageVersion = $release.tag_name.TrimStart('v')
|
|
|
|
$installerAsset = $release.assets | Where-Object { $_.name -eq $installerAssetName } | Select-Object -First 1
|
|
if (-not $installerAsset) {
|
|
throw "Could not find asset '$installerAssetName' in release $($release.tag_name)"
|
|
}
|
|
|
|
$checksumsAsset = $release.assets | Where-Object { $_.name -eq 'checksums.txt' } | Select-Object -First 1
|
|
if (-not $checksumsAsset) {
|
|
throw "Could not find asset 'checksums.txt' in release $($release.tag_name)"
|
|
}
|
|
|
|
Write-Host "Packaging UniGetUI Chocolatey package for release $($release.tag_name)"
|
|
Write-Host "Installer asset: $($installerAsset.browser_download_url)"
|
|
|
|
$installerPath = Join-Path $DownloadDir $installerAsset.name
|
|
$checksumsPath = Join-Path $DownloadDir $checksumsAsset.name
|
|
|
|
Download-File -Uri $installerAsset.browser_download_url -DestinationPath $installerPath
|
|
Download-File -Uri $checksumsAsset.browser_download_url -DestinationPath $checksumsPath
|
|
|
|
$sha256 = Get-ChecksumFromFile -ChecksumsPath $checksumsPath -AssetName $installerAsset.name
|
|
$installerUrl = $installerAsset.browser_download_url
|
|
}
|
|
|
|
New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null
|
|
New-Item -Path $DownloadDir -ItemType Directory -Force | Out-Null
|
|
New-Item -Path $StagingRoot -ItemType Directory -Force | Out-Null
|
|
|
|
if (Test-Path $StagingRoot) {
|
|
Remove-Item $StagingRoot -Recurse -Force
|
|
}
|
|
|
|
New-Item -Path $StagingRoot -ItemType Directory -Force | Out-Null
|
|
Copy-Item -Path $TemplateDir -Destination $StagingDir -Recurse
|
|
|
|
if ($useEmbeddedInstaller) {
|
|
$embeddedInstallerName = Split-Path $InstallerPath -Leaf
|
|
$embeddedInstallerPath = Join-Path $StagingDir 'tools' $embeddedInstallerName
|
|
Copy-Item -Path $InstallerPath -Destination $embeddedInstallerPath -Force
|
|
$sha256 = (Get-FileHash -Path $InstallerPath -Algorithm SHA256).Hash.ToUpperInvariant()
|
|
}
|
|
|
|
$nuspecTemplatePath = Join-Path $StagingDir 'unigetui.template.nuspec'
|
|
$installTemplatePath = Join-Path $StagingDir 'tools' 'chocolateyInstall.template.ps1'
|
|
$nuspecPath = Join-Path $StagingDir 'unigetui.nuspec'
|
|
$installScriptPath = Join-Path $StagingDir 'tools' 'chocolateyInstall.ps1'
|
|
|
|
$nuspecContent = (Get-Content $nuspecTemplatePath -Raw).Replace('$VAR1$', $packageVersion)
|
|
|
|
if ($useEmbeddedInstaller) {
|
|
$nuspecContent = $nuspecContent.Replace(
|
|
' <file src="tools\chocolateyUninstall.ps1" target="tools" />',
|
|
(' <file src="tools\chocolateyUninstall.ps1" target="tools" />' + "`r`n" + (' <file src="tools\{0}" target="tools" />' -f $embeddedInstallerName))
|
|
)
|
|
}
|
|
|
|
$installContent = Get-Content $installTemplatePath -Raw
|
|
|
|
if ($useEmbeddedInstaller) {
|
|
$installContent = $installContent.Replace(
|
|
'$Url = ''https://cdn.devolutions.net/download/Devolutions.UniGetUI.win-x64.$VAR1$.exe''',
|
|
('$InstallerPath = Join-Path $PSScriptRoot ''{0}''' -f $embeddedInstallerName)
|
|
)
|
|
$installContent = $installContent.Replace(' url = $Url', ' file = $InstallerPath')
|
|
$installContent = $installContent -replace "(?m)^\s*checksum\s*=.*\r?\n", ''
|
|
$installContent = $installContent -replace "(?m)^\s*checksumType\s*=.*\r?\n", ''
|
|
}
|
|
else {
|
|
$installContent = $installContent.Replace('$VAR2$', $sha256)
|
|
$installContent = $installContent.Replace(
|
|
"'https://cdn.devolutions.net/download/Devolutions.UniGetUI.win-x64.`$VAR1$.exe'",
|
|
"'$installerUrl'"
|
|
)
|
|
}
|
|
|
|
$installContent = $installContent.Replace('$VAR1$', $packageVersion)
|
|
|
|
Set-Content -Path $nuspecPath -Value $nuspecContent -Encoding utf8NoBOM
|
|
Set-Content -Path $installScriptPath -Value $installContent -Encoding utf8NoBOM
|
|
|
|
Push-Location $StagingDir
|
|
try {
|
|
& choco pack 'unigetui.nuspec' --outputdirectory $OutputPath
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "choco pack failed with exit code $LASTEXITCODE"
|
|
}
|
|
}
|
|
finally {
|
|
Pop-Location
|
|
}
|
|
|
|
$packagePath = Join-Path $OutputPath "unigetui.$packageVersion.nupkg"
|
|
if (-not (Test-Path $packagePath)) {
|
|
$packagePath = Get-ChildItem -Path $OutputPath -Filter 'unigetui.*.nupkg' | Sort-Object LastWriteTime -Descending | Select-Object -First 1 -ExpandProperty FullName
|
|
}
|
|
|
|
Write-Host "SHA256 (installer): $sha256"
|
|
Write-Host "Package written to: $packagePath"
|