build(签名流程): 优化签名流程以减少SignPath配额使用,添加相关控制

- 配置electron-builder仅使用SHA256算法签名,避免双签名以减半配额消耗
- 为签名脚本添加配额耗尽处理,超出后跳过后续签名任务
- 新增仅签名主安装包选项和发布工作流签名开关
- 在发布笔记中添加签名状态展示
- 更新自动更新文档,补充SignPath配额问题及解决方案
This commit is contained in:
2026-08-28 03:18:18 +08:00
parent 30451d505c
commit b404ffaef8
6 changed files with 58 additions and 5 deletions
+11 -1
View File
@@ -46,6 +46,11 @@ on:
description: '基础版本号,如 1.2.0' description: '基础版本号,如 1.2.0'
required: true required: true
default: '1.2.0' default: '1.2.0'
sign:
description: '是否使用 SignPath 签名(配额不足时自动跳过)'
required: true
type: boolean
default: true
permissions: permissions:
contents: write contents: write
@@ -56,11 +61,15 @@ jobs:
# 声明使用 Environment "BUILDER",才能读取其中的环境 Secret SIGNPATH_API_TOKEN # 声明使用 Environment "BUILDER",才能读取其中的环境 Secret SIGNPATH_API_TOKEN
environment: BUILDER environment: BUILDER
env: env:
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }} # 勾选 sign 才注入 token 走 SignPath 签名;不勾选则 token 为空,模块自动跳过签名
SIGNPATH_API_TOKEN: ${{ inputs.sign && secrets.SIGNPATH_API_TOKEN || '' }}
SIGNPATH_ORG_ID: 31ecd033-d59e-492b-a70b-b00a54bbc7c2 SIGNPATH_ORG_ID: 31ecd033-d59e-492b-a70b-b00a54bbc7c2
SIGNPATH_PROJECT_SLUG: Koring_Launcher SIGNPATH_PROJECT_SLUG: Koring_Launcher
SIGNPATH_SIGNING_POLICY_SLUG: Koring_Launcher_Dev_builder SIGNPATH_SIGNING_POLICY_SLUG: Koring_Launcher_Dev_builder
SIGNPATH_ARTIFACT_CONFIG_SLUG: ${{ secrets.SIGNPATH_ARTIFACT_CONFIG_SLUG }} SIGNPATH_ARTIFACT_CONFIG_SLUG: ${{ secrets.SIGNPATH_ARTIFACT_CONFIG_SLUG }}
# 内部测试阶段:SignPath 年度配额(约 500MB)耗尽时跳过签名、照常发布(产物未签名)。
# ⚠️ 正式发布请删除此行并升级 SignPath 套餐(同时建议 SIGNPATH_ONLY_INSTALLER=false 全量签名)。
SIGNPATH_SKIP_ON_QUOTA: 'true'
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@@ -113,6 +122,7 @@ jobs:
-BaseVersion "${{ inputs.version }}" ` -BaseVersion "${{ inputs.version }}" `
-FullVersion "${{ steps.version.outputs.full }}" ` -FullVersion "${{ steps.version.outputs.full }}" `
-Mode "${{ inputs.mode }}" ` -Mode "${{ inputs.mode }}" `
-SigningStatus "${{ inputs.sign && '已签名' || '未签名' }}" `
-OutputPath release-notes.md -OutputPath release-notes.md
- name: Publish GitHub Release - name: Publish GitHub Release
+12 -1
View File
@@ -269,11 +269,22 @@ src/
SigningPolicySlug / ArtifactConfigurationSlug? / Description / 文件部件 **Artifact**)→ SigningPolicySlug / ArtifactConfigurationSlug? / Description / 文件部件 **Artifact**)→
响应 **Location 头** = 请求 URL → 轮询 `status`/`isFinalStatus` → 完成后取 **signedArtifactLink** 下载; 响应 **Location 头** = 请求 URL → 轮询 `status`/`isFinalStatus` → 完成后取 **signedArtifactLink** 下载;
认证 `Authorization: Bearer <token>` 认证 `Authorization: Bearer <token>`
- ✅ 98.3MB setup.exe 提交成功(无大小限制问题);自动批准生效(InProgress → Completed 无人工介入) - ✅ 98.3MB setup.exe 提交成功;自动批准生效(InProgress → Completed 无人工介入)
- ✅ 小文件端到端签名成功,`signtool verify` 显示签名链 **Issued to: Lingke Network** - ✅ 小文件端到端签名成功,`signtool verify` 显示签名链 **Issued to: Lingke Network**
- ⚠️ 本机到 SignPath 下载 100MB+ 产物极慢(20min+),CIGitHub Actions)网络环境不受影响; - ⚠️ 本机到 SignPath 下载 100MB+ 产物极慢(20min+),CIGitHub Actions)网络环境不受影响;
模块已加下载重试(3 次)+ 临时文件替换 模块已加下载重试(3 次)+ 临时文件替换
**2026-08-28 CI 首跑教训(配额)**
- ⚠️ **SignPath 年度配额 ≈ 500MB**"Yearly quota for artifact size has been exceeded"),
一次全量签名构建(sha1+sha256 双签)即消耗 ~458MB,直接耗尽配额,发布失败。
- 修复(已落地):`win.signtoolOptions.signingHashAlgorithms: [sha256]` —— 只按 SHA256 单签,
每个文件只签一次,配额减半(全量构建 ~278MB);
可选 `SIGNPATH_ONLY_INSTALLER=true` 只签 setup.exe~97MB/构建)。
- **内部测试策略(已落地)**:workflow 设置 `SIGNPATH_SKIP_ON_QUOTA=true` —— 配额耗尽时
跳过签名、照常发布(产物未签名,electron-updater 发布者校验自动跳过);首次命中后记住状态,
后续文件不再重复提交。⚠️ 正式发布务必移除该开关。
- **治本仍需升级 SignPath 套餐**或等待年度配额重置;正式发布(run)建议全量签名 + 生产证书。
**验证点(首次 CI 运行)**:确认 SignPath 请求全部 Completed、`latest.yml` 的 sha512 **验证点(首次 CI 运行)**:确认 SignPath 请求全部 Completed、`latest.yml` 的 sha512
与上传的 signed setup.exe 一致、签名链显示 "Lingke Network"。 与上传的 signed setup.exe 一致、签名链显示 "Lingke Network"。
File diff suppressed because one or more lines are too long
+6 -1
View File
@@ -19,7 +19,12 @@ win:
requestedExecutionLevel: asInvoker requestedExecutionLevel: asInvoker
# SignPath 远程签名:scripts/signpath-sign.js # SignPath 远程签名:scripts/signpath-sign.js
# 无 SIGNPATH_API_TOKEN 时自动跳过(本地构建不受影响) # 无 SIGNPATH_API_TOKEN 时自动跳过(本地构建不受影响)
sign: scripts/signpath-sign.js # signingHashAlgorithms 只保留 sha256:避免 electron-builder 对每个文件
# 按 sha1+sha256 双算法各签一次(远程签名会重复消耗 SignPath 配额)
signtoolOptions:
signingHashAlgorithms:
- sha256
sign: scripts/signpath-sign.js
mac: mac:
target: dmg target: dmg
icon: build/icon.png icon: build/icon.png
+2
View File
@@ -15,6 +15,7 @@ param(
[Parameter(Mandatory = $true)][string] $BaseVersion, [Parameter(Mandatory = $true)][string] $BaseVersion,
[Parameter(Mandatory = $true)][string] $FullVersion, [Parameter(Mandatory = $true)][string] $FullVersion,
[Parameter(Mandatory = $true)][string] $Mode, [Parameter(Mandatory = $true)][string] $Mode,
[string] $SigningStatus = "已签名",
[string] $OutputPath = "release-notes.md" [string] $OutputPath = "release-notes.md"
) )
@@ -66,6 +67,7 @@ $content = @"
## ##
$FullVersion $FullVersion
$status $status
$SigningStatus
## ##
$commitsSection $commitsSection
+26 -1
View File
@@ -147,11 +147,36 @@ async function downloadWithRetry(downloadUrl, headers) {
/** /**
* electron-builder 自定义签名入口。 * electron-builder 自定义签名入口。
* configuration.path 为待签名文件绝对路径;无 token 时跳过(本地开发构建)。 * configuration.path 为待签名文件绝对路径;无 token 时跳过(本地开发构建)。
*
* SIGNPATH_SKIP_ON_QUOTA=true(内部测试用):
* SignPath 年度配额耗尽时跳过签名并继续构建(产物为未签名),
* 首次命中配额错误后记住状态,后续文件不再尝试提交。
* 正式发布请移除该开关并升级 SignPath 套餐。
*/ */
let quotaExhausted = false;
module.exports = async function signPathSign(configuration) { module.exports = async function signPathSign(configuration) {
if (!process.env.SIGNPATH_API_TOKEN || !process.env.SIGNPATH_ORG_ID) { if (!process.env.SIGNPATH_API_TOKEN || !process.env.SIGNPATH_ORG_ID) {
console.log(`[signpath-sign] no SignPath credentials, skip signing: ${configuration.name}`); console.log(`[signpath-sign] no SignPath credentials, skip signing: ${configuration.name}`);
return; return;
} }
await signWithSignPath(configuration.path); if (quotaExhausted) {
console.warn(`[signpath-sign] quota exhausted — SKIP signing (UNSIGNED): ${configuration.name}`);
return;
}
// 配额省用模式:只签最终安装包(isNsis=true),跳过内部 exe / 卸载器
if (process.env.SIGNPATH_ONLY_INSTALLER === 'true' && !configuration.isNsis) {
console.log(`[signpath-sign] SIGNPATH_ONLY_INSTALLER, skip inner file: ${configuration.name}`);
return;
}
try {
await signWithSignPath(configuration.path);
} catch (err) {
if (process.env.SIGNPATH_SKIP_ON_QUOTA === 'true' && /quota/i.test(err.message)) {
quotaExhausted = true;
console.warn(`[signpath-sign] SignPath quota exceeded — skip remaining files, artifact will be UNSIGNED: ${err.message}`);
return;
}
throw err;
}
}; };