为 Flutter 应用打包为 Flatpak 并以正确的姿势上架到 Flathub (下)
本文紧接“为 Flutter 应用打包为 Flatpak 并以正确的姿势上架到 Flathub (上)”,
主要记录上架 Flathub
的过程。
什么是 Flathub
简单理解为在 Linux 中使用 FLatpak 技术,类似 Microsoft Store / Mac App Store / Unbuntu Snap Store 的应用商店。 可以在其官网流浪并安装应用,Flathub 也是一个 repo,也可以通过通用命令安装应用:
APP_ID=<your-app-id>
flatpak install flathub $APP_ID
flatpak run $APP_ID
从哪里开始
由于 Flathub 相对普通的自打包有诸多额外限制, 因此强烈建议从 “For app authors” 开始仔细阅读每一个章节。 本文只从 “Table Habit” 创建于提交流程出发,记录整个过程和踩到的坑。
1. 基本要求
这里是一些基本要求,这里整理了指向官方文档的链接。需要注意的是下面内容只截至到本文发表时间保证有效,如有区别请以 Flathub 官方文档为准:
内容 | 备注 |
---|---|
包容性政策 | 请对照查看软件是否符合上架要求 |
App ID | 确保应用 AppID 符合上架要求,Flathub 要求使用 “reverse-DNS 命名法” |
许可 | 确保使用可以被合法分发的 license,且必须在 meteinfo.xml 中进行声明 |
权限 | 仔细阅读需要使用的权限,不然很容易被拒绝,详细说明后面章节会叙述;部分权限(e.g. dbus 相关)除非特殊原因肯定会被 lint CI 拒绝,请谨慎使用 |
还有一些构建时的限制:
- 构建时无网络访问,即在
bnuild-options
中使用--share=network
无效。 由于 Flutter 默认会在构建时自动下载相关依赖,因此使用 Flathub 官方教程在进行构建时几乎一定会失败。 后续会介绍使用flatpak-flutter
进行预处理,保证可以构建成功。 - 尽量从源码构建。紧接上一条,由于 Flutter 应用依赖外部下载,因此一种解决的方法便是预构建二进制文件,
再由 Flathub 直接分发。但由于不是由 Flathub 构建,可能出于导致各种各样的兼容性问题的原因,
Flathub 默认需要从源码构建(私货:个人强烈同意,从源码构建甚至是可重复构建从安全角度对于自由/开源软件是必须的)。
flatpak-flutter
可以避免二进制分发,后面会赘述。
2. 准备 Repo
使用 Github Cli:
gh repo fork --clone flathub/flathub
cd flathub
git checkout --track origin/new-pr
git checkout -b my-app-submission new-pr
或者手动操作:
- Fork Flathub/Flathub,同时取消勾选 “Copy the master branch only”。
-
执行以下操作:
git clone --branch=new-pr git@github.com:<your-github-name>/flathub.git && cd flathub git checkout -b my-app-submission new-pr
确保当前分支从 new-pr
派生并且命名为 my-app-submission
。
3. 需求文件与内容
确保上面创建的本地 repo 中包含以下文件,可选文件请参考 “Required files”:
manifest.yml/yaml/json
:清单文件必须存在,并以<app-id>.yml/yaml/json
命名。 如果使用flatpak-flutter
该文件会由对应的 Manifest 模板自动生成。
还有一些必要的文件 Flathub 几乎是强制要求在上游 repo 中维护。 不过实际查看中很多应用将这些文件放在了该 repo 中,如有需求请在提交后和维护者沟通, 这里(包含后续)默认这些文件存放在上游 repo:
metadata.xml
:需要通过 Flathub 定制的 appsstream 合法性验证,后续会详细说明。- 应用程序图标:要求是 svg 或者 png,后续也会再次说明;对于 GUI 应用是必选的。
<app-id>.desktop
:该文件对 GUI 应用是必选的。
4. flatpak-flutter
在“1. 基本要求”中,说明了 Flutter
应用默认在离线环境中几乎无法进行构建,
但 Flathub 又需要尽量避免直接提交二进制文件,
因此我们需要在清单文件中列出需要构建的 Flutter 应用中所有依赖 package 的源码构建信息。
上面如果手动操作的话需要查询 pubspec.lock
并手动或写一个脚本生成所有依赖描述,
这无疑是一项大工程,幸好有 flatpak-flutter
这个现成的轮子。
该项目的简单原理就是将 flutter 中所有需要下载的流程转化为 Manifest 中的描述格式, 一般包含:
flutter-sdk-x.y.z.json
:编译 FLutter 需要的依赖pubspec-sources.json
:pubspec.lock
中列出的依赖
具体说明和更多用法(比如处理外部依赖)可以直接看该项目的 README。
而对于一个简单的 Flutter 项目,我们需要做的是:
- 创建一个需要的 Manifest 模板文件,一般命名为
flatpak-flutter.yml/yaml
- 按 [5. Manifest] 中示例编写 Manifest 文件
- 执行
flatpak-flutter flatpak-flutter.yml
- 成功后,将生成的额外清单文件加入 repo
建议在
.gitignore
中加入如下内容,这些都是flathub / flatpak-flutter
运行过程中的中间产物:.flatpak-builder/ build/ repo/
5. Manifest
与上篇文章中的 “Manifest” 大致相同,但有些许区别。
下面还是先贴上 flathub/io.github.friesi23.mhabit
中使用的清单文件(flatpak-flutter.yaml
):
# yaml-language-server: $schema=https://raw.githubusercontent.com/flatpak/flatpak-builder/main/data/flatpak-manifest.schema.json
---
app-id: io.github.friesi23.mhabit
runtime: org.freedesktop.Sdk
runtime-version: "24.08"
sdk: org.freedesktop.Sdk
sdk-extensions:
- org.freedesktop.Sdk.Extension.llvm19
command: mhabit
separate-locales: false
finish-args:
- --share=network
- --share=ipc
- --device=dri
- --socket=fallback-x11
- --socket=wayland
- --talk-name=org.freedesktop.Notifications
modules:
- shared-modules/libsecret/libsecret.json
- name: jsoncpp
buildsystem: meson
config-opts:
- --buildtype=release
- --default-library=shared
sources:
- type: archive
url: https://github.com/open-source-parsers/jsoncpp/archive/refs/tags/1.9.6.tar.gz
sha256: f93b6dd7ce796b13d02c108bc9f79812245a82e577581c4c9aabe57075c90ea2
cleanup:
- "/lib/*.a"
- "/include"
- name: mhabit
buildsystem: simple
build-options:
arch:
x86_64:
env:
BUNDLE_PATH: build/linux/x64/release/bundle
aarch64:
env:
BUNDLE_PATH: build/linux/arm64/release/bundle
append-path: /usr/lib/sdk/llvm19/bin:/run/build/mhabit/flutter/bin
prepend-ld-library-path: /usr/lib/sdk/llvm19/lib
env:
PUB_CACHE: /run/build/mhabit/.pub-cache
build-commands:
- flutter build linux --release
- install -D $BUNDLE_PATH/mhabit /app/bin/mhabit
- install -Dm644 assets/logo/macos.svg /app/share/icons/hicolor/scalable/apps/io.github.friesi23.mhabit.svg
- install -Dm644 flatpak/io.github.friesi23.mhabit.metainfo.xml -t /app/share/metainfo
- install -Dm644 flatpak/io.github.friesi23.mhabit.desktop -t /app/share/applications
- cp -r $BUNDLE_PATH/lib /app/bin/lib
- cp -r $BUNDLE_PATH/data /app/bin/data
sources:
- type: git
url: https://github.com/FriesI23/mhabit.git
tag: flathub-v1.16.22+92
commit: cbedd98a6e24a6a5cc0a48bcea925928ac2087ae
disable-submodules: true
- type: git
url: https://github.com/flutter/flutter.git
tag: 3.24.5
dest: flutter
这里列出几个和自己构建不一样的部分:
5.1. runtime
截止该文章撰写时,Flathub 要求使用的 freedesktop 版本是 24.08,其他版本可能会被维护者问询或者拒绝, 如无其他必要请确保使用 Flathub 中支持(建议)使用的 runtime 包和对应版本。
runtime: org.freedesktop.Sdk
runtime-version: "24.08"
5.2. llvm19
编译 Flutter 需要使用 clang
,因此需要引入 llvm
。
请注意不同 runtime 对应的 llvm 版本,如果不清楚可以使用以下命令查看:
flatpak remote-ls flathub --user | grep org.freedesktop.Sdk.Extension.llvm
sdk-extensions:
- org.freedesktop.Sdk.Extension.llvm19
# ...
modules:
- name: mhabit
build-options:
# ...
append-path: /usr/lib/sdk/llvm19/bin:...
prepend-ld-library-path: /usr/lib/sdk/llvm19/lib
# ...
5.3. finish-args
权限是个人在提交验证错误的并要求修改的重灾区,因为 Flathub 不光检查清单文件的合法性, 还会有很多额外的限制,具体请看:“Linter”,并搜索以 “finish-args-“ 开头的语法。 个人在实践中主要遇到了一下几个问题:
finish-args-arbitrary-dbus-access
:Fluthub 禁止对任意 dbus 的访问,如有需要请申请具体的 dbus 名称, 一般不需要申请这个。- finish-args-portal-talk-name:Flatpak 默认允许
--talk-name=org.freedesktop.portal.<...>
,无需重复申请。 Flathub 会检查这些权限,如果存在则报错。
还有一些权限需要仔细检查是否需要:
--talk-name=org.freedesktop.Notifications
:如果代码或者依赖包不支持使用org.freedesktop.portal
接口弹出通知则需要该权限。--talk-name=org.freedesktop.<...>
:同上,如果代码使用protal
接口,则都不需要申请权限,反之亦然。
finish-args:
- --share=network
- --share=ipc
- --device=dri
- --socket=fallback-x11
- --socket=wayland
- --talk-name=org.freedesktop.Notifications
5.4. shared-modules
一些 Flathub 中已经定义好的库存放在 flathub/shared-modules
中,
可以通过使用 submodule 引入后直接使用:
git submodule add https://github.com/flathub/shared-modules.git
modules:
- shared-modules/libsecret/libsecret.json
5.5. flutter
通过以下方式引入 Flutter,由于 flakpak-flutter
脚本限制,
现在 sources 中的 App 和 Flutter 本身都需要使用 git 和 url 的方式引入,
因此如果有特殊需要可以主动修改并提 PR….
modules:
- name: mhabit
build-options:
# ...
append-path: ...:/run/build/mhabit/flutter/bin
env:
PUB_CACHE: /run/build/mhabit/.pub-cache
# ...
sources:
- type: git
url: https://github.com/FriesI23/mhabit.git
tag: flathub-v1.16.22+92
commit: cbedd98a6e24a6a5cc0a48bcea925928ac2087ae
disable-submodules: true
- type: git
url: https://github.com/flutter/flutter.git
tag: 3.24.5
dest: flutter
5.6. 其他
- Flathub 现在要求将所有的 modules 都内联(inline),因此如无必要尽量不要手动拆分为多个文件。
-
请务必在文件头引入以下内容保证基本格式符合 flatpak 要求。
# yaml-language-server: $schema=https://raw.githubusercontent.com/flatpak/flatpak-builder/main/data/flatpak-manifest.schema.json --- # manifest content # ...
6. Metainfo 和 Desktop
两者请在上游维护,并在 Manifest
中引用。以自己的应用为例:
在应用仓库维护如下结构:
/root/flatpak
├── io.github.friesi23.mhabit.desktop
├── io.github.friesi23.mhabit.metainfo.xml
└── screenshots
├── en-US
│ ├── 1.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ ├── 5.png
│ └── 6.png
└── zh-CN
├── 1.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
└── 6.png
在 Manifest 的 build-commands
中引用:
modules:
- name: mhabit
build-commands:
# 编译二进制文件
- flutter build linux --release
# 安装主程序
- install -D $BUNDLE_PATH/mhabit /app/bin/mhabit
# 安装图标
- install -Dm644 assets/logo/macos.svg /app/share/icons/hicolor/scalable/apps/io.github.friesi23.mhabit.svg
# 安装元信息
- install -Dm644 flatpak/io.github.friesi23.mhabit.metainfo.xml -t /app/share/metainfo
# 安装桌面文件
- install -Dm644 flatpak/io.github.friesi23.mhabit.desktop -t /app/share/applications
# 安装构建依赖
- cp -r $BUNDLE_PATH/lib /app/bin/lib
- cp -r $BUNDLE_PATH/data /app/bin/data
# ...
sources:
- type: git
url: https://github.com/FriesI23/mhabit.git
tag: flathub-v1.16.22+92
commit: cbedd98a6e24a6a5cc0a48bcea925928ac2087ae
disable-submodules: true
- type: git
url: https://github.com/flutter/flutter.git
tag: 3.24.5
dest: flutter
另外自己在实践中有几点需要注意:
- 必须包含至少一个截图,并描述在
screenshots/screenshot
节点中。 releases
节点必须包含且至少含有一个版本,且不要随意删除版本,否则可能出发 Flathub 审查。
有关元信息相关可以查看官方文档:“Appstream Validation Errors”
与其他应用商店类似,Falthub 也存在一个质量文档,可以查看 “Quality guidelines” 并遵嘱其中标准以获得更好的产品体验。
7. 准备本地构建
此时先保证已经执行了 flatpak-flutter flatpak-flutter.yml
,此时本地 repo 结构应如下:
├── .git
├── .gitignore
├── .gitmodules # submodule 描述信息,如果引入类似 shared-modules 则需要
├── flatpak-flutter.yml # 模板文件
├── flutter-sdk-3.24.5.json # 注:这里是你自己配置的 Flutter 版本
├── flutter-shared.sh.patch # Patch 文件也要包含在内
├── io.github.friesi23.mhabit.yml # 生成的 Manifest 文件
├── package_config.json
├── pubspec-sources.json # 第三方 package 的 Manifest 文件
└── shared-modules # 如果没有拉取 submodule,则使用 git submodule update --init --recursive 获取
# 检查 Manifest 文件是否符合 Flathub 要求
# 如果执行后有报错则一定要处理,否则后续提 PR 的测试构建 CI 必然会失败
# 理想情况什么都不会输出
# flatpak run --command=flatpak-builder-lint org.flatpak.Builder \
# manifest your-app-id.yml
flatpak run --command=flatpak-builder-lint org.flatpak.Builder manifest \
io.github.friesi23.mhabit.yml
# 检查 Metainfo 文件是否符合 Flathub 要求
# 具体同上,先保证这两个验证可以通过
# flatpak run --command=flatpak-builder-lint org.flatpak.Builder appstream \
# your-app-id.yml.metainfo.xml
flatpak run --command=flatpak-builder-lint org.flatpak.Builder appstream \
io.github.friesi23.mhabit.metainfo.xml
# 使用和 Flathub 相同的环境进行构建,如果失败可以尝试时候下面给出的命令尝试构建:
# flatpak-builder --repo=repo --force-clean --sandbox --user \
# --install --install-deps-from=flathub build your-app-id.yml
# 如果上面命令成功,则可能是环境问题
# e.g. 不要在 vscode 的内建 shell 内执行,建议使用标准的 Terminal
# 或者一个干净的 shell 环境,然后重复下面的 Flathub 构建命令。
#
# flatpak run --command=flathub-build org.flatpak.Builder your-app-id.yml
flatpak run --command=flathub-build org.flatpak.Builder \
io.github.friesi23.mhabit.yml
# 对构建产物(repo)再进行一次检查
# 具体同上述检查流程,保证没有报错,尽量没有警告
flatpak run --command=flatpak-builder-lint org.flatpak.Builder repo repo
8. 准备提交
这里引用原文:
Now open a pull request against the
new-pr
base branch on GitHub. The title of the PR should be “Add org.example.MyAwesomeApp”.
如图:
然后就是按 Review 结构进行修改,最后等待通过即可。
9. Done
通过后,该 PR 会被自动关闭,同时新生成一个 Flathub/<your.app.id>
的仓库,并会收到一个邀请,接收后即可自行维护,
这也代表该 App 已被 Flathub 接收,稍后便能在 flathub.org
中查询到。
后需维护请参考:“App Maintenance”。
总结
由于事先以自行查阅大量文档和其他应用的 repo 且已经成功构建出 Flatpak Single-file Bundle, 也因为 App 本省体量较小,因此提交时并没有遇到很多问题。
其中比较容易踩坑的就是权限问题,因为申请的权限即使用不到测试时也不会报错,而做减法永远是有风险的, 因此浪费了一定的时间排查。
应用上架的 PR 可以查看:“Add io.github.friesi23.mhabit #6732”
当然也建议查看其他 Open 或者 Closed 的 PR,参考学习别人的提交流程 (当然超过一两年没更新的应用就算了,可能不符合 Flathub 的最新标准),可以为极大加速应用被合并时间。
最后附赠一些可能有用的链接: