Skip to content

ci(desktop): exercise desktop packaging on a schedule - #11519

Merged
yiliang114 merged 2 commits into
mainfrom
ci/desktop-packaging-check
Sep 10, 2026
Merged

ci(desktop): exercise desktop packaging on a schedule#11519
yiliang114 merged 2 commits into
mainfrom
ci/desktop-packaging-check

Conversation

@yiliang114

Copy link
Copy Markdown
Collaborator

What this PR does

Makes the desktop release workflow callable and adds a scheduled caller that runs it as a dry run once a day, so the desktop packaging path is exercised continuously instead of only when someone dispatches a release.

Why it's needed

The desktop app bundles a Qwen Code runtime built from this repository's dist tree, so a change anywhere in the CLI can break desktop packaging. Nothing exercised that path outside a manual release dispatch: the only continuous integration job that touches the desktop package compiles the Tauri crate and runs its tests, and it runs solely when that package itself changes. A dependency added on the CLI side is invisible to it.

Desktop release 0.2.3-preview.0 is what that costs. A renderer dependency added two weeks earlier had put a native library into the bundled runtime that the AppImage bundler cannot process, and release day was the first time anyone found out — three of the four platform jobs failed at once, with the publish step queued behind them, and the failure had to be diagnosed under release pressure rather than next to the change that caused it.

A dry run builds the runtime, the installers and runs the packaged smoke tests on all four targets, which is exactly the coverage that was missing. It leaves the signing steps out, because those are gated on a real publish, so it does not cover macOS notarization or Windows Authenticode — the workflow comment says so rather than leaving the gap to be discovered.

Reviewer Test Plan

How to verify

Dispatch the new check manually and confirm it starts the release workflow's four build jobs against main, that each one reaches its packaged smoke test, and that the publish and mirror jobs are skipped. The release workflow itself should behave exactly as before when dispatched directly — the new trigger adds inputs, it does not change any existing one.

To confirm it catches the class of regression it exists for, dispatch it against a ref that carries a broken bundled runtime and check that the Linux job fails in the installer step rather than reporting success.

Evidence (Before & After)

N/A — CI configuration, no user-visible surface.

Tested on

OS Status
🍏 macOS N/A
🪟 Windows N/A
🐧 Linux ⚠️ not tested

Both files were checked for YAML validity and formatting; the workflow itself can only be verified by running it, which needs a dispatch on this repository.

Environment (optional)

N/A

Risk & Scope

  • Main risk or tradeoff: a daily run of four platform jobs, roughly 25 minutes each, two of them on macOS runners. Reducing the schedule or the platform set is a reasonable call to make at review time; the value is highest on Linux and macOS, which is where the packaging path is most fragile.
  • Not validated / out of scope: signed builds. A dry run cannot cover notarization or Authenticode, so a signing regression still surfaces only on release day. Covering that would mean either signing on a schedule or verifying signability separately, and neither belongs in this change.
  • Breaking changes / migration notes: none. The scheduled caller never publishes — it passes a dry run, and both the publish and the mirror jobs are additionally gated on their own conditions.

Linked Issues

Related to #8092.

中文说明

这个 PR 做了什么

把桌面发布 workflow 改成可被调用的,并新增一个定时调用方,每天以 dry run 的方式跑一次,让桌面打包链路持续得到验证,而不是只有在有人手动触发发布时才跑一次。

为什么需要

桌面应用打包的运行时来自本仓库的 dist 产物,因此 CLI 侧任何一处改动都可能打断桌面打包。而在手动发布之外,没有任何流程覆盖这条链路:唯一涉及桌面包的持续集成 job 只编译 Tauri crate 并跑它自己的测试,而且仅在该包自身发生改动时才触发。CLI 侧新增的依赖对它是不可见的。

desktop 0.2.3-preview.0 就是代价。两周前引入的一个渲染器依赖,把一个 AppImage 打包器无法处理的原生库带进了自带运行时,而发布当天才第一次暴露出来——四个平台 job 中有三个同时失败,发布步骤还排在它们后面,问题只能在发布压力下排查,而不是在引入改动的当时。

一次 dry run 会在四个目标上构建运行时、构建安装包并跑打包后的冒烟测试,这正是此前缺失的覆盖。它不包含签名步骤(那些以真实发布为前提),所以不覆盖 macOS 公证和 Windows Authenticode——workflow 注释里写明了这一点,而不是留给后人踩。

评审验证计划

如何验证

手动触发新的检查 workflow,确认它针对 main 启动了发布 workflow 的四个构建 job,每个都跑到打包后的冒烟测试,并且 publish 与 OSS 镜像两个 job 被跳过。直接触发发布 workflow 时行为应与之前完全一致——新触发器只是增加了入口,不改变任何已有入口。

要确认它确实能拦住这类回归,可以针对一个自带运行时已损坏的 ref 触发它,检查 Linux job 是在安装包构建步骤失败,而不是报成功。

证据(Before & After)

N/A——CI 配置改动,无用户可见界面。

测试平台

OS Status
🍏 macOS N/A
🪟 Windows N/A
🐧 Linux ⚠️ 未测试

两个文件都做了 YAML 合法性与格式检查;workflow 本身只能通过实际运行验证,这需要在本仓库上触发一次。

环境(可选)

N/A

风险与范围

  • 主要风险/取舍:每天四个平台 job,各约 25 分钟,其中两个跑在 macOS runner 上。是否收敛频率或平台范围,评审时可以决定;价值最高的是 Linux 和 macOS,这两个平台的打包链路最脆弱。
  • 未验证 / 不在范围内:签名构建。dry run 无法覆盖公证与 Authenticode,因此签名类回归仍然只能在发布当天暴露。要覆盖这一点,要么定时做签名构建,要么单独验证可签名性,两者都不属于本次改动。
  • 破坏性变更 / 迁移说明:无。定时调用方永远不会发布——它传入 dry run,而 publish 与镜像两个 job 本身还各有独立的条件门槛。

关联 Issue

关联 #8092

The desktop app bundles a Qwen Code runtime built from this repository's
dist tree, so a change anywhere in the CLI can break desktop packaging.
Nothing exercised that path outside a manual release dispatch: the only
job that touches it compiles the Tauri crate and runs its tests, and it
runs solely when the desktop package itself changes.

Desktop release 0.2.3-preview.0 is what that costs. A CLI dependency
added two weeks earlier had put a native library in the bundled runtime
that the AppImage bundler cannot process, and release day was the first
time anyone found out — on three of four platforms at once, with the
publish step already queued behind them.

Make the release workflow callable and add a scheduled caller that runs
it as a dry run, so a packaging regression surfaces the day it lands. A
dry run leaves the signing steps out, so it covers building the runtime,
the installers and the packaged smoke tests, but not macOS notarization
or Windows Authenticode.
@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: observed, not theoretical — and I checked the structural half rather than taking it on faith. The 0.2.3-preview.0 incident is named with a concrete failure mode (a renderer dependency put a native library into the bundled runtime that the AppImage bundler cannot process; three of four platform jobs failed on release day). Independently of that, the desktop_shell job in ci.yml says of itself "It does not need the bundled runtime, so it is cheap" — it compiles the Tauri crate and runs its release-config tests, and stops there. So a CLI-side dependency change genuinely is invisible to it, and desktop-release.yml was workflow_dispatch-only. The gap is real.

Direction: aligned. This closes a coverage hole by reusing the pipeline that already exists instead of adding a parallel one, which is the right shape: a re-implementation of the packaging steps would drift away from the very thing it is supposed to be protecting. CI-only, no user surface, so no CHANGELOG signal applies.

Size: not applicable — .github/workflows/** only, 73 added lines across 2 files, no core paths touched.

Approach: scope feels right and the diff is genuinely minimal — desktop-release.yml gains a workflow_call block mirroring the seven dispatch inputs by name and changes no existing line, so the dispatch path resolves inputs.* and its concurrency group identically to before. One thing I'd question, which you already raised yourself in Risk & Scope: the platform set. Two of the four daily jobs are macOS (arm64 + x64) on 10x-billed runners, but the bundled-runtime regression this exists to catch is arch-independent — it comes from the same dist/ either way. Linux is where the AppImage bundler actually broke and is the cheapest runner. Details in the review comment; it's your call, not a blocker.

Risk: no elevated risk signals — the Stage 1e path patterns match nothing here (no source files at all).

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题: 是已观测到的问题,不是理论性加固——而且结构性的那一半我自己去核实了,没有直接采信描述。0.2.3-preview.0 这次事故有具体的失效方式(一个渲染器依赖把原生库带进了自带运行时,AppImage 打包器无法处理,发布当天四个平台 job 中有三个失败)。除此之外,ci.yml 里的 desktop_shell job 自己的注释写着"它不需要自带运行时,所以很便宜"——它只编译 Tauri crate 并跑 release 配置测试,到此为止。所以 CLI 侧的依赖变化对它确实不可见,而 desktop-release.yml 此前只有 workflow_dispatch 一个入口。这个覆盖缺口是真实存在的。

方向: 对齐。它靠复用已有流水线来补覆盖,而不是新加一条平行的——这是对的形态:把打包步骤重新实现一遍,恰恰会与被保护的对象逐渐脱节。纯 CI 改动,无用户可见界面,因此不涉及 CHANGELOG 信号。

规模: 不适用——只涉及 .github/workflows/**,2 个文件新增 73 行,未触及核心路径。

方案: 范围合理,diff 也确实是最小的——desktop-release.yml 只新增了一个 workflow_call 块,按名字镜像了七个 dispatch 输入,没有改动任何已有行,所以 dispatch 路径解析 inputs.* 和它的并发组与之前完全一致。有一点我想提出来讨论(你在 Risk & Scope 里自己也提了):平台集合。四个每日 job 里有两个是 macOS(arm64 + x64),跑在 10 倍计费的 runner 上,但这个 workflow 要拦的自带运行时回归与架构无关——两边都来自同一份 dist/。Linux 才是 AppImage 打包器真正出问题的地方,也是最便宜的 runner。详见审查评论;这是你的判断,不是阻塞项。

风险: 无升级风险信号——Stage 1e 的路径模式在这里没有命中(完全没有源码文件)。

进入代码审查 🔍

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 806a295b2b30dfe2de23170c7a996ff787199706 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Code review

I read the whole 736-line callee, because converting a release workflow into a reusable one is exactly where a dry run can quietly stop being dry. The claims in the description hold up:

It cannot publish. publish is gated on inputs.dry_run == false, and sync-oss on github.event_name == 'workflow_dispatch' plus dry_run == false plus draft/prerelease both false. Under workflow_call with dry_run: true, both skip — no git tag, no GitHub release, no OSS mirror write. publish's contents: write is job-scoped, so the skipped job never exercises it, and the caller's own permissions: contents: read is minimal.

It holds no signing material. The caller deliberately omits secrets: inherit, so every signing secret resolves empty in the callee. Each secret-consuming step is gated on dry_run == false — updater key, macOS certificate, notarization, Windows certificate, vendor-binary signing, checksum refresh, both signature verifications. The one exception is Build desktop installers, which references TAURI_SIGNING_PRIVATE_KEY ungated but branches to --no-sign when DRY_RUN=true, so it never needs the value. That is a nice property to have by construction rather than by convention.

It cannot collide with a real release. The callee's concurrency group is desktop-release-${{ inputs.dry_run && inputs.version || 'publish' }}, so the daily run lands in desktop-release-0.0.0-packaging-check while a publish run lands in desktop-release-publish — disjoint. cancel-in-progress: false means it queues rather than cancelling anything.

It is not vacuous. This was the thing I most wanted to confirm, since a green scheduled run that tests nothing is worse than no run. Tracing the dry-run path through build: build:runtime → desktop tests → test:releasesmoke:runtimetauri build --no-sign → the per-OS smoke:packaged → artifact collection all still execute. It reaches the AppImage bundler, which is the precise step that broke 0.2.3-preview.0. 0.0.0-packaging-check also passes prepare's SemVer regex and the prerelease-suffix rule that prerelease: true imposes.

Blocker — Lint & Static is red, and this PR caused it

##[error].github/workflows/desktop-packaging-check.yml has no entry in
.github/workflows/.size-baseline. Add '1634 desktop-packaging-check.yml'
so its growth is tracked.

The workflow-size ratchet requires a baseline entry for every file under .github/workflows/, and the new workflow has none. This is not stale-main noise: check-workflow-size.sh downgrades to a ::warning:: when a file is byte-identical to the PR's base, and it emitted ::error::, with WORKFLOW_SIZE_BASE_SHA resolved to 0b2ba2d2. The PR added the file, so the PR owns the entry.

One line fixes it — add 1634 desktop-packaging-check.yml to .github/workflows/.size-baseline, alphabetically immediately before the existing desktop-release.yml entry. I checked whether the callee needs a bump too and it does not: it grew from 31677 to 32378 bytes, 701 over, inside the 4096-byte GROWTH_ALLOWANCE. There is no vitest mirror of the baseline to keep in sync, so that single line is the complete fix.

Non-blocking

sync-oss is now unreachable through the new entry point. Its if pins github.event_name == 'workflow_dispatch', which is false under workflow_call. Harmless today — the only caller passes dry_run: true, which already skips it — but this diff is what makes workflow_call a supported way in. A future caller doing a real publish that way would create the GitHub release and silently skip the OSS mirror, with nothing objecting. Worth either a comment on that job saying it is dispatch-only by design, or dropping the event-name clause now that dry_run/draft/prerelease already gate it.

Platform set. As raised in Stage 1: the bundled-runtime regression this exists to catch is arch-independent, so macOS arm64 and x64 are largely re-testing the same dist/ on the two most expensive runners. Linux daily plus macOS weekly — or one macOS arch daily — would keep most of the coverage at a fraction of the minutes. Also minor: four artifacts a day at retention-days: 14 means roughly 56 live artifact sets of installer output at steady state.

Test evidence

Unattended CI run — I did not build or execute anything from this PR. The evidence below is the PR's own check-runs on the reviewed commit, read through the API.

Lint & Static (ubuntu-latest, Node 22.x) is failure, caused by this PR, log excerpt quoted above. Desktop Shell (ubuntu-22.04) and Desktop Shell (windows-2022) are success, but note they are not evidence for this change — that job compiles the Tauri crate and explicitly does not build the bundled runtime. Test (ubuntu-latest, Node 22.x) and Integration Tests (no-AK, No Sandbox) were still in progress at review time; the remainder are skipped bot-orchestration jobs. Nothing here is pre-existing infra noise.

Final CI results for 806a295 (auto-updated by the triage finalize job after CI completed):

Check Conclusion
Lint & Static (ubuntu-latest, Node 22.x) ❌ failure
Test (ubuntu-latest, Node 22.x) ❌ failure
web-shell E2E Smoke (ubuntu-latest, Node 22.x) 🚫 cancelled
Classify PR ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Integration Tests (no-AK, No Sandbox) ✅ success

One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。

Not verified: that the scheduled run actually goes green end to end — and this one cannot be settled by reading. A manual workflow_dispatch dry run receives repo secrets; the scheduled/workflow_call run receives none, and that absence is new. tauri.conf.json sets createUpdaterArtifacts: true and the artifact-collection step expects .sig files, so the combination "updater artifacts enabled + empty signing key + --no-sign" has never run without secrets before. If Tauri objects to a missing updater key even under --no-sign, the scheduled run fails where a manual dry run passed. The remedy is the one already in your test plan: dispatch the new workflow once against main before merging. Neither @qwen-code /verify nor @qwen-code /tmux can settle this — one is an A/B harness against the base build, the other drives the TUI, and neither runs a Tauri packaging workflow — so I am naming the dispatch rather than a lane that would produce noise.

中文说明

代码审查

我把 736 行的被调用 workflow 完整读了一遍,因为把发布 workflow 改成可复用正是"dry run 悄悄变得不 dry"的地方。描述里的几条声明都成立:

它不可能发布。 publish 的门槛是 inputs.dry_run == falsesync-oss 的门槛是 github.event_name == 'workflow_dispatch' 加上 dry_run == false 再加 draft/prerelease 同时为 false。在 workflow_calldry_run: true 的情况下两个 job 都跳过——不建 git tag、不建 GitHub release、不写 OSS 镜像。publishcontents: write 是 job 级的,跳过后根本不会用到,而调用方自己的 permissions: contents: read 也是最小权限。

它不持有任何签名材料。 调用方刻意没有写 secrets: inherit,所以被调用方里每个签名 secret 都解析为空。所有消费 secret 的步骤都 gated 在 dry_run == false 上——updater key、macOS 证书、公证、Windows 证书、vendor 二进制签名、校验和刷新、两个签名校验。唯一的例外是 Build desktop installers,它未 gated 地引用了 TAURI_SIGNING_PRIVATE_KEY,但在 DRY_RUN=true 时走 --no-sign 分支,因此并不需要那个值。这个性质是靠结构保证的,而不是靠约定,很好。

它不会与真实发布撞车。 被调用方的并发组是 desktop-release-${{ inputs.dry_run && inputs.version || 'publish' }},所以每日运行落在 desktop-release-0.0.0-packaging-check,而发布运行落在 desktop-release-publish——互不相交。cancel-in-progress: false 意味着它排队,而不会取消任何东西。

它不是空转。 这是我最想确认的一点,因为一个"绿了但什么都没测"的定时运行比没有运行更糟。沿着 build 的 dry-run 路径追下去:build:runtime → desktop tests → test:releasesmoke:runtimetauri build --no-sign → 各平台的 smoke:packaged → 产物收集,全都仍然执行。它会走到 AppImage 打包器,也就是 0.2.3-preview.0 当时出问题的确切步骤。0.0.0-packaging-check 也能通过 prepare 的 SemVer 正则,以及 prerelease: true 所要求的预发布后缀规则。

阻塞项——Lint & Static 是红的,而且是本 PR 造成的

上方引用了原始日志。workflow 体积棘轮要求 .github/workflows/ 下每个文件都在 .size-baseline 里有记录,而新增的 workflow 没有。这不是 main 侧的陈旧噪声:check-workflow-size.sh 在文件与本 PR base 逐字节相同时会降级成 ::warning::,而这里输出的是 ::error::,且 WORKFLOW_SIZE_BASE_SHA 已解析为 0b2ba2d2。文件是本 PR 新增的,所以这条记录归本 PR。

一行即可修复——在 .github/workflows/.size-baseline 中加入 1634 desktop-packaging-check.yml,按字母序正好放在已有的 desktop-release.yml 那条前面。我也核对了被调用文件是否需要一起改:不需要,它从 31677 增长到 32378 字节,超出 701,在 4096 字节的 GROWTH_ALLOWANCE 之内。baseline 没有需要同步的 vitest 镜像,所以这一行就是完整的修复。

非阻塞

sync-oss 现在无法通过新入口到达。 它的 if 钉死了 github.event_name == 'workflow_dispatch',在 workflow_call 下为 false。今天无害——唯一的调用方传的是 dry_run: true,本来就会跳过——但正是这个 diff 让 workflow_call 成了一个受支持的入口。将来若有调用方通过它做真实发布,会创建 GitHub release 却静默跳过 OSS 镜像,而且没有任何东西会报错。建议要么在该 job 上加注释说明它是刻意仅限 dispatch,要么直接去掉 event-name 这一条——dry_run/draft/prerelease 已经足够把关了。

平台集合。 如 Stage 1 所述:这个 workflow 要拦的自带运行时回归与架构无关,所以 macOS arm64 和 x64 基本上是在两个最贵的 runner 上重复测同一份 dist/。Linux 每日 + macOS 每周,或者每日只跑一个 macOS 架构,能用少得多的分钟数保住大部分覆盖。另有一点小的:每天四个产物、retention-days: 14,稳态下大约会同时存在 56 组安装包产物。

测试证据

无人值守 CI 运行——我没有构建或执行本 PR 的任何代码。以下证据是所审查 commit 上本 PR 自己的 check-run,通过 API 读取。

Lint & Staticfailure,由本 PR 造成,日志摘录见上。Desktop Shell 两个平台为 success,但请注意它们不构成本次改动的证据——那个 job 只编译 Tauri crate,明确不构建自带运行时。Test (ubuntu-latest)Integration Tests (no-AK, No Sandbox) 在审查时仍在进行中;其余为跳过的 bot 编排 job。这里没有任何一项属于既有的基础设施噪声。上方的表格即为真实 check 名称与结论,由 finalize 任务在 CI 跑完后就地更新。

未验证:定时运行是否真的能端到端跑绿——而这一条无法靠阅读确定。手动 workflow_dispatch 的 dry run 能拿到仓库 secrets,而定时的 workflow_call 运行一个都拿不到,这个"缺失"是新出现的。tauri.conf.jsoncreateUpdaterArtifacts: true,且产物收集步骤期待 .sig 文件,所以"启用 updater 产物 + 签名 key 为空 + --no-sign"这个组合此前从未在无 secrets 的情况下跑过。如果 Tauri 即便在 --no-sign 下也要求 updater key 存在,定时运行就会在手动 dry run 能过的地方失败。补救办法正是你测试计划里已经写的那条:合并前针对 main 手动触发一次新 workflow。@qwen-code /verify@qwen-code /tmux 都解决不了这个问题——前者是针对 base 构建的 A/B 装置,后者驱动 TUI,两者都不会跑 Tauri 打包 workflow——所以我点名的是手动触发,而不是一条只会产生噪声的 lane。

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 806a295b2b30dfe2de23170c7a996ff787199706 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 2/5 — the design is sound and I could verify nearly all of it statically, but it cannot merge as-is: it trips the repo's own workflow-size ratchet and leaves Lint & Static red.

Stepping back: this is the rare CI PR where the motivation survives scrutiny. I went in expecting to argue that a daily four-platform packaging run is a lot of runner time for a hypothetical, and instead found a named incident plus a structural gap I could confirm from the existing job's own comment — desktop_shell says it does not need the bundled runtime, which is precisely the thing that broke. The problem is real and the evidence for it is in the repo, not just in the description.

The approach also matches what I would have proposed from the title and motivation alone: make the release workflow callable, add a thin scheduled caller that passes a dry run, and — the part that matters — do it without secrets: inherit, so the daily job structurally cannot hold signing material. Reusing the real pipeline rather than writing a lookalike is the right call, because a lookalike drifts from the thing it protects and eventually goes green while the release breaks. I traced the dry-run path to confirm it is not vacuous, and it reaches the AppImage bundler and the packaged smoke tests. That was my main worry going in and it is resolved.

So the block is narrow and mechanical. One line in .size-baseline — the gate prints the exact entry it wants — and the red check clears. I would not want that fix to be optional: the ratchet exists so workflow growth becomes a reviewed line, and a new workflow that skips it is exactly the drift the gate was written to stop.

Two things I'd genuinely like a human decision on, neither blocking:

  1. The platform set. The regression this exists to catch is arch-independent, so macOS arm64 and x64 mostly re-test the same dist/ on the two most expensive runners. You flagged this yourself as a review-time call; my read is Linux daily plus macOS on a longer interval keeps most of the value. Your call to make, and it can land as a follow-up.
  2. sync-oss is now dispatch-only by accident rather than by design. Its if pins github.event_name == 'workflow_dispatch', which this diff makes false for a newly-supported entry point. Harmless today; a trap for the next caller that publishes through workflow_call.

And one thing nobody can settle by reading: the scheduled run receives no secrets where a manual dry run receives all of them, and createUpdaterArtifacts: true has never been exercised with an empty signing key. Dispatch the new workflow once against main before merging — that single run resolves it, and it is also the only real proof that the four jobs reach their smoke tests.

Requesting changes for the baseline entry; everything else is discussion. @yiliang114

中文说明

Confidence: 2/5 —— 设计是站得住的,绝大部分我都能靠静态审查确认,但它现在不能合并:它触发了本仓库自己的 workflow 体积棘轮,让 Lint & Static 变红。

退一步看:这是少见的、动机经得起推敲的 CI PR。我一开始是准备论证"每天跑四个平台的打包,为的是一个假想问题,代价太大",结果发现既有具名事故,又有一个我能从既有 job 自己的注释里确认的结构性缺口——desktop_shell 写明它不需要自带运行时,而自带运行时恰恰就是当时出问题的东西。问题是真实的,而且证据在仓库里,不只在描述里。

方案也与我仅凭标题和动机就会提出的做法一致:把发布 workflow 改成可被调用,加一个很薄的定时调用方传入 dry run,并且——最关键的一点——secrets: inherit,于是这个每日 job 在结构上就不可能持有签名材料。复用真实流水线而不是写一个相似品是对的,因为相似品会与被保护对象逐渐脱节,最终在发布真正坏掉时自己报绿。我沿着 dry-run 路径追了一遍以确认它不是空转,它确实走到了 AppImage 打包器和打包后的冒烟测试。这是我进来时最主要的担心,现在已经排除。

所以阻塞点很窄、也很机械。.size-baseline 里加一行——gate 已经把它想要的记录原文打印出来了——红检查就会清掉。我不希望这个修复被当作可选:棘轮存在的意义就是让 workflow 的增长变成一行被审查的记录,而一个绕过它的新 workflow 正是这道 gate 当初要拦住的漂移。

有两件事我确实希望由人来决定,都不是阻塞项:

  1. 平台集合。 这个 workflow 要拦的回归与架构无关,所以 macOS arm64 和 x64 基本是在两个最贵的 runner 上重测同一份 dist/。你自己也把这标为评审时可以决定的一项;我的判断是 Linux 每日 + macOS 拉长周期,能保住大部分价值。这由你定,也可以作为后续改动落地。
  2. sync-oss 现在是"意外地"仅限 dispatch,而不是"刻意地"。 它的 if 钉死了 github.event_name == 'workflow_dispatch',而这个 diff 让一个新受支持的入口下该条件为 false。今天无害;但对下一个通过 workflow_call 做发布的调用方是个陷阱。

还有一件事是谁都无法靠阅读确定的:定时运行拿到的 secrets 为,而手动 dry run 能拿到全部,且 createUpdaterArtifacts: true 从未在签名 key 为空的情况下跑过。合并前针对 main 手动触发一次新 workflow——这一次运行就能定论,也是四个 job 确实跑到冒烟测试的唯一真实证据。

因为 baseline 这一条记录而 request changes;其余都是讨论。@yiliang114

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 806a295b2b30dfe2de23170c7a996ff787199706 · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs one line before this can merge — Lint & Static is red on a gate this PR trips:

##[error].github/workflows/desktop-packaging-check.yml has no entry in
.github/workflows/.size-baseline. Add '1634 desktop-packaging-check.yml'
so its growth is tracked.

Add 1634 desktop-packaging-check.yml to .github/workflows/.size-baseline (alphabetically just before desktop-release.yml). That is the whole fix — desktop-release.yml grew 701 bytes, inside the 4096-byte allowance, so it needs no bump.

Everything else is discussion, not blocking, and it is in my notes above. The design itself I could verify statically and it holds: the dry run cannot publish, holds no signing secrets, cannot collide with a real release's concurrency group, and is not vacuous — it does reach the AppImage bundler and the packaged smoke tests. The one thing worth doing before merge is dispatching the new workflow once against main, since a scheduled run receives no secrets where a manual dry run receives all of them, and createUpdaterArtifacts: true has never been exercised with an empty signing key.

中文说明

合并前需要加一行——Lint & Static 是红的,触发它的正是本 PR:原始报错见上。

.github/workflows/.size-baseline 中加入 1634 desktop-packaging-check.yml(按字母序放在 desktop-release.yml 前面)。这就是完整的修复——desktop-release.yml 增长了 701 字节,在 4096 字节的额度之内,因此不需要一起改。

其余都是讨论、不阻塞,详见上方评论。设计本身我能静态确认,而且成立:dry run 不可能发布、不持有签名 secrets、不会与真实发布的并发组撞车,也不是空转——它确实会走到 AppImage 打包器和打包后的冒烟测试。合并前值得做的一件事是针对 main 手动触发一次新 workflow,因为定时运行拿不到任何 secrets,而手动 dry run 能拿到全部,且 createUpdaterArtifacts: true 从未在签名 key 为空的情况下跑过。

Qwen Code · qwen3.8-max-2026-09-02

The workflow size ratchet requires every workflow file to carry a
recorded byte size, and a file it has never seen fails both the lint
gate and its vitest counterpart. Register the new check at its current
size; desktop-release.yml stays inside its growth allowance and needs no
bump.

Merging main also brings in the macOS and Linux packaging fixes, so this
branch now builds on the state the scheduled dry run will actually meet.
@yiliang114

Copy link
Copy Markdown
Collaborator Author

Fixed — 1634 desktop-packaging-check.yml is now in .size-baseline, alphabetically before desktop-release.yml, and main is merged in so this branch builds on the packaging fixes it will actually meet. desktop-release.yml is 33458 bytes against a 31677 baseline, 1781 inside the 4096 allowance, so it stays unbumped as you said.

On the point you flagged as worth checking before merge — createUpdaterArtifacts: true with an empty signing key — I verified it statically rather than by dispatching, because the new workflow cannot be dispatched until it is on the default branch. The pinned CLI (@tauri-apps/cli 2.11.4) carries both the flag and the branch it takes: NO_SIGN Skip code signing when bundling the app, and the message Updater signing is skipped due to --no-sign flag. alongside the error it replaces, A public key has been found, but no private key. So the dry run's --no-sign path skips updater signing outright instead of reaching the missing-key check; the updater artifacts are still bundled, only unsigned.

That leaves one thing genuinely unexercised until this merges: a run of the reusable workflow with no secrets passed at all. Dispatching the check once against main after merge is still the right first move, and if that surfaces anything the fix belongs in a follow-up rather than holding this.

中文说明

已修复——1634 desktop-packaging-check.yml 已按字母序加入 .size-baseline,位于 desktop-release.yml 之前,同时合入了 main,因此该分支现在构建在定时任务将真正面对的打包修复之上。desktop-release.yml 当前 33458 字节,基线 31677,增长 1781 字节,在 4096 额度之内,按你说的不做 bump。

关于你提出的合并前值得确认的那一点——createUpdaterArtifacts: true 且签名 key 为空——我用静态方式做了验证,而不是触发一次,因为新 workflow 在进入默认分支之前根本无法被 dispatch。锁定的 CLI(@tauri-apps/cli 2.11.4)同时带有该 flag 和它对应的分支逻辑:NO_SIGN Skip code signing when bundling the app,以及 Updater signing is skipped due to --no-sign flag. 这条消息,与它所替代的报错 A public key has been found, but no private key. 并存。因此 dry run 的 --no-sign 路径会直接跳过更新器签名,而不会走到缺少私钥的检查;更新器产物仍会生成,只是未签名。

因此在合并之前真正无法验证的只剩一件事:以完全不传 secrets 的方式运行这个可复用 workflow。合并后针对 main 触发一次检查仍然是正确的第一步;若届时暴露问题,修复应放在后续 PR,而不是卡住这一个。

@qwen-code-dev-bot qwen-code-dev-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved at head 9c6c2492.

Required CI is green here — Test (ubuntu-latest, Node 22.x), Lint & Static, Integration Tests (no-AK, No Sandbox) and web-shell E2E Smoke plus both Desktop Shell lanes completed successfully; only review-pr is still running.

The one blocking point from the earlier REQUEST_CHANGES is fixed: .github/workflows/.size-baseline now carries 1634 desktop-packaging-check.yml, which is byte-exact against the new file at this head (wc -c on the blob at this commit returns 1634), placed alphabetically before desktop-release.yml; the growth on desktop-release.yml stays inside the 4096-byte allowance, which is why Lint & Static is now green.

I re-derived the safety properties the design leans on rather than trusting the comment:

  • Every condition reads inputs.*, never github.event.inputs.*, so a workflow_call invocation sees the same values a dispatch does — and the workflow_call input set is identical to workflow_dispatch (all seven), with the caller supplying each one, so nothing can arrive silently undefined.
  • It cannot publish: the publish job is gated on inputs.dry_run == false, and the downstream job additionally requires github.event_name == 'workflow_dispatch', which a called workflow never satisfies.
  • It cannot collide with a real release: the concurrency group is desktop-release-${{ inputs.dry_run && inputs.version || 'publish' }}, so the check lands in desktop-release-0.0.0-packaging-check while a publish uses desktop-release-publish.
  • Not holding secrets is genuine, not just a claim: the caller omits secrets: inherit, and 'Build desktop installers' appends --no-sign when DRY_RUN is true, so createUpdaterArtifacts: true in tauri.conf.json does not need TAURI_SIGNING_PRIVATE_KEY on this path; the key requirement itself sits behind if: inputs.dry_run == false. Forks are excluded by the repository guard on the calling job.

No new Critical found. Two things I could not verify from the diff, both already flagged by the author: a workflow can only really be checked by running it, so the first manual dispatch against main is the proof this is not vacuous, and the cost is four ~25-minute jobs daily with two on macOS runners.

Note for whoever merges: my approval does not clear GitHub's aggregate verdict while the older CHANGES_REQUESTED (filed at 806a295b) stands unresolved — it needs a re-run of the reviewer at this head.

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blocking findings. Approval blockers: none.

Checked:

  • Input contract (caller → callee): all 7 required workflow_call inputs declared in desktop-release.yml are passed by desktop-packaging-check.yml; types match (boolean inputs passed as literal true/false).
  • Version validation: 0.0.0-packaging-check passes the semver regex in the prepare job (^[0-9]+\.[0-9]+\.[0-9]+([+-][0-9A-Za-z.-]+)?$) and the prerelease-suffix check (^[0-9]+\.[0-9]+\.[0-9]+-).
  • Signing-step isolation: all signing/certificate steps in desktop-release.yml are gated on inputs.dry_run == false (lines 251, 263, 299, 331, 384, 437). No signing step runs when dry_run: true.
  • publish job gated on inputs.dry_run == false — will not run.
  • sync-oss job gated on github.event_name == 'workflow_dispatch' — will not fire when called via workflow_call.
  • Smoke tests run unconditionally on all three platforms — consistent with PR description.
  • Schedule fallback: inputs.qwen_code_ref || 'main' correctly handles empty input when the trigger is schedule.
  • secrets: inherit omitted: correct — no signing step runs during a dry run, so no secrets are needed.
  • .size-baseline entry: 1634 desktop-packaging-check.yml is present in the diff, alphabetically ordered before desktop-release.yml.

Cross-check: One prior review (qwen-code-ci-bot, CHANGES_REQUESTED) requested the .size-baseline entry — that entry is now in the diff, resolving the blocker. The bot also noted that a manual dispatch before merge would confirm the createUpdaterArtifacts: true path with an empty signing key; that is a testing suggestion, not a code defect.

Not covered: execution rung (CI-only change; no local runtime to drive). This does not block approval — the workflow's correctness is verifiable from the static YAML contract, and the execution gap was already disclosed by the PR author.

Reviewed with AI assistance.

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.23.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants