外观
GitHub Actions
GitHub Actions 工作流可以在你推送标签时构建所有平台的应用、为其签名,并把它们连同更新文件上传到一个草稿 release,供你发布。每个平台在自己的 runner 上同时构建。MyGo 不需要 cgo,因此 Ubuntu runner 既能构建 Linux 应用也能构建 Windows 应用;macOS 应用需要 macOS runner,因为签名、公证和磁盘镜像需要 macOS。
工作流
在配置中设置了 updates.github 后,把以下内容保存为 .github/workflows/release.yml:
yaml
name: Release
on:
push:
tags: ["v*"]
permissions:
contents: write # the draft release, and the uploads to it
jobs:
draft-release:
runs-on: ubuntu-latest
steps:
- name: Create the draft release
env:
GH_TOKEN: ${{ github.token }}
run: |
if ! gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" > /dev/null 2>&1; then
gh release create "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" \
--draft --verify-tag --title "$GITHUB_REF_NAME" --generate-notes
fi
build:
needs: draft-release
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- { name: macOS, os: macos-latest, platform: "darwin/arm64,darwin/amd64" }
- { name: Windows, os: ubuntu-latest, platform: "windows/amd64,windows/arm64" }
- { name: Linux, os: ubuntu-latest, platform: "linux/amd64,linux/arm64" }
steps:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version-file: go.mod
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- name: Install NSIS, for the Windows installers
if: matrix.name == 'Windows' && runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y nsis
- name: Build and upload
env:
GH_TOKEN: ${{ github.token }}
MYGO_UPDATER_PRIVATE_KEY: ${{ secrets.MYGO_UPDATER_PRIVATE_KEY }}
run: bun run build -- -platform "${{ matrix.platform }}" -upload原生 UI项目没有前端,因此不需要 Bun:省略 setup-bun 和 bun install 步骤,改用 go.mod 锁定为 tool 的 CLI 构建:
yaml
run: go tool mygo build -platform "${{ matrix.platform }}" -upload把为更新签名的私钥,即 mygo-update.key 的内容(见自动更新),添加到仓库的 secrets 中:
sh
gh secret set MYGO_UPDATER_PRIVATE_KEY < path/to/mygo-update.key然后提升配置中的 version,提交并推送对应的标签:
sh
git tag v1.2.0
git push origin v1.2.0draft release 作业创建草稿 release v1.2.0。随后 build 作业为其矩阵的每一行各运行一次,同时进行,每次构建都上传到该草稿:macOS 磁盘镜像、Windows 安装程序、Linux 归档包、Debian 包和安装脚本,各自连同其更新文件。检查草稿并发布它:应用随即可更新到该版本。
- 无论工作流由哪个标签触发,
mygo build都上传到配置版本所对应的 release,其标签带updates的tagPrefix(v为默认)。可以添加一个步骤检查两者一致,见下文。 - 草稿在构建开始前就已存在,因此构建会上传到它:没有草稿时,每个
mygo build -upload都会创建自己的草稿。重新运行工作流会沿用该草稿,并替换其中的文件。 - 设置
fail-fast: false后,某个平台失败时其他平台仍会完成。使用"Re-run failed jobs"会只重新构建失败的那个,并上传到同一个草稿。 - 矩阵某一行的
platform可以列出多个目标,mygo build会依次构建它们。为每个目标(如windows/amd64和windows/arm64)各设一行,则可以在更多 runner 上同时构建。 - release 页面使用 GitHub 根据上次 release 以来的 pull request 生成的说明,你可以在草稿中编辑它。更新窗口显示的则是
CHANGELOG.md中该版本的小节,由更新清单携带。 - 工作流的
GITHUB_TOKEN让 runner 自带的gh能够创建 release 并上传到它。使用tagPrefix时,它还能让mygo build找到增量更新所基于的更早 release。
按目前的样子,工作流构建出的应用能运行,但没有为其他机器签名:macOS 应用是 ad hoc 签名,在其他 Mac 上会被 Gatekeeper 拦截;Windows 应用没有签名,SmartScreen 会警告用户。下面的小节会为它们签名。
为 macOS 应用签名并公证
macOS 那一行需要你的 Developer ID 证书和 Apple 公证服务的凭据(见签名与公证)。从"钥匙串访问"导出"Developer ID Application"证书及其私钥,存为带密码的 .p12 文件,并添加以下 secrets:
sh
base64 -i certificate.p12 | gh secret set MACOS_CERTIFICATE
gh secret set MACOS_CERTIFICATE_PASSWORD # the password of the .p12
gh secret set APPLE_ID # the Apple ID of your developer account
gh secret set APPLE_TEAM_ID # your team ID, as in the identity's (TEAMID)
gh secret set APPLE_APP_PASSWORD # an app-specific password of the Apple ID在 account.apple.com 的"登录与安全"下创建 App 专用密码。然后,在"Build and upload"步骤之前,把它们导入 macOS runner 的钥匙串:
yaml
- name: Import the certificate and the notary credentials
if: runner.os == 'macOS'
env:
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
run: |
keychain="$RUNNER_TEMP/signing.keychain-db"
password="$(openssl rand -base64 24)"
security create-keychain -p "$password" "$keychain"
security set-keychain-settings -lut 21600 "$keychain"
security unlock-keychain -p "$password" "$keychain"
echo "$MACOS_CERTIFICATE" | base64 --decode > "$RUNNER_TEMP/certificate.p12"
security import "$RUNNER_TEMP/certificate.p12" -k "$keychain" \
-P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
rm "$RUNNER_TEMP/certificate.p12"
security set-key-partition-list -S apple-tool:,apple: -s -k "$password" "$keychain"
security list-keychains -d user -s "$keychain" login.keychain
xcrun notarytool store-credentials notary --keychain "$keychain" \
--apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID" --password "$APPLE_APP_PASSWORD"
echo "NOTARY_KEYCHAIN=$keychain" >> "$GITHUB_ENV"该钥匙串与作业同生命周期,codesign 会在其中找到证书。配置使用证书签名、使用 profile 公证:在工作流中从该钥匙串读取,在你的 Mac 上则从登录钥匙串读取——那里没有设置 NOTARY_KEYCHAIN:
ts
export default defineConfig({
macos: {
signingIdentity: "Developer ID Application: Jane Doe (TEAMID)",
notarize: { keychainProfile: "notary", keychain: process.env.NOTARY_KEYCHAIN },
},
});此后 mygo build 会为应用签名,把磁盘镜像提交给公证服务并等待结果——通常需要几分钟——然后钉扎票据。也可以用 App Store Connect API 密钥代替 Apple ID:通过 --key、--key-id 和 --issuer 把密钥文件、ID 和发行者传给 notarytool store-credentials。
为 Windows 应用签名
在 Ubuntu runner 上,osslsigncode 用 .pfx 证书为 Windows 应用及其安装程序签名。把证书和它的密码添加到 secrets:
sh
base64 -i code-signing.pfx | gh secret set WINDOWS_CERTIFICATE
gh secret set WINDOWS_CERTIFICATE_PASSWORD对于 Windows 那一行,与 NSIS 一起安装 osslsigncode,把证书写入文件,并将路径和密码交给构建:
yaml
- name: Install NSIS and osslsigncode
if: matrix.name == 'Windows' && runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y nsis osslsigncode
- name: Write the certificate
if: matrix.name == 'Windows'
env:
WINDOWS_CERTIFICATE: ${{ secrets.WINDOWS_CERTIFICATE }}
run: |
echo "$WINDOWS_CERTIFICATE" | base64 --decode > "$RUNNER_TEMP/code-signing.pfx"
echo "WINDOWS_CERTIFICATE_FILE=$RUNNER_TEMP/code-signing.pfx" >> "$GITHUB_ENV"
- name: Build and upload
env:
GH_TOKEN: ${{ github.token }}
MYGO_UPDATER_PRIVATE_KEY: ${{ secrets.MYGO_UPDATER_PRIVATE_KEY }}
MYGO_WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}
run: bun run build -- -platform "${{ matrix.platform }}" -uploadts
export default defineConfig({
windows: { certificate: process.env.WINDOWS_CERTIFICATE_FILE },
});第一步替换原先只安装 NSIS 的步骤。在你机器上的构建没有该变量,不会签名。
在 Windows 上签名
私钥保存在硬件令牌或云服务(如 Azure Trusted Signing)中的证书,要用 signCommand 签名(见代码签名),它通常需要 signtool。此时应在 Windows runner 上构建 Windows 应用:它有 signtool,且 mygo build 会自行下载 NSIS;安装 NSIS 的步骤只用于 Linux runner:
yaml
- { name: Windows, os: windows-latest, platform: "windows/amd64,windows/arm64" }按照签名服务文档的说明,在 Windows 那一行"Build and upload"之前的步骤中登录该服务。除非设置 shell: bash,否则步骤在 Windows runner 上用 PowerShell 运行。
全新的 runner 每次运行时 mygo build 都会重新下载 NSIS。在"Build and upload"之前加一个步骤把它存入 Actions 缓存,构建就既不需要下载,也不需要访问下载源主机。锁文件锁定了 mygo-cli,mygo-cli 又锁定了 NSIS:
yaml
- name: Cache NSIS
if: runner.os == 'Windows'
uses: actions/cache@v6
with:
path: ~\AppData\Local\mygo\nsis-*
key: nsis-${{ hashFiles('bun.lock') }}在原生 UI 项目中,go.mod 锁定 CLI:缓存的 key 应基于 hashFiles('go.sum')。
检查版本
与配置版本不匹配的标签会把构建发布成另一个版本。请把版本保存在 package.json 中,由配置读取(见计算配置):
ts
import pkg from "./package.json" with { type: "json" };
export default defineConfig({
version: pkg.version,
});并在 draft release 作业创建草稿之前检查它,使错误的标签不触发任何构建:
yaml
- uses: actions/checkout@v7
- name: Check the version
run: test "$GITHUB_REF_NAME" = "v$(jq -r .version package.json)"原生 UI 项目的版本在 mygo.json 中:请改为检查 jq -r .version mygo.json。
发布 release
你可以查看草稿后自行发布,也可以让工作流在所有作业都上传完成后自动发布:
yaml
publish:
needs: build
runs-on: ubuntu-latest
steps:
- env:
GH_TOKEN: ${{ github.token }}
run: gh release edit "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" --draft=false使用 desktop-v 这样的 tagPrefix 时,请让工作流由它的标签触发(tags: ["desktop-v*"]),检查标签是否等于 desktop-v 加版本号,并用 --latest=false 创建和发布 release,使仓库的最新 release 仍属于其他 release(见构建与发布)。
发布预发布版本的标签(如 v1.3.0-beta.1)时也要加上 --prerelease,这样只有用户选择了预发布的应用才会更新到它(预发布版本)。
发布到 S3
配置了 updates.s3 时,mygo build -upload 会改为上传到存储桶(见发布到 S3)。用存储桶的凭据代替 GH_TOKEN 传给各作业:
yaml
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
MYGO_UPDATER_PRIVATE_KEY: ${{ secrets.MYGO_UPDATER_PRIVATE_KEY }}没有草稿 release:省略 draft release 作业以及 build 作业的 needs。某个平台的清单被其作业上传的那一刻,该平台的更新即已发布,工作流也不再需要 contents: write 权限。
不使用 release
没有 updates 时,-upload 无处可传。请不带它构建,并把安装程序保留为本次运行的 artifact;这也适用于每次 push,用来检查应用能否构建:
yaml
- run: bun run build -- -platform "${{ matrix.platform }}"
- uses: actions/upload-artifact@v7
with:
name: ${{ matrix.name }}
path: |
build/*/*.dmg
build/*/*-setup-*.exe
build/*/*.deb
build/*/*.tar.gzbuild 是新项目配置中 out 的默认值。