Add character management and release packaging
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ExePath,
|
||||
[string]$Version = "",
|
||||
[string]$OutputRoot = "dist",
|
||||
[string]$WindeployQtPath = "",
|
||||
[switch]$NoZip,
|
||||
[switch]$BuildInstaller,
|
||||
[string]$InnoCompilerPath = "",
|
||||
[string]$InstallerOutputDir = "",
|
||||
[string]$InstallerWorkOutputDir = ""
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Resolve-RepoRoot {
|
||||
$scriptDirectory = Split-Path -Parent $PSCommandPath
|
||||
return Split-Path -Parent $scriptDirectory
|
||||
}
|
||||
|
||||
function Resolve-FullPath {
|
||||
param(
|
||||
[string]$Path,
|
||||
[string]$BasePath
|
||||
)
|
||||
|
||||
if ([System.IO.Path]::IsPathRooted($Path)) {
|
||||
return [System.IO.Path]::GetFullPath($Path)
|
||||
}
|
||||
|
||||
return [System.IO.Path]::GetFullPath((Join-Path $BasePath $Path))
|
||||
}
|
||||
|
||||
function Read-ProjectVersion {
|
||||
param(
|
||||
[string]$RepoRoot
|
||||
)
|
||||
|
||||
$cmakeFile = Join-Path $RepoRoot "CMakeLists.txt"
|
||||
$content = Get-Content -LiteralPath $cmakeFile -Raw
|
||||
$match = [regex]::Match($content, 'project\s*\(\s*QtDesktopPet\s+VERSION\s+([0-9]+(?:\.[0-9]+){1,3})')
|
||||
if ($match.Success) {
|
||||
return $match.Groups[1].Value
|
||||
}
|
||||
|
||||
return "0.1.0"
|
||||
}
|
||||
|
||||
function Resolve-WindeployQt {
|
||||
param(
|
||||
[string]$ExplicitPath
|
||||
)
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($ExplicitPath)) {
|
||||
$resolved = Resolve-FullPath -Path $ExplicitPath -BasePath (Get-Location)
|
||||
if (Test-Path -LiteralPath $resolved) {
|
||||
return $resolved
|
||||
}
|
||||
|
||||
throw "windeployqt was not found at '$ExplicitPath'."
|
||||
}
|
||||
|
||||
$command = Get-Command "windeployqt.exe" -ErrorAction SilentlyContinue
|
||||
if ($null -ne $command) {
|
||||
return $command.Source
|
||||
}
|
||||
|
||||
throw "windeployqt.exe was not found. Add the Qt bin directory to PATH or pass -WindeployQtPath."
|
||||
}
|
||||
|
||||
function Resolve-InnoCompiler {
|
||||
param(
|
||||
[string]$ExplicitPath
|
||||
)
|
||||
|
||||
$candidatePaths = @()
|
||||
if (-not [string]::IsNullOrWhiteSpace($ExplicitPath)) {
|
||||
$candidatePaths += $ExplicitPath
|
||||
} else {
|
||||
$candidatePaths += @(
|
||||
"D:\Inno Setup 7\ISCC.exe",
|
||||
"D:\Inno Setup 6\ISCC.exe",
|
||||
"C:\Program Files (x86)\Inno Setup 7\ISCC.exe",
|
||||
"C:\Program Files (x86)\Inno Setup 6\ISCC.exe"
|
||||
)
|
||||
}
|
||||
|
||||
foreach ($candidatePath in $candidatePaths) {
|
||||
$resolved = Resolve-FullPath -Path $candidatePath -BasePath (Get-Location)
|
||||
if (Test-Path -LiteralPath $resolved) {
|
||||
return $resolved
|
||||
}
|
||||
}
|
||||
|
||||
if (-not [string]::IsNullOrWhiteSpace($ExplicitPath)) {
|
||||
throw "Inno Setup compiler was not found at '$ExplicitPath'."
|
||||
}
|
||||
|
||||
throw "ISCC.exe was not found. Install Inno Setup or pass -InnoCompilerPath."
|
||||
}
|
||||
|
||||
function Resolve-DefaultInstallerWorkOutputDir {
|
||||
param(
|
||||
[string]$RepoRoot
|
||||
)
|
||||
|
||||
$driveRoot = [System.IO.Path]::GetPathRoot([System.IO.Path]::GetFullPath($RepoRoot))
|
||||
if ([string]::IsNullOrWhiteSpace($driveRoot)) {
|
||||
return [System.IO.Path]::GetFullPath("QtDesktopPetInstallerOutput")
|
||||
}
|
||||
|
||||
return [System.IO.Path]::GetFullPath((Join-Path $driveRoot "QtDesktopPetInstallerOutput"))
|
||||
}
|
||||
|
||||
function Assert-RequiredPath {
|
||||
param(
|
||||
[string]$Path,
|
||||
[string]$Description
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
throw "$Description was not found: $Path"
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-VersionSegment {
|
||||
param(
|
||||
[string]$Value
|
||||
)
|
||||
|
||||
if ($Value -notmatch '^[0-9A-Za-z._-]+$') {
|
||||
throw "Version contains unsupported path characters: $Value"
|
||||
}
|
||||
}
|
||||
|
||||
function Assert-ChildPath {
|
||||
param(
|
||||
[string]$Path,
|
||||
[string]$ParentPath,
|
||||
[string]$Description
|
||||
)
|
||||
|
||||
$fullPath = [System.IO.Path]::GetFullPath($Path).TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar)
|
||||
$fullParentPath = [System.IO.Path]::GetFullPath($ParentPath).TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar)
|
||||
$expectedPrefix = $fullParentPath + [System.IO.Path]::DirectorySeparatorChar
|
||||
if (-not $fullPath.StartsWith($expectedPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw "$Description must stay inside output root: $fullPath"
|
||||
}
|
||||
}
|
||||
|
||||
function Copy-DirectoryFresh {
|
||||
param(
|
||||
[string]$Source,
|
||||
[string]$Destination
|
||||
)
|
||||
|
||||
if (Test-Path -LiteralPath $Destination) {
|
||||
Remove-Item -LiteralPath $Destination -Recurse -Force
|
||||
}
|
||||
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $Destination) | Out-Null
|
||||
Copy-Item -LiteralPath $Source -Destination $Destination -Recurse -Force
|
||||
}
|
||||
|
||||
function Invoke-CheckedProcess {
|
||||
param(
|
||||
[string]$FilePath,
|
||||
[string[]]$Arguments
|
||||
)
|
||||
|
||||
& $FilePath @Arguments
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "$FilePath exited with code $LASTEXITCODE."
|
||||
}
|
||||
}
|
||||
|
||||
$repoRoot = Resolve-RepoRoot
|
||||
$resolvedExePath = Resolve-FullPath -Path $ExePath -BasePath $repoRoot
|
||||
$resolvedOutputRoot = Resolve-FullPath -Path $OutputRoot -BasePath $repoRoot
|
||||
if ([string]::IsNullOrWhiteSpace($Version)) {
|
||||
$Version = Read-ProjectVersion -RepoRoot $repoRoot
|
||||
}
|
||||
Assert-VersionSegment -Value $Version
|
||||
|
||||
$packageName = "QtDesktopPet-$Version-windows-x64"
|
||||
$packageRoot = Join-Path $resolvedOutputRoot $packageName
|
||||
$installerFileName = "$packageName-setup.exe"
|
||||
$targetExePath = Join-Path $packageRoot "QtDesktopPet.exe"
|
||||
$resourcesRoot = Join-Path $repoRoot "resources"
|
||||
$charactersRoot = Join-Path $resourcesRoot "characters"
|
||||
$iconsRoot = Join-Path $resourcesRoot "icons"
|
||||
$licensePath = Join-Path $repoRoot "LICENSE"
|
||||
$readmePath = Join-Path $repoRoot "README.md"
|
||||
$installerScriptPath = Join-Path $repoRoot "installer\QtDesktopPet.iss"
|
||||
|
||||
Assert-RequiredPath -Path $resolvedExePath -Description "QtDesktopPet.exe"
|
||||
Assert-RequiredPath -Path (Join-Path $charactersRoot "shiroko\character.json") -Description "Default character package"
|
||||
Assert-RequiredPath -Path (Join-Path $iconsRoot "app_icon.ico") -Description "Application icon"
|
||||
Assert-RequiredPath -Path $licensePath -Description "LICENSE"
|
||||
Assert-RequiredPath -Path $readmePath -Description "README.md"
|
||||
|
||||
$windeployQt = Resolve-WindeployQt -ExplicitPath $WindeployQtPath
|
||||
Assert-ChildPath -Path $packageRoot -ParentPath $resolvedOutputRoot -Description "Release package directory"
|
||||
|
||||
if (Test-Path -LiteralPath $packageRoot) {
|
||||
Remove-Item -LiteralPath $packageRoot -Recurse -Force
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $packageRoot | Out-Null
|
||||
|
||||
Copy-Item -LiteralPath $resolvedExePath -Destination $targetExePath -Force
|
||||
Copy-DirectoryFresh -Source $charactersRoot -Destination (Join-Path $packageRoot "resources\characters")
|
||||
Copy-DirectoryFresh -Source $iconsRoot -Destination (Join-Path $packageRoot "resources\icons")
|
||||
Copy-Item -LiteralPath $licensePath -Destination (Join-Path $packageRoot "LICENSE") -Force
|
||||
Copy-Item -LiteralPath $readmePath -Destination (Join-Path $packageRoot "README.md") -Force
|
||||
|
||||
Write-Host "Running windeployqt: $windeployQt"
|
||||
Invoke-CheckedProcess -FilePath $windeployQt -Arguments @("--release", "--compiler-runtime", $targetExePath)
|
||||
|
||||
$manifestPath = Join-Path $packageRoot "package_manifest.txt"
|
||||
@(
|
||||
"QtDesktopPet release package",
|
||||
"Version: $Version",
|
||||
"CreatedUtc: $((Get-Date).ToUniversalTime().ToString("o"))",
|
||||
"SourceExe: $resolvedExePath",
|
||||
"Includes: QtDesktopPet.exe, Qt runtime, resources, LICENSE, README.md",
|
||||
"Excludes: tools, docs, reports, build, dist, .git"
|
||||
) | Set-Content -LiteralPath $manifestPath -Encoding UTF8
|
||||
|
||||
if (-not $NoZip) {
|
||||
$zipPath = Join-Path $resolvedOutputRoot "$packageName.zip"
|
||||
Assert-ChildPath -Path $zipPath -ParentPath $resolvedOutputRoot -Description "ZIP package"
|
||||
if (Test-Path -LiteralPath $zipPath) {
|
||||
Remove-Item -LiteralPath $zipPath -Force
|
||||
}
|
||||
|
||||
Compress-Archive -LiteralPath $packageRoot -DestinationPath $zipPath -Force
|
||||
Write-Host "ZIP package: $zipPath"
|
||||
}
|
||||
|
||||
if ($BuildInstaller) {
|
||||
Assert-RequiredPath -Path $installerScriptPath -Description "Inno Setup script"
|
||||
$resolvedInnoCompilerPath = Resolve-InnoCompiler -ExplicitPath $InnoCompilerPath
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($InstallerOutputDir)) {
|
||||
$installerFinalOutputDir = $repoRoot
|
||||
} else {
|
||||
$installerFinalOutputDir = Resolve-FullPath -Path $InstallerOutputDir -BasePath $repoRoot
|
||||
}
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($InstallerWorkOutputDir)) {
|
||||
$installerWorkOutputDir = Resolve-DefaultInstallerWorkOutputDir -RepoRoot $repoRoot
|
||||
} else {
|
||||
$installerWorkOutputDir = Resolve-FullPath -Path $InstallerWorkOutputDir -BasePath $repoRoot
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $installerWorkOutputDir | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path $installerFinalOutputDir | Out-Null
|
||||
|
||||
Invoke-CheckedProcess -FilePath $resolvedInnoCompilerPath -Arguments @(
|
||||
"/DAppVersion=$Version",
|
||||
"/DSourceDir=$packageRoot",
|
||||
"/DOutputDir=$installerWorkOutputDir",
|
||||
$installerScriptPath
|
||||
)
|
||||
|
||||
$builtInstallerPath = Join-Path $installerWorkOutputDir $installerFileName
|
||||
Assert-RequiredPath -Path $builtInstallerPath -Description "Built installer"
|
||||
|
||||
$finalInstallerPath = Join-Path $installerFinalOutputDir $installerFileName
|
||||
if ([System.IO.Path]::GetFullPath($builtInstallerPath) -ne [System.IO.Path]::GetFullPath($finalInstallerPath)) {
|
||||
Copy-Item -LiteralPath $builtInstallerPath -Destination $finalInstallerPath -Force
|
||||
}
|
||||
|
||||
Write-Host "Installer work output: $installerWorkOutputDir"
|
||||
Write-Host "Installer final output: $finalInstallerPath"
|
||||
}
|
||||
|
||||
Write-Host "Release package directory: $packageRoot"
|
||||
Reference in New Issue
Block a user