Build and Release #4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Release | |
| on: | |
| push: | |
| tags: | |
| - 'v*' | |
| workflow_dispatch: | |
| inputs: | |
| create_release: | |
| description: 'Create a release with current build' | |
| required: true | |
| default: false | |
| type: boolean | |
| jobs: | |
| build: | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 1 | |
| - name: Get version | |
| id: get_version | |
| shell: pwsh | |
| run: | | |
| if ($env:GITHUB_REF -like 'refs/tags/*') { | |
| # 已由 tag 推送触发,直接使用该 tag 作为版本 | |
| $version = $env:GITHUB_REF.Substring(10) | |
| $tag = $version | |
| } else { | |
| # 手动 workflow_dispatch 触发:使用 git 的语义化版本排序,选出最新 tag,然后生成 / 递增 beta 版本 | |
| # --refs 可避免出现带 ^{} 的 peeled 行,--sort=-version:refname 以版本名倒序排序,'v*' 仅匹配语义化版本前缀 | |
| $latestRef = git ls-remote --refs --tags --sort=-version:refname origin 'v*' | | |
| Where-Object { $_ -match 'refs/tags/(v\d+\.\d+\.\d+(?:-beta\.\d+)?)$' } | | |
| Select-Object -First 1 | |
| if (-not $latestRef) { | |
| # 仓库还没有任何符合语义版本的 tag,初始化为 v0.1.0-beta.1 | |
| $baseVersion = 'v0.1.0' | |
| $betaNumber = 1 | |
| $tag = "$baseVersion-beta.$betaNumber" | |
| } else { | |
| # 行格式: <hash>\trefs/tags/<tagname> | |
| if ($latestRef -match 'refs/tags/(.+)$') { | |
| $latestTag = $matches[1] | |
| } else { | |
| throw "无法从 ls-remote 输出中解析最新 tag:$latestRef" | |
| } | |
| if ($latestTag -match '^(v\d+\.\d+\.\d+)-beta\.(\d+)$') { | |
| # 最新即为 beta,直接在 beta 编号上 +1 | |
| $baseVersion = $matches[1] | |
| $betaNumber = [int]$matches[2] + 1 | |
| $tag = "$baseVersion-beta.$betaNumber" | |
| } else { | |
| # 最新为稳定版本,从该稳定版本开始新的 beta 序列 | |
| $latestTag -match '^(v\d+\.\d+\.)(\d+)$' | |
| $baseVersion = $matches[1] + ([int]$matches[2] + 1) | |
| $betaNumber = 1 | |
| $tag = "$baseVersion-beta.$betaNumber" | |
| } | |
| } | |
| $version = $tag | |
| git config --global user.email "actions@github.com" | |
| git config --global user.name "GitHub Actions" | |
| # 创建并推送新 beta tag | |
| git tag $tag | |
| git push origin $tag | |
| } | |
| echo "version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| echo "tag=$tag" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| echo "__version__ = '$version'" | Out-File -FilePath src/one_dragon/version.py -Encoding utf8 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11.9' | |
| - name: Install uv | |
| shell: pwsh | |
| run: | | |
| Invoke-WebRequest -Uri https://astral.sh/uv/install.ps1 | Invoke-Expression | |
| - name: Create and activate virtual environment | |
| shell: pwsh | |
| run: | | |
| uv venv .venv --python=3.11.12 | |
| - name: Install dependencies | |
| shell: pwsh | |
| run: | | |
| .\.venv\Scripts\Activate.ps1 | |
| uv sync --group dev | |
| - name: Download and extract UPX into venv Scripts | |
| shell: pwsh | |
| run: | | |
| $venvScripts = ".\.venv\Scripts" | |
| $upxDir = Join-Path $venvScripts "upx" | |
| $sourceUpxPath = Join-Path $upxDir "upx-4.2.3-win64" "upx.exe" | |
| $destinationUpxPath = Join-Path $venvScripts "upx.exe" | |
| $zipPath = "upx.zip" | |
| Invoke-WebRequest -Uri "https://github.com/upx/upx/releases/download/v4.2.3/upx-4.2.3-win64.zip" -OutFile $zipPath | |
| Expand-Archive -Path $zipPath -DestinationPath $upxDir -Force | |
| Move-Item -Path $sourceUpxPath -Destination $destinationUpxPath -Force | |
| Remove-Item -Path $upxDir -Recurse -Force | |
| Remove-Item -Path $zipPath -Force | |
| - name: Build executables | |
| shell: pwsh | |
| run: | | |
| .\.venv\Scripts\Activate.ps1 | |
| cd deploy | |
| pyinstaller "OneDragon-Installer.spec" | |
| pyinstaller "OneDragon-Launcher.spec" | |
| - name: Bundle dependencies into wheels | |
| shell: pwsh | |
| run: | | |
| # 激活虚拟环境 | |
| .\.venv\Scripts\Activate.ps1 | |
| # 创建目标目录并生成 wheel 包 | |
| New-Item -ItemType Directory -Path deploy/dist/wheels -Force | |
| uv export --no-hashes --no-dev --format requirements-txt > deploy/dist/requirements.txt | |
| pip wheel --wheel-dir=deploy/dist/wheels -r deploy/dist/requirements.txt | |
| # 压缩生成的 wheels 目录 | |
| Compress-Archive -Path deploy/dist/wheels/* -DestinationPath deploy/dist/StarRail-OneDragon-Environment.zip | |
| - name: Upload Installer | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: Installer | |
| path: deploy/dist/OneDragon-Installer.exe | |
| - name: Upload Launcher | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: Launcher | |
| path: deploy/dist/OneDragon-Launcher.exe | |
| - name: Upload Wheels | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: Wheels | |
| path: deploy/dist/StarRail-OneDragon-Environment.zip | |
| - name: Prepare release directory and models | |
| if: ${{ (github.event_name == 'workflow_dispatch' && inputs.create_release == true) || startsWith(github.ref, 'refs/tags/') }} | |
| shell: pwsh | |
| run: | | |
| # 将 dist 移动到当前目录的父目录下 | |
| $distDir = "deploy/dist" | |
| $parentDir = Split-Path -Path (Get-Location) -Parent | |
| $targetDist = Join-Path $parentDir "dist" | |
| Move-Item -Path $distDir -Destination $targetDist -Force | |
| $distDir = $targetDist | |
| $envDir = ".install" | |
| New-Item -ItemType Directory -Path $envDir -Force | Out-Null | |
| # 准备离线运行所需的 .install 资源 | |
| Invoke-WebRequest -Uri "https://github.com/OneDragon-Anything/OneDragon-Env/releases/download/StarRail-OneDragon/uv-x86_64-pc-windows-msvc.zip" -OutFile "$envDir/uv-x86_64-pc-windows-msvc.zip" | |
| Invoke-WebRequest -Uri "https://github.com/OneDragon-Anything/OneDragon-Env/releases/download/StarRail-OneDragon/MinGit.zip" -OutFile "$envDir/MinGit.zip" | |
| Invoke-WebRequest -Uri "https://github.com/OneDragon-Anything/OneDragon-Env/releases/download/StarRail-OneDragon/cpython-3.11.zip" -OutFile "$envDir/cpython-3.11.zip" | |
| # 将安装器复制到仓库根目录 | |
| Copy-Item "$distDir/OneDragon-Installer.exe" -Destination "OneDragon-Installer.exe" -Force | |
| # 打包启动器 | |
| Compress-Archive -Path "$distDir/OneDragon-Launcher.exe" -DestinationPath "$distDir/StarRail-OneDragon-Launcher.zip" -Force | |
| Copy-Item "$distDir/OneDragon-Launcher.exe" -Destination "OneDragon-Launcher.exe" -Force | |
| # 模型目录(放在仓库根 assets/models 下) | |
| $modelBase = "assets/models" | |
| New-Item -ItemType Directory -Path "$modelBase/onnx_ocr" -Force | Out-Null | |
| New-Item -ItemType Directory -Path "$modelBase/sim_uni" -Force | Out-Null | |
| New-Item -ItemType Directory -Path "$modelBase/world_patrol" -Force | Out-Null | |
| # 临时模型目录 | |
| $tempDir = "temp_models" | |
| New-Item -ItemType Directory -Path $tempDir -Force | |
| # 通过文件末尾最高8位数字获取最新模型 | |
| function Get-LatestModelByNumber { | |
| param ( | |
| [string]$repo, | |
| [string]$pattern | |
| ) | |
| $apiUrl = "https://api.github.com/repos/$repo/releases" | |
| $releases = Invoke-RestMethod -Uri $apiUrl -Headers @{ "Accept" = "application/vnd.github.v3+json" } | |
| $bestAsset = $null | |
| $maxNumber = -1 | |
| foreach ($release in $releases) { | |
| foreach ($asset in $release.assets) { | |
| if ($asset.name -match $pattern) { | |
| # 从文件名末尾提取8位数字(在.zip之前) | |
| if ($asset.name -match '(\d{8})\.zip$') { | |
| $number = [int]$matches[1] | |
| if ($number -gt $maxNumber) { | |
| $maxNumber = $number | |
| $bestAsset = @{ | |
| url = $asset.browser_download_url | |
| name = $asset.name | |
| version_number = $number | |
| } | |
| } | |
| } | |
| # 对于ppocrv5和其他没有8位数字后缀的模型,作为备用选项 | |
| elseif ($bestAsset -eq $null) { | |
| $bestAsset = @{ | |
| url = $asset.browser_download_url | |
| name = $asset.name | |
| version_number = 0 | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return $bestAsset | |
| } | |
| function Expand-ZipIntoNamedFolder { | |
| param ( | |
| [string]$url, | |
| [string]$downloadPath, | |
| [string]$destRoot, | |
| [string]$folderName | |
| ) | |
| Invoke-WebRequest -Uri $url -OutFile $downloadPath | |
| $targetPath = Join-Path $destRoot $folderName | |
| New-Item -ItemType Directory -Path $targetPath -Force | |
| Expand-Archive -Path $downloadPath -DestinationPath $targetPath -Force | |
| } | |
| # 获取 ppocrv5 模型 (onnx_ocr) | |
| $ppocrModel = Get-LatestModelByNumber -repo "OneDragon-Anything/OneDragon-Env" -pattern "ppocrv5\.zip$" | |
| if ($ppocrModel) { | |
| $ppocrName = $ppocrModel.name -replace "\.zip$", "" | |
| Write-Host "找到 ppocrv5 模型: $($ppocrModel.name) (版本: $($ppocrModel.version_number))" | |
| Expand-ZipIntoNamedFolder ` | |
| -url $ppocrModel.url ` | |
| -downloadPath "$tempDir/ppocrv5.zip" ` | |
| -destRoot "$modelBase/onnx_ocr" ` | |
| -folderName $ppocrName | |
| } else { | |
| Write-Warning "无法找到 ppocrv5 模型,使用备用版本" | |
| Expand-ZipIntoNamedFolder ` | |
| -url "https://github.com/OneDragon-Anything/OneDragon-Env/releases/download/ppocrv5/ppocrv5.zip" ` | |
| -downloadPath "$tempDir/ppocrv5.zip" ` | |
| -destRoot "$modelBase/onnx_ocr" ` | |
| -folderName "ppocrv5" | |
| } | |
| # 获取 sim uni 模型 | |
| $simUniModel = Get-LatestModelByNumber -repo "OneDragon-Anything/OneDragon-YOLO" -pattern "sim_uni.*\.zip$" | |
| if ($simUniModel) { | |
| $simUniName = $simUniModel.name -replace "\.zip$", "" | |
| Write-Host "找到 sim uni 模型: $($simUniModel.name) (版本: $($simUniModel.version_number))" | |
| Expand-ZipIntoNamedFolder ` | |
| -url $simUniModel.url ` | |
| -downloadPath "$tempDir/sim_uni.zip" ` | |
| -destRoot "$modelBase/sim_uni" ` | |
| -folderName $simUniName | |
| } else { | |
| Write-Warning "无法找到 sim_uni 模型,使用备用版本" | |
| Expand-ZipIntoNamedFolder ` | |
| -url "https://github.com/OneDragon-Anything/OneDragon-YOLO/releases/download/sr_model/yolov8n-640-simuni-0601.zip" ` | |
| -downloadPath "$tempDir/sim_uni.zip" ` | |
| -destRoot "$modelBase/sim_uni" ` | |
| -folderName "yolov8n-640-simuni-0601" | |
| } | |
| # 获取 world_patrol 模型 | |
| $worldPatrolModel = Get-LatestModelByNumber -repo "OneDragon-Anything/OneDragon-YOLO" -pattern "world_patrol.*\.zip$" | |
| if ($worldPatrolModel) { | |
| $worldPatrolName = $worldPatrolModel.name -replace "\.zip$", "" | |
| Write-Host "找到 world patrol 模型: $($worldPatrolModel.name) (版本: $($worldPatrolModel.version_number))" | |
| Expand-ZipIntoNamedFolder ` | |
| -url $worldPatrolModel.url ` | |
| -downloadPath "$tempDir/world_patrol.zip" ` | |
| -destRoot "$modelBase/world_patrol" ` | |
| -folderName $worldPatrolName | |
| } else { | |
| Write-Warning "无法找到 world patrol 模型,使用备用版本" | |
| Expand-ZipIntoNamedFolder ` | |
| -url "https://github.com/OneDragon-Anything/OneDragon-YOLO/releases/download/sr_model/yolov8n-640-simuni-0601.zip" ` | |
| -downloadPath "$tempDir/world_patrol.zip" ` | |
| -destRoot "$modelBase/world_patrol" ` | |
| -folderName "yolov8n-640-simuni-0601" | |
| } | |
| # 获取版本号 | |
| $version = "${{ steps.get_version.outputs.version }}" | |
| # 打包前删除不需要的目录 | |
| Remove-Item -Path "deploy/build" -Recurse -Force -ErrorAction SilentlyContinue | |
| Remove-Item -Path ".venv" -Recurse -Force -ErrorAction SilentlyContinue | |
| Remove-Item -Path ".install/uv_cache" -Recurse -Force -ErrorAction SilentlyContinue | |
| git reset --hard HEAD | |
| git clean -fd | Out-Null | |
| # 第一次打包(Full)枚举根目录全部项目 | |
| $items = Get-ChildItem -Force | ForEach-Object { $_.FullName } | |
| Compress-Archive -Path $items -DestinationPath "$distDir/StarRail-OneDragon-$version-Full.zip" -Force | |
| # 将环境依赖包放入 .install 后进行第二次打包(Full-Environment) | |
| Copy-Item "$distDir/StarRail-OneDragon-Environment.zip" -Destination "$envDir/StarRail-OneDragon-Environment.zip" -Force | |
| $items = Get-ChildItem -Force | ForEach-Object { $_.FullName } | |
| Compress-Archive -Path $items -DestinationPath "$distDir/StarRail-OneDragon-$version-Full-Environment.zip" -Force | |
| # 复制安装器 | |
| Copy-Item "OneDragon-Installer.exe" -Destination "StarRail-OneDragon-$version-Installer.exe" -Force | |
| # 将所有待发布文件移到当前目录 | |
| Move-Item -Path "$distDir/*.zip" -Destination (Get-Location) -Force | |
| - name: Generate Changelog | |
| id: changelog | |
| if: ${{ (github.event_name == 'workflow_dispatch' && inputs.create_release == true) || startsWith(github.ref, 'refs/tags/') }} | |
| shell: pwsh | |
| run: | | |
| $current_version_tag = "${{ steps.get_version.outputs.tag }}" # 从 get_version 步骤获取的标签 | |
| $commits = @() | |
| if ($env:GITHUB_REF -like 'refs/tags/*') { | |
| # 这是由标签触发的发布 | |
| # 尝试查找当前标签提交的父提交上的最新标签 | |
| $previous_tag_candidate = $(git describe --tags --abbrev=0 "$current_version_tag^" 2>$null) | |
| if ($previous_tag_candidate) { | |
| Write-Host "正在生成从 $previous_tag_candidate 到 $current_version_tag 的更新日志" | |
| $commits = git log --pretty=format:"%s|%h" "$previous_tag_candidate..$current_version_tag" | |
| } else { | |
| Write-Host "未找到相对于 $current_version_tag^ 的上一个标签。列出 $current_version_tag 的最后10个提交。" | |
| # 这可能是第一个标签。列出导致它的提交。 | |
| $commits = git log --pretty=format:"%s|%h" -n 10 "$current_version_tag" | |
| } | |
| } else { | |
| # 这是手动 workflow_dispatch 或分支推送 | |
| Write-Host "手动或分支构建。列出 HEAD 的最后5个提交。" | |
| $commits = git log --pretty=format:"%s|%h" -n 5 HEAD | |
| } | |
| # 按前缀分类提交 | |
| $categories = @{ | |
| "feat" = @() | |
| "fix" = @() | |
| "perf" = @() | |
| "refactor" = @() | |
| "style" = @() | |
| "docs" = @() | |
| "test" = @() | |
| "ci" = @() | |
| "build" = @() | |
| "chore" = @() | |
| "revert" = @() | |
| "other" = @() | |
| } | |
| foreach ($commit in $commits) { | |
| if ($commit -match "^(.+)\|(.+)$") { | |
| $message = $matches[1] | |
| $hash = $matches[2] | |
| # 提取冒号前的前缀 | |
| if ($message -match "^([^:]+):(.*)$") { | |
| $prefix = $matches[1].Trim().ToLower() | |
| $content = $matches[2].Trim() | |
| # 检查前缀是否在已知分类中 | |
| if ($categories.ContainsKey($prefix)) { | |
| $categories[$prefix] += "- $content ($hash)" | |
| } else { | |
| $categories["other"] += "- $message ($hash)" | |
| } | |
| } else { | |
| $categories["other"] += "- $message ($hash)" | |
| } | |
| } | |
| } | |
| # 生成分类后的更新日志 | |
| $changelog_content = "" | |
| $category_names = @{ | |
| "feat" = "✨ 新功能" | |
| "fix" = "🐛 Bug 修复" | |
| "perf" = "⚡ 性能优化" | |
| "refactor" = "♻️ 代码重构" | |
| "style" = "💄 样式调整" | |
| "docs" = "📄 文档更新" | |
| "test" = "✅ 测试" | |
| "ci" = "👷 CI/CD" | |
| "build" = "📦 构建" | |
| "chore" = "🔧 杂项" | |
| "revert" = "⏪ 回滚" | |
| "other" = "📝 其他更改" | |
| } | |
| # 定义输出顺序 | |
| $ordered_keys = @("feat", "fix", "perf", "refactor", "style", "docs", "test", "ci", "build", "chore", "revert", "other") | |
| foreach ($key in $ordered_keys) { | |
| if ($categories[$key].Count -gt 0) { | |
| if ($changelog_content -ne "") { | |
| $changelog_content += "`n`n" | |
| } | |
| $changelog_content += "## $($category_names[$key])`n" | |
| $changelog_content += ($categories[$key] -join "`n") | |
| } | |
| } | |
| if ([string]::IsNullOrWhiteSpace($changelog_content)) { | |
| $changelog_content = "没有新的更改或无法确定更新日志。" | |
| } | |
| $output_name = "clean_changelog" | |
| $delimiter = "CHANGELOG_DELIMITER_$(New-Guid)" | |
| echo "$output_name<<$delimiter" | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| echo $changelog_content | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| echo $delimiter | Out-File -FilePath $env:GITHUB_OUTPUT -Append | |
| - name: Create Release | |
| if: ${{ (github.event_name == 'workflow_dispatch' && inputs.create_release == true) || startsWith(github.ref, 'refs/tags/') }} | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: ${{ steps.get_version.outputs.tag }} | |
| name: "Release ${{ steps.get_version.outputs.version }}" | |
| body: | | |
| # 安装方式 | |
| - `StarRail-OneDragon-${{ steps.get_version.outputs.version }}-Full-Environment.zip` 为带环境的完整包,不需要额外下载资源。 | |
| - `StarRail-OneDragon-${{ steps.get_version.outputs.version }}-Full.zip` 为完整包,解压后选择解压目录为安装目录,只需要下载环境依赖。 | |
| - `StarRail-OneDragon-${{ steps.get_version.outputs.version }}-Installer.exe` 为精简安装程序,运行后会自动下载所需的资源。 | |
| - 如果你想更新启动器,前往主程序【设置】-【资源下载】页面更新,或者下载 `StarRail-OneDragon-Launcher.zip`,解压后替换。 | |
| - __不要下载Source Code__ | |
| 安装前请查看 [安装指南](https://onedragon-anything.github.io/sr/zh/quickstart.html) | |
| 若运行出错请查看 [自助排障指南](https://www.kdocs.cn/l/cbSJUUNotJ3Z) | |
| # 更新内容 | |
| ${{ steps.changelog.outputs.clean_changelog }} | |
| files: | | |
| StarRail-OneDragon-${{ steps.get_version.outputs.version }}-Full-Environment.zip | |
| StarRail-OneDragon-${{ steps.get_version.outputs.version }}-Full.zip | |
| StarRail-OneDragon-${{ steps.get_version.outputs.version }}-Installer.exe | |
| StarRail-OneDragon-Environment.zip | |
| StarRail-OneDragon-Launcher.zip | |
| generate_release_notes: false | |
| prerelease: ${{ contains(steps.get_version.outputs.tag, '-beta.') }} | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |