175 lines
9.8 KiB
PowerShell
175 lines
9.8 KiB
PowerShell
[CmdletBinding()]
|
|
param()
|
|
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function Fail([string]$Message) {
|
|
Write-Error "::error::$Message"
|
|
exit 1
|
|
}
|
|
|
|
function Require-Environment([string]$Name) {
|
|
$value = [Environment]::GetEnvironmentVariable($Name)
|
|
if ([string]::IsNullOrWhiteSpace($value)) {
|
|
Fail "Required environment variable '$Name' is not set."
|
|
}
|
|
return $value
|
|
}
|
|
|
|
function Get-ActualArchitecture {
|
|
$architecture = $env:PROCESSOR_ARCHITEW6432
|
|
if ([string]::IsNullOrWhiteSpace($architecture)) {
|
|
$architecture = $env:PROCESSOR_ARCHITECTURE
|
|
}
|
|
switch ($architecture.ToUpperInvariant()) {
|
|
'AMD64' { return 'x86_64' }
|
|
'X86' { return 'x86_32' }
|
|
'ARM64' { return 'arm64' }
|
|
default { Fail "Unsupported Windows runner architecture: $architecture." }
|
|
}
|
|
}
|
|
|
|
function Get-Checksum([string]$ManifestPath, [string]$Asset) {
|
|
$line = Get-Content -LiteralPath $ManifestPath | Where-Object {
|
|
$_ -match '^([0-9A-Fa-f]{128})\s+\*?(.+)$' -and $Matches[2] -eq $Asset
|
|
} | Select-Object -First 1
|
|
if (-not $line) {
|
|
Fail "Godot $GodotVersion does not publish required $edition asset '$Asset' for Windows/$HostArch at $script:BaseUrl. Choose a supported stable release and architecture."
|
|
}
|
|
return ($line -split '\s+')[0].ToLowerInvariant()
|
|
}
|
|
|
|
function Download-AndVerify([string]$Asset, [string]$Destination, [string]$ExpectedChecksum) {
|
|
Invoke-WebRequest -Uri "$script:BaseUrl/$Asset" -OutFile $Destination
|
|
$actual = (Get-FileHash -LiteralPath $Destination -Algorithm SHA512).Hash.ToLowerInvariant()
|
|
if ($actual -ne $ExpectedChecksum) {
|
|
Fail "SHA-512 verification failed for $Asset."
|
|
}
|
|
}
|
|
|
|
function Find-Editor([string]$Directory) {
|
|
$console = Get-ChildItem -LiteralPath $Directory -Recurse -File -Filter 'Godot*console.exe' -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
if ($console) { return $console.FullName }
|
|
$editor = Get-ChildItem -LiteralPath $Directory -Recurse -File -Filter 'Godot*.exe' -ErrorAction SilentlyContinue | Select-Object -First 1
|
|
if ($editor) { return $editor.FullName }
|
|
return $null
|
|
}
|
|
|
|
function Install-ExportTemplates([string]$StagingDirectory, [string]$TemplateDirectory, [string]$TemplateAsset, [string]$ExpectedVersion) {
|
|
$stagedTemplates = Join-Path $StagingDirectory 'templates'
|
|
$stagedVersion = Join-Path $stagedTemplates 'version.txt'
|
|
if (-not (Test-Path -LiteralPath $stagedVersion -PathType Leaf)) {
|
|
Fail "The export-template archive '$TemplateAsset' did not contain templates/version.txt."
|
|
}
|
|
$actualVersion = (Get-Content -LiteralPath $stagedVersion -Raw).Trim()
|
|
if ($actualVersion -ne $ExpectedVersion) {
|
|
Fail "The export-template archive '$TemplateAsset' reports template version '$actualVersion', expected '$ExpectedVersion'."
|
|
}
|
|
New-Item -ItemType Directory -Force -Path $TemplateDirectory | Out-Null
|
|
Get-ChildItem -LiteralPath $stagedTemplates -Force | Copy-Item -Destination $TemplateDirectory -Recurse -Force
|
|
Remove-Item -LiteralPath $StagingDirectory -Recurse -Force
|
|
if (-not (Test-Path -LiteralPath (Join-Path $TemplateDirectory 'version.txt') -PathType Leaf)) {
|
|
Fail "The export-template archive '$TemplateAsset' could not be installed to '$TemplateDirectory'."
|
|
}
|
|
}
|
|
|
|
$GodotVersion = Require-Environment 'GODOT_VERSION'
|
|
$HostOs = Require-Environment 'GODOT_HOST_OS'
|
|
$HostArch = Require-Environment 'GODOT_HOST_ARCH'
|
|
$Dotnet = Require-Environment 'GODOT_DOTNET'
|
|
$ProjectPath = Require-Environment 'GODOT_PROJECT_PATH'
|
|
$ExportPreset = Require-Environment 'GODOT_EXPORT_PRESET'
|
|
$ExportMode = Require-Environment 'GODOT_EXPORT_MODE'
|
|
$ExportFileName = Require-Environment 'GODOT_EXPORT_FILE_NAME'
|
|
$ActionRoot = Require-Environment 'GODOT_ACTION_ROOT'
|
|
$Workspace = Require-Environment 'GITHUB_WORKSPACE'
|
|
$GithubOutput = Require-Environment 'GITHUB_OUTPUT'
|
|
|
|
if ($GodotVersion -notmatch '^4(\.[0-9]+){1,2}-stable$') { Fail 'godot-version must be a stable Godot 4 tag such as 4.7.2-stable. Alpha, beta, RC, and dev tags are not supported.' }
|
|
if ($HostOs -ne 'windows') { Fail "host-os must be 'windows' when this action runs on Windows." }
|
|
if ($Dotnet -notin @('true', 'false')) { Fail "dotnet must be 'true' or 'false'." }
|
|
if ($ExportMode -notin @('release', 'debug')) { Fail "export-mode must be 'release' or 'debug'." }
|
|
if ([string]::IsNullOrWhiteSpace($ExportPreset)) { Fail 'export-preset must not be empty.' }
|
|
if ([string]::IsNullOrWhiteSpace($ExportFileName) -or $ExportFileName -in @('.', '..') -or $ExportFileName.IndexOfAny([IO.Path]::GetInvalidFileNameChars()) -ge 0 -or $ExportFileName.Contains('/') -or $ExportFileName.Contains('\')) { Fail 'export-file-name must be a filename without path separators.' }
|
|
if ($HostArch -notin @('x86_32', 'x86_64', 'arm64')) { Fail "Unsupported Windows host-arch '$HostArch'." }
|
|
if ($HostArch -ne (Get-ActualArchitecture)) { Fail "host-arch '$HostArch' does not match this Windows runner ($(Get-ActualArchitecture))." }
|
|
|
|
$workspaceDirectory = [IO.Path]::GetFullPath($Workspace)
|
|
if ([IO.Path]::IsPathRooted($ProjectPath)) { Fail 'project-path must be relative to the checked-out workspace.' }
|
|
$ProjectDirectory = [IO.Path]::GetFullPath((Join-Path $workspaceDirectory $ProjectPath))
|
|
$projectRelativePath = [IO.Path]::GetRelativePath($workspaceDirectory, $ProjectDirectory)
|
|
if ($projectRelativePath -eq '..' -or $projectRelativePath.StartsWith("..$([IO.Path]::DirectorySeparatorChar)") -or [IO.Path]::IsPathRooted($projectRelativePath)) { Fail 'project-path must remain inside the checked-out workspace.' }
|
|
if (-not (Test-Path -LiteralPath (Join-Path $ProjectDirectory 'project.godot') -PathType Leaf)) { Fail "No project.godot was found in project-path '$ProjectPath'." }
|
|
$PresetFile = Join-Path $ProjectDirectory 'export_presets.cfg'
|
|
if (-not (Test-Path -LiteralPath $PresetFile -PathType Leaf)) { Fail "No export_presets.cfg was found in project-path '$ProjectPath'." }
|
|
if (-not (Select-String -LiteralPath $PresetFile -SimpleMatch "name=`"$ExportPreset`"" -Quiet)) { Fail "Export preset '$ExportPreset' was not found in export_presets.cfg." }
|
|
|
|
if ($Dotnet -eq 'true') {
|
|
$edition = 'dotnet'
|
|
$editorAsset = switch ($HostArch) {
|
|
'x86_32' { "Godot_v${GodotVersion}_mono_win32.zip" }
|
|
'x86_64' { "Godot_v${GodotVersion}_mono_win64.zip" }
|
|
'arm64' { "Godot_v${GodotVersion}_mono_windows_arm64.zip" }
|
|
}
|
|
$templatesAsset = "Godot_v${GodotVersion}_mono_export_templates.tpz"
|
|
$templateVersion = ($GodotVersion -replace '-', '.') + '.mono'
|
|
} else {
|
|
$edition = 'standard'
|
|
$editorAsset = switch ($HostArch) {
|
|
'x86_32' { "Godot_v${GodotVersion}_win32.exe.zip" }
|
|
'x86_64' { "Godot_v${GodotVersion}_win64.exe.zip" }
|
|
'arm64' { "Godot_v${GodotVersion}_windows_arm64.exe.zip" }
|
|
}
|
|
$templatesAsset = "Godot_v${GodotVersion}_export_templates.tpz"
|
|
$templateVersion = $GodotVersion -replace '-', '.'
|
|
}
|
|
|
|
$script:BaseUrl = "https://github.com/godotengine/godot/releases/download/$GodotVersion"
|
|
$cacheDirectory = Join-Path $ActionRoot "cache\$GodotVersion\$edition\windows-$HostArch"
|
|
$editorDirectory = Join-Path $cacheDirectory 'editor'
|
|
$userHome = Join-Path $cacheDirectory 'home'
|
|
$templateDirectory = Join-Path $userHome "AppData\Roaming\Godot\export_templates\$templateVersion"
|
|
$marker = Join-Path $cacheDirectory '.complete'
|
|
|
|
$editorExecutable = $null
|
|
if ((Test-Path -LiteralPath $marker -PathType Leaf) -and (Test-Path -LiteralPath (Join-Path $templateDirectory 'version.txt') -PathType Leaf)) {
|
|
$editorExecutable = Find-Editor $editorDirectory
|
|
}
|
|
|
|
if (-not $editorExecutable) {
|
|
if (Test-Path -LiteralPath $cacheDirectory) { Remove-Item -LiteralPath $cacheDirectory -Recurse -Force }
|
|
New-Item -ItemType Directory -Force -Path $editorDirectory | Out-Null
|
|
$manifestPath = Join-Path $cacheDirectory 'SHA512-SUMS.txt'
|
|
$editorArchive = Join-Path $cacheDirectory $editorAsset
|
|
$templatesArchive = Join-Path $cacheDirectory $templatesAsset
|
|
$templatesStagingDirectory = Join-Path $cacheDirectory 'templates-staging'
|
|
Invoke-WebRequest -Uri "$script:BaseUrl/SHA512-SUMS.txt" -OutFile $manifestPath
|
|
$editorChecksum = Get-Checksum $manifestPath $editorAsset
|
|
$templatesChecksum = Get-Checksum $manifestPath $templatesAsset
|
|
Download-AndVerify $editorAsset $editorArchive $editorChecksum
|
|
Download-AndVerify $templatesAsset $templatesArchive $templatesChecksum
|
|
Expand-Archive -LiteralPath $editorArchive -DestinationPath $editorDirectory -Force
|
|
Expand-Archive -LiteralPath $templatesArchive -DestinationPath $templatesStagingDirectory -Force
|
|
Install-ExportTemplates $templatesStagingDirectory $templateDirectory $templatesAsset $templateVersion
|
|
$editorExecutable = Find-Editor $editorDirectory
|
|
if (-not $editorExecutable) { Fail 'The Godot editor archive did not contain an executable.' }
|
|
if (-not (Test-Path -LiteralPath (Join-Path $templateDirectory 'version.txt') -PathType Leaf)) { Fail 'The export-template archive did not contain version.txt.' }
|
|
@("release=$GodotVersion", "editor=$editorAsset", "templates=$templatesAsset") | Set-Content -LiteralPath $marker -NoNewline
|
|
}
|
|
|
|
$safePreset = ($ExportPreset -replace '[^A-Za-z0-9_.-]', '_').Trim('_')
|
|
if ([string]::IsNullOrWhiteSpace($safePreset)) { $safePreset = 'export' }
|
|
$buildDirectory = Join-Path $ActionRoot "builds\$safePreset-$ExportMode-$PID"
|
|
$outputFile = Join-Path $buildDirectory $ExportFileName
|
|
New-Item -ItemType Directory -Force -Path $buildDirectory | Out-Null
|
|
|
|
$env:USERPROFILE = $userHome
|
|
$env:APPDATA = Join-Path $userHome 'AppData\Roaming'
|
|
$env:LOCALAPPDATA = Join-Path $userHome 'AppData\Local'
|
|
& $editorExecutable --headless --path $ProjectDirectory "--export-$ExportMode" $ExportPreset $outputFile
|
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
if (-not (Test-Path -LiteralPath $outputFile)) { Fail "Godot completed without creating the expected output '$ExportFileName'." }
|
|
|
|
"build-directory=$buildDirectory" | Add-Content -LiteralPath $GithubOutput
|