如何用 5 条命令完成桌面客户端白标构建:Qwen Code 换肤打包完整实战指南
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
Qwen Code 的 desktop-shell 内置了 Desktop Brand Builder 技能:你只需一份brand.json和一个零依赖脚本,就能完成 Tauri 桌面壳的白标构建,产出 DMG、NSIS、AppImage、deb 安装包。本文讲清输入规则、三层补丁机制与交叉编译的完整链路。
一图看懂白标构建全链路
整条链路只有一句话:一份brand.json进,一个零依赖 Node 脚本跑三层补丁,build:runtime按目标平台捆绑运行时,最后tauri build出多平台安装包。
brand.json (brandId + logo) | v brand-create.mjs ── 三层补丁:配置层 tauri.conf.json / 资源层 icons / 界面层 bootstrap | v build:runtime (QWEN_DESKTOP_TARGET 决定捆绑哪个平台的 Node 运行时) | v npx tauri build ──> dmg / nsis / appimage / deb桌面壳启动后呈现的就是 Web Shell 界面,换肤替换的正是这类界面中的品牌标识:
Qwen Code 的桌面端已从早期的 Electron 实现迁到 Tauri,packages/desktop-shell是唯一桌面实现,品牌化挂载点也就收敛为三处。
前置准备:环境与构建目录约定
开始动手前,先确认下表里的依赖都已就位:
| 需要什么 | 在哪找 | 说明 |
|---|---|---|
| Node ≥ 18 | node -v确认 | 品牌脚本零依赖,纯 Node 即可运行 |
| Tauri CLI v2 | desktop-shell 的 package.json 已声明@tauri-apps/cli | 执行npm install后由npx tauri build调用 |
| 独立构建克隆 | 每个品牌自己克隆一份 | 工作仓库保持干净;每品牌一个全新克隆,检出brand-<brandId>分支 |
| 仓库只读约束 | 本仓库 | 所有修改只发生在构建克隆内,严禁改动源仓库任何文件 |
技能说明与完整流程定义在 SKILL.md,建议先读一遍再动手。克隆或检出分支失败时必须立即停止并报告,不得当作分支已创建而继续。
最小输入清单:快速上手命令链
必填项只有两个。缺失时只询问一次,不要反复追问;字段齐全后直接进入构建,无确认步骤。
| 项 | 要求 |
|---|---|
brandId | 必须匹配^[a-z][a-z0-9-]*$:小写字母开头,只允许小写字母、数字、短横线 |
logo | 本地路径且文件必须存在;扩展名限.png/.jpg/.jpeg/.svg/.ico/.webp,建议 1024px 以上方形 PNG |
可选项全部用于覆盖自动派生值,不填就走默认:
| 项 | 用途 |
|---|---|
website | 官网地址,用于推导 appId |
appName/appId/artifactPrefix | 覆盖对应的派生值 |
updaterEndpoints | 你的更新源端点数组;空数组表示禁用应用内更新 |
updaterPubkey | base64 公钥;updaterEndpoints非空时必填 |
target | mac/win/linux/all |
下面这条命令链按顺序可直接复制(以nova-lab为例),覆盖克隆 → 写配置 → 装依赖 → 跑脚本 → 打包:
# 1) 每品牌独立克隆 BUILD_ROOT="$PWD/brand-builds/nova-lab-$(date +%s)" mkdir -p "$BUILD_ROOT" git clone --branch main --single-branch \ https://gitcode.com/GitHub_Trending/qw/qwen-code \ "$BUILD_ROOT/qwen-code" cd "$BUILD_ROOT/qwen-code" git checkout -B brand-nova-lab origin/main{ "brandId": "nova-lab", "logo": "/absolute/path/to/logo.png", "website": "https://novahq.io" }# 2) 装依赖:根部(build:runtime 会调用 cross-env 等)与 desktop-shell 都要装 npm install cd packages/desktop-shell && npm install --workspaces=false && cd ../.. # 3) 运行品牌脚本(路径传绝对路径) node packages/desktop-shell/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs \ --shell-root "$BUILD_ROOT/qwen-code/packages/desktop-shell" \ --config "$BUILD_ROOT/brand.json" # 4) 打包(宿主平台) cd packages/desktop-shell npm run build:runtime --workspaces=false npx tauri build脚本跑完会打印一份 JSON 报告(brandId、appName、appId、artifactPrefix、updaterEndpoints、被补丁的tauriConfig路径、icons结果、bootstrapPatched文件列表),对照检查后再打包。
缺省字段由脚本按确定性规则推导,无需你补充。以nova-lab走一遍推导链:
| 字段 | 推导规则 | nova-lab 示例 |
|---|---|---|
appName | 按短横线分段首字母大写,空格连接 | Nova Lab |
artifactPrefix | 同样处理,但用短横线连接 | Nova-Lab |
appId | websitehost 去掉www.后反转标签、追加.desktop(host 需至少两段) | io.novahq.desktop |
appId回退 | host 不合法时用app.<brandId>.desktop | app.nova-lab.desktop |
updaterEndpoints | 默认空数组,禁用应用内更新 | [] |
细节:ai、api、cli、ide、sdk、ui、url这些段会被整体大写,所以acme-cli会得到Acme CLI而非Acme Cli。
脚本到底改了什么:三层补丁拆解
brand-create.mjs 的全部工作就是三层补丁,逐层看它动了什么、防住了什么。
配置层:tauri.conf.json 的四个字段
tauri.conf.json 被改写四处:productName换成品牌名、identifier换成 appId、bundle.shortDescription换成「<品牌名> desktop shell for the Qwen Code Web Shell」、plugins.updater.endpoints换成品牌端点。端点为空时还会连带把bundle.createUpdaterArtifacts置为false,并把plugins.updater.pubkey清空。
防御设计一:pubkey 置空字符串而不是删字段。原因写在源码注释里——tauri-plugin-updater的 schema 声明pubkey: String且没有 serde 默认值,直接删字段会让应用启动时反序列化失败;而端点同为空时,空字符串不会触发任何更新检查,无害。
防御设计二:失败关闭(fail closed)。若你在 brand.json 里提供了更新配置(端点非空或给了公钥),但目标tauri.conf.json缺少plugins.updater段(常见于手工改过的 shell-root),脚本在写入任何文件之前直接报错。否则它会悄悄丢弃已校验的更新配置,交付一个永远无法更新的品牌包。
资源层:图标全家桶重生成
脚本调用 Tauri CLI 的icon子命令,从你的 logo 重新生成src-tauri/icons/下整套图标。调用方式值得注意:脚本先用require.resolve('@tauri-apps/cli/tauri.js')解析 CLI 入口,再以spawnSync让 logo 路径作为普通 argv 元素传递,全程不经过命令解释器——路径里的$(cmd)或反引号无法构成 shell 注入。
CLI 失败时按 logo 格式降级:PNG 则只复制覆盖icons/icon.png并告警「其余尺寸仍是旧 logo」;非 PNG 则不动任何图标,提示转成 PNG 后手动重跑。
界面层:bootstrap 启动 UI 换肤
logo 被复制为bootstrap/brand-logo<扩展名>,然后 bootstrap/index.html 与bootstrap/bootstrap.js中的Qwen Code字面量全部替换为品牌名,qwen-code-logo.svg引用换成品牌 logo 文件名。
防御设计三:JS/HTML 双语境转义。两个文件里品牌名所处的上下文不同:bootstrap.js中是单引号字符串字面量,脚本先用JSON.stringify转义反斜杠、换行等控制字符,再剥掉外层双引号并补转单引号——只转单引号不够,以反斜杠结尾的名字会「逃逸」掉结束引号;index.html中是文本与双引号属性,按&→<→>→"→'顺序做实体转义,先转&避免新实体被二次转义。替换全部使用函数式 replacer,防止品牌名里的$&之类被展开为匹配文本。
最后说回开头:detectAlreadyBranded单次使用守卫。脚本启动时检查productName是否已偏离默认值Qwen Code Desktop,是则拒绝运行——因为 bootstrap 补丁依赖原始字面量,pubkey/endpoints 变更也无法回滚,脚本天然不可逆。同理,若你的appName恰好等于Qwen Code Desktop,加载期就会报错,否则第二次运行会在文件中重复拼接品牌串。
进阶与易踩坑:交叉编译、密钥配对与产物定位
四个高频坑,按「现象 → 原因 → 正确做法」排查。
交叉编译启动即报 exec format error
- 现象:目标平台装机后应用启动即崩。
- 原因:prepare-runtime.js 按
QWEN_DESKTOP_TARGET(默认宿主平台)下载并捆绑对应平台的 Node 运行时,漏设就会把错误架构的二进制打进包。 - 做法:每个
tauri build --target之前,带着环境变量重跑build:runtime,如QWEN_DESKTOP_TARGET=aarch64-apple-darwin npm run build:runtime --workspaces=false。不支持的目标值会直接抛错,不要硬试。
应用内更新永远校验失败
- 现象:更新检查每次都签名校验失败。
- 原因:更新器用配置里的公钥验签;用官方公钥配自建源,或干脆漏了公钥,必然不匹配。脚本在加载期就强制端点非空必须有
updaterPubkey。 - 做法:
npx @tauri-apps/cli signer generate -w ~/.tauri/my-brand.key生成独立密钥对,.key作为TAURI_SIGNING_PRIVATE_KEY放入你的构建 CI,.pub内容贴进 brand.json。
找不到打包产物
- 现象:打包结束但
bundle/下看不到文件。 - 原因:宿主目标与指定 triple 的产物路径不同。
- 做法:宿主目标看
packages/desktop-shell/src-tauri/target/release/bundle/;指定 triple 看src-tauri/target/<triple>/release/bundle/,子目录dmg/、nsis/、appimage/、deb/与配置中的 targets 一一对应。
品牌包与官方更新源串线⚠️
- 现象:品牌客户端开始轮询官方更新源,或被官方源推送更新。
- 原因:端点默认沿用官方配置,违反隔离约定。
- 做法:
updaterEndpoints默认必须为空;只有品牌方自建了更新源才填,且必须搭配自备公钥与签名密钥,严禁复用上游任何凭据。
打包后 5 步验证与排障速查
验证按顺序执行:
- 确认产物出现在
src-tauri/target/release/bundle/(交叉编译时为src-tauri/target/<triple>/release/bundle/)的dmg/、nsis/、appimage/或deb/中; - 对每个产物计算
sha256sum(macOS 用shasum -a 256); - macOS 上对生成的 DMG 执行
hdiutil verify; - 只有文件真实存在,才能声称产出了对应平台安装包;
- 向使用方汇报:产物路径、SHA-256、应用名、appId 与构建目录。
常见失败场景的处理:
| 场景 | 处理方式 |
|---|---|
brandId非法 | 展示正则^[a-z][a-z0-9-]*$,请对方修正 |
| logo 缺失或扩展名不符 | 要求提供合法本地路径与支持格式 |
| 品牌脚本缺失 | 报告packages/desktop-shell/.agents/skills/desktop-brand-builder/scripts/brand-create.mjs不存在,附预期命令 |
| shell-root 已品牌化 | 脚本拒绝运行,从全新克隆重新开始 |
| 构建中途失败 | 保留构建目录,返回最后的有用错误行与完整日志路径或失败命令 |
两条硬性约定 ⚠️:失败时不得删除构建目录,它用于事后排查而非重试品牌步骤;严禁在同一克隆中重跑brand-create,脚本是单次使用的——品牌配置有误时,丢弃该克隆从头再来。
收尾
最小命令链:一份brand.json+ 一次brand-create.mjs+ 每目标一次build:runtime+npx tauri build。它适合团队以自有品牌分发 Qwen Code 桌面壳的白标场景。边界要记牢:脚本单次使用,重跑须用新克隆;签名与更新源必须完全自建,严禁与官方发布体系混用。
【免费下载链接】qwen-codeAn open-source AI coding agent that lives in your terminal.项目地址: https://gitcode.com/GitHub_Trending/qw/qwen-code
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考