外观
构建与分发
mygo build 将项目变成供人们安装的应用:macOS 上的应用 bundle 和磁盘镜像,Windows 上的可执行文件和安装程序,Linux 上的可执行文件、desktop 条目、附带安装脚本的归档包以及 Debian 软件包。
sh
bun run build # mygo build, in a project with a web frontend
go tool mygo build # in a project of native UI它会:
- 有 Web 前端时,生成 TypeScript 客户端,使前端能够针对 Go 代码构建,并运行配置中的
buildCommand,将前端构建到frontendDist; - 以生产模式编译应用,有前端时将其嵌入:不包含 Web 检查器,并链接配置中的名称、标识符和版本;
- 为每个平台打包到
build/<os>-<arch>/(配置中的out); - 对其能签名的内容进行签名;配置了更新时,还会写出已签名的更新归档,以及基于已发布版本的增量更新。
平台
-platform 列出 GOOS/GOARCH 目标,默认为本机平台:
sh
mygo build -platform darwin/universal,windows/amd64,windows/arm64,linux/amd64,linux/arm64darwin/universal 将 arm64 和 amd64 合并到一个应用中。MyGo 不需要 cgo,因此任何机器都能为所有平台编译。某些步骤需要特定平台的工具,在其他平台上会附带说明地跳过:macOS 应用只有在 macOS 上才会签名并制成磁盘镜像;在其他系统上,只有在安装了 NSIS 时才会制作 Windows 安装程序。
CLI 默认 CGO_ENABLED=0。如果应用的依赖需要 cgo,请在环境中为 mygo dev、mygo build 和 mygo generate 设置 CGO_ENABLED=1。这类构建需要目标平台的 C 编译器;交叉编译时,请将 CC 设置为合适的交叉编译器。
| 平台 | build/<os>-<arch>/ 中的内容 |
|---|---|
| macOS | 已签名的 My App.app,以及 my-app-0.1.0-arm64.dmg(darwin/universal 时为 my-app-0.1.0.dmg);配置了更新时还有 install.sh |
| Windows | My App.exe、应用的文件,以及安装程序 my-app-setup-0.1.0-amd64.exe;配置了更新时还有 install.ps1 |
| Linux | my-app、my-app.desktop、my-app.png、用于应用所定义文件类型的 my-app.xml、应用的文件、它们的归档 my-app-0.1.0-linux-amd64.tar.gz(附带 install.sh),以及 Debian 软件包 my-app_0.1.0_amd64.deb |
其他参数:-debug 保留检查器等开发功能,-skip-dmg 和 -skip-notarize 跳过相应步骤,-sign 覆盖 macOS 的签名身份,-o 指定输出目录。参见 CLI。
名称、图标与版本
配置描述应用:
ts
export default defineConfig({
name: "My App",
identifier: "com.example.myapp",
version: "1.2.0",
copyright: "© 2026 Example Inc.",
});name是用户看到的东西:应用 bundle、可执行文件、菜单、安装程序。identifier是应用独有的反向 DNS 名称。系统以它为键保存偏好设置、权限和注册信息:一旦发布就不要再更改。version是应用的版本,App.Version()返回它,更新机制也比较它。
图标是 resources/icon.png,或配置中的 icon:一个正方形 PNG,最好是 1024×1024。mygo build 会把它制成 macOS 的 .icns、Windows 可执行文件的图标资源以及 Linux desktop 条目的图标。在它旁边,同名加 -dev 后缀的文件(resources/icon-dev.png)如果存在,就是 mygo dev 构建的开发应用的图标,以便在 Dock 或任务栏中与正式应用区分;它不会随应用发布。
在 macOS 上,macos.icon 可以添加一个资源目录(asset catalog),macOS 会优先使用它而不是 .icns:可以是用 actool 编译出的 Assets.car,其应用图标名为 AppIcon(或 macos.infoPlist 为 CFBundleIconName 设置的名称);也可以是 Icon Composer 的 .icon,即 macOS 26 的分层图标,mygo build 会用 Xcode 26 或更高版本的 actool 编译它,并用 actool 从中渲染出的 .icns 替代由 PNG 生成的 .icns。在其他环境构建或编译失败时,会跳过 .icon 并给出提示,应用继续使用由 PNG 生成的 .icns。mygo dev 显示的是 .icns。
macos.icon 也可以是你自己的 .icns,应用将使用它来代替从 PNG 渲染出的那个:适用于按尺寸分别绘制的图标,例如 16 和 32 磅使用更近的裁切,可用 iconutil 从 .iconset 生成。mygo dev 仍显示 PNG 生成的那个。
ts
export default defineConfig({
macos: { icon: "AppIcon.icon" }, // outside resources, which ship as they are
});对于没有 Xcode 的构建(如 Linux 上的 CI),请把 actool 从 .icon 编译出的 Assets.car 提交到版本库,并将其设为 macos.icon。.icon 必须命名为 AppIcon.icon:actool 以文件名命名图标,当 --app-icon 与之不符时不会编译任何内容。
sh
mkdir -p icon
xcrun actool AppIcon.icon --compile icon --platform macosx --target-device mac \
--minimum-deployment-target 12.0 --app-icon AppIcon \
--output-partial-info-plist icon/partial.plist这会写出 icon/Assets.car,对应 macos: { icon: "icon/Assets.car" }。
资源
应用在运行时读取的文件(如数据库种子数据、辅助二进制文件或托盘图标的图片)会随应用一起发布。项目 resources 目录中的一切都会被复制进应用,配置中 resources 列出的文件和目录也一样,以其基本名称放置:
ts
export default defineConfig({
resources: ["third_party/licenses", "bin/helper"],
});运行时,App.Path(mygo.PathResources) 返回它们所在的位置:macOS 应用中是 Contents/Resources,其他平台是可执行文件所在目录,go run 和 go test 下则是项目的 resources 目录:
go
dir, _ := mygo.App.Path(mygo.PathResources)
seed := filepath.Join(dir, "seed.db")mygo dev 也会把它们复制到开发应用中,并在它们变化时重新构建。隐藏文件会被排除。
平台资源
有些资源只属于某个平台,例如为特定系统和处理器构建的程序。resources 中以平台命名的目录用于存放它们,其内部布局与 resources 本身相同:
resources/
├── data/seed.db every app
├── darwin/ macOS apps
├── darwin-arm64/bin/server macOS on Apple silicon
├── darwin-amd64/bin/server macOS on Intel
├── linux-amd64/bin/server Linux on x86-64
├── linux-arm64/bin/server Linux on ARM64
└── windows-amd64/bin/server.exe Windows on x86-64它们的命名与 -platform 的目标一致:只写系统名(darwin、linux 或 windows)适用于该系统的所有应用,写系统名加架构名(如 darwin-arm64 或 windows-amd64)则只适用于一个目标。应用发布时,其目标对应的平台目录会与其他资源合并,因此上面的 Linux x86-64 应用拥有 data/seed.db 和自己的 bin/server,不含其他平台的内容。若两个目录都会安装同一路径,构建会失败;按照其他工具的命名方式命名的目录(如 darwin-x64 或 macos,它们会被发布到所有平台)同样导致失败。当某个系统有其他架构的目录而该目标自身没有目录时(如 linux-amd64 旁缺少 linux/arm64),构建也会给出提示。
通用 macOS 应用(darwin/universal)会发布 darwin 和 darwin-universal 目录,并把 darwin-arm64 与 darwin-amd64 合并:一个目录中的每个文件都需要在另一个目录中有对应文件,程序和库会被合成通用二进制(universal binary),如同 lipo 所做的那样,其他文件则必须完全一致。同时服务两种架构的内容(例如本来就是通用二进制的程序)请放在 darwin 中。
mygo dev 发布运行它的那台机器对应的平台目录。在 go run 和 go test 下,PathResources 就是原样的 resources 目录,包括平台目录。
辅助可执行文件
应用运行的程序(如用其他语言编写的服务器或 ffmpeg 之类的工具)也是资源:把各平台的构建产物放入其平台目录,并从那里运行。
go
dir, _ := mygo.App.Path(mygo.PathResources)
cmd := exec.Command(filepath.Join(dir, "bin", "server"))同样的代码在 Windows 上运行的是 bin\server.exe,exec.Command 即使省略扩展名也能根据绝对路径找到程序。
mygo build 会把它们与应用一起签名,以便 Gatekeeper 和 SmartScreen 接受它们,公证也能通过:
- 在 macOS 上,资源中的每个 Mach-O 可执行文件和库,以及包含它们的每个 app、framework 或插件 bundle,都会由内向外用
signingIdentity签名;使用 Developer ID 时还带有 hardened runtime 和时间戳。默认的 ad-hoc 签名只签署尚无签名的代码:Apple 芯片不运行未签名的代码。 - 在 Windows 上,配置了
certificate或signCommand时,资源中所有未签名的可执行文件和 DLL 都会被签名。已由发布者签名的保留其签名。
在 hardened runtime 下,某些程序需要 entitlements,例如即时编译代码的 JavaScript 运行时需要 com.apple.security.cs.allow-jit。代码会保留其签名时携带的 entitlements,因此这类运行时的官方构建原样即可工作;macos.helperEntitlements 则按代码在应用资源中的路径为其指定 entitlements,例如 resources/darwin-arm64/bin/server 对应 bin/server:
ts
export default defineConfig({
macos: {
signingIdentity: "Developer ID Application: Jane Doe (TEAMID)",
helperEntitlements: { "bin/server": "server.entitlements.plist" },
},
});xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>包的原生库
使用 purego 加载原生库的 Go 包(如 terminal 插件加载 libghostty-vt),在其源码旁的 mygo-plugin.json 中列出各平台的构建:每个平台一个文件、下载地址及其 SHA-256:
json
{
"libraries": [
{
"name": "libexample",
"files": {
"darwin-arm64": { "name": "libexample.dylib", "url": "https://…/libexample-darwin-arm64.dylib", "sha256": "…" },
"linux-amd64": { "name": "libexample.so", "url": "https://…/libexample-linux-amd64.so", "sha256": "…" },
"windows-amd64": { "name": "example.dll", "url": "https://…/example-windows-amd64.dll", "sha256": "…" }
}
}
]
}mygo build 和 mygo dev 会在应用所构建依赖的包中找到这些声明,把每个库下载一次到用户缓存(<cache>/mygo/natives/<sha256>/),校验其 SHA-256,然后以其名称安装到应用资源的顶层,并像那里的其他代码一样签名;通用 macOS 应用会得到包含两种架构的单个文件。包从 PathResources 加载它。为包中没有对应文件的平台构建会失败。
macOS
mygo build 生成 app bundle,为其签名,并放入磁盘镜像;镜像的窗口会引导用户把应用拖到"应用程序"文件夹。
签名与公证
默认的 ad-hoc 签名应用只能在构建它的那台 Mac 上运行,在其他 Mac 上会被 Gatekeeper 拦截。要发布应用,请使用 Apple Developer Program 的 Developer ID 为其签名,并让 Apple 对其进行公证:
ts
export default defineConfig({
macos: {
signingIdentity: "Developer ID Application: Jane Doe (TEAMID)",
notarize: { keychainProfile: "notary" },
},
});先将公证服务的凭据存入钥匙串(一次即可):
sh
xcrun notarytool store-credentials notary之后 mygo build 会以 hardened runtime 为应用签名,把磁盘镜像提交给公证服务,并将票据(staple)同时钉在磁盘镜像和应用上——更新发布的正是应用。security find-identity -v -p codesigning 列出你的身份,mygo doctor 统计 Developer ID 的数量。
Info.plist 与 entitlements
macos.infoPlist 向应用的 Info.plist 添加键,或替换 MyGo 生成的内容,例如使用摄像头或麦克风的页面所需的用途说明:
ts
export default defineConfig({
macos: {
minimumSystemVersion: "13.0",
infoPlist: {
NSCameraUsageDescription: "Scan documents with the camera.",
LSApplicationCategoryType: "public.app-category.productivity",
},
entitlements: "entitlements.plist",
dmgTitle: "My App Installer",
},
});entitlements 用一份 entitlements 属性列表为应用签名,helperEntitlements 则用于辅助可执行文件。minimumSystemVersion 是应用可运行的最旧 macOS 版本,默认为 12.0。
描述文件
受限的 entitlements(如 Communication Notifications)需要授权它们的描述文件(provisioning profile),Developer ID 分发也不例外。将 macos.provisioningProfile 设为其路径(相对于项目或绝对路径)。build 和 dev 会在签名前把它复制到 bundle 的 Contents/embedded.provisionprofile,因此签名后的应用、磁盘镜像和更新归档都包含它。
ts
export default defineConfig({
macos: {
signingIdentity: process.env.MACOS_SIGNING_IDENTITY,
provisioningProfile: process.env.MACOS_PROVISIONING_PROFILE,
entitlements: "entitlements.plist",
},
});描述文件必须与签名证书、bundle 标识符和 entitlements 相匹配,包括应用标识符和团队标识符。MyGo 按原样嵌入它;它不会创建描述文件,也不会从中推导 entitlements。Apple 在应用运行时验证授权是否有效。描述文件不能与 ad-hoc 签名(-sign -)一起使用。dev 除非传入 -sign(它不会使用 macos.signingIdentity),否则以 ad-hoc 签名,且其应用标识符为 <identifier>.dev,而应用的描述文件并不授权该标识符:请使用计算配置在开发构建中省略描述文件和受限 entitlements,
ts
export default defineConfig(({ command }) => ({
macos: {
signingIdentity: process.env.MACOS_SIGNING_IDENTITY,
...(command === "build" && {
provisioningProfile: process.env.MACOS_PROVISIONING_PROFILE,
entitlements: "entitlements.plist",
}),
},
}));或者传入 -sign 并为 dev 提供一个授权 <identifier>.dev 的描述文件。dev 会监视描述文件,在其变化时重新构建。
请勿将描述文件纳入版本控制,并通过构建环境提供其路径。参见 Apple 的《代码签名内幕:描述文件》。
Windows
可执行文件带有图标、版本信息和清单(manifest,包含按监视器 DPI 感知、现代控件),运行时不显示控制台窗口。若主包中有你自己的 .syso 文件,则会取代它们。
安装程序
mygo build 还会生成 my-app-setup-1.2.0-amd64.exe,其命名与磁盘镜像一样采用应用的 slug 和架构,因此上传到同一个 release 的两个目标的安装程序都能保留。它为当前用户将应用安装到 %LOCALAPPDATA%\Programs\My App,无需管理员权限,并允许应用自行更新;它会添加"开始"菜单快捷方式和"设置"中列出的卸载程序,并注册应用的 URL scheme 和文件关联。最后一页提供运行应用和创建桌面快捷方式的选项,且默认勾选;静默安装(/S)不会创建快捷方式。
快捷方式、"设置 > 应用"中的条目以及安装程序的标题显示应用的 name;当可执行文件及其文件夹需要保持简单名称时,则显示 windows.displayName——例如显示为 天气助手 的应用,其可执行文件位于 %LOCALAPPDATA%\Programs\weather 下的 weather.exe:
ts
export default defineConfig({
name: "weather",
windows: { displayName: "天气助手" },
});文件名不能包含的字符(如 : 和 ?)只在快捷方式名称中替换为 -。设置了 displayName 的版本,其安装程序会替换早期版本以该名称创建的快捷方式。
windows.installDirName 只命名安装文件夹,适用于其他场合都保留原名的应用,例如为只处理 ASCII 路径的工具(如使用控制台代码页的脚本)提供 ASCII 文件夹。此时应用安装在 %LOCALAPPDATA%\Programs\ZhiLian,而其可执行文件、快捷方式和"设置 > 应用"中的条目名为 智链:
ts
export default defineConfig({
name: "智链",
windows: { installDirName: "ZhiLian" },
});已安装的应用会留在其文件夹中:新版本的安装程序会安装到已安装版本的文件夹——用户可能改选过该文件夹——install.ps1 和更新也是如此。新的 installDirName 只适用于新的安装,以及应用被卸载后重新安装时的情况。
安装程序由 NSIS 制作。在 Windows 上,mygo build 在已安装 NSIS 时使用它,否则在首次需要时下载:官方 NSIS 3.13 zip,取自 MyGo 的 GitHub releases 上的副本,或从 SourceForge 下载,按其 SHA-256 校验后保存在 %LOCALAPPDATA%\mygo。在 macOS 和 Linux 上,要制作 Windows 安装程序需自行安装 NSIS(brew install makensis、apt install nsis);没有它,mygo build 会跳过安装程序。
安装程序钩子
windows.installerInclude 为安装程序添加应用自己的步骤:一个 NSIS 脚本(相对于项目目录),安装程序在 MUI2.nsh 及其语言文件之后包含它,与 electron-builder 的 include 相同。在它定义的宏中,customInstall 在安装结束时运行,此时应用的文件已放入 $INSTDIR;customUnInstall 在卸载时、卸载程序删除 $INSTDIR 之前运行;customInit 在 .onInit 中、安装程序启动时运行,例如在任何东西安装之前检查应用所需的前提条件。脚本中未定义的宏会被省略;脚本不存在时,mygo build 会停止。
ts
export default defineConfig({
windows: { installerInclude: "build/installer.nsh" },
});下面的脚本把应用随附的辅助可执行文件(打包为 resources/windows/bin/helper.exe,安装后位于 $INSTDIR\bin\helper.exe)复制到用户的数据目录,并在卸载时将其删除:
nsis
; build/installer.nsh
!macro customInstall
CreateDirectory "$LOCALAPPDATA\My App\bin"
CopyFiles /SILENT "$INSTDIR\bin\helper.exe" "$LOCALAPPDATA\My App\bin"
!macroend
!macro customUnInstall
Delete "$LOCALAPPDATA\My App\bin\helper.exe"
RMDir "$LOCALAPPDATA\My App\bin"
!macroend这些宏在静默安装和卸载(/S)中同样会运行——install.ps1 就是这样运行安装程序的:可用 IfSilent 区分两者,消息框需要 /SD 才能自动应答。更新不使用安装程序替换应用的文件,因此不会运行任何宏。customUnInstall 调用的函数属于卸载程序,名称以 un. 开头。
安装程序为当前用户安装且无需管理员权限,以便应用能够自行更新:钩子应限于用户可写入的位置,如 $LOCALAPPDATA 和 HKCU。安装程序本身已允许用户选择文件夹,卸载程序也只删除应用的文件夹,保留应用的数据。签名对安装程序及其卸载程序的适用方式与没有钩子时相同。
代码签名
未签名的应用会让 SmartScreen 向用户发出警告。请使用证书对可执行文件、辅助可执行文件、安装程序及其卸载程序进行签名:
ts
export default defineConfig({
windows: { certificate: "certs/code-signing.pfx" },
});密码来自 MYGO_WINDOWS_CERTIFICATE_PASSWORD 环境变量。在 Windows 上使用 Windows SDK 中的 signtool 签名,在其他平台上使用 osslsigncode。timestampUrl 更改时间戳服务器,默认为 http://timestamp.digicert.com。
硬件令牌或云服务(如 Azure Trusted Signing)中的证书使用各自的命令签名,其中 %1 表示文件:
ts
export default defineConfig({
windows: {
signCommand: "signtool sign /fd sha256 /tr http://timestamp.digicert.com /td sha256 /a %1",
},
});Linux
mygo build 写出可执行文件(以应用的 slug 命名,默认为应用名的小写形式)、desktop 条目和图标,以及 Debian 软件包;该软件包将应用安装到 /opt/my-app,并安装其 desktop 条目、图标、URL scheme 和文件类型:
ts
export default defineConfig({
linux: {
maintainer: "Jane Doe <jane@example.com>",
comment: "Take notes",
categories: ["Office"],
depends: ["libayatana-appindicator3-1"],
command: "my-app",
},
});该软件包依赖 GTK 3 和 WebKitGTK;depends 可添加更多依赖包。不显示网页的应用——无论在窗口还是 ui.WebView 中都不显示,即native UI应用——只需要 GTK:webview: false 会把 WebKitGTK 从其软件包中去除,install.sh 和 mygo doctor(在项目中)也不再检查它,因为应用从不加载它。maintainer 默认为 package.json 的 author,否则为应用名。comment 在 desktop 条目和软件包中描述应用,categories 决定它在应用菜单中的归类(默认为 Utility)。应用可从应用菜单打开。command 还会为它提供一个命令:由软件包提供时为 /usr/bin/my-app,由 install.sh 提供时为 ~/.local/bin/my-app;不设置时,两者都不会添加命令,从而把该名称留给同名的命令行工具,例如应用自身的工具。
应用一旦发布就保留其 slug。它的 Linux 文件都以 slug 命名,更新时按同名文件替换:若更新到一个 slug 不同的版本,旧的可执行文件会残留在原地,而 install.sh 注册的 desktop 条目仍会运行它;新 slug 的 Debian 软件包则是另一个包,与旧包并存安装。请在首次发布前确定 slug,例如 云音乐 用 cloud-music,因为按其名称本身会得到 yun-yin-le。
安装脚本
每次 Linux 构建还会得到应用的归档包 my-app-1.2.0-linux-amd64.tar.gz 和 install.sh;后者无需 root 权限为用户安装:应用放在 ~/.local/my-app.app(在那里它可以自行更新),以及它的 desktop 条目、图标、URL scheme 和文件类型;设置了 linux.command 时,该命令安装到 ~/.local/bin,除非那里已有其他程序。配置了更新后,脚本会下载本机更新清单所指明的最新版本,并按 updates.publicKey 校验其大小和 Ed25519 签名(与应用更新程序的做法相同),然后才安装,因此一行命令即可安装应用:
sh
curl -fsSL https://github.com/me/my-app/releases/latest/download/install.sh | sh当 tagPrefix 不是 v 时,任意 release 中的脚本(如 …/releases/download/desktop-v1.2.0/install.sh)都会安装带有该前缀标签的最新 release。
它用 openssl pkeyutl -rawin 校验签名,OpenSSL 3.0 及以后版本支持该命令(Debian 12、Ubuntu 22.04、Fedora 36、RHEL 9 及以后)。没有它时(如 OpenSSL 1.1.1),脚本不下载任何东西并说明原因;此时可以从 release 下载归档包并交给脚本——脚本会安装传入的或在其旁边找到的归档包,但不做校验。
-upload 会把它与归档包一起发布;使用 updates.url 时,请把它发布在清单旁边。当它与本版本的归档包放在一起(如 build/linux-amd64/ 中)时,脚本改为安装那个归档包;它也会安装传入的归档包:
sh
sh install.sh my-app-1.2.0-linux-amd64.tar.gz
sh install.sh --uninstall # keeps the settings and data of the app再次运行脚本时,它会把找到的版本安装到已安装版本之上。应用需要 WebKitGTK,而 install.sh 不会安装它:当系统库缓存中没有 WebKitGTK 时,脚本会在安装完成后说明这一点,并给出在 Debian、Ubuntu、Fedora、Arch 和 openSUSE 上安装它的命令,除非 linux.webview 为 false。应用自行更新时,会注册新版本的 desktop 条目和文件类型,因此新增了 URL scheme 或文件类型的版本也能获得注册。希望由系统包管理器更新应用的用户适合用 Debian 软件包;其他人适合用 install.sh,它是能自行更新的 Linux 安装方式。
macOS 与 Windows 的安装脚本
配置了更新后,同一个 install.sh 也能在 macOS 上安装应用,Windows 构建还会得到 install.ps1,因此在每个系统上都只需一行命令安装应用:
sh
curl -fsSL https://github.com/me/my-app/releases/latest/download/install.sh | shpowershell
irm https://github.com/me/my-app/releases/latest/download/install.ps1 | iex在 macOS 上,install.sh 下载该 Mac 架构的更新清单所指明的归档包,没有则用 darwin-universal 的(即使在 Rosetta 下的 shell 中,也会选择 Apple 芯片的 darwin-arm64),并按 updates.publicKey 校验其大小和 Ed25519 签名,与应用更新程序的做法相同(macOS 没有处理 Ed25519 签名的工具,因此脚本用 openssl 和 shasum 计算哈希,并用 JavaScript for Automation 完成曲线运算);若应用正在运行则先退出它,然后替换 /Applications 中的 bundle——当用户无法写入 /Applications,或应用已在 ~/Applications 中时,则替换 ~/Applications 中的——随后打开应用。它从不请求 root 权限;同名但 bundle 标识符不同的应用不受影响。curl 不会设置隔离属性,因此应用打开时不会出现 Gatekeeper 针对下载应用的提示。sh install.sh --uninstall 删除应用并保留其设置和数据。
install.ps1 可在 Windows PowerShell 5.1 和 PowerShell 7 中运行。它下载与本 PC 架构(amd64 或 arm64)对应的最新版本的安装程序,按 SHA256SUMS 校验其 SHA-256;若应用正在运行则先退出它;然后在已安装版本的文件夹中静默运行安装程序(/S),并打开应用。卸载照常通过安装程序进行,即从"设置 > 应用"卸载。
配置了更新的构建会把 SHA256SUMS 写入输出目录,其中按 sha256sum 格式列出各安装程序、归档包和脚本的 SHA-256;-upload 会将它们一并发布,并合并其他平台早前上传的行:即同一 GitHub release 中的行,或 updates.url 为 updates.s3 所提供的 SHA256SUMS 中的行,同时去掉被本次上传替换的旧版本的文件行。它按构建时的方式命名文件;GitHub 会把上传文件名中的空格变成点,install.ps1 遵循这一规则。使用 updates.url 和自己的服务器时,请把脚本和 SHA256SUMS 发布在清单旁边,其中包含每个平台最新文件的行。
SHA256SUMS 没有签名:它能发现损坏或被截断的下载,但防不住有人替换了 release 文件的情况。在 Windows 上,请为安装程序签名(代码签名),以便 Windows 显示制作方是谁。
测试时,MY_APP_DOWNLOAD_URL(应用 slug 的大写形式)指定两个脚本的下载位置,MY_APP_INSTALL_DIR 指定 macOS 上应用的文件夹,MY_APP_NO_OPEN=1 则不打开应用。
语言
带有消息目录的应用会向各系统声明其语言,源语言排在首位,系统最终会回退到它:
- macOS:
CFBundleDevelopmentRegion、CFBundleLocalizations,并在Contents/Resources中为每种语言建立一个本地化文件夹,按 Xcode 的命名方式命名(de.lproj、zh-Hans.lproj,zh-Hant-HK对应zh-HK.lproj),这样 AppKit 就会用应用显示的语言输出它自己的文本(服务菜单、面板),"系统设置"也会列出应用支持的语言。 - Linux:desktop 条目中的
Name[xx]和Comment[xx],按 POSIX 区域设置命名(de、zh_CN、zh_TW、sr@latin)。 - Windows:安装程序提供每种语言版本,并选择与用户显示语言匹配的语言,否则使用源语言;只使用中文(
"i18n": { "source": "zh-Hans" })的应用,其安装程序只有中文版,但其中 MyGo 自身的少量文本——桌面快捷方式的复选框以及关于正在运行的应用的提示——是英文的。NSIS 3 带有约 60 种语言的文件,遇到它没有的语言时构建会失败,并列出它支持哪些语言。文件属性为每种语言各有一组字符串表。
应用在各语言下的名称和描述来自其消息目录的 mygo.appName 和 mygo.appDescription;配置中的 name 和 linux.comment 是源语言下的名称与描述,在 Windows 上若设置了 windows.displayName,则用它代替 name。没有消息目录的应用不声明任何语言。
在 macOS 上,Info.plist 中的其他字符串,例如系统请求麦克风或系统音频权限时显示的理由(NSMicrophoneUsageDescription、NSAudioCaptureUsageDescription),按照 Xcode 工程的方式翻译:在应用的资源中为每种语言放置一个 InfoPlist.strings,构建会保留它们,并在文件缺少应用名称时补上应用名。
resources/darwin/
├── ja.lproj/InfoPlist.strings "NSMicrophoneUsageDescription" = "音声メモを録音します。";
└── zh-Hans.lproj/InfoPlist.strings "NSMicrophoneUsageDescription" = "用麦克风录语音笔记。";该文件采用 Xcode 写出的文本格式(UTF-8 或 UTF-16),或 XML 属性列表。
URL scheme 与文件类型
配置中的 urlSchemes 和 fileAssociations(参见深度链接和文件关联)由每种安装包负责注册:macOS 应用写入其 Info.plist,Windows 由安装程序注册,Linux 由 desktop 条目、安装脚本和 Debian 软件包注册。
发布
在配置中设置 updates.github 后,mygo build -upload 会把磁盘镜像、安装程序、软件包、Linux 归档包和安装脚本以及更新文件上传到该版本对应的 GitHub release,标签为 v1.2.0,不存在时将其创建为草稿。请检查草稿后再发布。它需要已登录的 GitHub CLI(gh)。使用 updates.s3 时,它会把上述文件上传到 Amazon S3 或兼容服务(如 Cloudflare R2)的存储桶,凭据放在 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY 中(参见发布到 S3)。
在 CI 中于各平台构建并上传到同一个 release:macOS runner 负责签名、公证和制作磁盘镜像;任何 runner 都能编译并打包 Windows 和 Linux 应用。请把签名机密保存在 CI secrets 中:MYGO_WINDOWS_CERTIFICATE_PASSWORD、MYGO_UPDATER_PRIVATE_KEY,以及 macOS runner 钥匙串中的公证凭据。GitHub Actions 提供了一个完成全部流程的工作流。