Skip to content

feat(linux): AppImage 嵌入更新信息并发布 .zsync 增量更新(AppImageUpdate/AppImageLauncher 支持) - #83

Merged
std-microblock merged 1 commit into
std-microblock:masterfrom
std-external:fix/78-appimage-update-info
Oct 8, 2026
Merged

std-microblock merged 1 commit into
std-microblock:masterfrom
std-external:fix/78-appimage-update-info

Conversation

@std-external

@std-external std-external commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

为 Linux AppImage 加上 AppImage 的「更新信息(update information)」并随 release 发布 .zsync,让 AppImageUpdate / AppImageLauncher / AppManager 等工具能够做增量更新(只下载 diff)。Fixes #78。

现状与根因

  • std-microblock/CeleMod 的 release 里只有 CeleMod_x.y.z_amd64.AppImage,没有 .zsync;AppImage 的 .upd_info ELF 段也是空的,所以 AppImageUpdate 之类的工具认不出更新源(issue Linux: Embed AppImage update information #78)。
  • Tauri 的 AppImage 打包链路本身不暴露 appimagetool 的 -u:tauri-bundler(crates/tauri-bundler/src/bundle/linux/appimage/linuxdeploy.rs:172)只是把 AppDir 交给 linuxdeploy,Bundle > Linux > AppImage 也没有对应配置项。
  • 但 tauri 下载的 linuxdeploy 的 appimage 输出插件支持用环境变量传:linuxdeploy-plugin-appimage 的 src/main.cpp:138 会依次读 LDAI_UPDATE_INFORMATION / UPDATE_INFORMATION / LDAI_UPD_INFO / UPD_INFO,并转成 appimagetool -u <值>;appimagetool 收到后会把字符串写进 ELF 的 .upd_info 段(appimagetool/src/appimagetool.c:1033-1075),并调用 zsyncmake 生成同名 .zsync(src/appimagetool.c:1133-1155)。所以不用改打包器,只要在 CI 里设置好这个变量 + 装 zsync。
  • 另一个坑:zsyncmake 生成的文件落在当前工作目录(<AppImage 文件名>.zsync),不是 AppImage 旁边。而 tauri-cli 在 build 的 setup() 里会 set_current_dir(dirs.tauri)(crates/tauri-cli/src/build.rs:166,即 src-tauri/),所以 CI 里这个文件会出现在 src-tauri/CeleMod_x.y.z_amd64.AppImage.zsync,需要收进 target/release/bundle/appimage/ 才能作为产物/资产上传。

改动

.github/workflows/Build.yml

  • Linux 依赖加 zsync(提供 zsyncmake,appimagetool 靠它生成 .zsync)。

  • 新增 Configure AppImage delta updates:tag 构建写入 channel latest,master 构建写入 channel nightly,PR 构建不写(产物不带更新信息):

    UPDATE_INFORMATION=gh-releases-zsync|<owner>|<repo>|<channel>|CeleMod_*_amd64.AppImage.zsync
    
  • 新增 Collect AppImage delta update file:把 .zsync 收进 target/release/bundle/appimage/(find -maxdepth 2 覆盖上面说的 src-tauri/ 和万一回到仓库根目录两种情况);如果一个都没生成就直接 ::error:: 失败,避免 release 里 AppImage 带更新信息却没有对应 .zsync。

  • 新增 Upload AppImage delta update file:.zsync 作为构建产物上传(nightly 走这条)。

  • 新增 Upload AppImage delta update to release:tag 发布时用 gh release upload ... --clobber 把 .zsync 传到 release(tauri-action 的产物白名单里没有 .zsync,不会替我们传;用 gh release upload 而不是 action-gh-release 是为了不动 release 标题/正文,并保证重跑幂等)。

scripts/publish-nightly.cjs / scripts/publish-nightly.test.cjs

  • nightly 发布同样要求存在 .AppImage.zsync:缺失时和缺平台产物一样拒绝覆盖旧 release(否则带 nightly 更新信息的 AppImage 会指向一个不存在的资产)。测试 fixture 补上该文件,并新增一个「缺 .zsync 不发布」用例。

macOS / Windows / PR 构建行为不变。

验证

用 CI 里同一套工具(tauri 从 tauri-apps/binary-releases 下载的 linuxdeploy-07333c6、linuxdeploy-plugin-appimage continuous,以及 appimagetool)在本地按 Build.yml 里新增的两段脚本原样跑了一遍(tag 与 master 各一次),并用同一个 AppDir 跑了不带 UPDATE_INFORMATION 的对照:

场景 .upd_info .zsync
GITHUB_REF=refs/tags/v1.2.1(channel latest) gh-releases-zsync|std-microblock|CeleMod|latest|CeleMod_*_amd64.AppImage.zsync 生成,Filename/SHA-1 与 AppImage 一致
GITHUB_REF=refs/heads/master(channel nightly) gh-releases-zsync|std-microblock|CeleMod|nightly|CeleMod_*_amd64.AppImage.zsync 生成,Filename/SHA-1 与 AppImage 一致
不设 UPDATE_INFORMATION(对照 / PR 构建) 空 不生成
$ readelf -p .upd_info target/release/bundle/appimage/CeleMod_1.2.1_amd64.AppImage
String dump of section '.upd_info':
  [     0]  gh-releases-zsync|std-microblock|CeleMod|latest|CeleMod_*_amd64.AppImage.zsync

$ head -c 300 target/release/bundle/appimage/CeleMod_1.2.1_amd64.AppImage.zsync | strings | head -6
zsync: 0.6.2
Filename: CeleMod_1.2.1_amd64.AppImage
Blocksize: 2048
Length: 961016
URL: CeleMod_1.2.1_amd64.AppImage
SHA-1: 7acd68c1f5a9a6b947216e02de4d5268cdea13a6   # == sha1sum CeleMod_1.2.1_amd64.AppImage

$ rename 步骤: './CeleMod_1.2.1_amd64.AppImage.zsync' -> 'target/release/bundle/appimage/CeleMod_1.2.1_amd64.AppImage.zsync'

另外校验了更新信息里的文件名模式能被 AppImageUpdate 的匹配方式(fnmatch,见 AppImageUpdate GithubReleasesZsyncUpdateInformation.cpp)命中 release 资产名:

pattern 'CeleMod_*_amd64.AppImage.zsync' matches published asset 'CeleMod_1.2.1_amd64.AppImage.zsync'

仓库内检查:

  • node --test scripts/publish-nightly.test.cjs → 9/9 通过
  • cd src-tauri && cargo test --lib → 105/105 通过(本次未改 Rust 代码)
  • actionlint 1.7.7(带 shellcheck 0.10.0)检查 .github/workflows/Build.yml → 无问题;YAML 可正常解析
  • 未改前端代码,故未跑前端测试

补充说明:channel 用 tag 名是 AppImageUpdate 支持的写法(latest 走 /releases/latest,其它值走 /releases/tags/<tag>),所以 nightly 版 AppImage 更新到 nightly release、正式版更新到 latest release;AppImage 内部嵌的是相对文件名,AppImageUpdate 会以 .zsync 的下载地址为基准解析到同 release 里的 AppImage。

Linux AppImages shipped without update information, so AppImageUpdate,
AppImageLauncher and friends could not perform delta updates (issue std-microblock#78).

linuxdeploy-plugin-appimage passes $UPDATE_INFORMATION to appimagetool -u,
which embeds the string into the .upd_info ELF section and generates the
.zsync file. Wire that up in the release and nightly builds.
@std-microblock
std-microblock merged commit d568b5b into std-microblock:master Oct 8, 2026
5 checks passed
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.

Linux: Embed AppImage update information

2 participants