Files
chengnan/.gitea/workflows/build-windows-release.yml
luochen570 1701f36942
Some checks failed
Build Windows Release / Build Windows x64 Release (MSVC + Qt) (push) Failing after 49s
ci: wait for Python installer on Windows
2026-05-30 22:03:08 +08:00

246 lines
12 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: Build Windows Release
on:
push:
branches:
- main
tags:
- 'v*'
workflow_dispatch:
permissions:
contents: write
jobs:
build-windows-release:
name: Build Windows x64 Release (MSVC + Qt)
# Windows runner 已注册标签windows-latest、windows-2022、self-hosted均为 host 模式。
runs-on: windows-2022
env:
BUILD_TYPE: Release
QT_VERSION: 6.7.3
QT_ARCH: win64_msvc2022_64
QT_DIR_NAME: msvc2022_64
PACKAGE_BASENAME: chengnan-windows-x64-msvc
PACKAGE_NAME: chengnan-windows-x64-msvc
steps:
- name: Prepare source
shell: powershell
run: |
$ErrorActionPreference = 'Stop'
$work = Join-Path $env:RUNNER_TEMP 'chengnan-src'
$srcFixed = Join-Path $env:RUNNER_TEMP 'chengnan-work'
$zip = Join-Path $env:RUNNER_TEMP 'chengnan-src.zip'
if (Test-Path $work) { Remove-Item -Recurse -Force $work }
if (Test-Path $srcFixed) { Remove-Item -Recurse -Force $srcFixed }
if (Test-Path $zip) { Remove-Item -Force $zip }
New-Item -ItemType Directory -Force -Path $work | Out-Null
$serverUrl = if ($env:GITHUB_SERVER_URL) { $env:GITHUB_SERVER_URL.TrimEnd('/') } else { 'https://git.luochen570.cn' }
$archiveUrl = "$serverUrl/$env:GITHUB_REPOSITORY/archive/$env:GITHUB_SHA.zip"
Write-Host "Downloading source archive from $archiveUrl"
Invoke-WebRequest -Uri $archiveUrl -OutFile $zip
Expand-Archive -Path $zip -DestinationPath $work -Force
$src = Get-ChildItem -Path $work -Directory | Select-Object -First 1
if (-not $src) { throw 'Source archive extraction produced no directory' }
Copy-Item -Path $src.FullName -Destination $srcFixed -Recurse -Force
Write-Host "Source dir: $srcFixed"
- name: Install official Qt for MSVC
shell: powershell
run: |
$ErrorActionPreference = 'Stop'
$qtRoot = Join-Path $env:RUNNER_TEMP 'qt-msvc'
$qtPrefix = Join-Path $qtRoot "$env:QT_VERSION\$env:QT_DIR_NAME"
$windeployqt = Join-Path $qtPrefix 'bin\windeployqt.exe'
if (Test-Path $windeployqt) {
Write-Host "Qt already installed: $qtPrefix"
exit 0
}
$pyLauncher = Get-Command py -ErrorAction SilentlyContinue
if ($pyLauncher) {
$pythonExe = $pyLauncher.Source
$pythonArgs = @('-3')
} else {
$python = Get-Command python -ErrorAction SilentlyContinue
if ($python) {
$pythonExe = $python.Source
$pythonArgs = @()
} else {
$pythonRoot = Join-Path $env:RUNNER_TEMP 'python312'
$pythonExe = Join-Path $pythonRoot 'python.exe'
if (-not (Test-Path $pythonExe)) {
$installer = Join-Path $env:RUNNER_TEMP 'python-installer.exe'
$pythonUrl = 'https://www.python.org/ftp/python/3.12.10/python-3.12.10-amd64.exe'
Write-Host "Downloading Python from $pythonUrl"
Invoke-WebRequest -Uri $pythonUrl -OutFile $installer
$installArgs = @('/quiet', 'InstallAllUsers=0', "TargetDir=$pythonRoot", 'Include_pip=1', 'Include_test=0', 'PrependPath=0')
$proc = Start-Process -FilePath $installer -ArgumentList $installArgs -Wait -PassThru
if ($proc.ExitCode -ne 0) { throw "Python installer failed, exit code: $($proc.ExitCode)" }
}
if (-not (Test-Path $pythonExe)) { throw "Python installation finished but python.exe was not found: $pythonExe" }
$pythonArgs = @()
}
}
& $pythonExe @pythonArgs -m pip install --user --upgrade aqtinstall
if ($LASTEXITCODE -ne 0) { throw "pip install aqtinstall failed, exit code: $LASTEXITCODE" }
& $pythonExe @pythonArgs -m aqt install-qt windows desktop $env:QT_VERSION $env:QT_ARCH -O $qtRoot -m qtimageformats
if ($LASTEXITCODE -ne 0) { throw "aqt Qt installation failed, exit code: $LASTEXITCODE" }
if (-not (Test-Path $windeployqt)) { throw "Qt installation finished but windeployqt was not found: $windeployqt" }
Write-Host "Qt installed: $qtPrefix"
- name: Configure, build, and deploy with MSVC
shell: powershell
run: |
$ErrorActionPreference = 'Stop'
$src = Join-Path $env:RUNNER_TEMP 'chengnan-work'
$qtRoot = Join-Path $env:RUNNER_TEMP 'qt-msvc'
$qtPrefix = Join-Path $qtRoot "$env:QT_VERSION\$env:QT_DIR_NAME"
$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
if (-not (Test-Path $vswhere)) { throw "vswhere not found: $vswhere. Install Visual Studio 2022 Build Tools with Desktop development with C++." }
$vsPath = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
if ([string]::IsNullOrWhiteSpace($vsPath)) { throw 'Visual Studio 2022 C++ build tools were not found. Install Desktop development with C++.' }
$vsDevCmd = Join-Path $vsPath 'Common7\Tools\VsDevCmd.bat'
if (-not (Test-Path $vsDevCmd)) { throw "VsDevCmd.bat not found: $vsDevCmd" }
$vsCmake = Join-Path $vsPath 'Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin'
if (Test-Path (Join-Path $vsCmake 'cmake.exe')) { $env:PATH = "$vsCmake;$env:PATH" }
if (-not (Get-Command cmake -ErrorAction SilentlyContinue)) { throw 'cmake was not found on PATH or in Visual Studio bundled CMake.' }
$buildDir = Join-Path $src 'build-windows'
$distDir = Join-Path $src 'dist-windows\chengnan'
$script = Join-Path $src 'build-windows.ps1'
$cmd = "`"$vsDevCmd`" -arch=x64 -host_arch=x64 && powershell -NoProfile -ExecutionPolicy Bypass -File `"$script`" -QtPrefix `"$qtPrefix`" -BuildDir `"$buildDir`" -DistDir `"$distDir`" -Generator `"Visual Studio 17 2022`" -Arch x64 -Config Release"
Write-Host "Building with Visual Studio at: $vsPath"
Write-Host "Using Qt: $qtPrefix"
& cmd.exe /c $cmd
if ($LASTEXITCODE -ne 0) { throw "MSVC build failed, exit code: $LASTEXITCODE" }
$readme = Join-Path $distDir 'README.txt'
@(
'chengnan Windows x64 package',
'',
'Usage:',
'1. Extract the whole directory.',
'2. Double-click chengnan.exe to run.',
'',
'Note:',
'- This package is built by Windows Runner with MSVC + official Qt.',
'- Do not copy chengnan.exe alone; keep DLLs and Qt plugin directories together.'
) | Out-File -FilePath $readme -Encoding ascii
- name: Package
shell: powershell
run: |
$ErrorActionPreference = 'Stop'
$shortSha = $env:GITHUB_SHA.Substring(0, 10)
$version = if ($env:GITHUB_REF_TYPE -eq 'tag') { $env:GITHUB_REF_NAME } else { "build-$shortSha" }
$zipName = "$env:PACKAGE_BASENAME-$version.zip"
$sourceDir = Join-Path $env:RUNNER_TEMP 'chengnan-work'
$dist = Join-Path $sourceDir 'dist-windows\chengnan'
if (-not (Test-Path "$dist\chengnan.exe")) { throw "Missing build output: $dist\chengnan.exe" }
Push-Location (Split-Path $dist -Parent)
if (Test-Path (Join-Path $sourceDir $zipName)) { Remove-Item -Force (Join-Path $sourceDir $zipName) }
Compress-Archive -Path 'chengnan\*' -DestinationPath (Join-Path $sourceDir $zipName) -Force
Pop-Location
$zipPath = Join-Path $sourceDir $zipName
$hash = (Get-FileHash $zipPath -Algorithm SHA256).Hash.ToLowerInvariant()
"$hash $zipName" | Out-File -FilePath "$zipPath.sha256" -Encoding ascii
Write-Host "Package: $zipPath"
Write-Host "SHA256: $hash"
- name: Verify package
shell: powershell
run: |
$ErrorActionPreference = 'Stop'
$shortSha = $env:GITHUB_SHA.Substring(0, 10)
$version = if ($env:GITHUB_REF_TYPE -eq 'tag') { $env:GITHUB_REF_NAME } else { "build-$shortSha" }
$zipName = "$env:PACKAGE_BASENAME-$version.zip"
$sourceDir = Join-Path $env:RUNNER_TEMP 'chengnan-work'
$zipPath = Join-Path $sourceDir $zipName
$shaPath = "$zipPath.sha256"
if (-not (Test-Path $zipPath)) { throw "zip does not exist: $zipPath" }
if (-not (Test-Path $shaPath)) { throw "sha256 does not exist: $shaPath" }
if ((Get-Item $zipPath).Length -le 0) { throw "zip file is empty: $zipPath" }
Write-Host "Verified package: $zipPath"
- name: Upload package to repository release
shell: powershell
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
$ErrorActionPreference = 'Stop'
$token = $env:RELEASE_TOKEN
if ([string]::IsNullOrWhiteSpace($token)) { $token = $env:GITHUB_TOKEN }
if ([string]::IsNullOrWhiteSpace($token)) { throw 'Missing release token. Configure RELEASE_TOKEN in repository secrets or ensure GITHUB_TOKEN is available.' }
$shortSha = $env:GITHUB_SHA.Substring(0, 10)
$version = if ($env:GITHUB_REF_TYPE -eq 'tag') { $env:GITHUB_REF_NAME } else { "build-$shortSha" }
$zipName = "$env:PACKAGE_BASENAME-$version.zip"
$sourceDir = Join-Path $env:RUNNER_TEMP 'chengnan-work'
$zipPath = Join-Path $sourceDir $zipName
$shaPath = "$zipPath.sha256"
$serverUrl = if ($env:GITHUB_SERVER_URL) { $env:GITHUB_SERVER_URL.TrimEnd('/') } else { 'https://git.luochen570.cn' }
$repo = $env:GITHUB_REPOSITORY
$apiBase = "$serverUrl/api/v1/repos/$repo"
$tagName = $version
$releaseName = if ($env:GITHUB_REF_TYPE -eq 'tag') { $env:GITHUB_REF_NAME } else { "chengnan $shortSha" }
$isPrerelease = if ($env:GITHUB_REF_TYPE -eq 'tag') { $false } else { $true }
$headers = @{ Authorization = "token $token"; Accept = 'application/json' }
$releasePayload = @{
tag_name = $tagName
target_commitish = $env:GITHUB_SHA
name = $releaseName
body = "Automated Windows x64 MSVC build from Gitea Actions.`n"
draft = $false
prerelease = $isPrerelease
} | ConvertTo-Json -Depth 5
try {
$release = Invoke-RestMethod -Method Post -Uri "$apiBase/releases" -Headers $headers -ContentType 'application/json' -Body $releasePayload
} catch {
$release = Invoke-RestMethod -Method Get -Uri "$apiBase/releases/tags/$tagName" -Headers $headers
}
function Remove-ExistingAsset([string]$assetName) {
$assets = Invoke-RestMethod -Method Get -Uri "$apiBase/releases/$($release.id)/assets" -Headers $headers
foreach ($asset in @($assets)) {
if ($asset.name -eq $assetName) {
Invoke-RestMethod -Method Delete -Uri "$apiBase/releases/assets/$($asset.id)" -Headers $headers | Out-Null
}
}
}
function Upload-Asset([string]$path) {
$name = Split-Path $path -Leaf
Remove-ExistingAsset $name
$uploadUrl = "$apiBase/releases/$($release.id)/assets?name=$([uri]::EscapeDataString($name))"
$responseFile = Join-Path $env:RUNNER_TEMP "gitea-upload-response.json"
$curl = Join-Path $env:SystemRoot 'System32\curl.exe'
$status = & $curl --silent --show-error --location `
--header "Authorization: token $token" `
--form "attachment=@$path;filename=$name" `
--write-out '%{http_code}' `
--output $responseFile `
$uploadUrl
if ($status -ne '200' -and $status -ne '201') {
Write-Error "Upload release asset failed, HTTP ${status}: $name"
if (Test-Path $responseFile) { Get-Content $responseFile }
exit 1
}
Write-Host "Uploaded release asset: $name"
}
Upload-Asset $zipPath
Upload-Asset $shaPath
Write-Host "Release assets uploaded to $serverUrl/$repo/releases/tag/$tagName"