Files
koring-launcher/.github/workflows/release.yml
T
dream_pep 37a5bb7538 refactor(updater, ci): 重构版本处理逻辑,适配新的版本格式
调整GitHub Release工作流的beta版本号格式,将`beta.$buildId`改为`$buildId.beta`,对齐新的版本命名规范
新增统一的版本标签解析与过滤逻辑,支持识别多种版本格式并屏蔽旧版beta候选
重写版本比较函数,修复旧版本比较的逻辑问题
重构更新检查与通道配置流程,不再依赖electron-updater的内置频道规则
优化发布说明获取的回退逻辑,统一使用generic feed结合多镜像源提升更新稳定性
2026-09-05 19:08:23 +08:00

306 lines
14 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.
# Koring Launcher 构建 & 发布流水线(手动触发):构建 → SignPath 签名 → 发布 GitHub Release
#
# 手动触发(Actions 页面 -> Run workflow):
# mode 构建模式:beta(测试)或 run(正式)
# ref 构建来源分支/tag/commit(留空 = 默认分支)
# sign 是否使用 SignPath 签名
# 无 version 输入:base 自动读 package.json(版本单一事实源,消除本地/CI 版本双轨)。
# BUILD ID = GitHub Run Number(严格递增,无分钟级冲突),
# 最终版本号 = {base}-{buildId}(如 1.2.0-12),tag = v{base}-{buildId}。
#
# 发布行为:
# - beta:创建 GitHub prerelease(不占用 "Latest" 位)
# - run :创建正式 release
# - Release 标题命名(仅展示名,tag/真实版本号不变):run → "{base}"(如 1.2.1),beta → "BETA {base}"(如 BETA 1.2.1
# - Release 正文为中文:版本信息(当前版本 / 编译状态 / 构建来源 commit)+ 提交记录(默认折叠)
# - 上传产物(Windows job):koring-launcher-{full}-setup.exe + latest.yml
# - LinuxAppImage)由 build-linux job 追加:*.AppImage + latest-linux.ymlbeta 另加 beta-linux.yml
# - 构建元数据(commit / buildId)写入 src/lib/buildInfo.ts,打包进渲染层供 UI 显示
#
# Secrets
# SIGNPATH_API_TOKEN SignPath API Token(必填;放在 Environment "BUILDER" 的环境 Secrets 中)
# SIGNPATH_ARTIFACT_CONFIG_SLUG 产物配置 slug(项目只有一个配置时可留空;仓库级 Secret)
#
# 组织 ID / slug 非密钥,直接写在下方 env(SignPath 后台确认值):
# OrganizationId 31ecd033-d59e-492b-a70b-b00a54bbc7c2
# ProjectSlug Koring_Launcher
# SigningPolicySlug Koring_Launcher_Dev_builder
#
# ⚠️ 签名策略说明:
# - 审批流程:SignPath 后台已关闭人工审批(自动批准),CI 可全自动。
# - 证书:当前策略为测试证书,用户机器默认不信任(SmartScreen / 杀软警告),
# 正式对外发布需生产证书(OV/EV)+ 对应生产签名策略,届时只换 SIGNPATH_SIGNING_POLICY_SLUG。
#
# ⚠️ 版本号注意(electron-updater 语义):
# - {base}-{buildId} 属于 semver prereleaseelectron-updater 默认 allowPrerelease=false 会跳过带
# prerelease 后缀的新版本,客户端必须在 electron/updater.ts 设置 autoUpdater.allowPrerelease=true
# (已落地),否则如 v1.2.1-4 永远检测不到 v1.2.1-5。
# - 开启后:已安装同格式版本的用户可正常收到更高 buildId 的更新(buildId 数值比较)。
# - 若未来发布不带 buildId 的稳定版本(如 1.2.0),稳定版用户不会自动升级到带 buildId 的构建。
# - 切换 BUILD ID 方案(时间 ID → Run Number)时,旧格式数值更大(2608271921 > 12),
# 老用户不会自动升级 —— 切换时应同时提升 base(如 1.3.0-12 > 1.2.0-2608271921)。
name: Koring Launcher Builder
on:
workflow_dispatch:
inputs:
mode:
description: '构建模式:beta(测试)/ run(正式)'
required: true
type: choice
options:
- beta
- run
ref:
description: '构建来源分支 / tag / commit(留空 = 默认分支)'
required: false
default: ''
sign:
description: '是否使用 SignPath 签名(配额不足时自动跳过)'
required: true
type: boolean
default: true
permissions:
contents: write
jobs:
build-sign-publish:
runs-on: windows-latest
# 声明使用 Environment "BUILDER",才能读取其中的环境 Secret SIGNPATH_API_TOKEN
environment: BUILDER
env:
# 勾选 sign 才注入 token 走 SignPath 签名;不勾选则 token 为空,模块自动跳过签名
SIGNPATH_API_TOKEN: ${{ inputs.sign && secrets.SIGNPATH_API_TOKEN || '' }}
SIGNPATH_ORG_ID: 31ecd033-d59e-492b-a70b-b00a54bbc7c2
SIGNPATH_PROJECT_SLUG: Koring_Launcher
SIGNPATH_SIGNING_POLICY_SLUG: Koring_Launcher_Dev_builder
SIGNPATH_ARTIFACT_CONFIG_SLUG: ${{ secrets.SIGNPATH_ARTIFACT_CONFIG_SLUG }}
# 内部测试阶段:SignPath 年度配额(约 500MB)耗尽时跳过签名、照常发布(产物未签名)。
# ⚠️ 正式发布请删除此行并升级 SignPath 套餐(同时建议 SIGNPATH_ONLY_INSTALLER=false 全量签名)。
SIGNPATH_SKIP_ON_QUOTA: 'true'
# Electron 二进制 / electron-builder 辅助工具下载源:
# GitHub release-assets.githubusercontent.com 偶发 TLS 断连(本次 CI 即失败于此),
# 统一走 npmmirror 镜像更稳(对 pnpm install 的 electron postinstall 同样生效)。
ELECTRON_MIRROR: https://npmmirror.com/mirrors/electron/
ELECTRON_BUILDER_BINARIES_MIRROR: https://npmmirror.com/mirrors/electron-builder-binaries/
steps:
- uses: actions/checkout@v4
with:
# 需要完整历史:生成中文提交记录(自上个 release tag 以来)
fetch-depth: 0
# 构建来源:默认默认分支,可指定分支/tag/commit
ref: ${{ inputs.ref || '' }}
- uses: pnpm/action-setup@v4
with:
version: 11.7.0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
# BUILD ID = GitHub Run Number(严格递增,无分钟级冲突)
# base 自动读 package.json(版本单一事实源)
# 版本格式(数字标识在 allowPrerelease 的通道逻辑中会被当"自定义频道",但正式版用户
# allowPrerelease=false,走 /releases/latest(按 GitHub 的 prerelease 标记而非 semver):
# 只要发布时不加 --prerelease{base}-{buildId} 就是最新正式版,可正常识别):
# beta → {base}-beta.{buildId}(如 1.2.1-beta.13,频道 beta
# run → {base}-{buildId}(如 1.2.1-13,带构建尾号)
- name: Generate BUILD ID & set version
id: version
shell: pwsh
run: |
$buildId = "$env:GITHUB_RUN_NUMBER"
$base = node scripts/version.js get
if ("${{ inputs.mode }}" -eq "beta") {
$full = node scripts/version.js build ci "$buildId.beta"
} else {
$full = node scripts/version.js build ci "$buildId"
}
"build_id=$buildId" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"base=$base" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
"full=$full" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
Write-Host "BUILD ID: $buildId -> version: $full (base: $base)"
# 生成构建元数据(commit / 编译号)→ 打包进渲染层,UI 显示构建来源
- name: Generate build info
shell: pwsh
run: node scripts/gen-build-info.js ${{ inputs.mode }}
env:
BUILD_ID: ${{ steps.version.outputs.build_id }}
- name: Build renderer + main (${{ inputs.mode }})
shell: pwsh
run: |
if ("${{ inputs.mode }}" -eq "beta") {
pnpm build:beta
pnpm icon:beta
} else {
pnpm build:run
pnpm icon:run
}
# 关键步骤:win.sign 自定义签名(SignPath)在打包过程中逐个签名内部 exe 与 setup.exe
# 失败重试 1 次:Electron 二进制下载偶发 TLS 断连,重试只补下载缺失部分(已成功的 zip 会进缓存)
- name: Package & sign (SignPath)
shell: pwsh
run: |
for ($attempt = 1; $attempt -le 2; $attempt++) {
pnpm exec electron-builder --win --publish never
if ($LASTEXITCODE -eq 0) { break }
Write-Host "electron-builder 打包失败(第 $attempt 次,exit=$LASTEXITCODE),5 秒后重试..."
if ($attempt -eq 2) { exit $LASTEXITCODE }
Start-Sleep -Seconds 5
}
- name: Generate Chinese release notes
shell: pwsh
run: |
./scripts/release-notes.ps1 `
-BaseVersion "${{ steps.version.outputs.base }}" `
-FullVersion "${{ steps.version.outputs.full }}" `
-Mode "${{ inputs.mode }}" `
-SigningStatus "${{ inputs.sign && '已签名' || '未签名' }}" `
-Commit "${{ github.sha }}" `
-OutputPath release-notes.md
- name: Publish GitHub Release
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$tag = "v${{ steps.version.outputs.full }}"
# Release 标题命名(仅展示名;tag / 真实版本号是 {base}-beta.{id} 或 {base}-{id}):
# 正式/预览版 → "1.2.5-13"(带构建尾号);测试版 → "BETA 1.2.5-beta.13"(带 Run Num
$title = if ("${{ inputs.mode }}" -eq "beta") { "BETA ${{ steps.version.outputs.full }}" } else { "${{ steps.version.outputs.full }}" }
# 资产列表(beta 额外上传 latest-beta.ymlGitHub provider 频道取件(latest-beta)不再 404 回退)
$assets = @(
"dist-electron/koring-launcher-${{ steps.version.outputs.full }}-setup.exe",
"dist-electron/latest.yml",
"release-notes.md"
)
if ("${{ inputs.mode }}" -eq "beta") {
Copy-Item "dist-electron/latest.yml" "dist-electron/latest-beta.yml" -Force
$assets += "dist-electron/latest-beta.yml"
}
# 上传前校验资产存在(避免 gh 对缺失文件的模糊报错)
foreach ($a in $assets) {
if (-not (Test-Path -LiteralPath $a)) {
Write-Error "资产不存在: $a"
exit 1
}
}
# 参数数组统一 splat--prerelease 仅在 beta 追加;
# 不能传空串占位(gh 会把位置参数当资产 glob,空串 → "no matches found for" 报错)
$ghArgs = @($tag) + $assets
$ghArgs += @("--title", $title, "--notes-file", "release-notes.md")
if ("${{ inputs.mode }}" -eq "beta") {
$ghArgs += "--prerelease"
}
gh release create @ghArgs
# ==================== LinuxAppImage====================
# electron-updater 对 Linux 只原生支持 AppImage 自动更新(deb/rpm 走系统包管理器)。
# Windows job 创建 Release 后,本 job 把 AppImage + latest-linux.yml 上传到同一 Release。
# ⚠️ Linux 清单文件带 -linux 前缀:provider 取 latest-linux.ymlbeta 先试 beta-linux.yml 再回退),
# 必须上传,否则 Linux 端更新取不到版本。
build-linux:
needs: build-sign-publish
runs-on: ubuntu-latest
env:
ELECTRON_MIRROR: https://npmmirror.com/mirrors/electron/
ELECTRON_BUILDER_BINARIES_MIRROR: https://npmmirror.com/mirrors/electron-builder-binaries/
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.ref || '' }}
- uses: pnpm/action-setup@v4
with:
version: 11.7.0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
# 与 Windows job 相同逻辑:BUILD ID = 同一 workflow run 的 RUN_NUMBER,产物版本一致
- name: Set version (same RUN_NUMBER)
id: linuxver
shell: pwsh
run: |
$buildId = "$env:GITHUB_RUN_NUMBER"
if ("${{ inputs.mode }}" -eq "beta") {
$full = node scripts/version.js build ci "$buildId.beta"
} else {
$full = node scripts/version.js build ci "$buildId"
}
"full=$full" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
Write-Host "Linux version: $full"
- name: Generate build info
shell: pwsh
run: node scripts/gen-build-info.js ${{ inputs.mode }}
env:
BUILD_ID: ${{ github.run_number }}
- name: Build renderer + main (${{ inputs.mode }})
shell: pwsh
run: |
if ("${{ inputs.mode }}" -eq "beta") {
pnpm build:beta
pnpm icon:beta
} else {
pnpm build:run
pnpm icon:run
}
- name: Package AppImage
shell: pwsh
run: |
for ($attempt = 1; $attempt -le 2; $attempt++) {
pnpm exec electron-builder --linux AppImage --publish never
if ($LASTEXITCODE -eq 0) { break }
Write-Host "electron-builder(AppImage) 失败(第 $attempt 次,exit=$LASTEXITCODE),5 秒后重试..."
if ($attempt -eq 2) { exit $LASTEXITCODE }
Start-Sleep -Seconds 5
}
- name: Upload AppImage assets to Release
shell: pwsh
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$tag = "v${{ steps.linuxver.outputs.full }}"
$appImage = (Get-ChildItem "dist-electron/*.AppImage" -ErrorAction Stop | Select-Object -First 1).Name
$assets = @(
"dist-electron/$appImage",
"dist-electron/latest-linux.yml"
)
# beta:补 beta-linux.ymlprovider 先取该文件,避免 404 再回退)
if ("${{ inputs.mode }}" -eq "beta") {
Copy-Item "dist-electron/latest-linux.yml" "dist-electron/beta-linux.yml" -Force
$assets += "dist-electron/beta-linux.yml"
}
foreach ($a in $assets) {
if (-not (Test-Path -LiteralPath $a)) {
Write-Error "资产不存在: $a"
exit 1
}
}
gh release upload $tag @assets --clobber
Write-Host "已上传到 $tag : $($assets -join ', ')"