Skip to content

构建与分发 ​

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

它会:

  1. 有 Web 前端时,生成 TypeScript 客户端,使前端能够针对 Go 代码构建,并运行配置中的 buildCommand,将前端构建到 frontendDist;
  2. 以生产模式编译应用,有前端时将其嵌入:不包含 Web 检查器,并链接配置中的名称、标识符和版本;
  3. 为每个平台打包到 build/<os>-<arch>/(配置中的 out);
  4. 对其能签名的内容进行签名;配置了更新时,还会写出已签名的更新归档,以及基于已发布版本的增量更新。

平台 ​

-platform 列出 GOOS/GOARCH 目标,默认为本机平台:

sh
mygo build -platform darwin/universal,windows/amd64,windows/arm64,linux/amd64,linux/arm64

darwin/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
WindowsMy App.exe、应用的文件,以及安装程序 my-app-setup-0.1.0-amd64.exe;配置了更新时还有 install.ps1
Linuxmy-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 | sh
powershell
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 提供了一个完成全部流程的工作流。