From d987e1c04f54e7c862e7413bec8f11c77a930714 Mon Sep 17 00:00:00 2001 From: haa Date: Thu, 8 Oct 2026 10:04:53 +0200 Subject: [PATCH 1/9] docs: 3-platform release matrix brief --- .loom/design/task-brief-release-matrix.md | 180 ++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 .loom/design/task-brief-release-matrix.md diff --git a/.loom/design/task-brief-release-matrix.md b/.loom/design/task-brief-release-matrix.md new file mode 100644 index 0000000..9f2ea8b --- /dev/null +++ b/.loom/design/task-brief-release-matrix.md @@ -0,0 +1,180 @@ +# Devin Brief: Release 流水线扩到 Linux + macOS 三平台 + +## 项目 +`/home/haa/sites/promptkey`,当前 `master` HEAD `5c4efc3`(含 2.0.5 bump + CHANGELOG)。新建分支 `feat/release-matrix-linux-macos`。 + +## 背景 + +用户要求(原话):「可能要发这三版本哦」—— **Windows / Linux / macOS 三个平台的安装包都要从 Release 出**。 + +### 现状(我已核实) + +- `.github/workflows/release.yml` **只有 `build-windows` 一个 job**(`runs-on: windows-latest`),产出 `tauri build` 的 NSIS `.exe` + `.msi` +- `.github/workflows/ci.yml` 的 `rust-unix` job 已用 `matrix.os: [ubuntu-latest, macos-latest]` 跑 `cargo check --all-targets`,**说明两个平台的代码能编译**(这是 2.0.5 跨平台 trait 重构后的既有资产) +- `tauri.conf.json:18` → `"targets": "all"`(全平台目标) +- 图标资产齐全:`icons/icon.icns`(345KB)、`icon.ico`、`128x128.png`、`128x128@2x.png`、`32x32.png`、`icon.png` +- 依赖已按平台隔离:`tauri-plugin-autostart`(三平台 autostart)、macOS 专属 `macos-private-api` + `image-png` feature +- **当前 `master` 版本号是 2.0.5,HEAD 已打 tag `v2.0.5`(Windows Release 已发布成功)** + +### 硬约束:Windows 绝不能再炸 + +2.0.5 的 Windows Release 是刚发布的(MSI 4988928B + exe 3678036B,均成功)。**你在改 workflow 时,Windows job 的现有步骤要保留原样**(含 capabilities 校验、bundle 校验、NSIS/MSI 上传),只做**新增**,不要重构 Windows job。 + +--- + +# 任务 + +## Task 1: release.yml 增加 Linux + macOS job + +新增两个 job,与 `build-windows` **并列**(不是串联,任一平台失败不影响其他平台发布)。 + +### Linux job + +- `runs-on: ubuntu-latest` +- **Tauri 2 Linux 系统依赖必须装**(否则 `tauri build` 必炸): + ```bash + sudo apt-get update + sudo apt-get install -y libwebkit2gtk-4.1-dev \ + libgtk-3-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + patchelf + ``` + **具体包名你要自己核实**(这是最容易踩的坑:Tauri 2 需要 webkit2gtk **4.1**,不是 4.0),并且加一步 `pkg-config --exists webkit2gtk-4.1` 之类的**前置校验**,依赖缺失时立刻失败并给出清晰报错,而不是等到 bundle 阶段才炸 +- Rust `stable`(照抄 Windows job 的工具链安装方式) +- 装 Node LTS + `@tauri-apps/cli@2.12.0`(与 Windows job 同版本) +- 编译门 `cargo check --workspace --all-targets` +- `tauri build --verbose` +- 产物:`target/release/bundle/deb/*.deb` + `target/release/bundle/appimage/*.AppImage` +- **校验**:两个产物至少一个存在,否则 throw +- 上传到 Release(`softprops/action-gh-release@v2`,**与 Windows 共用同一个 Release**,追加 asset 而非新建) +- **注意**:Linux 构建时 `custom-protocol` feature 的处理 —— `tauri build` release 会自动加 `--features custom-protocol`,确认这是对的 + +### macOS job + +- `runs-on: macos-latest` +- Rust stable + Node LTS + tauri CLI 2.12.0 +- **目标架构要论证**:`aarch64-apple-darwin` / `x86_64-apple-darwin` / universal? + - Apple Silicon 是主流,但 Intel Mac 仍在用 + - **你给出推荐 + 理由**,并在交付里说明。如果做一个,说明另一个为何不做(或后续做) +- 编译门 `cargo check --workspace --all-targets` +- **macOS 打包前置**: + - `.icns` 已在(`icons/icon.icns`) + - 产物:`target/release/bundle/macos/*.app` → 打成 `.dmg`(`tauri build` 的 `dmg` target,或用 `create-dmg`) + - **未签名未公证是预期**:workflow 不能尝试签名(无证书),但要在日志里明确输出「unsigned / not notarized」提示,并确认 `tauri.conf.json` 没有配 `macOS.signingIdentity`(有就报错打断,别让构建假装在签) +- 产物:`*.dmg`(如果同时产出 `.app.tar.gz` 也一起传) +- 上传到同一个 Release + +## Task 2: Release body 要三平台通用 + +现在 release.yml 的 body 只写了 Windows 安装说明。改成三平台结构: + +```markdown +## PromptKey vX.Y.Z + +### 下载 +| 平台 | 安装包 | 说明 | +|---|---|---| +| Windows | `PromptKey_*_x64-setup.exe` | NSIS,中英双语向导(推荐) | +| Windows | `PromptKey_*_x64_en-US.msi` | MSI 备选 | +| Linux | `PromptKey_*_amd64.deb` | Debian/Ubuntu | +| Linux | `PromptKey_*_amd64.AppImage` | 通用,无需安装 | +| macOS | `PromptKey_*_aarch64.dmg` | Apple Silicon(拖入 Applications) | + +### 安装说明 +(每平台 2-3 行** + +### 系统要求 +- Windows 10/11 x64 + WebView2 Runtime +- Linux: glibc ≥ 2.31(Debian 12+ / Ubuntu 22.04+)+ WebKit2GTK 4.1 +- macOS 11+ + +### 注意 +- 三个平台均**未做代码签名**(SmartScreen / Gatekeeper 提示属正常) +- macOS 首次运行需在「系统设置 → 隐私与安全性」中点「仍要打开」 +- macOS 自动注入需在「系统设置 → 隐私与安全性 → 辅助功能」中授权 +- Wayland 会话下自动注入受平台限制,应用内会明确提示降级 +``` + +(表格内容以你实际产出的文件名为准,别照抄我的假设文件名) + +## Task 3: 三平台产物的本地校验脚本 + +在 `scripts/` 下加一个校验脚本(或在 workflow 内 step 实现),对每个平台的产物做基本健全性检查: + +- `.exe` → PE32 可执行 +- `.deb` → `ar t` 能列出 `debian-binary`/`control.tar.*`/`data.tar.*` +- `.AppImage` → 有 `AI\x02` 魔数(type-2 AppImage)且是可执行文件 +- `.dmg` → 有 `koly` trailer(Apple Disk Image) +- 每种格式的**最小体积阈值**(防空文件/截断上传),阈值你定并说明依据 + +## Task 4: 文档 + +- `docs/PLATFORMS.md`:补「打包产物」一栏(每平台的产物文件名 + 安装方式),并把 CI 三平台构建的状态写进去 +- README:下载区说明三个平台各自的安装包怎么选 + +## Task 5(重要): 你不能自己跑 Release,所以必须给「待执行清单」 + +本机无 cargo、无法跑 workflow。你的交付必须包含一个**明确的执行清单**,让我(或用户)在合并后照做: + +``` +合并 → 打 tag → workflow 跑 → 逐平台检查 asset 是否齐全 → 下载每个产物 → box 分发 → 告知用户 +``` + +并列出**每平台可能失败的点**和对应的排查命令。 + +--- + +# 硬约束 + +- 无 cargo,本机不编译;**CI 必须全绿**(含新增的两个 job) +- **不得破坏 2.0.5 已发布的 Windows Release**;Windows job 只增不改 +- 三平台 job 并行,互不阻塞 +- 产物都要上传到**同一个** Release(软props action 的 `files` 支持多 job 追加) +- 一个 PR,多个 commit 按 job 分(linux / macos / docs / verify),push,`--base master`,**不合并** +- 遵守 LOOM 流程 + +--- + +## 最终交付 + +``` +## Linux job +(系统依赖包清单 + 你怎么核实的 + 前置校验;产物与校验) + +## macOS job +(**目标架构的推荐 + 论证**;未签名处理;产物与校验) + +## Release body +(三平台表格 + 注意项) + +## 校验脚本 +(每种格式怎么验 + 体积阈值依据) + +## 文档 +(PLATFORMS.md / README 改了什么) + +## 待执行清单(合并后) +(从合并到分发的逐步操作 + 每平台失败点排查) + +## 风险 +(哪些是你无法在本机验证的;Linux 构建依赖在不同 runner 镜像上可能的差异;macOS 架构选择的风险) +``` + +```json +COMPLETION_NOTIFY +source: assistant +task: promptkey-release-matrix-3platform +deliverables: +- release.yml extended with linux + macos jobs, parallel to windows, windows job untouched +- linux system deps correct & verified with early pkg-config gate +- macos target arch recommended with reasoning; unsigned/not-notarized explicit; no fake signing +- three-platform release body with download matrix + per-platform notes +- artifact sanity verification script (PE/deb/AppImage magic/dmg koly + size floors) +- docs/PLATFORMS.md + README updated for 3-platform artifacts +- CI green including both new jobs +- explicit post-merge execution checklist (tag → verify → distribute) +- branch feat/release-matrix-linux-macos + PR against master (not merged) +status: success +errors: none +``` From 3e97d4439f60e6c225763d608e135b9467bf3978 Mon Sep 17 00:00:00 2001 From: haa Date: Thu, 8 Oct 2026 10:27:16 +0200 Subject: [PATCH 2/9] chore(scripts): release artifact verifier + portable capabilities gate - verify_release_artifacts.sh: per-format sanity checks for every release artifact (PE/MZ+PE sig, OLE2 msi, ar members for deb, ELF+AI\x02 for AppImage, koly trailer for dmg, gzip magic) plus minimum-size floors an order of magnitude below real sizes; --selftest fabricates fixtures (13/13 pass locally). - check_build_capabilities.mjs: node port of the Windows job's pwsh post-build check on gen/schemas/capabilities.json so the linux/macos jobs get the same "capabilities actually resolved into the build" proof. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- scripts/check_build_capabilities.mjs | 40 +++++ scripts/verify_release_artifacts.sh | 234 +++++++++++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 scripts/check_build_capabilities.mjs create mode 100755 scripts/verify_release_artifacts.sh diff --git a/scripts/check_build_capabilities.mjs b/scripts/check_build_capabilities.mjs new file mode 100644 index 0000000..94b9416 --- /dev/null +++ b/scripts/check_build_capabilities.mjs @@ -0,0 +1,40 @@ +#!/usr/bin/env node +// Post-build capability gate — portable (node) version of the pwsh step in the +// Windows release job, for the Linux/macOS release jobs. +// +// tauri-build resolves capabilities/ at compile time and rewrites +// gen/schemas/capabilities.json with the resolved map. This file is the +// build-time proof that capabilities made it into the binary's context: +// an empty {} here = the 2.0.x bug where every plugin:* IPC call is +// ACL-denied at runtime while app commands still answer. +// +// Run AFTER `tauri build`. Exits non-zero if the resolved map is empty or +// does not cover both webview labels (main, wheel-panel). + +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +const path = join(process.cwd(), 'gen/schemas/capabilities.json'); +const caps = JSON.parse(readFileSync(path, 'utf8')); + +const names = Object.keys(caps); +if (names.length === 0) { + console.error('tauri build resolved ZERO capabilities — plugin:* IPC would be ACL-denied at runtime (the 2.0.x regression)'); + process.exit(1); +} + +const labels = []; +for (const n of names) { + for (const w of caps[n].windows ?? []) { + labels.push(typeof w === 'string' ? w : w?.identifier); + } +} + +for (const req of ['main', 'wheel-panel']) { + if (!labels.includes(req) && !labels.includes('*')) { + console.error(`resolved capabilities do not cover window '${req}' (${labels.join(', ')})`); + process.exit(1); + } +} + +console.log(`resolved capabilities: ${names.join(', ')} — windows: ${labels.join(', ')}`); diff --git a/scripts/verify_release_artifacts.sh b/scripts/verify_release_artifacts.sh new file mode 100755 index 0000000..62bd5c7 --- /dev/null +++ b/scripts/verify_release_artifacts.sh @@ -0,0 +1,234 @@ +#!/usr/bin/env bash +# verify_release_artifacts.sh — sanity-check release artifacts before they are +# attached to a GitHub Release (and again after download, per release checklist). +# +# Catches the realistic failure modes of a build→upload pipeline: zero-byte +# files, truncated uploads, HTML error pages saved under a binary name, and +# bundler output in the wrong format. It does NOT validate payload correctness. +# +# Usage: +# verify_release_artifacts.sh [--strict] FILE [FILE ...] +# verify_release_artifacts.sh --selftest +# +# --strict also require the executable bit on .AppImage. Used in CI right +# after `tauri build`. Default is warn-only: files fetched back +# over HTTP lose mode bits, so downloaded assets legitimately +# arrive non-executable. +# +# Per-format checks (dispatched on lowercase extension): +# .exe 'MZ' header + 'PE\0\0' signature at the e_lfanew offset (NSIS) +# .msi OLE2 compound-document magic D0 CF 11 E0 A1 B1 1A E1 +# .deb ar magic '!\n' + members debian-binary / control.tar.* / +# data.tar.* listed by `ar t` +# .appimage ELF magic + type-2 AppImage marker 'AI\x02' at offset 8 +# .dmg 'koly' UDIF trailer in the last 512 bytes +# .tar.gz gzip magic 1F 8B (optional updater .app.tar.gz) +# +# Size floors sit roughly an order of magnitude below observed real sizes — +# they exist to catch empty/truncated artifacts, not to grade content: +# exe 1 MiB (2.0.5 NSIS exe ≈ 3.6 MB), msi 1 MiB (2.0.5 msi ≈ 4.9 MB), +# deb 1 MiB (bundled rust binary ⇒ several MB after xz), AppImage 10 MiB +# (bundles WebKitGTK ⇒ typically tens of MB), dmg 1 MiB, tar.gz 256 KiB. + +set -uo pipefail + +STRICT=0 +SELFTEST=0 +FILES=() +for arg in "$@"; do + case "$arg" in + --strict) STRICT=1 ;; + --selftest) SELFTEST=1 ;; + -h|--help) sed -n '2,33p' "$0"; exit 0 ;; + *) FILES+=("$arg") ;; + esac +done + +FAILURES=0 +err() { echo " FAIL: $*" >&2; FAILURES=$((FAILURES + 1)); } + +# hex_at FILE OFFSET COUNT → lowercase hex string of COUNT bytes at OFFSET +hex_at() { + od -An -tx1 -j"$2" -N"$3" -- "$1" 2>/dev/null | tr -d ' \n' +} + +# filesize FILE → byte count (GNU + BSD stat) +filesize() { + stat -c %s -- "$1" 2>/dev/null || stat -f %z -- "$1" +} + +# expect_magic FILE OFFSET EXPECTED_HEX LABEL +expect_magic() { + local f=$1 off=$2 want=$3 label=$4 + local got + got=$(hex_at "$f" "$off" "${#want}" ) + # ${#want} is hex chars; od -N takes bytes → half + got=$(hex_at "$f" "$off" $(( ${#want} / 2 ))) + if [ "$got" != "$want" ]; then + err "$f: bad $label magic (got '${got:-}', want '$want')" + return 1 + fi + return 0 +} + +# min_size FILE BYTES +min_size() { + local f=$1 min=$2 sz + sz=$(filesize "$f") + if [ -z "$sz" ] || [ "$sz" -lt "$min" ]; then + err "$f: size ${sz:-?}B below floor ${min}B (empty or truncated artifact)" + return 1 + fi + return 0 +} + +check_one() { + local f=$1 ext base + if [ ! -f "$f" ]; then err "$f: file not found (unmatched glob?)"; return; fi + base=$(basename -- "$f") + ext=$(echo "${base##*.}" | tr 'A-Z' 'a-z') + echo " checking $f ($(filesize "$f") bytes)" + case "$base" in + *.AppImage|*.appimage) + expect_magic "$f" 0 "7f454c46" "ELF" || return + expect_magic "$f" 8 "414902" "type-2 AppImage marker (offset 8)" || return + if [ -x "$f" ]; then + : + elif [ "$STRICT" = 1 ]; then + err "$f: AppImage is not executable (bundler output must be +x)" + return + else + echo " warn: $f not executable (expected for HTTP-downloaded assets; users run chmod +x)" + fi + min_size "$f" 10485760 || return + ;; + *.exe) + expect_magic "$f" 0 "4d5a" "MZ" || return + # e_lfanew: little-endian uint32 at offset 0x3C points to the PE signature + local eol + eol=$(od -An -tu4 -j60 -N4 -- "$f" 2>/dev/null | tr -d ' ') + if [ -z "$eol" ]; then err "$f: truncated before e_lfanew (DOS header)"; return; fi + expect_magic "$f" "$eol" "50450000" "PE signature" || return + min_size "$f" 1048576 || return + ;; + *.msi) + expect_magic "$f" 0 "d0cf11e0a1b11ae1" "OLE2 compound" || return + min_size "$f" 1048576 || return + ;; + *.deb) + expect_magic "$f" 0 "213c617263683e0a" "ar archive (!)" || return + local members + members=$(ar t -- "$f" 2>/dev/null) || { err "$f: 'ar t' failed — not a readable archive"; return; } + echo "$members" | grep -qx 'debian-binary' || { err "$f: missing debian-binary member"; return; } + echo "$members" | grep -qx 'control.tar.*' || { err "$f: missing control.tar.* member"; return; } + echo "$members" | grep -qx 'data.tar.*' || { err "$f: missing data.tar.* member"; return; } + min_size "$f" 1048576 || return + ;; + *.dmg) + # UDIF trailer ('koly') sits at the start of the final 512-byte block + local got + got=$(tail -c 512 -- "$f" 2>/dev/null | od -An -tx1 -N4 | tr -d ' \n') + if [ "$got" != "6b6f6c79" ]; then err "$f: no 'koly' trailer — not a UDIF dmg (got '${got:-}')"; return; fi + min_size "$f" 1048576 || return + ;; + *.tar.gz|*.tgz) + expect_magic "$f" 0 "1f8b" "gzip" || return + min_size "$f" 262144 || return + ;; + *) + err "$f: no verifier for extension '$ext' — add one, don't ship unchecked artifacts" + return + ;; + esac + echo " OK ($ext)" +} + +selftest() { + # Fabricates minimal-format fixtures and asserts the verifier accepts the + # good ones and rejects the broken ones. Needs GNU tools (truncate, dd, ar); + # intended for developer machines and CI, not for the runners' hot path. + local d; d=$(mktemp -d) + trap 'rm -rf "$d"' RETURN + local sz=2097152 big=12582912 + + # good fixtures ------------------------------------------------------- + printf 'MZ' > "$d/ok_x64-setup.exe" + printf '\x80\x00\x00\x00' | dd of="$d/ok_x64-setup.exe" bs=1 seek=60 conv=notrunc status=none + printf 'PE\x00\x00' | dd of="$d/ok_x64-setup.exe" bs=1 seek=128 conv=notrunc status=none + truncate -s "$sz" "$d/ok_x64-setup.exe" + + printf '\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1' > "$d/ok_x64_en-US.msi" + truncate -s "$sz" "$d/ok_x64_en-US.msi" + + # real ar archive with the three required member names + ( cd "$d" + echo '2.0' > debian-binary + : > control.tar.xz + truncate -s "$sz" data.tar.xz + ar rcs good_amd64.deb debian-binary control.tar.xz data.tar.xz + rm -f debian-binary control.tar.xz data.tar.xz ) + + printf '\x7f\x45\x4c\x46\x02\x01\x01\x00' > "$d/ok_amd64.AppImage" + printf '\x41\x49\x02' | dd of="$d/ok_amd64.AppImage" bs=1 seek=8 conv=notrunc status=none + truncate -s "$big" "$d/ok_amd64.AppImage" + chmod +x "$d/ok_amd64.AppImage" + + truncate -s "$sz" "$d/ok_aarch64.dmg" + printf 'koly' | dd of="$d/ok_aarch64.dmg" bs=1 seek=$((sz - 512)) conv=notrunc status=none + + printf '\x1f\x8b\x08\x00' > "$d/ok.app.tar.gz" + truncate -s 524288 "$d/ok.app.tar.gz" + + # bad fixtures -------------------------------------------------------- + printf 'MZ' > "$d/bad_no_pe.exe" # MZ but no PE sig + truncate -s "$sz" "$d/bad_no_pe.exe" + printf '404' > "$d/bad_html.exe" # error page as .exe + : > "$d/bad_empty.deb" # zero bytes + printf '\x7f\x45\x4c\x46\x02\x01\x01\x00' > "$d/bad_magic.AppImage" # ELF but no AI\x02 + truncate -s "$big" "$d/bad_magic.AppImage"; chmod +x "$d/bad_magic.AppImage" + cp "$d/ok_amd64.AppImage" "$d/bad_noexec.AppImage"; chmod -x "$d/bad_noexec.AppImage" + truncate -s "$sz" "$d/bad_trailer.dmg" # no koly trailer + printf '\x21\x3c\x61\x72\x63\x68\x3e\x0a' > "$d/bad_members.deb" # ar magic, no members + + local pass=0 fail=0 t + for f in "$d/ok_x64-setup.exe" "$d/ok_x64_en-US.msi" "$d/good_amd64.deb" \ + "$d/ok_amd64.AppImage" "$d/ok_aarch64.dmg" "$d/ok.app.tar.gz"; do + echo "selftest expect-PASS $f" + if FAILURES=0; check_one "$f" && [ "$FAILURES" = 0 ]; then pass=$((pass+1)); else fail=$((fail+1)); echo " !! expected pass"; fi + done + for f in "$d/bad_no_pe.exe" "$d/bad_html.exe" "$d/bad_empty.deb" \ + "$d/bad_magic.AppImage" "$d/bad_trailer.dmg" "$d/bad_members.deb"; do + echo "selftest expect-FAIL $f" + if FAILURES=0; check_one "$f" && [ "$FAILURES" = 0 ]; then fail=$((fail+1)); echo " !! expected FAIL but passed"; else pass=$((pass+1)); fi + done + # strict mode must additionally reject the non-executable AppImage + STRICT=1; echo "selftest expect-FAIL(--strict) $d/bad_noexec.AppImage" + if FAILURES=0; check_one "$d/bad_noexec.AppImage" && [ "$FAILURES" = 0 ]; then + fail=$((fail+1)); echo " !! expected FAIL under --strict but passed" + else pass=$((pass+1)); fi + STRICT=0 + + echo "selftest: $pass passed, $fail failed" + [ "$fail" = 0 ] +} + +if [ "$SELFTEST" = 1 ]; then + selftest + exit $? +fi + +if [ "${#FILES[@]}" = 0 ]; then + echo "usage: $0 [--strict] FILE [FILE ...] | $0 --selftest" >&2 + exit 2 +fi + +echo "verify_release_artifacts: ${#FILES[@]} file(s), strict=$STRICT" +for f in "${FILES[@]}"; do + check_one "$f" +done + +if [ "$FAILURES" -gt 0 ]; then + echo "verify_release_artifacts: $FAILURES check(s) FAILED" >&2 + exit 1 +fi +echo "verify_release_artifacts: all artifacts OK" From 06f864ce260c44e33a109daa033fa4f47b0dd1fa Mon Sep 17 00:00:00 2001 From: haa Date: Thu, 8 Oct 2026 10:27:30 +0200 Subject: [PATCH 3/9] ci(release): prepare-release job + linux build job + 3-platform body MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - prepare-release: materializes the GitHub Release in a ~seconds job so parallel platform publish steps always take action-gh-release's update path — eliminates the 422 already_exists / duplicate-release race (softprops/action-gh-release#616, #705). Step is tag-gated so workflow_dispatch builds still work. - build-linux on ubuntu-22.04 (NOT ubuntu-latest): oldest supported base keeps glibc floor at 2.35 matching the documented Ubuntu 22.04+/ Debian 12+ requirement, and sidesteps linuxdeploy's known 24.04 failures (FUSE2 renamed to libfuse2t64; bundled strip dies on .relr.dyn in glibc>=2.36, tauri-apps/tauri#14796). - apt deps = ci.yml's proven -dev set + patchelf/file/libfuse2 for bundling; pkg-config preflight gate fails fast with a precise missing list instead of dying 20min into bundling. - tauri build --bundles deb,appimage — "all" would also try rpm, which needs rpmbuild we don't install. - Artifacts sanity-checked via verify_release_artifacts.sh --strict, then appended to the shared Release. - Release body rewritten as a three-platform download matrix (kept byte-identical in every publish step → order-independent final text). - build-windows steps untouched apart from the body text. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 237 +++++++++++++++++++++++++++++++++- 1 file changed, 232 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c7ea8ae..9226f2f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,60 @@ env: CARGO_TERM_COLOR: always jobs: + # Materialize the GitHub Release for this tag BEFORE any build job reaches + # its publish step. Parallel jobs racing action-gh-release's create path can + # hit `422 already_exists` or even create duplicate releases + # (softprops/action-gh-release#616 / #705); a ~seconds job up front means + # every publish step below only ever takes the update path. The job always + # runs (its step is tag-gated) so `needs:` stays satisfied on + # workflow_dispatch builds too. + # + # The `body:` below is repeated identically in every publish step — the + # final Release text is then independent of job completion order. + prepare-release: + name: Prepare GitHub Release + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Create/update release for this tag + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + body: | + ## PromptKey ${{ github.ref_name }} + + ### 下载 + + | 平台 | 安装包 | 说明 | + |---|---|---| + | Windows | `*_x64-setup.exe` | NSIS 安装向导(中英双语,推荐) | + | Windows | `*_x64_en-US.msi` | MSI 备选 | + | Linux | `*_amd64.deb` | Debian / Ubuntu 系,apt/dpkg 安装 | + | Linux | `*_amd64.AppImage` | 免安装通用包,`chmod +x` 后运行 | + | macOS | `*_aarch64.dmg` | Apple Silicon(M 系列) | + | macOS | `*_x64.dmg` | Intel Mac | + + ### 安装说明 + + - **Windows**:运行安装包按向导完成。需系统已安装 WebView2 Runtime(Windows 11 与多数 Windows 10 自带;安装包不会自动安装)。 + - **Linux · deb**:`sudo apt install ./*_amd64.deb`(依赖声明含 WebKitGTK 4.1 / libayatana-appindicator)。 + - **Linux · AppImage**:`chmod +x` 后直接执行;运行时需 FUSE2(Ubuntu 24.04+:`sudo apt install libfuse2t64`)。 + - **macOS**:打开 dmg 拖入 Applications;首次运行被 Gatekeeper 拦截时:系统设置 → 隐私与安全性 →「仍要打开」。 + + ### 系统要求 + + - Windows 10 / 11(x64)+ WebView2 Runtime + - Linux:glibc ≥ 2.35(Ubuntu 22.04+ / Debian 12+ 或更新)+ WebKitGTK 4.1,X11 会话 + - macOS 11+(Apple Silicon 选 aarch64 包,Intel 选 x64 包) + + ### 注意 + + - 三个平台均**未做代码签名/公证**:Windows SmartScreen 与 macOS Gatekeeper 的「未知开发者」提示属预期。 + - macOS 自动注入需在「系统设置 → 隐私与安全性 → 辅助功能」中授权(设置页可一键触发系统授权对话框)。 + - Linux Wayland 会话下自动注入只能到达 XWayland 客户端;应用内会明确提示降级,管理功能不受影响。 + - 更新内容见 [CHANGELOG.md](https://github.com/Haaaiawd/PromptKey/blob/master/CHANGELOG.md) + build-windows: name: Build Windows installer runs-on: windows-latest @@ -93,17 +147,190 @@ jobs: body: | ## PromptKey ${{ github.ref_name }} + ### 下载 + + | 平台 | 安装包 | 说明 | + |---|---|---| + | Windows | `*_x64-setup.exe` | NSIS 安装向导(中英双语,推荐) | + | Windows | `*_x64_en-US.msi` | MSI 备选 | + | Linux | `*_amd64.deb` | Debian / Ubuntu 系,apt/dpkg 安装 | + | Linux | `*_amd64.AppImage` | 免安装通用包,`chmod +x` 后运行 | + | macOS | `*_aarch64.dmg` | Apple Silicon(M 系列) | + | macOS | `*_x64.dmg` | Intel Mac | + ### 安装说明 - - `PromptKey_*_x64-setup.exe` —— NSIS 安装包(推荐,支持中英双语安装向导) - - `PromptKey_*_x64_en-US.msi` —— MSI 安装包(备选) + + - **Windows**:运行安装包按向导完成。需系统已安装 WebView2 Runtime(Windows 11 与多数 Windows 10 自带;安装包不会自动安装)。 + - **Linux · deb**:`sudo apt install ./*_amd64.deb`(依赖声明含 WebKitGTK 4.1 / libayatana-appindicator)。 + - **Linux · AppImage**:`chmod +x` 后直接执行;运行时需 FUSE2(Ubuntu 24.04+:`sudo apt install libfuse2t64`)。 + - **macOS**:打开 dmg 拖入 Applications;首次运行被 Gatekeeper 拦截时:系统设置 → 隐私与安全性 →「仍要打开」。 ### 系统要求 - - Windows 10 / 11(x64) - - 需要系统已安装 **WebView2 Runtime**(Windows 11 与大多数 Windows 10 自带;本安装包不会自动安装。如无请从微软官网下载 Evergreen Bootstrapper) + + - Windows 10 / 11(x64)+ WebView2 Runtime + - Linux:glibc ≥ 2.35(Ubuntu 22.04+ / Debian 12+ 或更新)+ WebKitGTK 4.1,X11 会话 + - macOS 11+(Apple Silicon 选 aarch64 包,Intel 选 x64 包) ### 注意 - - 本版本**未做代码签名**:Windows SmartScreen 可能提示「未知发布者」,选择「仍要运行」即可。 + + - 三个平台均**未做代码签名/公证**:Windows SmartScreen 与 macOS Gatekeeper 的「未知开发者」提示属预期。 + - macOS 自动注入需在「系统设置 → 隐私与安全性 → 辅助功能」中授权(设置页可一键触发系统授权对话框)。 + - Linux Wayland 会话下自动注入只能到达 XWayland 客户端;应用内会明确提示降级,管理功能不受影响。 - 更新内容见 [CHANGELOG.md](https://github.com/Haaaiawd/PromptKey/blob/master/CHANGELOG.md) files: | target/release/bundle/nsis/*.exe target/release/bundle/msi/*.msi + + # ubuntu-22.04 deliberately, NOT ubuntu-latest: it is the oldest supported + # base that ships WebKitGTK 4.1, so the glibc floor stays at 2.35 (matches + # the documented Ubuntu 22.04+/Debian 12+ requirement — building on 24.04 + # would silently raise it to 2.39). It also sidesteps linuxdeploy's known + # 24.04 failures (FUSE2 renamed to libfuse2t64; its bundled strip dies on + # .relr.dyn sections of glibc ≥ 2.36 — tauri-apps/tauri#14796). + build-linux: + name: Build Linux packages + needs: prepare-release + runs-on: ubuntu-22.04 + timeout-minutes: 60 + env: + # linuxdeploy and appimagetool are themselves AppImages; FUSE mounts are + # unreliable on CI runners → extract-and-run instead of mounting. + APPIMAGE_EXTRACT_AND_RUN: "1" + # linuxdeploy's bundled strip (binutils 2.35) dies on .relr.dyn sections + # in glibc ≥ 2.36 system libs. Inert on 22.04; protects a future bump. + NO_STRIP: "true" + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Rust (stable) + uses: dtolnay/rust-toolchain@stable + + # Same -dev set as ci.yml's rust-unix job (known-good on Ubuntu) plus the + # bundling extras: patchelf for AppImage, libfuse2 to run linuxdeploy, + # libxdo-dev per the Tauri prerequisites list. + - name: Install Tauri system deps + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + pkg-config build-essential \ + libglib2.0-dev libgtk-3-dev \ + libwebkit2gtk-4.1-dev libjavascriptcoregtk-4.1-dev libsoup-3.0-dev \ + libayatana-appindicator3-dev librsvg2-dev \ + libxdo-dev libssl-dev \ + patchelf file libfuse2 + + # Fail HERE with a precise missing list instead of dying ~20min into + # bundling. webkit2gtk **4.1** (not 4.0) is the Tauri 2 requirement. + - name: "Preflight: pkg-config + tool gate" + run: | + missing="" + for pc in glib-2.0 gobject-2.0 gio-2.0 gdk-3.0 gtk+-3.0 \ + libsoup-3.0 javascriptcoregtk-4.1 webkit2gtk-4.1 \ + ayatana-appindicator3-0.1 librsvg-2.0; do + pkg-config --exists "$pc" || missing="$missing pc:$pc" + done + for tool in pkg-config patchelf ar dpkg-deb file; do + command -v "$tool" >/dev/null || missing="$missing tool:$tool" + done + if [ -n "$missing" ]; then + echo "::error::Missing Tauri Linux build dependencies:$missing" + exit 1 + fi + echo "deps OK: webkit2gtk-4.1 + gtk+-3.0 + libsoup-3.0 + appindicator + rsvg + patchelf" + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + key: linux + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: lts/* + + - name: Install Tauri CLI + run: npm install -g "@tauri-apps/cli@2.12.0" + + - name: Compile gate (cargo check) + run: cargo check --workspace --all-targets + + # Static capability gate (same as ci.yml frontend job). + - name: Verify capability files cover both windows + run: node scripts/check_capabilities.mjs + + # `--bundles deb,appimage`, not "all": tauri.conf.json's targets:"all" + # also includes rpm on Linux, which needs rpmbuild we don't install. + # `tauri build` auto-adds --features custom-protocol for release builds — + # same as the Windows job, correct here too. + - name: Build + bundle (tauri build) + run: tauri build --verbose --bundles deb,appimage + + # Post-build proof that capabilities were resolved into the binary's + # context (node port of the Windows job's pwsh check). + - name: Verify resolved capabilities made it into the build + run: node scripts/check_build_capabilities.mjs + + - name: List + verify bundle artifacts + run: | + find target/release/bundle -type f -exec ls -l {} + + shopt -s nullglob + artifacts=(target/release/bundle/deb/*.deb target/release/bundle/appimage/*.AppImage) + if [ ${#artifacts[@]} -eq 0 ]; then + echo "::error::No Linux bundle artifacts produced (expected .deb / .AppImage)" + exit 1 + fi + scripts/verify_release_artifacts.sh --strict "${artifacts[@]}" + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: promptkey-linux-${{ github.ref_name }} + path: | + target/release/bundle/deb/*.deb + target/release/bundle/appimage/*.AppImage + if-no-files-found: error + + - name: Publish GitHub Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + body: | + ## PromptKey ${{ github.ref_name }} + + ### 下载 + + | 平台 | 安装包 | 说明 | + |---|---|---| + | Windows | `*_x64-setup.exe` | NSIS 安装向导(中英双语,推荐) | + | Windows | `*_x64_en-US.msi` | MSI 备选 | + | Linux | `*_amd64.deb` | Debian / Ubuntu 系,apt/dpkg 安装 | + | Linux | `*_amd64.AppImage` | 免安装通用包,`chmod +x` 后运行 | + | macOS | `*_aarch64.dmg` | Apple Silicon(M 系列) | + | macOS | `*_x64.dmg` | Intel Mac | + + ### 安装说明 + + - **Windows**:运行安装包按向导完成。需系统已安装 WebView2 Runtime(Windows 11 与多数 Windows 10 自带;安装包不会自动安装)。 + - **Linux · deb**:`sudo apt install ./*_amd64.deb`(依赖声明含 WebKitGTK 4.1 / libayatana-appindicator)。 + - **Linux · AppImage**:`chmod +x` 后直接执行;运行时需 FUSE2(Ubuntu 24.04+:`sudo apt install libfuse2t64`)。 + - **macOS**:打开 dmg 拖入 Applications;首次运行被 Gatekeeper 拦截时:系统设置 → 隐私与安全性 →「仍要打开」。 + + ### 系统要求 + + - Windows 10 / 11(x64)+ WebView2 Runtime + - Linux:glibc ≥ 2.35(Ubuntu 22.04+ / Debian 12+ 或更新)+ WebKitGTK 4.1,X11 会话 + - macOS 11+(Apple Silicon 选 aarch64 包,Intel 选 x64 包) + + ### 注意 + + - 三个平台均**未做代码签名/公证**:Windows SmartScreen 与 macOS Gatekeeper 的「未知开发者」提示属预期。 + - macOS 自动注入需在「系统设置 → 隐私与安全性 → 辅助功能」中授权(设置页可一键触发系统授权对话框)。 + - Linux Wayland 会话下自动注入只能到达 XWayland 客户端;应用内会明确提示降级,管理功能不受影响。 + - 更新内容见 [CHANGELOG.md](https://github.com/Haaaiawd/PromptKey/blob/master/CHANGELOG.md) + files: | + target/release/bundle/deb/*.deb + target/release/bundle/appimage/*.AppImage + From 0524a7678e3aa383bab38becdd483390aacd45aa Mon Sep 17 00:00:00 2001 From: haa Date: Thu, 8 Oct 2026 10:27:45 +0200 Subject: [PATCH 4/9] =?UTF-8?q?ci(release):=20macOS=20build=20matrix=20?= =?UTF-8?q?=E2=80=94=20aarch64=20+=20x86=5F64=20single-arch=20dmgs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two vanilla per-arch tauri builds (matrix, fail-fast: false) instead of universal2: each artifact is the standard build path, independently verifiable, and one failing arch can't block the other. aarch64 builds natively on the arm64 runner; x86_64 cross-compiles via rustup target (Tauri docs approach). universal2 would merge both into one download but adds lipo + dual-target risk we can't verify locally — noted as a follow-up option. - Unsigned/notarized-by-design: preflight fails loudly if bundle.macOS.signingIdentity is ever set without provisioned certs (ad-hoc '-' signing is fine), and logs the Gatekeeper expectation. - Same gates as linux: cargo check --target, capability files check, resolved-capabilities proof, dmg koly-trailer + size verification. - Publishes dmg/*.dmg (+ optional macos/*.app.tar.gz) to the shared Release. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 133 ++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9226f2f..5783bfb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -334,3 +334,136 @@ jobs: target/release/bundle/deb/*.deb target/release/bundle/appimage/*.AppImage + # Two single-arch dmgs instead of one universal2 binary: each build is a + # vanilla `tauri build` — the lowest-risk path we can reason about without a + # local Mac — and a failing arch can't block the other (fail-fast: false). + # macos-latest is an arm64 runner: aarch64 compiles natively, x86_64 + # cross-compiles via `rustup target` (same approach as the Tauri docs). + # Coverage: every Mac since 2020 vs. shipping aarch64-only. + build-macos: + name: Build macOS bundle (${{ matrix.arch }}) + needs: prepare-release + runs-on: macos-latest + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + include: + - arch: aarch64 + target: aarch64-apple-darwin + - arch: x64 + target: x86_64-apple-darwin + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Rust (stable) + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + key: macos-${{ matrix.arch }} + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: lts/* + + - name: Install Tauri CLI + run: npm install -g "@tauri-apps/cli@2.12.0" + + # No signing certs exist on the runner and none are provisioned — the + # dmg ships unsigned/unnotarized BY DESIGN. Fail loudly if someone adds + # a real signingIdentity without provisioning certs, instead of letting + # the bundler silently fail at the codesign step 20 minutes in. + # ('-' = ad-hoc signing is fine; tauri ad-hoc signs .app by default.) + - name: "Preflight: unsigned-build guard + assets" + run: | + node -e ' + const c = require("./tauri.conf.json"); + const m = (c.bundle && c.bundle.macOS) || {}; + if (m.signingIdentity && m.signingIdentity !== "-") { + throw new Error(`bundle.macOS.signingIdentity=${m.signingIdentity} but no certs on the runner — remove it or provision signing first`); + } + console.log("unsigned build confirmed — .app gets ad-hoc signature only, no notarization; Gatekeeper warning is expected"); + ' + test -s icons/icon.icns || { echo "::error::icons/icon.icns missing/empty — dmg icon would be blank"; exit 1; } + echo "icon.icns present ($(stat -f %z icons/icon.icns) bytes)" + + - name: Compile gate (cargo check) + run: cargo check --workspace --all-targets --target ${{ matrix.target }} + + - name: Verify capability files cover both windows + run: node scripts/check_capabilities.mjs + + - name: Build + bundle (tauri build) + run: tauri build --verbose --target ${{ matrix.target }} --bundles dmg + + - name: Verify resolved capabilities made it into the build + run: node scripts/check_build_capabilities.mjs + + - name: List + verify bundle artifacts + run: | + find target/release/bundle -type f -exec ls -l {} + + shopt -s nullglob + artifacts=(target/release/bundle/dmg/*.dmg target/release/bundle/macos/*.app.tar.gz) + if [ ${#artifacts[@]} -eq 0 ]; then + echo "::error::No macOS bundle artifacts produced (expected .dmg)" + exit 1 + fi + scripts/verify_release_artifacts.sh --strict "${artifacts[@]}" + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: promptkey-macos-${{ matrix.arch }}-${{ github.ref_name }} + path: | + target/release/bundle/dmg/*.dmg + target/release/bundle/macos/*.app.tar.gz + if-no-files-found: error + + - name: Publish GitHub Release + if: startsWith(github.ref, 'refs/tags/') + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + body: | + ## PromptKey ${{ github.ref_name }} + + ### 下载 + + | 平台 | 安装包 | 说明 | + |---|---|---| + | Windows | `*_x64-setup.exe` | NSIS 安装向导(中英双语,推荐) | + | Windows | `*_x64_en-US.msi` | MSI 备选 | + | Linux | `*_amd64.deb` | Debian / Ubuntu 系,apt/dpkg 安装 | + | Linux | `*_amd64.AppImage` | 免安装通用包,`chmod +x` 后运行 | + | macOS | `*_aarch64.dmg` | Apple Silicon(M 系列) | + | macOS | `*_x64.dmg` | Intel Mac | + + ### 安装说明 + + - **Windows**:运行安装包按向导完成。需系统已安装 WebView2 Runtime(Windows 11 与多数 Windows 10 自带;安装包不会自动安装)。 + - **Linux · deb**:`sudo apt install ./*_amd64.deb`(依赖声明含 WebKitGTK 4.1 / libayatana-appindicator)。 + - **Linux · AppImage**:`chmod +x` 后直接执行;运行时需 FUSE2(Ubuntu 24.04+:`sudo apt install libfuse2t64`)。 + - **macOS**:打开 dmg 拖入 Applications;首次运行被 Gatekeeper 拦截时:系统设置 → 隐私与安全性 →「仍要打开」。 + + ### 系统要求 + + - Windows 10 / 11(x64)+ WebView2 Runtime + - Linux:glibc ≥ 2.35(Ubuntu 22.04+ / Debian 12+ 或更新)+ WebKitGTK 4.1,X11 会话 + - macOS 11+(Apple Silicon 选 aarch64 包,Intel 选 x64 包) + + ### 注意 + + - 三个平台均**未做代码签名/公证**:Windows SmartScreen 与 macOS Gatekeeper 的「未知开发者」提示属预期。 + - macOS 自动注入需在「系统设置 → 隐私与安全性 → 辅助功能」中授权(设置页可一键触发系统授权对话框)。 + - Linux Wayland 会话下自动注入只能到达 XWayland 客户端;应用内会明确提示降级,管理功能不受影响。 + - 更新内容见 [CHANGELOG.md](https://github.com/Haaaiawd/PromptKey/blob/master/CHANGELOG.md) + files: | + target/release/bundle/dmg/*.dmg + target/release/bundle/macos/*.app.tar.gz From 49c9788b7b4b9a66453f9cf13867bb2cb913fbc1 Mon Sep 17 00:00:00 2001 From: haa Date: Thu, 8 Oct 2026 10:27:45 +0200 Subject: [PATCH 5/9] docs: three-platform release artifacts in PLATFORMS.md + README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PLATFORMS.md: packaging rows updated to the real CI artifacts and a new "Release 打包产物与流水线" section documents the job graph, per-platform artifact names, pre-publish checks, install steps, and the honesty constraints (ubuntu-22.04 glibc floor, dual-arch dmg over universal2, unsigned everywhere). - README: platform table lists real artifact globs; Linux quickstart covers apt-deb + FUSE2 note for AppImage; macOS quickstart explains the aarch64/x64 dmg choice and the Gatekeeper bypass; CI/CD paragraph describes the parallel three-platform release. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- README.md | 28 +++++++++++++++------------- docs/PLATFORMS.md | 25 ++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 405f29d..4b49e45 100644 --- a/README.md +++ b/README.md @@ -69,10 +69,10 @@ PromptKey 是一个专为 AI 重度用户设计的系统级提示词管理器: | 平台 | 状态 | 产物 | 验证情况 | |------|------|------|----------| -| **Windows 10/11 x64** | ✅ 完整支持 | NSIS / MSI | **已实机验收**(2.0.4 轮盘、注入、拖拽排序均经用户实测) | -| **Linux (X11)** | ⚠️ 代码就绪 | AppImage / deb | **编译通过 + 单测/E2E 通过,未做真机验证**;X11 注入/热键/上下文已实现,真机行为待确认 | +| **Windows 10/11 x64** | ✅ 完整支持 | `*_x64-setup.exe`(NSIS)/ `*_x64_en-US.msi` | **已实机验收**(2.0.4 轮盘、注入、拖拽排序均经用户实测) | +| **Linux (X11)** | ⚠️ 代码就绪 | `*_amd64.deb` / `*_amd64.AppImage` | **编译通过 + 单测/E2E 通过,未做真机验证**;X11 注入/热键/上下文已实现,真机行为待确认 | | **Linux (Wayland)** | ⚠️ 降级可用 | 同上 | 管理/剪贴板可用;X11 路径只能到达 XWayland 客户端,**原生 Wayland 窗口的自动注入未支持**,界面会明确提示降级 | -| **macOS** | ⚠️ 代码就绪 | app / dmg | **编译路径就绪,未做真机验证**;AX 注入 + 剪贴板兜底已实现,需授予「辅助功能」权限 | +| **macOS** | ⚠️ 代码就绪 | `*_aarch64.dmg`(Apple Silicon)/ `*_x64.dmg`(Intel) | **编译路径就绪,未做真机验证**;AX 注入 + 剪贴板兜底已实现,需授予「辅助功能」权限 | > ⚠️ **诚实声明**:Windows 是唯一经过真机验收的平台。Linux/macOS 的实现经过编译验证与单元/E2E 测试,但**从未在真机上运行过**——首次使用可能遇到我们尚未发现的问题,欢迎反馈(见文末)。CI 会验证 Linux/macOS 的编译,但不验证运行时行为。 @@ -89,21 +89,23 @@ PromptKey 是一个专为 AI 重度用户设计的系统级提示词管理器: ### Linux ```bash -# AppImage:赋予执行权限后直接运行 -chmod +x PromptKey-*.AppImage && ./PromptKey-*.AppImage +# deb(推荐,自动带入 WebKitGTK 4.1 等依赖) +sudo apt install ./*_amd64.deb -# deb -sudo dpkg -i promptkey_*.deb +# AppImage:赋予执行权限后直接运行 +chmod +x *_amd64.AppImage && ./*_amd64.AppImage ``` -- 需要 X11 会话(或接受 Wayland 降级行为);运行时依赖 WebKitGTK。 +- 需要 X11 会话(或接受 Wayland 降级行为);运行时依赖 WebKitGTK 4.1(deb 已声明依赖;AppImage 自带)。 +- AppImage 需要 FUSE2:Ubuntu 24.04+ 为 `sudo apt install libfuse2t64`(22.04 为 `libfuse2`)。 - XWayland 下大部分能力可用;纯 Wayland 会话会在设置页显示「当前会话为 Wayland,自动注入能力受限」。 ### macOS -1. 打开 `.dmg`,拖入 Applications。 -2. **首次使用会请求「辅助功能」权限**:未授权时自动注入会降级为「复制到剪贴板 + 提示」。可在 设置 → 辅助功能权限 行重新触发系统授权对话框。 -3. 未公证(需付费开发者账号):首次打开可能被 Gatekeeper 拦截,右键 → 打开 即可绕过。 +1. 按芯片选 dmg:Apple Silicon(M 系列)下 `*_aarch64.dmg`,Intel Mac 下 `*_x64.dmg`。 +2. 打开 dmg,拖入 Applications。 +3. **首次使用会请求「辅助功能」权限**:未授权时自动注入会降级为「复制到剪贴板 + 提示」。可在 设置 → 辅助功能权限 行重新触发系统授权对话框。 +4. 未签名/未公证:首次打开可能被 Gatekeeper 拦截 —— 右键 → 打开,或 系统设置 → 隐私与安全性 →「仍要打开」。 ### 使用 @@ -162,7 +164,7 @@ tauri build # 产出 .deb + .AppImage(target/release/bundle/) **macOS**:`cargo run` / `tauri build`(产出 `.app` + `.dmg`)。公证需付费 Apple Developer 账号,`tauri.conf.json` 未配置签名。 -CI/CD:PR 与 master push 运行 `cargo check`/`clippy`/`test`(Windows + Linux + macOS)+ 前端静态检查;推送 `v*` tag 自动构建并发布 GitHub Release(见 `.github/workflows/`)。 +CI/CD:PR 与 master push 运行 `cargo check`/`clippy`/`test`(Windows + Linux + macOS)+ 前端静态检查;推送 `v*` tag 触发三平台**并行**构建(Windows NSIS/MSI、Linux deb/AppImage、macOS aarch64+x64 dmg),逐产物做 magic/体积校验后追加到同一个 GitHub Release(见 `.github/workflows/release.yml` 与 `docs/PLATFORMS.md` 的打包产物表)。 ### 项目结构 @@ -189,7 +191,7 @@ PromptKey/ ├── capabilities/ # Tauri IPC 权限(main + wheel-panel 两个 webview) ├── tests/e2e/ # Playwright 端到端测试(stub __TAURI__,4 套件 60+ 断言) ├── docs/ # PLATFORMS.md / COMPONENTS.md / screenshots/ -├── scripts/ # make_icons.py(多平台图标)/ screenshots.py / check_capabilities.mjs +├── scripts/ # make_icons.py(多平台图标)/ screenshots.py / check_capabilities.mjs / check_build_capabilities.mjs / verify_release_artifacts.sh ├── blueprint/ # 历史 PRD / RFC / 复杂度审计(1.x → 2.0 演进档案) ├── .loom/design/ # 现行设计文档(见下方索引) └── .github/workflows/ # CI + Release diff --git a/docs/PLATFORMS.md b/docs/PLATFORMS.md index 55e6a16..81e622d 100644 --- a/docs/PLATFORMS.md +++ b/docs/PLATFORMS.md @@ -58,7 +58,7 @@ IPC 仍走命名管道;引擎重启/防抖语义不变(失败重发不锁防 | IPC | Unix domain socket(`$XDG_RUNTIME_DIR` 或 `$TMPDIR/promptkey-$UID/`,目录 0700 / socket 0600) | ⚠️ 同上 | | 自动启动 | `tauri-plugin-autostart` → `~/.config/autostart/promptkey.desktop`(XDG Autostart) | ⚠️ 同上 | | 配置路径 | `${XDG_CONFIG_HOME:-~/.config}/promptkey/` | ⚠️ 同上 | -| 打包 | `tauri build` → `.deb` + `.AppImage` | ⚠️ CI 仅验证编译 | +| 打包 | Release workflow `build-linux`(ubuntu-22.04 runner,刻意压低 glibc 下限)→ `*_amd64.deb` + `*_amd64.AppImage` | ⚠️ CI 构建 + 格式/体积校验,未真机安装 | **已知风险(未真机验证推断)**: - XTEST 在某些发行版被禁/受限时会表现为「粘贴可用、逐键无效」——诊断可见注入策略耗时。 @@ -93,7 +93,7 @@ IPC 仍走命名管道;引擎重启/防抖语义不变(失败重发不锁防 | 配置路径 | `~/Library/Application Support/PromptKey/` | ⚠️ 同上 | | 图标 | `icons/icon.icns`(`scripts/make_icons.py` 生成,PNG-payload icns)+ 单色 template tray icon | ⚠️ 生成产物已验证,视觉未真机 | | 轮盘窗口 | 透明无边框需要 Tauri `macos-private-api` feature(未文档化的 WKWebView API)——已按 `cfg(macos)` 作用域启用;**App Store 审核风险不适用**(本应用走未签名 dmg 直发) | ⚠️ 同上 | -| 打包 | `tauri build` → `.app` + `.dmg`;**未公证**(需付费账号,文档说明绕过方式) | ⚠️ CI 仅验证编译 | +| 打包 | Release workflow `build-macos` matrix(arm64 runner 原生 aarch64 + 交叉 x86_64)→ `*_aarch64.dmg` + `*_x64.dmg`;**未签名/未公证**,workflow 内设 `signingIdentity` 护栏 | ⚠️ CI 构建 + 格式/体积校验,未真机运行 | **权限缺失时的行为**:`status_json()` 报告 `injection=needs-permission` + `notes=["ax_permission_missing"]`, 设置页出现「辅助功能权限」行可一键触发系统授权;授权前注入降级为「复制到剪贴板并提示」而非静默失败。 @@ -135,6 +135,25 @@ Windows 端为命名管道,Unix 端为 UDS——协议字节级一致,`platf | 前台上下文 | ✅ | ⚠️ | ⚠️ | ⚠️ | | IPC | ✅ | ⚠️ | ⚠️ | ⚠️ | | 自动启动 | ⚠️ | ⚠️ | ⚠️ | ⚠️ | -| 打包产物 | ✅ | ⚠️(CI 构建) | — | ⚠️(CI 构建,未公证) | +| 打包产物 | ✅(Release 产出 NSIS+MSI) | ⚠️(Release 产出 deb+AppImage) | — | ⚠️(Release 产出双架构 dmg,未公证) | ✅=实机/实际验证;⚠️=代码就绪未实机;❌=有意不支持。 + +--- + +## Release 打包产物与流水线(`.github/workflows/release.yml`) + +推送 `v*` tag 触发:`prepare-release` 先建/更新 GitHub Release(避免并行 job 抢建产生 422/重复 release), +随后 `build-windows` / `build-linux` / `build-macos` 三平台**并行**构建、各自把产物追加到同一个 Release—— +任一平台失败不阻塞其余平台发布(互相无 `needs` 于彼此)。 + +| 平台 | Job / Runner | 产物文件 | 打包前校验 | 安装方式 | +|------|-------------|----------|-----------|----------| +| Windows | `build-windows` / windows-latest | `PromptKey_*_x64-setup.exe`(NSIS)、`PromptKey_*_x64_en-US.msi` | capabilities 解析 + 产物存在性 | 运行安装向导;需 WebView2 Runtime | +| Linux | `build-linux` / **ubuntu-22.04** | `*_amd64.deb`、`*_amd64.AppImage` | pkg-config 依赖门 + `scripts/verify_release_artifacts.sh`(ar members / ELF+`AI\x02` / 体积下限) | `sudo apt install ./*.deb`;或 `chmod +x` AppImage 直接运行(需 FUSE2) | +| macOS | `build-macos` / macos-latest(arm64) | `*_aarch64.dmg`(Apple Silicon)、`*_x64.dmg`(Intel,交叉编译) | unsigned 护栏(`signingIdentity` 必须为未配置)+ `koly` trailer / 体积下限 | 拖入 Applications;Gatekeeper「仍要打开」;注入需辅助功能授权 | + +**产物诚实性约定**: +- Linux 在 ubuntu-22.04 构建 → glibc 下限 2.35,与「Ubuntu 22.04+ / Debian 12+」的要求一致;不用 ubuntu-latest 以免下限被悄悄抬高,也规避 linuxdeploy 在 24.04 的已知故障(tauri-apps/tauri#14796)。 +- macOS 出**两个单架构 dmg** 而非 universal2:各自是原生 `tauri build` 路径,风险最低、可独立验证;Intel Mac 仍被覆盖。 +- 三平台全部**未签名/未公证**,Release 说明与本文档如实标注;`verify_release_artifacts.sh` 只做格式与体积健全性检查,不验证内容正确性。 From a742b81ae4e6b8ce581bde5f5610284e73e62369 Mon Sep 17 00:00:00 2001 From: haa Date: Thu, 8 Oct 2026 10:28:09 +0200 Subject: [PATCH 6/9] chore(loom): TASK-012 release-matrix task + platform decisions Records TASK-012 (3-platform release pipeline) and the four consequential decisions in DECISIONS.md: ubuntu-22.04 pin over ubuntu-latest, prepare-release to kill the gh-release create race, single-arch dmg matrix over universal2, and explicit deb/appimage bundle pinning. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .loom/DECISIONS.md | 22 +++++++++++++ .loom/state.json | 4 +-- .loom/tasks.json | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 2 deletions(-) diff --git a/.loom/DECISIONS.md b/.loom/DECISIONS.md index 3b99d28..5b283ca 100644 --- a/.loom/DECISIONS.md +++ b/.loom/DECISIONS.md @@ -1,3 +1,25 @@ # Decision History Current truth belongs in PROJECT.md and linked design documents. This file preserves consequential superseding decisions. + +## 2026-10-08 — TASK-012 release matrix (3-platform) + +- **Linux runner pinned to `ubuntu-22.04`, not `ubuntu-latest`.** Newer base silently raises the + produced binaries' glibc floor (24.04 → 2.39) and breaks AppImage bundling: linuxdeploy's bundled + strip (binutils 2.35) dies on `.relr.dyn` sections of glibc ≥ 2.36 system libs and FUSE2 was + renamed to `libfuse2t64` (tauri-apps/tauri#14796). 22.04 keeps glibc floor at 2.35 = the documented + "Ubuntu 22.04+/Debian 12+" requirement. Supersedes the brief's `ubuntu-latest` suggestion. +- **`prepare-release` job creates the GitHub Release up front.** Parallel platform jobs racing + `action-gh-release`'s create path can hit `422 already_exists` or produce duplicate releases + (softprops/action-gh-release#616/#705). With the release materialized in a seconds-long job, + every publish step only ever takes the update path. Windows job needs no `needs:` — its publish + step runs ~20min into the build, long after prepare-release finishes. +- **macOS ships two single-arch dmgs (aarch64 native + x86_64 cross) rather than universal2.** + Each artifact is the vanilla `tauri build --target` path — lowest-risk option that can't be + verified locally; arch failures are isolated (`fail-fast: false`); Intel Macs stay covered. + universal2 remains a follow-up option once a real Mac can smoke-test it. +- **Linux bundles pinned to `--bundles deb,appimage`.** `targets:"all"` in tauri.conf.json would + also attempt rpm on Linux, which requires `rpmbuild` we intentionally don't install. +- **`APPIMAGE_EXTRACT_AND_RUN=1` + `NO_STRIP=true` on the linux job.** CI runners can't reliably + FUSE-mount linuxdeploy/appimagetool (themselves AppImages); NO_STRIP future-proofs a runner bump + to 24.04. Both inert when unnecessary. diff --git a/.loom/state.json b/.loom/state.json index 071268f..f11d3ff 100644 --- a/.loom/state.json +++ b/.loom/state.json @@ -2,9 +2,9 @@ "schema_version": 2, "project": { "name": "promptkey", - "status": "complete", + "status": "building", "created_at": "2026-10-06T08:16:56.246Z", - "updated_at": "2026-10-07T07:38:06.890Z" + "updated_at": "2026-10-08T08:17:24.075Z" }, "understanding": { "confirmed": [ diff --git a/.loom/tasks.json b/.loom/tasks.json index 3dca522..c6d8cd5 100644 --- a/.loom/tasks.json +++ b/.loom/tasks.json @@ -1218,6 +1218,85 @@ "created_at": "2026-10-07T06:47:00.011Z", "updated_at": "2026-10-07T07:38:06.889Z", "completed_at": "2026-10-07T07:38:06.889Z" + }, + { + "id": "TASK-012", + "title": "Release pipeline: extend release.yml to Linux + macOS (3-platform matrix)", + "outcome": "Pushing a v* tag produces Windows NSIS/MSI + Linux deb/AppImage + macOS dmg (aarch64 + x86_64) on a single GitHub Release, with per-artifact sanity verification, an honest three-platform release body, and zero changes to the proven Windows build steps.", + "acceptance": [ + { + "criterion": "release.yml gains build-linux (ubuntu-22.04, apt deps + pkg-config preflight gate, tauri build --bundles deb,appimage) and build-macos (matrix aarch64-apple-darwin + x86_64-apple-darwin, signingIdentity guard) jobs running in parallel with the untouched build-windows job", + "verify_by": "review .github/workflows/release.yml diff; python YAML parse; confirm windows job steps byte-identical", + "evidence": "" + }, + { + "criterion": "A prepare-release job creates/updates the tag's GitHub Release up front so parallel platform publish steps always take the update path (no action-gh-release create race, softprops#616/#705)", + "verify_by": "review job graph; all publish steps set identical body+generate_release_notes", + "evidence": "" + }, + { + "criterion": "scripts/verify_release_artifacts.sh validates per-format magic (MZ+PE, OLE2, ar members, ELF+AI\\x02, koly trailer, gzip) and minimum-size floors, and its --selftest passes locally", + "verify_by": "run scripts/verify_release_artifacts.sh --selftest on this machine", + "evidence": "" + }, + { + "criterion": "Release body documents all three platforms (download table, install notes, system requirements, unsigned/Wayland caveats)", + "verify_by": "review body string in workflow; consistent across prepare-release and each publish step", + "evidence": "" + }, + { + "criterion": "docs/PLATFORMS.md gains a Release packaging section and README download/install instructions reflect actual artifact names", + "verify_by": "review diffs", + "evidence": "" + }, + { + "criterion": "PR opened from feat/release-matrix-linux-macos against master, not merged; post-merge execution checklist delivered", + "verify_by": "gh pr status; checklist text in delivery", + "evidence": "" + } + ], + "done_when": [], + "boundaries": [ + "No Windows job step changes (capabilities check, bundle check, NSIS/MSI upload untouched); only its release body text is updated per the three-platform requirement", + "No code signing or notarization anywhere (no certs; explicit unsigned logging instead)", + "No local cargo build or workflow run possible — verification is static review + script selftest + post-merge checklist", + "No universal2 macOS binary — two single-arch dmgs chosen as the lower-risk, independently-verifiable path" + ], + "depends_on": [], + "reads": [ + ".loom/design/task-brief-release-matrix.md", + ".github/workflows/release.yml", + ".github/workflows/ci.yml", + "tauri.conf.json", + "Cargo.toml", + "capabilities/default.json", + "scripts/check_capabilities.mjs", + "docs/PLATFORMS.md", + "README.md" + ], + "touches": [ + ".github/workflows/release.yml", + "scripts/verify_release_artifacts.sh", + "scripts/check_build_capabilities.mjs", + "docs/PLATFORMS.md", + "README.md", + ".loom/" + ], + "implements": ".loom/design/task-brief-release-matrix.md", + "design_exemption": "", + "capability_hooks": [], + "capability_exemption": "CI/release engineering within existing design surface; no new professional capability domain — platform packaging knowledge is sourced inline in the brief and workflow comments.", + "integrity_version": 1, + "covers": [], + "status": "active", + "progress": { + "completed": [], + "current": "", + "next": "" + }, + "evidence": [], + "created_at": "2026-10-08T08:16:50.774Z", + "updated_at": "2026-10-08T08:17:24.074Z" } ] } From c23a0f0eb165e634c87e62983a435b19c981fa60 Mon Sep 17 00:00:00 2001 From: haa Date: Thu, 8 Oct 2026 10:30:15 +0200 Subject: [PATCH 7/9] fix(release): macOS bundle paths live under target//release/bundle/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `tauri build --target X` namespaces cargo's output directory — explicit --target (even matching the host) emits to target//release/, not target/release/. Glob, artifact upload, and publish files for the macOS job now use target/${{ matrix.target }}/release/bundle/. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5783bfb..92ff683 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -406,11 +406,13 @@ jobs: - name: Verify resolved capabilities made it into the build run: node scripts/check_build_capabilities.mjs + # NOTE: passing --target namespaces cargo's output dir — bundles land in + # target//release/bundle/, NOT target/release/bundle/. - name: List + verify bundle artifacts run: | - find target/release/bundle -type f -exec ls -l {} + + find "target/${{ matrix.target }}/release/bundle" -type f -exec ls -l {} + shopt -s nullglob - artifacts=(target/release/bundle/dmg/*.dmg target/release/bundle/macos/*.app.tar.gz) + artifacts=("target/${{ matrix.target }}/release/bundle/dmg/"*.dmg "target/${{ matrix.target }}/release/bundle/macos/"*.app.tar.gz) if [ ${#artifacts[@]} -eq 0 ]; then echo "::error::No macOS bundle artifacts produced (expected .dmg)" exit 1 @@ -422,8 +424,8 @@ jobs: with: name: promptkey-macos-${{ matrix.arch }}-${{ github.ref_name }} path: | - target/release/bundle/dmg/*.dmg - target/release/bundle/macos/*.app.tar.gz + target/${{ matrix.target }}/release/bundle/dmg/*.dmg + target/${{ matrix.target }}/release/bundle/macos/*.app.tar.gz if-no-files-found: error - name: Publish GitHub Release @@ -465,5 +467,5 @@ jobs: - Linux Wayland 会话下自动注入只能到达 XWayland 客户端;应用内会明确提示降级,管理功能不受影响。 - 更新内容见 [CHANGELOG.md](https://github.com/Haaaiawd/PromptKey/blob/master/CHANGELOG.md) files: | - target/release/bundle/dmg/*.dmg - target/release/bundle/macos/*.app.tar.gz + target/${{ matrix.target }}/release/bundle/dmg/*.dmg + target/${{ matrix.target }}/release/bundle/macos/*.app.tar.gz From 4d1651c39cb6bcbc284b89c350367586a43a1e68 Mon Sep 17 00:00:00 2001 From: haa Date: Thu, 8 Oct 2026 10:31:14 +0200 Subject: [PATCH 8/9] =?UTF-8?q?chore(loom):=20TASK-012=20progress=20?= =?UTF-8?q?=E2=80=94=20implementation=20done,=20awaiting=20PR=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .loom/tasks.json | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.loom/tasks.json b/.loom/tasks.json index c6d8cd5..95e88e8 100644 --- a/.loom/tasks.json +++ b/.loom/tasks.json @@ -1290,13 +1290,19 @@ "covers": [], "status": "active", "progress": { - "completed": [], - "current": "", - "next": "" + "completed": [ + "verify_release_artifacts.sh written; --selftest 13/13 pass locally (real ar/od/truncate fixtures)", + "check_build_capabilities.mjs verified against real gen/schemas/capabilities.json", + "release.yml: prepare-release + build-linux (ubuntu-22.04, pkg-config gate) + build-macos matrix (aarch64+x86_64) + 3-platform body; windows job steps byte-identical apart from body", + "post-push self-review caught target// namespacing bug in macOS artifact paths — fixed in c23a0f0", + "docs/PLATFORMS.md + README updated; PR #17 opened against master (not merged); ci.yml checks running" + ], + "current": "Waiting on PR CI (ci.yml checks — release.yml jobs only trigger on tags)", + "next": "Confirm PR checks green; fill acceptance evidence; deliver execution checklist" }, "evidence": [], "created_at": "2026-10-08T08:16:50.774Z", - "updated_at": "2026-10-08T08:17:24.074Z" + "updated_at": "2026-10-08T08:31:10.251Z" } ] } From 549df0f02c1d3a0bd482d2bdd410bc6d9e979e62 Mon Sep 17 00:00:00 2001 From: haa Date: Thu, 8 Oct 2026 10:38:20 +0200 Subject: [PATCH 9/9] =?UTF-8?q?chore(loom):=20TASK-012=20done=20=E2=80=94?= =?UTF-8?q?=20PR=20#17=20green,=20evidence=20recorded?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .loom/state.json | 4 ++-- .loom/tasks.json | 57 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/.loom/state.json b/.loom/state.json index f11d3ff..e91a3c3 100644 --- a/.loom/state.json +++ b/.loom/state.json @@ -2,9 +2,9 @@ "schema_version": 2, "project": { "name": "promptkey", - "status": "building", + "status": "complete", "created_at": "2026-10-06T08:16:56.246Z", - "updated_at": "2026-10-08T08:17:24.075Z" + "updated_at": "2026-10-08T08:38:14.930Z" }, "understanding": { "confirmed": [ diff --git a/.loom/tasks.json b/.loom/tasks.json index 95e88e8..e0d9fc8 100644 --- a/.loom/tasks.json +++ b/.loom/tasks.json @@ -1227,32 +1227,32 @@ { "criterion": "release.yml gains build-linux (ubuntu-22.04, apt deps + pkg-config preflight gate, tauri build --bundles deb,appimage) and build-macos (matrix aarch64-apple-darwin + x86_64-apple-darwin, signingIdentity guard) jobs running in parallel with the untouched build-windows job", "verify_by": "review .github/workflows/release.yml diff; python YAML parse; confirm windows job steps byte-identical", - "evidence": "" + "evidence": "python yaml parse: jobs = prepare-release, build-windows, build-linux(needs:prepare-release, ubuntu-22.04, 14 steps), build-macos(needs:prepare-release, matrix aarch64/x64, 13 steps); windows steps diff vs HEAD = identical apart from body text" }, { "criterion": "A prepare-release job creates/updates the tag's GitHub Release up front so parallel platform publish steps always take the update path (no action-gh-release create race, softprops#616/#705)", "verify_by": "review job graph; all publish steps set identical body+generate_release_notes", - "evidence": "" + "evidence": "prepare-release job runs at workflow start (tag-gated step); linux/macos carry needs:prepare-release; windows untouched — its publish step runs ~20min in, long after release exists. All 4 publish bodies verified byte-identical." }, { "criterion": "scripts/verify_release_artifacts.sh validates per-format magic (MZ+PE, OLE2, ar members, ELF+AI\\x02, koly trailer, gzip) and minimum-size floors, and its --selftest passes locally", "verify_by": "run scripts/verify_release_artifacts.sh --selftest on this machine", - "evidence": "" + "evidence": "selftest output: 13 passed, 0 failed — fabricated PE/MZ, OLE2, real ar deb, ELF+AI\\x02, koly-trailer dmg, gzip fixtures accepted; no-PE/html/empty/no-marker/no-trailer/no-members/non-exec all rejected" }, { "criterion": "Release body documents all three platforms (download table, install notes, system requirements, unsigned/Wayland caveats)", "verify_by": "review body string in workflow; consistent across prepare-release and each publish step", - "evidence": "" + "evidence": "6-row download table (exe/msi/deb/AppImage/aarch64 dmg/x64 dmg), per-platform install notes, glibc>=2.35 + WebKitGTK4.1 + macOS11+ requirements, unsigned + Gatekeeper + AX + Wayland-degradation notes; identical in prepare-release and all 3 publish steps" }, { "criterion": "docs/PLATFORMS.md gains a Release packaging section and README download/install instructions reflect actual artifact names", "verify_by": "review diffs", - "evidence": "" + "evidence": "PLATFORMS.md: new 'Release 打包产物与流水线' section (job table + honesty constraints) + updated packaging rows; README: real artifact globs in platform table, apt-deb + FUSE2 notes, per-arch dmg selection, Gatekeeper bypass" }, { "criterion": "PR opened from feat/release-matrix-linux-macos against master, not merged; post-merge execution checklist delivered", "verify_by": "gh pr status; checklist text in delivery", - "evidence": "" + "evidence": "https://github.com/Haaaiawd/PromptKey/pull/17 open; checklist delivered in session response" } ], "done_when": [], @@ -1288,7 +1288,7 @@ "capability_exemption": "CI/release engineering within existing design surface; no new professional capability domain — platform packaging knowledge is sourced inline in the brief and workflow comments.", "integrity_version": 1, "covers": [], - "status": "active", + "status": "done", "progress": { "completed": [ "verify_release_artifacts.sh written; --selftest 13/13 pass locally (real ar/od/truncate fixtures)", @@ -1297,12 +1297,47 @@ "post-push self-review caught target// namespacing bug in macOS artifact paths — fixed in c23a0f0", "docs/PLATFORMS.md + README updated; PR #17 opened against master (not merged); ci.yml checks running" ], - "current": "Waiting on PR CI (ci.yml checks — release.yml jobs only trigger on tags)", - "next": "Confirm PR checks green; fill acceptance evidence; deliver execution checklist" + "current": "complete", + "next": "" }, - "evidence": [], + "evidence": [ + "PR #17 (feat/release-matrix-linux-macos → master, unmerged). ci.yml checks all green: cargo check/clippy/test (win), cargo check (ubuntu+macos), frontend, e2e — run 37750387593. release.yml jobs only fire on v* tags; static verification done locally: YAML parses, all embedded bash passes bash -n, node snippets pass node --check, 4 publish bodies byte-identical, build-windows steps verified programmatically identical to HEAD except body text.", + "scripts/verify_release_artifacts.sh --selftest → 13/13 (6 valid fixtures pass, 7 broken fixtures correctly rejected incl. strict-mode exec-bit check).", + "scripts/check_build_capabilities.mjs run against real gen/schemas/capabilities.json → 'resolved capabilities: default — windows: main, wheel-panel'.", + { + "type": "acceptance_results", + "results": [ + { + "criterion": "release.yml gains build-linux (ubuntu-22.04, apt deps + pkg-config preflight gate, tauri build --bundles deb,appimage) and build-macos (matrix aarch64-apple-darwin + x86_64-apple-darwin, signingIdentity guard) jobs running in parallel with the untouched build-windows job", + "evidence": "python yaml parse: jobs = prepare-release, build-windows, build-linux(needs:prepare-release, ubuntu-22.04, 14 steps), build-macos(needs:prepare-release, matrix aarch64/x64, 13 steps); windows steps diff vs HEAD = identical apart from body text" + }, + { + "criterion": "A prepare-release job creates/updates the tag's GitHub Release up front so parallel platform publish steps always take the update path (no action-gh-release create race, softprops#616/#705)", + "evidence": "prepare-release job runs at workflow start (tag-gated step); linux/macos carry needs:prepare-release; windows untouched — its publish step runs ~20min in, long after release exists. All 4 publish bodies verified byte-identical." + }, + { + "criterion": "scripts/verify_release_artifacts.sh validates per-format magic (MZ+PE, OLE2, ar members, ELF+AI\\x02, koly trailer, gzip) and minimum-size floors, and its --selftest passes locally", + "evidence": "selftest output: 13 passed, 0 failed — fabricated PE/MZ, OLE2, real ar deb, ELF+AI\\x02, koly-trailer dmg, gzip fixtures accepted; no-PE/html/empty/no-marker/no-trailer/no-members/non-exec all rejected" + }, + { + "criterion": "Release body documents all three platforms (download table, install notes, system requirements, unsigned/Wayland caveats)", + "evidence": "6-row download table (exe/msi/deb/AppImage/aarch64 dmg/x64 dmg), per-platform install notes, glibc>=2.35 + WebKitGTK4.1 + macOS11+ requirements, unsigned + Gatekeeper + AX + Wayland-degradation notes; identical in prepare-release and all 3 publish steps" + }, + { + "criterion": "docs/PLATFORMS.md gains a Release packaging section and README download/install instructions reflect actual artifact names", + "evidence": "PLATFORMS.md: new 'Release 打包产物与流水线' section (job table + honesty constraints) + updated packaging rows; README: real artifact globs in platform table, apt-deb + FUSE2 notes, per-arch dmg selection, Gatekeeper bypass" + }, + { + "criterion": "PR opened from feat/release-matrix-linux-macos against master, not merged; post-merge execution checklist delivered", + "evidence": "https://github.com/Haaaiawd/PromptKey/pull/17 open; checklist delivered in session response" + } + ], + "at": "2026-10-08T08:38:14.928Z" + } + ], "created_at": "2026-10-08T08:16:50.774Z", - "updated_at": "2026-10-08T08:31:10.251Z" + "updated_at": "2026-10-08T08:38:14.929Z", + "completed_at": "2026-10-08T08:38:14.929Z" } ] }