文档

应用更新

nsh/updater 让打包后的应用检查新版本、下载并替换自己。界面由你自己画,模块只提供状态和动作。它更新的是整个应用;如果只想给宿主应用下发 NSH 界面,请看签名包。

平台行为
macOS、Windows、Linux检查、下载、替换应用并重启
iOS、Android只检查,并给出引导用户前往的链接
Web无需处理,部署即更新,checkForUpdate 返回 null

#设置来源

从 GitHub Releases 更新,写在 nsh.json:

{ "version": "1.2.0", "updater": { "github": "owner/repo" } }

从自己的服务器更新必须配签名密钥,否则谁控制了服务器,谁就控制了应用运行的代码(nsh keygen keys/update,.secret 不要放进仓库):

{
  "version": "1.2.0",
  "updater": {
    "url": "https://dl.example.com/app/latest.json",
    "publicKey": "<keys/update.pub 的内容>"
  }
}

github 也可以配 publicKey,配了之后会拒绝未签名的清单。

#使用

import { checkForUpdate, downloadUpdate, installAndRestart } from "nsh/updater";
import { openUrl } from "nsh";

const update = await checkForUpdate(); // 已是最新时为 null
if (update?.canInstall) {
  await downloadUpdate(({ received, total }) => setProgress(received / (total ?? 1)));
  await installAndRestart();
} else if (update) {
  await openUrl(update.url);
}

update 包含 version、notes、url 和 canInstall。iOS、Android 上 canInstall 为 false;桌面端如果应用装在用户无法写入的位置(如系统包),也为 false。开发运行时 checkForUpdate 返回 null。

installAndRestart 会结束当前进程,需要保存的内容请先保存。如果失败,应用保持原样。

#发布新版本

nsh.json 的 version 必须符合 semver,并且每次发布都要增大。设置 "updater" 后,nsh package 还会写出一个以平台命名的压缩包。在各平台分别打包,然后生成清单:

nsh package --target linux   # My-App-1.3.0-linux-x86_64.tar.gz
nsh package --target macos   # My-App-1.3.0-macos-aarch64.tar.gz
nsh package --target windows # My-App-1.3.0-windows-x86_64.zip

nsh update-manifest *.tar.gz *.zip \
  --notes CHANGES.md \
  --key keys/update.secret \
  --link ios=https://apps.apple.com/app/id000000000

把压缩包、latest.json 和 latest.json.sig(使用 --key 时)上传为 GitHub Release 的附件,或放到自己服务器的同一目录。GitHub 上标记为 draft 或 prerelease 的版本不会被推送。

#校验内容

  • 清单签名(配了 publicKey 时)。
  • 清单的应用 id,必须是当前应用。
  • 版本号:只提供更新的版本,旧清单无法让应用降级。
  • URL:只允许 HTTPS(明文 HTTP 仅限本机测试)。
  • 压缩包的大小和 SHA-256。
  • 新应用的 id 和可执行文件,macOS 上还有代码签名。
  • 当前进程就是这个包本身:运行在其他应用内的 NSH 模块无法替换宿主。

macOS 上 nsh package 用的是 ad hoc 签名,可用于测试,但在其他 Mac 上可能被 Gatekeeper 拦截。正式分发时,请用 Developer ID 给 .app 签名并公证,再以同样的文件名重新打包(tar -czf <压缩包> <Name>.app),然后运行 nsh update-manifest。

#Rust 应用

原生 Rust 应用直接使用 nsh-updater,并提供自己的 HTTP 传输。 用 Config::check 验证清单,用 Download 验证更新包;之后调用 Installation::install_and_launch,仅在成功后退出旧进程。它会保留旧安装, 直到新进程能够启动;启动失败时回滚。分别调用 install 和 launch 不提供 这项回滚保证。这里检测的是启动错误,无法检测进程已经启动后发生的崩溃。

Config、Manifest、Asset 和 Available 均为非穷尽结构体。 使用 Default::default() 创建,再给所需字段赋值。