feat(desktop): show complete publication approval summary
This commit is contained in:
1 parent
fedaed7139
commit
24d73cae8d
10 files changed
+264
-6
No files matched your search
@@ -18,8 +18,8 @@
|
||||
en: 990ff7e56dd6005a
|
||||
zh: 18f9dc23c520dc8c
|
||||
/desktop-ci-publication/package-and-publish:
|
||||
en: c17307a96efdde3a
|
||||
zh: 53ba328b149ed407
|
||||
en: 1aaab994b182e893
|
||||
zh: 54bc0689b1f3a7c5
|
||||
/desktop-ci-publication/recovery-and-records:
|
||||
en: e92bfad06162443e
|
||||
zh: 6c3e3d8ece673217
|
||||
|
||||
@@ -39,7 +39,7 @@ Select the platform's `Package Desktop` workflow, preset, source, channel and ve
|
||||
|
||||
Packaging only retains files. After success, open **Publish Desktop · COS → Run workflow** on `master`, enter `package_run_id` from the packaging run URL, and optionally enter `package_run_attempt`. Blank selects the latest attempt, which must have succeeded; it never falls back to an older success. An explicit attempt selects that exact successful build. Windows must be signed; macOS must finish notarization and stapling. GitHub job evidence selects the original runner; its local manifest supplies the preset, version, source and channel before approval. No installer or request artifact is downloaded. Retained packages with the old `publishRequested=false` flag remain eligible; missing or changed local files stop publication.
|
||||
|
||||
Before uploading, the publisher hashes all retained files, checks version and destination metadata, and rechecks the platform signatures. macOS verifies the DMG and extracts the ZIP to verify the app's ticket; Windows checks Authenticode, the configured signer thumbprint and the timestamp. Preparation writes an approval summary with version, channel, platform, deployment, source ref/SHA, package run/attempt, object keys and proposed tag. Both publishing jobs wait for **Review deployments → Approve and deploy**, then recheck the approved manifest and upload plan digest. Changed bytes or destinations reject. Approval cannot edit a version; reject and rebuild instead. Both deployments upload missing binaries, verify remote bytes, then update YAML. There is no files-only mode in this workflow. Production finally creates `desktop-v<version>` at the recorded packaging commit; an existing tag must name the same commit.
|
||||
Before uploading, the publisher hashes all retained files, checks version and destination metadata, and rechecks the platform signatures. macOS verifies the DMG and extracts the ZIP to verify the app's ticket; Windows checks Authenticode, the configured signer thumbprint and the timestamp. Preparation writes a Chinese approval summary with separate version, channel, platform and deployment fields, the retained test release ID, full installer/feed URLs, a link to the original packaging attempt, source ref/SHA, platform verification results and proposed tag. Folded details include the COS bucket and file sizes/SHA-256 of the upload bytes, including rewritten YAML. Public-repository summaries expose the test prefix and download URLs; the prefix is not authentication. Both publishing jobs wait for **Review deployments → Approve and deploy**, then recheck the approved manifest and upload plan digest. Changed bytes or destinations reject. Approval cannot edit a version; reject and rebuild instead. Both deployments upload missing binaries, verify remote bytes, then update YAML. There is no files-only mode in this workflow. Production finally creates `desktop-v<version>` at the recorded packaging commit; an existing tag must name the same commit.
|
||||
|
||||
<a id="recovery-and-records"></a>
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ Environment 审批限制此工作流,不限制同一机器账户运行的任
|
||||
|
||||
打包只保留文件。成功后,在 `master` 上打开 **Publish Desktop · COS → Run workflow**,填写打包运行 URL 中的 `package_run_id`,并可填写 `package_run_attempt`。留空选择最新尝试,且该尝试必须成功,不会回退到较早的成功尝试;显式填写则选择对应的成功构建。Windows 必须完成签名,macOS 必须完成公证和 stapling。GitHub job 证据确定原 runner,本机清单在审批前提供 preset、版本、源码和通道。不会下载安装包或请求 artifact。旧清单的 `publishRequested=false` 不影响发布资格;本机文件缺失或变化会停止发布。
|
||||
|
||||
上传前,发布器校验全部留存文件的哈希、版本及目标信息,再复核平台签名。macOS 校验 DMG,并解压 ZIP 校验 app 票据;Windows 校验 Authenticode、配置的签名证书指纹及时间戳。准备阶段生成审批摘要,列出版本、通道、平台、环境、源码 ref/SHA、package run/attempt、对象路径及拟创建的 tag。两种发布 job 均等待 **Review deployments → Approve and deploy**,随后重新核对已批准的 manifest 与上传计划摘要。文件或目标变化会被拒绝。审批不能修改版本;应拒绝并重新打包。两种环境都上传缺失文件、校验远端字节,再更新 YAML。此工作流没有只上传文件的模式。production 最后为记录的打包提交创建 `desktop-v<version>`;已有 tag 必须指向同一提交。
|
||||
上传前,发布器校验全部留存文件的哈希、版本及目标信息,再复核平台签名。macOS 校验 DMG,并解压 ZIP 校验 app 票据;Windows 校验 Authenticode、配置的签名证书指纹及时间戳。准备阶段生成中文审批摘要,分别展示版本、通道、平台和环境,以及留存的 test release ID、安装包与升级 YAML 的完整 URL、原打包尝试链接、源码 ref/SHA、平台校验结果及拟创建的 tag。折叠详情包含 COS 存储桶,以及实际上传字节的大小和 SHA-256(包括改写后的 YAML)。公开仓库的摘要会公开测试前缀和下载 URL;前缀不提供访问鉴权。两种发布 job 均等待 **Review deployments → Approve and deploy**,随后重新核对已批准的 manifest 与上传计划摘要。文件或目标变化会被拒绝。审批不能修改版本;应拒绝并重新打包。两种环境都上传缺失文件、校验远端字节,再更新 YAML。此工作流没有只上传文件的模式。production 最后为记录的打包提交创建 `desktop-v<version>`;已有 tag 必须指向同一提交。
|
||||
|
||||
<a id="recovery-and-records"></a>
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/** Render the verified publication plan for GitHub's native deployment reviewers. */
|
||||
import { createHash } from 'node:crypto'
|
||||
import { createReadStream } from 'node:fs'
|
||||
import type { DesktopRetainedSource } from './desktop-release-store.ts'
|
||||
import type { DesktopUploadPlan } from './desktop-upload-plan.ts'
|
||||
|
||||
function text(value: string): string {
|
||||
return value.replace(/[&<>"'|`\r\n]/gu, character => `&#${character.charCodeAt(0)};`)
|
||||
}
|
||||
|
||||
function code(value: string): string { return `<code>${text(value)}</code>` }
|
||||
function row(label: string, value: string): string { return `| ${label} | ${value} |` }
|
||||
function link(url: string, label = url): string { return `<a href="${text(url)}">${text(label)}</a>` }
|
||||
|
||||
/**
|
||||
* Render Chinese approval details after package, destination and signature verification succeeds.
|
||||
* @param source Verified retained packaging identity.
|
||||
* @param plan Validated upload plan; YAML contents are the bytes that will be uploaded.
|
||||
* @param origin Validated HTTPS download origin from the publishing configuration.
|
||||
* @param approval Digest bound to the approved manifest and upload plan.
|
||||
* @returns GitHub-flavored Markdown with final URLs and upload-byte fingerprints, without credentials or local paths.
|
||||
*/
|
||||
export async function desktopPublicationSummary(
|
||||
source: DesktopRetainedSource, plan: DesktopUploadPlan, origin: string, approval: string,
|
||||
): Promise<string> {
|
||||
const url = (key: string) => `${origin}/${key.split('/').map(encodeURIComponent).join('/')}`
|
||||
const files = []
|
||||
for (const file of plan.artifacts) {
|
||||
const hash = createHash('sha256')
|
||||
let size = 0
|
||||
const bytes = file.contents === undefined ? createReadStream(file.path) : [Buffer.from(file.contents)]
|
||||
for await (const chunk of bytes) { size += chunk.length; hash.update(chunk) }
|
||||
files.push(row(link(url(file.key), file.filename), `${size} 字节 | ${code(hash.digest('hex'))}`))
|
||||
}
|
||||
const installerExtension = plan.target === 'win-x64' ? '.exe' : '.dmg'
|
||||
const installers = plan.artifacts.filter(file => !file.channelMetadata && file.filename.endsWith(installerExtension))
|
||||
const feeds = plan.artifacts.filter(file => file.channelMetadata)
|
||||
const runUrl = `https://github.com/${source.repository}/actions/runs/${source.runId}/attempts/${source.attempt}`
|
||||
const environment = plan.environment === 'test' ? 'desktop-test' : 'desktop-production'
|
||||
const tag = plan.environment === 'production' ? code(`desktop-v${plan.version}`) : '不创建(test 环境)'
|
||||
return [
|
||||
'', '## 发布前确认', '',
|
||||
`核对以下信息后,点击 **Review deployments**,选择 ${code(environment)} 并点击 **Approve and deploy**;信息有误请点击 **Reject**。`, '',
|
||||
'| 项目 | 发布信息 |', '| --- | --- |',
|
||||
row('发布环境', code(plan.environment)),
|
||||
row('版本号', code(plan.version)),
|
||||
row('升级通道', code(source.channel)),
|
||||
row('平台', code(plan.target)),
|
||||
...(plan.environment === 'test' ? [row('测试 release ID', `${code(source.testReleaseId!)}<br>测试下载路径的随机前缀,用于降低地址被猜到的可能性;连续升级测试需使用相同值。`)] : []),
|
||||
row('下载地址预览', `${installers.map(file => link(url(file.key))).join('<br>')}<br>发布后的目标地址,审批前不保证可下载。`),
|
||||
row('升级 YAML', `${feeds.map(file => link(url(file.key))).join('<br>')}<br>本次发布将更新此文件;审批前可能不存在或仍指向旧版本。`),
|
||||
row('打包来源', `${link(runUrl, `查看打包运行 #${source.runId} · 第 ${source.attempt} 次尝试`)}<br>源码:${code(`${source.sourceRef} @ ${source.commit}`)}<br>打包机器:${code(source.runnerName)}`),
|
||||
row('签名与公证', plan.target === 'win-x64' ? 'EXE 签名校验通过' : 'DMG 校验通过<br>ZIP 内应用校验通过'),
|
||||
row('Git tag', tag), '', '### 批准后将执行', '',
|
||||
'1. 重新校验产物与发布目标;若与审批摘要不一致则停止。',
|
||||
'2. 上传缺少的文件,跳过内容一致的已有文件;发现内容冲突则停止。',
|
||||
'3. 更新上述升级 YAML,并保存发布记录。',
|
||||
...(plan.environment === 'production' ? ['4. 为实际打包提交创建上述 Git tag;若 tag 创建失败,记录“已发布,待补 tag”。'] : []), '',
|
||||
...(plan.environment === 'test' ? ['**本次只发布 test,不修改 production。**', ''] : []),
|
||||
'<details>', '<summary>技术详情与文件清单</summary>', '',
|
||||
`COS 存储桶:${code(plan.bucket)}`, '',
|
||||
...(plan.environment === 'test' ? [
|
||||
'release ID 取自原打包产物清单。打包时优先使用表单 `test_release_id`;留空则使用机器 `presets/test.env` 中的 `DOWNLOAD_TEST_RELEASE_ID`。发布时不重新选择。', '',
|
||||
] : []),
|
||||
`审批摘要 SHA-256:${code(approval)}`, '',
|
||||
`共 ${plan.artifacts.length} 个文件;大小及 SHA-256 对应实际上传字节,包括更新后的 YAML 内容。`, '',
|
||||
'| 文件 | 大小 | SHA-256 |', '| --- | --- | --- |', ...files, '', '</details>', '',
|
||||
'此摘要在公开仓库中可公开查看,包含下载路径及 test 发布的 release ID。随机前缀不能替代访问鉴权。', '',
|
||||
].join('\n')
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import { desktopPublishCos } from './desktop-publish-cos.ts'
|
||||
import { createDesktopPublicationRecord, wasDesktopAttemptPublished, withDesktopChannelLock } from './desktop-publish-record.ts'
|
||||
import { publishDesktopRelease } from './desktop-publish.ts'
|
||||
import { desktopPublicationFailure } from './desktop-publication-failure.ts'
|
||||
import { desktopPublicationSummary } from './desktop-publication-summary.ts'
|
||||
import { publishDesktopTag } from './desktop-publish-tag.ts'
|
||||
|
||||
const routeSchema = z.object({ runId: z.string(), attempt: z.string(), commit: z.string(), runnerName: z.string(),
|
||||
@@ -62,8 +63,7 @@ if (process.argv[1] !== undefined && resolve(process.argv[1]) === import.meta.fi
|
||||
if (mode === '--check') {
|
||||
if (env.GITHUB_OUTPUT) await appendFile(env.GITHUB_OUTPUT, `approval=${approval}\nlabel=${plan.version} → ${manifest.source.channel} / ${plan.target} / ${plan.environment}\n`)
|
||||
if (env.GITHUB_STEP_SUMMARY) await appendFile(env.GITHUB_STEP_SUMMARY,
|
||||
`\n## Confirm Desktop publication\n\nVersion: **${plan.version}**; channel: **${manifest.source.channel}**; environment: **${plan.environment}**; platform: **${plan.target}**\n\nSource: ${manifest.source.sourceRef} @ ${route.commit}\n\nPackage: ${route.runId}, attempt ${route.attempt}\n\nTag: ${plan.environment === 'production' ? `desktop-v${plan.version}` : 'none (test)'}\n\nApproved package SHA-256: ${approval}\n\n` +
|
||||
plan.artifacts.map(file => `- ${file.channelMetadata ? 'Feed' : 'Artifact'}: ${file.key}`).join('\n') + '\n')
|
||||
await desktopPublicationSummary(manifest.source, plan, config.origin, approval))
|
||||
if (env.GITHUB_OUTPUT && config.githubApp) await appendFile(env.GITHUB_OUTPUT,
|
||||
`app-client-id=${config.githubApp.clientId}\napp-key-file=${config.githubApp.privateKeyFile}\n`)
|
||||
process.stdout.write(`desktop publish: verified ${plan.target} ${plan.version}; no credentials read or COS requests made\n`)
|
||||
|
||||
@@ -4,6 +4,7 @@ import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { expect, it } from 'vitest'
|
||||
import { retainDesktopCi } from '../scripts/retain-desktop-ci.ts'
|
||||
import { desktopPublicationSummary } from '../scripts/desktop-publication-summary.ts'
|
||||
import { prepareDesktopPublication } from '../scripts/publish-desktop-ci.ts'
|
||||
|
||||
it.each(['mac-arm64', 'win-x64'] as const)('preflights a complete retained %s build without credentials or source checkout manifests', async (target) => {
|
||||
@@ -51,6 +52,13 @@ it.each(['mac-arm64', 'win-x64'] as const)('preflights a complete retained %s bu
|
||||
expect(prepared.plan.version).toBe('1.0.0')
|
||||
expect(prepared.plan.artifacts.every(file => file.path.startsWith(retained))).toBe(true)
|
||||
expect(signatureChecks).toBe(1)
|
||||
const summary = await desktopPublicationSummary(prepared.manifest.source, prepared.plan, prepared.config.origin, 'c'.repeat(64))
|
||||
await expect(summary).toMatchFileSnapshot(`./expected/publication-${target}.md`)
|
||||
const feed = prepared.plan.artifacts.find(file => file.channelMetadata)!
|
||||
expect(summary).toContain(createHash('sha256').update(feed.contents!).digest('hex'))
|
||||
expect(summary).toContain('https://github.com/owner/repo/actions/runs/10/attempts/2')
|
||||
expect(summary).not.toContain(root)
|
||||
expect(summary).not.toContain('absent-credentials')
|
||||
await expect(prepareDesktopPublication({ ...env, DESKTOP_APPROVED_PACKAGE: 'wrong' }, signature)).rejects.toThrow(/changed after approval/u)
|
||||
await expect(prepareDesktopPublication({ ...env, RUNNER_NAME: 'other-machine' }, signature)).rejects.toThrow(/different machine/u)
|
||||
await writeFile(join(retained, `${base}.${extension}`), 'changed')
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import { expect, it } from 'vitest'
|
||||
import { desktopPublicationSummary } from '../scripts/desktop-publication-summary.ts'
|
||||
import type { DesktopRetainedSource } from '../scripts/desktop-release-store.ts'
|
||||
import type { DesktopUploadPlan } from '../scripts/desktop-upload-plan.ts'
|
||||
|
||||
const source: DesktopRetainedSource = {
|
||||
repository: 'owner/repo', runId: '20', attempt: '3', commit: 'a'.repeat(40), workflowCommit: 'b'.repeat(40),
|
||||
sourceRef: 'dsh-v1.0.0', channel: 'latest', runnerName: 'windows-publisher', target: 'win-x64',
|
||||
preset: 'production', productVersion: '1.0.0', version: '1.0.0', testReleaseId: null, qualification: 'signed',
|
||||
}
|
||||
const plan: DesktopUploadPlan = {
|
||||
environment: 'production', target: 'win-x64', version: '1.0.0', bucket: 'production-1234567890',
|
||||
publicUrl: 'https://download.example.com/dsh-desk/feeds/win-x64/', secretIdEnvName: 'SECRET_ID', secretKeyEnvName: 'SECRET_KEY',
|
||||
artifacts: [
|
||||
{ path: '/not-read/installer.exe', filename: 'Harness 1.0.0.exe', key: 'dsh-desk/bin/win-x64/latest/Harness 1.0.0.exe',
|
||||
contents: 'installer bytes', contentType: 'application/octet-stream', channelMetadata: false },
|
||||
{ path: '/not-read/latest.yml', filename: 'latest.yml', key: 'dsh-desk/feeds/win-x64/latest.yml',
|
||||
contents: 'version: 1.0.0\n', contentType: 'application/yaml', channelMetadata: true },
|
||||
],
|
||||
}
|
||||
|
||||
it('shows production destinations and tag after approval with upload-byte fingerprints', async () => {
|
||||
const summary = await desktopPublicationSummary(source, plan, 'https://download.example.com', 'c'.repeat(64))
|
||||
await expect(summary).toMatchFileSnapshot('./expected/publication-production.md')
|
||||
expect(summary).toContain('/latest/Harness%201.0.0.exe')
|
||||
expect(summary).toContain('https://download.example.com/dsh-desk/feeds/win-x64/latest.yml')
|
||||
expect(summary).toContain(createHash('sha256').update('version: 1.0.0\n').digest('hex'))
|
||||
expect(summary).not.toContain('测试 release ID |')
|
||||
expect(summary).not.toContain('SECRET_KEY')
|
||||
expect(summary).not.toContain('/not-read/')
|
||||
})
|
||||
|
||||
it('escapes runner display text without adding Markdown rows or HTML elements', async () => {
|
||||
const summary = await desktopPublicationSummary({ ...source, runnerName: 'runner|\n<script>`&"' }, plan,
|
||||
'https://download.example.com', 'c'.repeat(64))
|
||||
expect(summary).toContain('runner| <script>`&"')
|
||||
expect(summary).not.toContain('<script>')
|
||||
})
|
||||
|
||||
it('fails instead of rendering success when an upload file cannot be read', async () => {
|
||||
await expect(desktopPublicationSummary(source, { ...plan,
|
||||
artifacts: [{ path: '/missing-desktop-summary-artifact/installer.exe', filename: 'installer.exe',
|
||||
key: 'installer.exe', contentType: 'application/octet-stream', channelMetadata: false }],
|
||||
}, 'https://download.example.com', 'c'.repeat(64))).rejects.toThrow()
|
||||
})
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
## 发布前确认
|
||||
|
||||
核对以下信息后,点击 **Review deployments**,选择 <code>desktop-test</code> 并点击 **Approve and deploy**;信息有误请点击 **Reject**。
|
||||
|
||||
| 项目 | 发布信息 |
|
||||
| --- | --- |
|
||||
| 发布环境 | <code>test</code> |
|
||||
| 版本号 | <code>1.0.0</code> |
|
||||
| 升级通道 | <code>nightly</code> |
|
||||
| 平台 | <code>mac-arm64</code> |
|
||||
| 测试 release ID | <code>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</code><br>测试下载路径的随机前缀,用于降低地址被猜到的可能性;连续升级测试需使用相同值。 |
|
||||
| 下载地址预览 | <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/mac-arm64/deepseek-harness-1.0.0-mac-arm64.dmg">https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/mac-arm64/deepseek-harness-1.0.0-mac-arm64.dmg</a><br>发布后的目标地址,审批前不保证可下载。 |
|
||||
| 升级 YAML | <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/feeds/mac-arm64/nightly-mac.yml">https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/feeds/mac-arm64/nightly-mac.yml</a><br>本次发布将更新此文件;审批前可能不存在或仍指向旧版本。 |
|
||||
| 打包来源 | <a href="https://github.com/owner/repo/actions/runs/10/attempts/2">查看打包运行 #10 · 第 2 次尝试</a><br>源码:<code>master @ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb</code><br>打包机器:<code>fixture-runner</code> |
|
||||
| 签名与公证 | DMG 校验通过<br>ZIP 内应用校验通过 |
|
||||
| Git tag | 不创建(test 环境) |
|
||||
|
||||
### 批准后将执行
|
||||
|
||||
1. 重新校验产物与发布目标;若与审批摘要不一致则停止。
|
||||
2. 上传缺少的文件,跳过内容一致的已有文件;发现内容冲突则停止。
|
||||
3. 更新上述升级 YAML,并保存发布记录。
|
||||
|
||||
**本次只发布 test,不修改 production。**
|
||||
|
||||
<details>
|
||||
<summary>技术详情与文件清单</summary>
|
||||
|
||||
COS 存储桶:<code>fixture-1250000000</code>
|
||||
|
||||
release ID 取自原打包产物清单。打包时优先使用表单 `test_release_id`;留空则使用机器 `presets/test.env` 中的 `DOWNLOAD_TEST_RELEASE_ID`。发布时不重新选择。
|
||||
|
||||
审批摘要 SHA-256:<code>cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc</code>
|
||||
|
||||
共 4 个文件;大小及 SHA-256 对应实际上传字节,包括更新后的 YAML 内容。
|
||||
|
||||
| 文件 | 大小 | SHA-256 |
|
||||
| --- | --- | --- |
|
||||
| <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/mac-arm64/deepseek-harness-1.0.0-mac-arm64.dmg">deepseek-harness-1.0.0-mac-arm64.dmg</a> | 11 字节 | <code>eac5da462cb16554774d722fa4093739ffc9ade2f4c6e8ee54041e71908f1408</code> |
|
||||
| <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/mac-arm64/deepseek-harness-1.0.0-mac-arm64.zip">deepseek-harness-1.0.0-mac-arm64.zip</a> | 23 字节 | <code>7039e7b3e00d39fbfb2948ba31b6216ac9ffff01b4117a1cec08025255d9ad10</code> |
|
||||
| <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/mac-arm64/deepseek-harness-1.0.0-mac-arm64.zip.blockmap">deepseek-harness-1.0.0-mac-arm64.zip.blockmap</a> | 16 字节 | <code>4fd6c7d73e713d7f54f173dfc845d046d267f9e62c80a92670b0696131bfb74a</code> |
|
||||
| <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/feeds/mac-arm64/nightly-mac.yml">nightly-mac.yml</a> | 276 字节 | <code>86180a8b9207744d8300e993040f28ad8812f16cf30367b2f04f5dcfadb87452</code> |
|
||||
|
||||
</details>
|
||||
|
||||
此摘要在公开仓库中可公开查看,包含下载路径及 test 发布的 release ID。随机前缀不能替代访问鉴权。
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
## 发布前确认
|
||||
|
||||
核对以下信息后,点击 **Review deployments**,选择 <code>desktop-production</code> 并点击 **Approve and deploy**;信息有误请点击 **Reject**。
|
||||
|
||||
| 项目 | 发布信息 |
|
||||
| --- | --- |
|
||||
| 发布环境 | <code>production</code> |
|
||||
| 版本号 | <code>1.0.0</code> |
|
||||
| 升级通道 | <code>latest</code> |
|
||||
| 平台 | <code>win-x64</code> |
|
||||
| 下载地址预览 | <a href="https://download.example.com/dsh-desk/bin/win-x64/latest/Harness%201.0.0.exe">https://download.example.com/dsh-desk/bin/win-x64/latest/Harness%201.0.0.exe</a><br>发布后的目标地址,审批前不保证可下载。 |
|
||||
| 升级 YAML | <a href="https://download.example.com/dsh-desk/feeds/win-x64/latest.yml">https://download.example.com/dsh-desk/feeds/win-x64/latest.yml</a><br>本次发布将更新此文件;审批前可能不存在或仍指向旧版本。 |
|
||||
| 打包来源 | <a href="https://github.com/owner/repo/actions/runs/20/attempts/3">查看打包运行 #20 · 第 3 次尝试</a><br>源码:<code>dsh-v1.0.0 @ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</code><br>打包机器:<code>windows-publisher</code> |
|
||||
| 签名与公证 | EXE 签名校验通过 |
|
||||
| Git tag | <code>desktop-v1.0.0</code> |
|
||||
|
||||
### 批准后将执行
|
||||
|
||||
1. 重新校验产物与发布目标;若与审批摘要不一致则停止。
|
||||
2. 上传缺少的文件,跳过内容一致的已有文件;发现内容冲突则停止。
|
||||
3. 更新上述升级 YAML,并保存发布记录。
|
||||
4. 为实际打包提交创建上述 Git tag;若 tag 创建失败,记录“已发布,待补 tag”。
|
||||
|
||||
<details>
|
||||
<summary>技术详情与文件清单</summary>
|
||||
|
||||
COS 存储桶:<code>production-1234567890</code>
|
||||
|
||||
审批摘要 SHA-256:<code>cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc</code>
|
||||
|
||||
共 2 个文件;大小及 SHA-256 对应实际上传字节,包括更新后的 YAML 内容。
|
||||
|
||||
| 文件 | 大小 | SHA-256 |
|
||||
| --- | --- | --- |
|
||||
| <a href="https://download.example.com/dsh-desk/bin/win-x64/latest/Harness%201.0.0.exe">Harness 1.0.0.exe</a> | 15 字节 | <code>e34210a6de4f653edf588301431c3d69a633638cbf587345cc50a7fed9f38f4c</code> |
|
||||
| <a href="https://download.example.com/dsh-desk/feeds/win-x64/latest.yml">latest.yml</a> | 15 字节 | <code>8850e6075dda0080c526269a4de29848c32c2a65e3d81f4a64ed8ce53d4b47c1</code> |
|
||||
|
||||
</details>
|
||||
|
||||
此摘要在公开仓库中可公开查看,包含下载路径及 test 发布的 release ID。随机前缀不能替代访问鉴权。
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
## 发布前确认
|
||||
|
||||
核对以下信息后,点击 **Review deployments**,选择 <code>desktop-test</code> 并点击 **Approve and deploy**;信息有误请点击 **Reject**。
|
||||
|
||||
| 项目 | 发布信息 |
|
||||
| --- | --- |
|
||||
| 发布环境 | <code>test</code> |
|
||||
| 版本号 | <code>1.0.0</code> |
|
||||
| 升级通道 | <code>nightly</code> |
|
||||
| 平台 | <code>win-x64</code> |
|
||||
| 测试 release ID | <code>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</code><br>测试下载路径的随机前缀,用于降低地址被猜到的可能性;连续升级测试需使用相同值。 |
|
||||
| 下载地址预览 | <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/win-x64/deepseek-harness-1.0.0-win-x64.exe">https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/win-x64/deepseek-harness-1.0.0-win-x64.exe</a><br>发布后的目标地址,审批前不保证可下载。 |
|
||||
| 升级 YAML | <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/feeds/win-x64/nightly.yml">https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/feeds/win-x64/nightly.yml</a><br>本次发布将更新此文件;审批前可能不存在或仍指向旧版本。 |
|
||||
| 打包来源 | <a href="https://github.com/owner/repo/actions/runs/10/attempts/2">查看打包运行 #10 · 第 2 次尝试</a><br>源码:<code>master @ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb</code><br>打包机器:<code>fixture-runner</code> |
|
||||
| 签名与公证 | EXE 签名校验通过 |
|
||||
| Git tag | 不创建(test 环境) |
|
||||
|
||||
### 批准后将执行
|
||||
|
||||
1. 重新校验产物与发布目标;若与审批摘要不一致则停止。
|
||||
2. 上传缺少的文件,跳过内容一致的已有文件;发现内容冲突则停止。
|
||||
3. 更新上述升级 YAML,并保存发布记录。
|
||||
|
||||
**本次只发布 test,不修改 production。**
|
||||
|
||||
<details>
|
||||
<summary>技术详情与文件清单</summary>
|
||||
|
||||
COS 存储桶:<code>fixture-1250000000</code>
|
||||
|
||||
release ID 取自原打包产物清单。打包时优先使用表单 `test_release_id`;留空则使用机器 `presets/test.env` 中的 `DOWNLOAD_TEST_RELEASE_ID`。发布时不重新选择。
|
||||
|
||||
审批摘要 SHA-256:<code>cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc</code>
|
||||
|
||||
共 3 个文件;大小及 SHA-256 对应实际上传字节,包括更新后的 YAML 内容。
|
||||
|
||||
| 文件 | 大小 | SHA-256 |
|
||||
| --- | --- | --- |
|
||||
| <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/win-x64/deepseek-harness-1.0.0-win-x64.exe">deepseek-harness-1.0.0-win-x64.exe</a> | 23 字节 | <code>7039e7b3e00d39fbfb2948ba31b6216ac9ffff01b4117a1cec08025255d9ad10</code> |
|
||||
| <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bin/win-x64/deepseek-harness-1.0.0-win-x64.exe.blockmap">deepseek-harness-1.0.0-win-x64.exe.blockmap</a> | 16 字节 | <code>4fd6c7d73e713d7f54f173dfc845d046d267f9e62c80a92670b0696131bfb74a</code> |
|
||||
| <a href="https://example.com/dsh-desk/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/feeds/win-x64/nightly.yml">nightly.yml</a> | 272 字节 | <code>faccf50f8398d6781568114dda9a678751e4c01044dd9424d00237c69e58cb59</code> |
|
||||
|
||||
</details>
|
||||
|
||||
此摘要在公开仓库中可公开查看,包含下载路径及 test 发布的 release ID。随机前缀不能替代访问鉴权。
|
||||
Reference in new issue
Block a user