fpack is built for CI: no prompts, stable exit codes, JSON output, redacted logs. The recipes below use GitHub Actions; the commands are the same anywhere.
Principles#
-
Pin the version so CI is reproducible:
dart pub global activate fpack 1.1.16. -
Build each platform on its own OS. iOS and macOS on
macos-*, Windows onwindows-*, Linux onubuntu-*; Android and web run anywhere (why). -
Secrets through environment variables. Never in
fpack.yaml(Secrets and variables). - Upload
dist/. It contains the artifacts andSHA256SUMS. -
Use
--jsonand exit codes to decide what happens next (Exit codes and JSON).
Without a TTY, spinners and colors are turned off and a heartbeat line is printed every 60 seconds so CI does not time out on silence.
Android#
jobs:
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: subosito/flutter-action@v2
- run: dart pub global activate fpack 1.1.16 && echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH
- run: fpack build apk aab --split-per-abi=both
env:
FPACK_ANDROID_KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
FPACK_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
FPACK_ANDROID_KEY_ALIAS: upload
- uses: actions/upload-artifact@v7
with: { name: android, path: dist/ }
Create the secret with base64 -i upload.jks | pbcopy (Linux: base64 -w0 upload.jks). Set
FPACK_ANDROID_KEY_PASSWORD too if it differs from the keystore password. fpack writes the keystore to
build/fpack/secrets/ with mode 0600 and deletes it after the build. Details: Android.
iOS and macOS#
apple:
runs-on: macos-latest
steps:
- uses: actions/checkout@v7
- uses: subosito/flutter-action@v2
- run: dart pub global activate fpack 1.1.16 && echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH
- run: fpack build ipa dmg --json > result.json
Signed and notarized macOS builds need certificates in a temporary keychain and notary credentials: macOS: export the certificates for CI
has the complete import step with an App Store Connect API key. Use --notarize-no-wait plus a later
fpack notarize finish if you prefer not to hold a runner while Apple processes the upload.
Windows#
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v7
- uses: subosito/flutter-action@v2
- run: dart pub global activate fpack 1.1.16
- run: fpack build windows exe
Inno Setup is preinstalled on GitHub's Windows runners; for code signing, base64 the .pfx
into a secret and decode it on the runner (Windows).
Linux and web#
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: subosito/flutter-action@v2
- run: sudo apt-get install -y ninja-build libgtk-3-dev rpm
- run: dart pub global activate fpack 1.1.16 && echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH
- run: fpack build linux deb rpm web
For snaps on ubuntu-24.04, set linux.snap.build_mode: destructive; use ubuntu-24.04-arm
for an arm64 snap (Linux).
Tips#
-
Cache the core: the native core (about 4 MB) is downloaded once per version into
~/.cache/fpack(Linux),~/Library/Caches/fpack(macOS) or%LOCALAPPDATA%\fpack(Windows). Cache that folder to skip the download. -
Machine-readable results:
fpack build … --json > result.jsonwrites every artifact with path, size, SHA-256 and notarization state; human output goes to stderr. - Fail fast: exit code 3 means a prerequisite is missing; nothing was built.
-
A version per run: pass
--build-number "$GITHUB_RUN_NUMBER"so reruns never collide with existing artifacts (versions). -
Preview first:
fpack build --all --dry-runin a pull request shows what a release would do.
fpack 为 CI 而生:不提问、退出码稳定、有 JSON 输出、日志已脱敏。下面的示例用 GitHub Actions,命令在任何 CI 中都一样。
原则#
- 固定版本,保证 CI 可复现:
dart pub global activate fpack 1.1.16。 -
每个平台在自己的系统上构建。 iOS 和 macOS 用
macos-*,Windows 用windows-*,Linux 用ubuntu-*;Android 和 Web 在哪里都行(为什么)。 - 密钥通过环境变量传入。 永远不要写进
fpack.yaml(密钥与环境变量)。 - 上传
dist/。 里面有产物和SHA256SUMS。 - 用
--json和退出码决定下一步(退出码与 JSON)。
没有 TTY 时,进度动画和颜色会关闭,每 60 秒输出一行心跳,避免 CI 因长时间无输出而超时。
Android#
jobs:
android:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: subosito/flutter-action@v2
- run: dart pub global activate fpack 1.1.16 && echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH
- run: fpack build apk aab --split-per-abi=both
env:
FPACK_ANDROID_KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }}
FPACK_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
FPACK_ANDROID_KEY_ALIAS: upload
- uses: actions/upload-artifact@v7
with: { name: android, path: dist/ }
用 base64 -i upload.jks | pbcopy(Linux:base64 -w0 upload.jks)生成密钥内容。如果密钥密码与密钥库密码不同,再设置
FPACK_ANDROID_KEY_PASSWORD。fpack 会把密钥库以 0600 权限写到 build/fpack/secrets/,构建后删除。详情:Android。
iOS 与 macOS#
apple:
runs-on: macos-latest
steps:
- uses: actions/checkout@v7
- uses: subosito/flutter-action@v2
- run: dart pub global activate fpack 1.1.16 && echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH
- run: fpack build ipa dmg --json > result.json
已签名并公证的 macOS 构建需要在临时钥匙串里准备证书,以及公证凭证:macOS:为 CI 导出证书里有完整的导入步骤(用 App Store Connect API 密钥)。如果不想在 Apple 处理期间占着 runner,可以用
--notarize-no-wait,之后再运行 fpack notarize finish。
Windows#
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v7
- uses: subosito/flutter-action@v2
- run: dart pub global activate fpack 1.1.16
- run: fpack build windows exe
GitHub 的 Windows runner 预装了 Inno Setup;代码签名时,把 .pfx 做成 base64 存入密钥,在 runner 上解码(Windows)。
Linux 与 Web#
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: subosito/flutter-action@v2
- run: sudo apt-get install -y ninja-build libgtk-3-dev rpm
- run: dart pub global activate fpack 1.1.16 && echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH
- run: fpack build linux deb rpm web
在 ubuntu-24.04 上构建 snap 时,设置 linux.snap.build_mode: destructive;arm64 的 snap 用
ubuntu-24.04-arm(Linux)。
小技巧#
-
缓存核心:原生核心(约 4 MB)每个版本只下载一次,存放在
~/.cache/fpack(Linux)、~/Library/Caches/fpack(macOS)或%LOCALAPPDATA%\fpack(Windows)。缓存这个目录就能跳过下载。 -
机器可读的结果:
fpack build … --json > result.json会写出每个产物的路径、大小、SHA-256 和公证状态;人类可读的输出走 stderr。 - 尽早失败:退出码 3 表示缺少前置条件,什么都没构建。
-
每次运行一个版本:传
--build-number "$GITHUB_RUN_NUMBER",重跑就不会与已有产物冲突(版本)。 - 先预览:在 pull request 里运行
fpack build --all --dry-run,可以看到发布会做什么。