生产发布检查清单
在打标签 v0.7.0 并交付给用户之前,必须完成以下所有事项。各步骤按依赖关系排列,请从上至下依次完成。
1. 生成 Tauri 签名密钥对
状态: 阻塞项 -- 没有密钥对,自动更新功能将完全失效,用户永远无法收到更新。
Tauri 更新器需要一个 Ed25519 密钥对。私钥用于对每个发布包进行签名,公钥嵌入到应用二进制文件中以验证更新。
# 安装 Tauri CLI(如果尚未安装)
cargo install tauri-cli --locked
# 生成密钥对
cargo tauri signer generate -w ~/.tauri/librefang.key
命令将输出:
Your public key was generated successfully:
dW50cnVzdGVkIGNvb... <-- COPY THIS
Your private key was saved to: ~/.tauri/librefang.key
妥善保存这两个值,后续步骤 2 和步骤 3 会用到。
2. 在 tauri.conf.json 中设置公钥
状态: 阻塞项 -- 必须在构建之前替换占位符。
打开 crates/librefang-desktop/tauri.conf.json,将:
"pubkey": "PLACEHOLDER_REPLACE_WITH_GENERATED_PUBKEY"
替换为步骤 1 中生成的实际公钥字符串:
"pubkey": "dW50cnVzdGVkIGNvb..."
3. 添加 GitHub 仓库密钥
状态: 阻塞项 -- 没有这些密钥,CI/CD 发布工作流将会失败。
前往 GitHub 仓库 -> Settings -> Secrets and variables -> Actions -> New repository secret,添加以下密钥:
| 密钥名称 | 值 | 是否必需 |
|---|---|---|
TAURI_SIGNING_PRIVATE_KEY | ~/.tauri/librefang.key 的文件内容 | 是 |
TAURI_SIGNING_PRIVATE_KEY_PASSWORD | 生成密钥时设置的密码(如果没有设置则为空字符串) | 是 |
可选 -- macOS 代码签名
如果不配置这些密钥,macOS 用户将看到"来自身份不明的开发者"的警告。需要一个 Apple 开发者账户($99/年)。
| 密钥名称 | 值 |
|---|---|
APPLE_CERTIFICATE | Base64 编码的 .p12 证书文件 |
APPLE_CERTIFICATE_PASSWORD | .p12 文件的密码 |
APPLE_SIGNING_IDENTITY | 例如 Developer ID Application: Your Name (TEAMID) |
APPLE_ID | 你的 Apple ID 邮箱 |
APPLE_PASSWORD | 从 appleid.apple.com 生成的应用专用密码 |
APPLE_TEAM_ID | 你的 10 位 Team ID |
生成 Base64 编码证书的方法:
base64 -i Certificates.p12 | pbcopy
可选 -- Windows 代码签名
如果不配置,Windows SmartScreen 可能会向用户发出警告。需要一个 EV 代码签名证书。
在 tauri.conf.json 的 bundle.windows 下设置 certificateThumbprint,并将证书添加到 CI 中的 Windows 运行器。
4. 创建图标资源
状态: 需验证 -- 图标可能是占位符。
以下图标文件必须存在于 crates/librefang-desktop/icons/ 目录中:
| 文件 | 尺寸 | 用途 |
|---|---|---|
icon.png | 1024x1024 | 源图标,macOS .icns 生成 |
icon.ico | 多尺寸 | Windows 任务栏、安装程序 |
32x32.png | 32x32 | 系统托盘、小尺寸场景 |
128x128.png | 128x128 | 应用列表 |
128x128@2x.png | 256x256 | HiDPI/Retina 显示屏 |
确认这些是正式的品牌图标(而非 Tauri 默认图标)。可以从单个源 SVG 文件生成所有尺寸:
# 使用 ImageMagick
convert icon.svg -resize 1024x1024 icon.png
convert icon.svg -resize 32x32 32x32.png
convert icon.svg -resize 128x128 128x128.png
convert icon.svg -resize 256x256 128x128@2x.png
convert icon.svg -resize 256x256 -define icon:auto-resize=256,128,64,48,32,16 icon.ico
5. 设置 librefang.ai 安装域名
状态: 需验证 -- 用户应能通过 curl -fsSL https://librefang.ai/install.sh | sh 进行安装。
可选方案:
- GitHub Pages:将
librefang.ai指向 GitHub Pages 站点,将/install.sh重定向到仓库最新发布版本的scripts/install.sh,将/install.ps1重定向到scripts/install.ps1。 - Cloudflare Workers / Vercel:以正确的
Content-Type: text/plain头部提供安装脚本。 - 原始 GitHub 重定向:如有需要,使用
librefang.ai路由到仓库托管的安装器产物(可靠性较低)。
安装脚本的引用路径:
https://librefang.ai/install.sh-> 提供scripts/install.shhttps://librefang.ai/install.ps1-> 提供scripts/install.ps1
域名配置完成后,用户即可通过以下方式安装:
curl -fsSL https://librefang.ai/install.sh | sh
6. 验证 Dockerfile 构建
状态: 需验证 -- Dockerfile 必须能生成可用的镜像。
docker build -t librefang:local .
docker run --rm librefang:local --version
docker run --rm -p 4545:4545 -v librefang-data:/data librefang:local start
确认以下事项:
- 二进制文件能正常运行并打印版本号
start命令能启动内核和 API 服务器- 端口 4545 可访问
/data卷在容器重启之间保持数据持久化
7. 本地验证安装脚本
状态: 发布前需验证。
Linux/macOS
# 针对真实的 GitHub 发布进行测试(首次打标签之后)
bash scripts/install.sh
# 或仅进行语法检查
bash -n scripts/install.sh
shellcheck scripts/install.sh
Windows (PowerShell)
# 针对真实的 GitHub 发布进行测试(首次打标签之后)
powershell -ExecutionPolicy Bypass -File scripts/install.ps1
# 或仅进行语法检查
pwsh -NoProfile -Command "Get-Content scripts/install.ps1 | Out-Null"
Docker 冒烟测试
docker build -f scripts/docker/install-smoke.Dockerfile .
8. 编写 v0.7.0 的 CHANGELOG.md
状态: 需验证 -- 确认变更日志涵盖所有已交付的功能。
发布工作流会在每个 GitHub Release 正文中包含指向 CHANGELOG.md 的链接。确保该文件存在于仓库根目录,并涵盖以下内容:
- 全部 15 个 crate 及其功能说明
- 核心功能:44 个通道、60 个工具、49 个驱动、130+ 个模型
- 安全系统(9 个 SOTA 方案 + 7 个关键修复)
- 带自动更新功能的桌面应用
- 从 OpenClaw 的迁移路径
- Docker 和 CLI 安装选项
9. 首次发布 -- 打标签并推送
当步骤 1-8 全部完成后:
# 确保所有地方的版本号一致
grep '"version"' crates/librefang-desktop/tauri.conf.json
grep '^version' Cargo.toml
# 提交最终变更
git add -A
git commit -m "chore: prepare v0.7.0 release"
# 打标签并推送
git tag v0.7.0
git push origin main --tags
此操作将触发发布工作流,该工作流会:
- 为 4 个目标平台构建桌面安装包(Linux、macOS x86、macOS ARM、Windows)
- 生成已签名的
latest.json供自动更新器使用 - 为 5 个目标平台构建 CLI 二进制文件
- 构建并推送多架构 Docker 镜像
- 创建包含所有产物的 GitHub Release
10. 发布后验证
发布工作流完成后(约 15-30 分钟):
GitHub Release 页面
-
.msi和.exe存在(Windows 桌面版) -
.dmg存在(macOS 桌面版) -
.AppImage和.deb存在(Linux 桌面版) -
latest.json存在(自动更新清单) - CLI
.tar.gz归档存在(5 个目标平台) - CLI
.zip存在(Windows) - 每个 CLI 归档对应的 SHA256 校验文件存在
自动更新清单
访问:https://github.com/librefang/librefang/releases/latest/download/latest.json
- JSON 格式有效
- 包含
signature字段(非空字符串) - 包含所有平台的下载 URL
- 版本号与标签匹配
Docker 镜像
docker pull ghcr.io/librefang/librefang:latest
docker pull ghcr.io/librefang/librefang:0.7.0
# 验证两种架构
docker run --rm ghcr.io/librefang/librefang:latest --version
容器镜像漏洞扫描
每个运行时镜像在其 digest 被提升为共享标签之前,都会先经过 Trivy 扫描。
扫描范围。 两个工作流通过 .github/actions/trivy-image-scan 这个共享的 composite action 执行同一套扫描:
| 工作流 | 扫描对象 | 触发时机 |
|---|---|---|
docker-build.yml | linux/amd64 运行时镜像,在 /api/health 启动探针之后 | 推送到 main 且改动了 Dockerfile 或其输入 |
release.yml(docker-scan) | 每个原生架构的发布 digest —— linux/amd64 在 ubuntu-24.04、linux/arm64 在 ubuntu-24.04-arm | 每个发布标签,位于单架构推送与 manifest 发布之间 |
每次扫描同时覆盖 OS 软件包(来自 node:*-bookworm-slim 基础镜像的 Debian 包)和 语言包(内置的 Node.js 与 Python 依赖)—— 即 --pkg-types os,library —— 并对最终镜像保持 密钥扫描 开启。
既不传 --severity 也不传 --ignore-unfixed,因此完全不施加任何过滤,报告包含所有严重级别的全部发现,无论是否有可用修复。
执行阈值。 该门禁以 仅报告 模式发布,目前 并未 强制执行。
运行时镜像存在大量历史遗留问题 —— issue #6694 于 2026-08-01 测得 10 个 critical 和 95 个 high —— 因此一旦启用强制执行,main 和每次发布在第一次运行时就会失败。
该测量使用的是 Trivy 0.57.0 而非本流水线固定的 0.72.0,且未重新测量,因此它只能作为「为何先以报告模式发布」的量级依据,而不是首次运行会报出的实际数字。
启用它需要改 两行,两行都不可省。
阈值本身在一个地方 —— .github/actions/trivy-image-scan/action.yml 的 fail-on 输入,两个工作流都不覆盖它:
off → critical → high
第二行是 .github/workflows/release.yml 中 docker-scan 作业扫描步骤上的 continue-on-error: true。
在报告模式下,正是这个标记让发布路径上的「仅报告」名副其实:扫描器故障 —— arm64 冷启动下载漏洞库、GHCR 抖动、Trivy 资产 404 —— 会让该步骤失败,若没有它,失败的 docker-scan 会跳过 docker-manifest,连带整个标签提升一起中断。
这些故障都不说明镜像存在安全问题,因此当前扫描不会因任何原因阻断发布,这正是一个未启用强制执行的门禁应有的行为。
该步骤在运行记录中仍标记为失败,因此扫描器故障依然可见,不会被当作一次干净的扫描。
移动 fail-on 的同时必须删除这个标记,否则你会在启用阈值的同时让扫描失去阻断能力。
docker-build.yml 中的 main 扫描刻意没有这个标记:没有任何东西依赖它,因此那里的扫描器故障会把工作流标红作为信号,且不造成任何阻塞。
启用之后,当镜像存在任何达到或超过该阈值的 可修复 漏洞,或存在任何内嵌密钥时,作业即失败。
把判定范围限定在可修复的发现上并不等于 --ignore-unfixed:无修复的发现仍然保留在报告、产物和作业摘要中,被收窄的只是通过/失败的判定,因为一个无法修复的 CVE 并不能给维护者任何可执行的动作。
门禁实际拦截的是什么。 它是 标签提升门禁,而非发布门禁。
发布流水线使用 push-by-digest 构建每个架构,因此在 docker-scan 运行之前,两个单架构 digest 就已经(以无标签的形式)存在于 GHCR 中;这正是两个原生构建作业把结果交给 manifest 步骤的方式。
扫描失败会阻断 docker-manifest,进而阻断 publish_arch_repo、deploy_fly、deploy_render 和 sync_aur_docker。
其效果是:任何面向用户的标签(:VERSION、:latest、:lts)都不会被创建或移动到未通过门禁的 digest 上,下游部署也不会消费它。
遗留在 GHCR 中的无标签 digest 无法通过名称拉取。
报告保留在哪里。 三个位置,产物请求保留 30 天 —— 实际生效值取决于仓库的产物保留上限,因为 upload-artifact 会静默地把超出上限的请求截断:
- 工作流产物,名为
trivy-main-linux-amd64(main)以及trivy-release-linux-amd64/trivy-release-linux-arm64(发布),每个包含原始 Trivy JSON、同一次扫描的 SARIF 编码,以及记录了平台 digest、扫描器版本、数据库时间戳、阈值和判定结果的-summary.json。 - 运行的作业摘要,其中列出平台 digest、Trivy 与漏洞数据库版本、按严重级别统计的总数与可修复数,以及每条 CRITICAL / HIGH 发现的软件包、已安装版本、修复版本、CVE 编号和严重级别。
- GitHub code scanning,分类为
trivy-main-linux-amd64、trivy-release-linux-amd64和trivy-release-linux-arm64—— 每个被扫描镜像一个分类,因为 code scanning 会替换同一分类下的既有结果,共用分类会丢掉其中一个架构。
扫描器故障不等于扫描通过。 漏洞数据库在独立的步骤中刷新并重试三次,之后扫描本身使用 --skip-db-update 运行。
因此数据库下载失败会以独立的 Scanner infrastructure failure 错误终止作业,而不会产出一份看起来像干净镜像的零发现报告。
同样的区分也适用于报告缺失、为空或被截断的情况,以及报告未识别出操作系统的情况 —— 运行时镜像基于 Debian,出现这种组合意味着扫描器失败,而不是镜像干净。
判定逻辑由 scripts/check-image-vulns.py --self-test 中的固定用例集锁定,由 ci.yml 的 Image Vulnerability Gate Self-Test 作业在每个 PR 上运行。
桌面应用自动更新(使用 v0.7.1 进行测试)
- 从发布页面安装 v0.7.0
- 打标签 v0.7.1 并推送
- 等待发布工作流完成
- 打开 v0.7.0 应用 -- 10 秒后应当:
- 显示 "LibreFang Updating..." 通知
- 下载并安装 v0.7.1
- 自动重启到 v0.7.1
- 右键点击托盘 -> "Check for Updates" -> 应显示 "Up to Date"
安装脚本
# Linux/macOS
curl -fsSL https://librefang.ai/install.sh | sh
librefang --version # 应输出 v0.7.0
# Windows PowerShell
irm https://librefang.ai/install.ps1 | iex
librefang --version
速查 -- 依赖关系图
Step 1 (密钥生成) ──┬──> Step 2 (公钥写入配置)
└──> Step 3 (密钥存入 GitHub)
│
Step 4 (图标) ─────────────┤
Step 5 (域名) ─────────────┤
Step 6 (Dockerfile) ───────┤
Step 7 (安装脚本) ─────────┤
Step 8 (CHANGELOG) ────────┘
│
v
Step 9 (打标签 + 推送)
│
v
Step 10 (验证)
步骤 4-8 可以并行执行。步骤 1-3 是顺序依赖的,必须首先完成。