跳转到主内容

SeatFlow GitHub Actions 持续集成与发布工作流详解:单元测试、多平台发布、Dependabot 安全紧急发布

CI/CD 工作流

SeatFlow 使用 GitHub Actions 实现持续集成与持续发布。仓库中共有三条工作流,覆盖代码校验、多平台构建发布和安全紧急响应。

工作流总览

文件 触发条件 用途
unit-tests.yml Push / PR(C# 文件变更) 增量构建 + 条件测试(含 WASM 目标校验)
release.yml version.json 变更推送至 main;手动触发 多平台构建 → vpk 打包 → OSS 上传 → GitHub Release(含 Web 构建验证)
dependabot-auto-release.yml Dependabot PR(安全类型) 自动审批合并 → 生成发布说明 → bump 版本 → 触发发布
代码提交 ──→ unit-tests.yml ──→ (main, version.json) ──→ release.yml
                                         ↑
              dependabot-auto-release.yml ┘ (push version.json)

1. 单元测试工作流 (unit-tests.yml)

触发条件

on:
  push:
    paths: ['**.cs', '**.csproj', '**.slnx', '**.axaml']
  pull_request:
    paths: ['**.cs', '**.csproj', '**.slnx', '**.axaml']

任何 C# 源码(.cs)、项目文件(.csproj、.slnx)或 Avalonia 标记文件(.axaml)的变更都会触发。路径过滤避免了 README 或文档变更浪费 CI 资源。

并发控制

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

同一分支/PR 上如果有新提交,旧的运行中 job 会被自动取消,节省 runner 时间。

增量测试策略

工作流使用 dorny/paths-filter@v4 实现增量测试——只运行变更相关的测试项目,而非全量测试:

变更路径 运行的测试
SeatFlow.Core/** Core.Tests
SeatFlow.Infrastructure/** Infrastructure.Tests
SeatFlow.Application/** Application.Tests
SeatFlow.Core.Tests/** Core.Tests
SeatFlow.Infrastructure.Tests/** Infrastructure.Tests
SeatFlow.Application.Tests/** Application.Tests

依赖传递规则:修改 Core 层会同时触发 Infrastructure 和 Application 的测试(因为上层依赖 Core)。修改 Infrastructure 层同理会触发 Application 测试。这一规则体现在 job 的条件判断中:

# Core.Tests 运行条件
- if: steps.filter.outputs.core == 'true'
      || steps.filter.outputs.infra == 'true'
      || steps.filter.outputs.tests-core == 'true'

# Infrastructure.Tests 运行条件
- if: steps.filter.outputs.core == 'true'
      || steps.filter.outputs.infra == 'true'
      || steps.filter.outputs.tests-infra == 'true'

# Application.Tests 运行条件
- if: steps.filter.outputs.core == 'true'
      || steps.filter.outputs.infra == 'true'
      || steps.filter.outputs.app == 'true'
      || steps.filter.outputs.tests-app == 'true'

执行流程

checkout (fetch-depth: 0)
  → 路径过滤 (dorny/paths-filter)
  → setup-dotnet 10.0.x
  → dotnet build (全量编译)
  → dotnet test --no-build (条件运行)
  • fetch-depth: 0:获取完整 git 历史,路径过滤需要 diff 信息
  • 先全量 dotnet build 确保一致性,再 --no-build 运行测试避免重复编译
  • Presentation 层无独立测试项目,仅参与构建验证
  • 涉及浏览器目标(net10.0-browser)的变更会安装 wasm-tools 工作负载并构建 SeatFlow.Browser,确保 WASM 目标可编译

2. 发布工作流 (release.yml)

触发条件

on:
  push:
    branches: ['main']
    paths: ['version.json']
  workflow_dispatch:
  • 自动触发:向 main 分支推送 version.json 变更(版本号 bump)
  • 手动触发:通过 GitHub Actions UI 的 workflow_dispatch 手动运行

并发控制

concurrency: release-${{ github.ref }}

防止同一分支同时运行多个发布流程。

Job 1: build — 多平台构建 + Velopack 打包

矩阵策略:

RID 运行环境 产物
win-x64 ubuntu-latest SeatFlow-{ver}-Setup.exe
linux-x64 ubuntu-latest SeatFlow-{ver}-linux-x64.AppImage

macOS 平台(osx-x64、osx-arm64)已在矩阵中注释,待后续启用。

执行步骤:

checkout → setup-python → 解析 version.json → setup-dotnet 10.0.x
  → 安装系统依赖 (squashfs-tools, zstd)
  → 安装 vpk (dotnet tool install -g)
  → dotnet publish (自包含, -r {rid})
  → vpk pack ([win]/[linux], 生成安装程序 + .nupkg + 更新源)
  → 上传 artifacts

vpk pack 关键参数:

参数 Windows Linux
入口文件 SeatFlow.exe SeatFlow
图标 Assets/installer.ico Assets/SF_icon_mini_background.png
额外参数 --noPortable —
Delta 格式 BestSpeed (Zstandard) BestSpeed (Zstandard)

Delta 格式锁定为 BestSpeed (Zstandard),因为 BestSize (bsdiff) 与客户端 Update.exe 不兼容。

产物上传:

  • publish/out/*.exe — Windows 安装程序
  • publish/out/*.AppImage — Linux AppImage
  • publish/out/*.nupkg — NuGet 更新包
  • publish/out/releases.*.json / publish/out/RELEASES-* — 自动更新源
  • if-no-files-found: error — 产物缺失时报错而非静默跳过

Web 静态站(构建验证):

CI 还会安装 wasm-tools 工作负载并执行:

dotnet publish src/SeatFlow.Browser -c Release

该产物当前仅用于验证浏览器端可正常构建与发布,不随桌面安装包分发,也不部署到生产环境。

Job 2: release — GitHub Release + OSS 上传

依赖:needs: build(等待构建完成)

权限:

permissions:
  contents: write   # 创建 Release + 上传 assets
environment: OSS    # 访问 OSS 环境级 Secrets/Vars

执行步骤:

checkout → setup-python → 解析版本 → 安装 oss2, packaging
  → 预检:
    ├─ 版本 Tag 已存在? → 终止
    └─ 版本号 > 最新 Release? → 否则终止
  → 下载 artifacts (release-* 合并)
  → 补齐安装程序文件名中的版本号
     (SeatFlow Setup.exe → SeatFlow Setup-1.4.2.exe)
  → 验证 RELEASE.md 存在
  → OSS 上传 (若凭证已配置):
    ├─ 校验 releases.json 版本索引
    ├─ 上传安装包 → releases/{version}/
    ├─ 上传更新包 → updates/
    ├─ 上传 RELEASE.md
    └─ 更新 releases.json
  → 创建 GitHub Release (gh release create)

OSS 凭证预检:若 OSS_KEY_ID / OSS_KEY_SECRET / OSS_ENDPOINT / OSS_BUCKET 中任一项未配置,自动跳过 OSS 上传,仅创建 GitHub Release。

version.json 解析:两个 job 各自独立解析 version.json。build job 将版本号通过 outputs 传递给 release job,release job 使用 needs.build.outputs.version 确保版本一致。

发布触发链路

手动修改 version.json → commit → push main
  → release.yml 构建
  → GitHub Release 发布

或:

GitHub Actions UI → workflow_dispatch → release.yml

3. Dependabot 紧急安全发布 (dependabot-auto-release.yml)

触发条件

on:
  pull_request:
    types: [opened, reopened, synchronize, closed]
    branches: [main]

监听 Dependabot PR 的完整生命周期。通过 job 级别 if 条件筛选实际执行场景。

双重守卫条件

两个 job 共享相同的核心过滤逻辑:

  1. PR 作者检查:github.event.pull_request.user.login == 'dependabot[bot]' —— 仅处理 Dependabot 创建的 PR,人类 PR 不受影响
  2. 安全类型检测:contains(github.event.pull_request.body, 'GHSA-') —— Dependabot 安全 PR 的 body 必然包含 GitHub 安全公告 ID(格式 GHSA-xxxx-xxxx-xxxx),此条件精确区分安全更新和常规版本更新

Job 1: auto-merge — 自动审批与合并

触发时机:PR opened / reopened / synchronize(非 closed)

执行步骤:

gh pr review --approve
  → gh pr merge --auto --squash
  • --approve:自动化审批,绕过 "Require approvals" 分支保护规则
  • --auto --squash:启用 GitHub 原生 auto-merge,所有 required checks 通过后自动 squash 合并
  • 如需绕过审批规则,需在分支保护中将 dependabot[bot] 添加为例外

Job 2: prepare-release — 生成文档并触发发布

触发时机:PR closed + merged == true

执行步骤:

checkout main (含 GITHUB_TOKEN)
  → 提取 PR 元数据:
    ├─ PR title → 包名 + 版本范围 (如 "Bump X from 1.0 to 1.1")
    └─ PR body → GHSA ID
  → 获取安全公告详情 (gh api /advisories/{GHSA})
    ├─ 严重等级 (Critical/High/Moderate/Low)
    ├─ CVSS 评分
    └─ 漏洞描述
    └─ API 失败时回退到 PR body 内容
  → bump version.json patch 版本 (1.4.1 → 1.4.2)
  → 读取 .github/security-release-template.md 模板
  → 替换 {placeholder} 占位符 → 写入 RELEASE.md
  → 生成标准 Keep a Changelog 条目 → 插入 CHANGELOG.md 顶部
  → commit + push 三文件 (version.json, RELEASE.md, CHANGELOG.md)

push 后的连锁反应:

prepare-release push (version.json 变更)
  → release.yml 触发
  → 完整构建 + 发布流程

安全发布模板

.github/security-release-template.md 定义了紧急安全发布的 RELEASE.md 模板,使用 {placeholder} 占位符:

占位符 填充来源
{version} bump 后的新版本号
{ghsa_id} PR body 正则提取
{ghsa_url} https://github.com/advisories/{ghsa_id}
{severity_label} Advisory API → 中文化等级标签
{cvss_score} Advisory API → CVSS 评分
{package_name} PR title 解析
{from_version} / {to_version} PR title 解析
{description} Advisory API → 漏洞描述
{release_date} 当前日期
{repo} github.repository

模板内容包括醒目的升级提醒、漏洞信息表格、漏洞描述和升级方式说明。

CHANGELOG 自动生成

workflow 生成符合 Keep a Changelog 格式的条目,插入 CHANGELOG.md 的 # Changelog 标题行之后:

## [1.4.2] — 2026-07-25

### Security
- [GHSA-xxxx-xxxx-xxxx](https://github.com/advisories/GHSA-xxxx-xxxx-xxxx) (**CRITICAL**): 漏洞摘要
- 更新 `Package.Name` 从 1.0.0 到 1.0.1

分支保护配置

为使 Dependabot auto-merge 正常工作,需在仓库 Settings → Branches 中为 main 分支配置以下规则:

必需规则

规则 设置 原因
Require a pull request before merging ✅ 所有变更必须走 PR
├─ Require approvals 1 人类 PR 需要审批
└─ Allow bypass (指定角色) dependabot[bot] Dependabot PR 免审批但仍需 CI 通过
Require status checks to pass ✅ auto-merge 的前置条件
└─ Require branches up to date ✅ 保证线性历史

推荐规则

规则 设置
Require conversation resolution ✅
Do not allow bypassing above settings ✅
Allow deletions ❌
Allow force pushes ❌

required status checks

在 Require status checks to pass 中添加 unit-tests workflow 的 job 名称。Dependabot PR 提交后,CI 通过 → GitHub 自动 squash merge → prepare-release job 触发 → bump 版本 → 自动发布。


版本号管理

release 工作流的触发依赖于 version.json 的变更。平时版本号由 scripts/version.py 管理:

# 查看当前版本
python3 scripts/version.py show

# bump 补丁版本
python3 scripts/version.py bump-app patch --force

紧急安全发布时,dependabot-auto-release.yml 自动执行 patch bump,无需人工介入。

版本号管理的完整说明见 10-version-migration.md。


环境变量与密钥

GitHub Secrets

密钥 用途 层级
OSS_KEY_ID 阿里云 OSS AccessKey ID Environment: OSS
OSS_KEY_SECRET 阿里云 OSS AccessKey Secret Environment: OSS

GitHub Variables

变量 用途 层级
OSS_ENDPOINT OSS endpoint(如 oss-cn-hongkong.aliyuncs.com) Environment: OSS
OSS_BUCKET OSS bucket 名称 Environment: OSS

工作流级环境变量

env:
  DOTNET_NOLOGO: 'true'              # 禁用 .NET 横幅
  DOTNET_CLI_TELEMETRY_OPTOUT: 'true' # 禁用遥测
  PROJECT_PATH: 'SeatFlow.Desktop'
  CONFIGURATION: 'Release'

手动触发发布

除了 version.json push 触发外,可以通过 GitHub Actions UI 手动触发发布:

  1. 进入仓库 Actions 页面
  2. 选择 Release SeatFlow workflow
  3. 点击 Run workflow
  4. 选择 main 分支,点击 Run workflow

手动触发同样执行完整 pipeline(构建 + vpk + OSS + Release),但不会 bump 版本号——使用的是 version.json 中的当前版本。


故障排查

release.yml:vpk 命令不可用

错误信息:"dotnet tool restore" to make the "vpk" command available.

原因:仓库 dotnet-tools.json 中声明了 vpk 为本地工具,但 CI 未 restore。解决:workflow 中已使用 dotnet tool install -g vpk 全局安装,并用直接调用 vpk(而非 dotnet vpk)绕过本地 manifest 解析。

release.yml:文件未附带版本号

OSS 上传的安装程序文件名中缺少版本号(如 SeatFlow Setup.exe 而非 SeatFlow Setup-1.4.2.exe)。

原因:release.yml 曾缺失版本号补齐步骤。已通过在 "Download artifacts" 后添加 "Version installer filenames" 步骤修复——对不含版本号的文件名自动插入版本号。

dependabot-auto-release.yml:OSS 凭证为空

错误:TypeError: can only concatenate str (not "NoneType") to str

原因:OSS Secrets/Vars 未配置。已添加预检逻辑——凭证缺失时 sys.exit(0) 优雅跳过 OSS 上传,不影响 GitHub Release 创建。配置方式见 环境变量与密钥。

auto-merge 不生效

  • 确认分支保护中启用了 Allow auto-merge
  • 确认 dependabot[bot] 已添加到 Allow specified actors to bypass required pull request approvals
  • 确认 required status checks 已在 PR 上通过

相关文档