Files
rgb-led-strip-controller/scripts/release/package-velopack.ps1
T
fristy c9e643d617 Initial commit: RGB LED Strip Controller v0.3.0
- WPF + WinRT BLE controller for ELK-BLEDOM compatible LED strips

- Multi-device control, effects, grayscale and temperature modes

- Velopack updates via Gitea Releases, Gitea Actions CI
2026-09-04 23:31:11 +03:00

272 lines
8.6 KiB
PowerShell

[CmdletBinding()]
param(
[string]$Project = "src/RgbLedStripController/RgbLedStripController.csproj",
[string]$Configuration = "Release",
[string]$Runtime = "win-x64",
[string]$Channel = "win-x64-stable",
[string]$PackId = "Fristy.RgbLedStripController",
[string]$PackTitle = "RGB Led Strip Controller",
[string]$PackAuthors = "fristy",
[string]$GiteaRepoUrl = "https://git.fristy.net/fristy/rgb-led-strip-controller",
[string]$InstallerIcon = "src/RgbLedStripController/Assets/AppIcon.ico",
[string]$PfxPath = "",
[string]$SignToolPath = "signtool.exe",
[int]$SignParallel = 1,
[switch]$SkipSigning,
[switch]$SkipCloudCheck
)
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$repoRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\.."))
$projectPath = [System.IO.Path]::GetFullPath((Join-Path $repoRoot $Project))
$nugetConfigPath = [System.IO.Path]::GetFullPath((Join-Path $repoRoot "NuGet.Config"))
if (-not (Test-Path -LiteralPath $projectPath)) {
throw "Project file not found: $projectPath"
}
[xml]$projectXml = Get-Content -Raw -LiteralPath $projectPath
$versionNode = $null
foreach ($propertyGroup in @($projectXml.Project.PropertyGroup)) {
if ($propertyGroup.PSObject.Properties.Name -contains "Version" -and -not [string]::IsNullOrWhiteSpace($propertyGroup.Version)) {
$versionNode = $propertyGroup.Version
break
}
}
$version = [string]$versionNode
if ([string]::IsNullOrWhiteSpace($version)) {
throw "No <Version> found in project file: $projectPath"
}
function Assert-WorkspacePath {
param([string]$Path)
$fullPath = [System.IO.Path]::GetFullPath($Path)
if (-not $fullPath.StartsWith($repoRoot, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Refusing to use path outside workspace: $fullPath"
}
return $fullPath
}
function Resolve-AbsolutePath {
param([string]$Path)
if ([string]::IsNullOrWhiteSpace($Path)) {
throw "Path value cannot be empty."
}
if ([System.IO.Path]::IsPathRooted($Path)) {
return [System.IO.Path]::GetFullPath($Path)
}
return [System.IO.Path]::GetFullPath((Join-Path $repoRoot $Path))
}
function Get-GiteaReleaseTags {
param([string]$RepoUrl)
$uri = [Uri]$RepoUrl
$segments = $uri.AbsolutePath.Trim("/").Split("/") | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
if ($segments.Count -lt 2) {
throw "Could not parse owner/repo from GiteaRepoUrl: $RepoUrl"
}
$owner = $segments[-2]
$repo = $segments[-1]
$apiUrl = "{0}://{1}/api/v1/repos/{2}/{3}/releases?draft=false&limit=50" -f $uri.Scheme, $uri.Authority, $owner, $repo
try {
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$releases = Invoke-RestMethod -Uri $apiUrl -Method Get -ErrorAction Stop
}
catch {
throw "Gitea releases could not be read: $apiUrl`n$($_.Exception.Message)"
}
return @($releases | ForEach-Object { $_.tag_name } | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })
}
function Get-MaxVersionFromTags {
param([string[]]$Tags)
$versions = @()
foreach ($tag in $Tags) {
$clean = $tag.TrimStart("v", "V")
$parsed = $null
if ([Version]::TryParse($clean, [ref]$parsed)) {
$versions += $parsed
}
}
if ($versions.Count -eq 0) {
return $null
}
return ($versions | Sort-Object -Descending | Select-Object -First 1)
}
function Read-SecretAsPlainText {
param([string]$Prompt)
$secureString = Read-Host -Prompt $Prompt -AsSecureString
if ($secureString.Length -eq 0) {
throw "Empty password is not allowed."
}
$bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureString)
try {
return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
}
finally {
if ($bstr -ne [IntPtr]::Zero) {
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr)
}
}
}
function Resolve-SignToolExecutable {
param([string]$RequestedPath)
if (Test-Path -LiteralPath $RequestedPath) {
return [System.IO.Path]::GetFullPath($RequestedPath)
}
$command = Get-Command -Name $RequestedPath -ErrorAction SilentlyContinue
if ($null -ne $command) {
return $command.Source
}
throw "signtool.exe could not be resolved. Pass -SignToolPath with an absolute path if needed."
}
$publishDir = Assert-WorkspacePath (Join-Path $repoRoot "artifacts\publish\$Runtime\$version")
$releaseDir = Assert-WorkspacePath (Join-Path $repoRoot "artifacts\velopack\$Channel")
$mainExe = "RgbLedStripController.exe"
$dotnet = "dotnet"
$signingEnabled = (-not $SkipSigning.IsPresent) -and (-not [string]::IsNullOrWhiteSpace($PfxPath))
if ((-not $SkipSigning.IsPresent) -and [string]::IsNullOrWhiteSpace($PfxPath)) {
Write-Warning "PfxPath not supplied; Authenticode signing will be skipped. Pass -PfxPath to enable it."
}
$signToolExecutable = $null
$signToolDirectory = $null
$plainPassword = $null
$resolvedPfxPath = $null
$resolvedInstallerIconPath = Resolve-AbsolutePath -Path $InstallerIcon
$signParams = $null
if (-not (Test-Path -LiteralPath $resolvedInstallerIconPath)) {
throw "Installer icon file not found: $resolvedInstallerIconPath"
}
$env:DOTNET_CLI_HOME = Join-Path $repoRoot ".dotnet"
$env:APPDATA = Join-Path $repoRoot ".appdata"
$env:NUGET_PACKAGES = Join-Path $repoRoot ".nuget\packages"
$env:NUGET_HTTP_CACHE_PATH = Join-Path $repoRoot ".nuget\http-cache"
$localVersion = [Version]$version
$cloudVersion = $null
if (-not $SkipCloudCheck.IsPresent) {
$cloudVersion = Get-MaxVersionFromTags -Tags (Get-GiteaReleaseTags -RepoUrl $GiteaRepoUrl)
}
if ($null -ne $cloudVersion) {
if ($localVersion -lt $cloudVersion) {
throw "Local version $version is older than Gitea version $cloudVersion. Bump the project version before packaging."
}
if ($localVersion -eq $cloudVersion) {
throw "Gitea already contains version $version. Packaging with the same version is blocked."
}
}
if ($signingEnabled) {
$resolvedPfxPath = Resolve-AbsolutePath -Path $PfxPath
if (-not (Test-Path -LiteralPath $resolvedPfxPath)) {
throw "PFX file not found: $resolvedPfxPath"
}
if ($SignParallel -lt 1) {
throw "SignParallel must be at least 1."
}
$signToolExecutable = Resolve-SignToolExecutable -RequestedPath $SignToolPath
$signToolDirectory = Split-Path -Parent $signToolExecutable
if (-not [string]::IsNullOrWhiteSpace($signToolDirectory)) {
$env:PATH = $signToolDirectory + [System.IO.Path]::PathSeparator + $env:PATH
}
$plainPassword = Read-SecretAsPlainText -Prompt "PFX password"
$signParams = "/fd SHA256 /f `"$resolvedPfxPath`" /p `"$plainPassword`""
}
if (Test-Path -LiteralPath $publishDir) {
Remove-Item -LiteralPath $publishDir -Recurse -Force
}
New-Item -ItemType Directory -Force -Path $publishDir | Out-Null
New-Item -ItemType Directory -Force -Path $releaseDir | Out-Null
$publishArgs = @(
"publish",
$projectPath,
"-c", $Configuration,
"-r", $Runtime,
"--self-contained", "true",
"-p:PublishSingleFile=true",
"-o", $publishDir,
"--configfile", $nugetConfigPath
)
& $dotnet @publishArgs
if ($LASTEXITCODE -ne 0) {
throw "dotnet publish failed with exit code $LASTEXITCODE"
}
$packArgs = @(
"tool", "run", "vpk", "--",
"pack",
"--packId", $PackId,
"--packVersion", $version,
"--packDir", $publishDir,
"--mainExe", $mainExe,
"--packTitle", $PackTitle,
"--packAuthors", $PackAuthors,
"--runtime", $Runtime,
"--channel", $Channel,
"--icon", $resolvedInstallerIconPath,
"--outputDir", $releaseDir
)
if ($signingEnabled) {
$packArgs += @(
"--signParams", $signParams,
"--signParallel", $SignParallel.ToString()
)
}
& $dotnet @packArgs
if ($LASTEXITCODE -ne 0) {
throw "vpk pack failed with exit code $LASTEXITCODE"
}
Write-Host ""
Write-Host "Velopack package build complete." -ForegroundColor Green
Write-Host "Version: $version"
Write-Host "Gitea: $(if ($null -ne $cloudVersion) { $cloudVersion.ToString() } else { 'none' })"
Write-Host "Signing: $(if ($signingEnabled) { 'enabled' } else { 'skipped' })"
if ($signingEnabled -and -not [string]::IsNullOrWhiteSpace($signToolExecutable)) {
Write-Host "SignTool: $signToolExecutable"
}
Write-Host "Icon: $resolvedInstallerIconPath"
Write-Host "PublishDir: $publishDir"
Write-Host "ReleaseDir: $releaseDir"
Write-Host "Gitea repo: $GiteaRepoUrl"
Write-Host "Tag: v$version"
Write-Host ""
Write-Host "Upload with: scripts/release/upload-feed.ps1 (vpk upload gitea)." -ForegroundColor Yellow