name: Build Windows Release on: push: branches: - main tags: - 'v*' workflow_dispatch: permissions: contents: write jobs: build-windows-release: name: Build Windows x64 Release # 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_msvc2019_64 QT_DIR_NAME: msvc2019_64 DIST_DIR: dist-windows\chengnan PACKAGE_BASENAME: chengnan-windows-x64 steps: - name: Checkout uses: actions/checkout@v4 - name: Setup MSVC developer command prompt uses: ilammy/msvc-dev-cmd@v1 with: arch: x64 - name: Setup Python uses: actions/setup-python@v5 with: python-version: '3.x' - name: Install Qt shell: powershell run: | $ErrorActionPreference = 'Stop' python -m pip install --upgrade pip python -m pip install aqtinstall python -m aqt install-qt windows desktop $env:QT_VERSION $env:QT_ARCH --outputdir C:\Qt --modules qtimageformats $qtPrefix = "C:\Qt\$env:QT_VERSION\$env:QT_DIR_NAME" $windeployqt = Join-Path $qtPrefix 'bin\windeployqt.exe' if (-not (Test-Path $windeployqt)) { throw "Qt 安装不完整,找不到 windeployqt:$windeployqt" } "QT_PREFIX=$qtPrefix" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 - name: Build and deploy Qt runtime shell: powershell run: | $ErrorActionPreference = 'Stop' powershell -ExecutionPolicy Bypass -File .\build-windows.ps1 -QtPrefix "$env:QT_PREFIX" -Config $env:BUILD_TYPE if (-not (Test-Path $env:DIST_DIR)) { throw "发布目录不存在:$env:DIST_DIR" } if (-not (Test-Path "$env:DIST_DIR\chengnan.exe")) { throw "构建产物不存在:$env:DIST_DIR\chengnan.exe" } - 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" @" chengnan Windows x64 发布包 运行方式: 1. 解压整个目录。 2. 双击 chengnan.exe 运行。 说明: - 本目录由 Windows Runner 使用 MSVC + Qt 构建生成。 - 请不要只复制 chengnan.exe,需保留同目录 DLL 和 Qt 插件目录。 "@ | Out-File -FilePath "$env:DIST_DIR\README.txt" -Encoding utf8 if (Test-Path $zipName) { Remove-Item -Force $zipName } Compress-Archive -Path "$env:DIST_DIR\*" -DestinationPath $zipName -Force $hash = (Get-FileHash $zipName -Algorithm SHA256).Hash.ToLowerInvariant() "$hash $zipName" | Out-File -FilePath "$zipName.sha256" -Encoding ascii "PACKAGE_ZIP=$zipName" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 "RELEASE_TAG=$version" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 Write-Host "Package: $zipName" Write-Host "SHA256: $hash" - name: Verify package shell: powershell run: | $ErrorActionPreference = 'Stop' if (-not (Test-Path $env:PACKAGE_ZIP)) { throw "zip 不存在:$env:PACKAGE_ZIP" } if (-not (Test-Path "$env:PACKAGE_ZIP.sha256")) { throw "sha256 不存在:$env:PACKAGE_ZIP.sha256" } $items = Get-ChildItem -Path $env:PACKAGE_ZIP | Select-Object -ExpandProperty FullName if (-not $items) { throw "zip 文件为空或不可读:$env:PACKAGE_ZIP" } - name: Upload build artifact uses: actions/upload-artifact@v3 with: name: chengnan-windows-x64 path: | ${{ env.PACKAGE_ZIP }} ${{ env.PACKAGE_ZIP }}.sha256 if-no-files-found: error - 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 '缺少发布 Token:请在仓库 Secrets 中配置 RELEASE_TOKEN,或确保 Gitea Actions 提供 GITHUB_TOKEN。' } $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 = $env:RELEASE_TAG $releaseName = if ($env:GITHUB_REF_TYPE -eq 'tag') { $env:GITHUB_REF_NAME } else { "chengnan $($env:GITHUB_SHA.Substring(0, 10))" } $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 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" $status = & curl.exe --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 "上传 Release 附件失败,HTTP $status:$name" if (Test-Path $responseFile) { Get-Content $responseFile } exit 1 } Write-Host "Uploaded release asset: $name" } Upload-Asset $env:PACKAGE_ZIP Upload-Asset "$env:PACKAGE_ZIP.sha256" Write-Host "Release assets uploaded to $serverUrl/$repo/releases/tag/$tagName"