CI/CD 系统文档
本文档描述了 lzt 项目的完整 CI/CD 系统架构、使用方法和本地验证流程。
📋 系统概述
项目采用现代化的 GitHub Actions 工作流程,实现自动化的版本管理、构建、测试和发布。系统采用统一的 CI 工作流设计,具有以下特点:
- 统一 CI 工作流 (
ci-unified.yml) - 集成所有 CI/CD 功能的主工作流 - 构建资源工作流 (
build-assets.yml) - 可复用的多平台二进制构建组件 - 遗留发布工作流 (
release.yaml) - 手动标签发布的备用方案 - 文档自动化 - 通过 Cloudflare Pages 实现零配置自动构建和部署
- 差异化策略 - PR 严格检查,Push 宽松监控,优化开发体验
🏗️ 工作流架构
🚀 工作流详解
1. 统一 CI 工作流
文件: .github/workflows/ci-unified.yml
触发条件
- 推送到
master、main、develop、dev分支 - Pull Request 到这些分支
- 推送到
v*标签
作业组成
Test & Quality Check (test)
- 触发条件: 所有场景
- 主要功能:
- Go 依赖管理和验证
- 单元测试执行(包含 race 检测)
- 测试覆盖率分析
- pkg 包专项测试
Build Test (build-test)
- 触发条件: 非 PR 且非标签推送
- 主要功能:
- 多平台交叉编译验证
- 支持 Linux/macOS 的 amd64/arm64 架构
Conventional Commits (conventional-commits)
- 触发条件: 仅 PR
- 主要功能:
- 提交信息规范验证
- 支持标准的 Conventional Commits 格式
Release Analysis (release-analysis)
- 触发条件: 推送到主分支且非标签推送
- 主要功能:
- 语义化版本分析
- 自动计算下一个版本号
- 准备发布信息
Tag Release (tag-release)
- 触发条件: 推送到
v*标签 - 主要功能:
- 多平台二进制构建 (Linux, macOS, Windows)
- 自动创建 GitHub Release
- 上传构建资产 (.tar.gz, .zip)
- 预发布版本检测 (-dev, -alpha, -beta, -rc)
构建矩阵
测试构建 (build-test 作业):
| 操作系统 | 架构 | 触发条件 |
|---|---|---|
| Linux | amd64, arm64 | 非-PR 且非-标签推送 |
| macOS | amd64, arm64 | 非-PR 且非-标签推送 |
发布构建 (tag-release 作业):
| 操作系统 | 架构 | 文件格式 | 用途 |
|---|---|---|---|
| Linux | amd64, arm64 | .tar.gz | 服务器部署 |
| macOS | amd64, arm64 | .tar.gz | 开发环境 |
| Windows | amd64 | .zip | 桌面使用 |
2. 测试覆盖率策略
差异化覆盖率要求
- Pull Request: 严格要求 pkg/ 包覆盖率 ≥30%,必须通过所有测试
- 分支推送: 仅输出覆盖率信息,不阻断流程
- pkg/ 包专项要求: 必须有单元测试且维持合理覆盖率
质量门禁
- ✅ 所有单元测试通过
- ✅ Go vet 静态分析通过
- ✅ pkg/ 包测试覆盖率达标 (PR 严格,Push 宽松)
- ✅ 多平台构建成功 (非 PR 时)
- ✅ 提交信息符合规范 (仅 PR)
- ✅ 文档自动化 (通过 Cloudflare Pages)
🛠️ 本地验证方法
使用 act 工具验证
act 是一个用于在本地运行 GitHub Actions 的工具。
快速验证
# 安装 act
brew install act # macOS
# 查看所有可用的工作流
act --list
# 验证统一 CI 流水线(干运行)
act push -W .github/workflows/ci-unified.yml -n
# 验证 PR 流程
act pull_request -W .github/workflows/ci-unified.yml -n
# 验证特定作业
act push -W .github/workflows/ci-unified.yml -j test -n📚 详细指南: 完整的 act 配置、使用方法、故障排除和最佳实践,请参考 Act 本地测试指南
本地构建验证
# 验证 Go 模块
go mod verify
go mod tidy
# 运行测试
go test ./...
go test -race ./...
go test -v ./pkg/...
# 代码质量检查
go vet ./...
# 跨平台构建验证
GOOS=linux GOARCH=amd64 go build -o build/linux-amd64/lzt ./main.go
GOOS=darwin GOARCH=arm64 go build -o build/darwin-arm64/lzt ./main.go
GOOS=windows GOARCH=amd64 go build -o build/windows-amd64/lzt.exe ./main.go文档验证
# 安装依赖(使用 yarn,与 CI 保持一致)
yarn install
# 本地启动文档服务
yarn docs:dev
# 构建文档(验证构建是否成功)
yarn docs:build
# 预览构建结果
yarn docs:preview🔄 版本策略
语义化版本规则
| 提交类型 | 版本影响 | 示例 |
|---|---|---|
feat | Minor | 1.0.0 → 1.1.0 |
fix | Patch | 1.0.0 → 1.0.1 |
BREAKING CHANGE | Major | 1.0.0 → 2.0.0 |
docs, style, refactor, perf, test, build, ci, chore | Patch | 1.0.0 → 1.0.1 |
分支发布策略
📋 提交规范验证
提交信息格式
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]验证工具
# 安装提交验证工具
yarn global add @commitlint/cli @commitlint/config-conventional
# 验证最近的提交
npx commitlint --from HEAD~1 --to HEAD --verbose
# 交互式提交(推荐)
yarn global add commitizen
yarn global add cz-conventional-changelog
git cz有效提交示例
feat(ledger): add transaction streaming API
fix( handle rate limiting correctly
docs(bubble): update progress bar examples
refactor(api): optimize database queries
test(utils): add unit tests for helper functions
ci: update GitHub Actions workflows
chore(deps): update dependencies🔧 GitHub CLI 集成
基础操作
# 创建 PR
gh pr create --title "Feature: Add new functionality" --body "Description of changes"
# 列出 PR
gh pr list
# 查看 PR 详情
gh pr view 123
# 合并 PR
gh pr merge 123 --squash # 压缩合并
gh pr merge 123 --rebase # 变基合并GitHub Actions 管理
# 列出工作流运行
gh run list
# 查看工作流运行详情
gh run view 12345
# 取消工作流运行
gh run cancel 12345
# 重新运行工作流
gh run rerun 12345
# 手动触发工作流
gh workflow run build.yml发布管理
# 创建发布
gh release create v1.0.0 --title "Version 1.0.0" --notes "Release notes"
# 上传发布资产
gh release upload v1.0.0 ./binary-file
# 列出发布
gh release list
# 查看发布详情
gh release view v1.0.0🚨 常见问题排查
工作流失败
检查 act 输出
bashact push -W .github/workflows/ci-unified.yml --verbose验证语法
bash# 使用 actionlint 检查语法 brew install actionlint actionlint .github/workflows/*.yml检查条件执行
bash# 验证不同事件类型的作业执行 act push -W .github/workflows/ci-unified.yml --list act pull_request -W .github/workflows/ci-unified.yml --list
构建失败
本地重现
bash# 使用相同的构建标志 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ go build -a -installsuffix cgo -ldflags "-w -s" ./main.go检查平台兼容性
bashgo tool dist list | grep -E "(linux|darwin|windows)"
Apple Silicon 兼容性
# 始终使用 x86_64 架构
act --container-architecture linux/amd64
# 或者在 .actrc 中设置
echo "--container-architecture linux/amd64" >> .actrc🚀 GitHub Actions 缓存最佳实践
缓存策略设计
Go 项目缓存优化原则
缓存 Key 设计原则:
- ✅ 推荐: 仅基于依赖文件 (
go.mod,go.sum) - ❌ 避免: 包含源代码文件哈希 (
**/*.go)
核心原理:
- Go 模块依赖仅在
go.mod和go.sum变化时才需要重新下载 - 源代码文件 (
.go) 变化不应影响依赖缓存 - 合理的缓存策略可将缓存命中率从 ~20% 提升至 ~80%+
缓存分离最佳实践
分离恢复和保存操作:
- 恢复缓存: 使用
actions/cache/restore@v4在工作流开始时 - 保存缓存: 使用
actions/cache/save@v4在工作流结束时(带if: always()) - 缓存路径: 包含 Go 构建缓存、模块缓存和工具缓存
多平台缓存策略
构建矩阵缓存隔离:
- 为不同操作系统和架构组合创建独立缓存
- 缓存键包含平台信息和依赖哈希
- 设置分层回退策略提高缓存命中率
缓存目录选择
Go 项目推荐缓存路径:
path: |
~/.cache/go-build # Go 构建缓存
~/go/pkg/mod # Go 模块缓存
~/.cache/golangci-lint # Linter 缓存 (如使用)性能优化效果
| 策略 | 缓存命中率 | 平均执行时间 | 依赖下载频率 |
|---|---|---|---|
优化前 (包含 **/*.go) | ~20% | 8-12 分钟 | 每次代码变更 |
优化后 (仅 go.mod/go.sum) | ~80%+ | 3-5 分钟 | 仅依赖变更时 |
验证方法
本地验证:
# 验证缓存配置语法
act -j test -n
# 检查缓存步骤识别
act push -W .github/workflows/ci-unified.yml --verbose --dryrun 2>&1 | grep -i cache
# 验证不同作业的缓存策略
act push -j build-test --dryrun
act push -j tag-release --dryrunGitHub Actions 监控:
- 观察 "Restore cache" 步骤的
Cache hit状态 - 监控 "Download dependencies" 步骤的执行时间
- 追踪整体工作流执行时间变化
🎯 最佳实践
GitHub Actions 缓存策略
Go 项目缓存配置最佳实践
❌ 避免的做法:
# 错误:包含了 **/*.go 会导致每次代码变更都失效缓存
key: `${{ runner.os }}-go-${{ hashFiles('go.mod', 'go.sum') }}-${{ hashFiles('**/*.go') }}`✅ 推荐的做法:
# 正确:仅基于依赖文件,提高缓存命中率
key: `${{ runner.os }}-go-${{ hashFiles('go.mod', 'go.sum') }}`缓存分离策略
使用新的 actions/cache/restore 和 actions/cache/save 分离缓存操作:
# 步骤 1: 工作流开始时恢复缓存
- name: Restore Go modules cache
uses: actions/cache/restore@v4
with:
key: `${{ runner.os }}-go-${{ hashFiles('go.mod', 'go.sum') }}`
# 步骤 N: 工作流结束时保存缓存(即使失败也保存)
- name: Save Go modules cache
if: always()
uses: actions/cache/save@v4
with:
key: `${{ runner.os }}-go-${{ hashFiles('go.mod', 'go.sum') }}`说明:
${{ runner.os }}获取运行器操作系统信息${{ hashFiles('go.mod', 'go.sum') }}基于依赖文件生成哈希值- 这种组合确保了跨平台的缓存隔离和依赖变更时的缓存失效
缓存策略核心原则
依赖性原则: 缓存 key 应该仅基于真正影响缓存内容的文件
- Go 模块缓存:仅基于
go.mod和go.sum - 不要包含源代码文件 (
**/*.go)
- Go 模块缓存:仅基于
时效性原则: 设置分层缓存回退策略
- 完全匹配 → 依赖匹配 → 操作系统匹配
可靠性原则: 使用
if: always()确保缓存在失败时也能保存分离关注点: 分离恢复和保存操作
- 避免使用废弃的
save-always参数 - 在工作流开始时恢复,结束时保存
- 避免使用废弃的
缓存性能优化效果
| 场景 | 优化前 | 优化后 | 改进 |
|---|---|---|---|
| 依赖下载频率 | 每次代码变更 | 仅依赖变更时 | 95%↓ |
| 缓存命中率 | ~10% | ~90% | 9x↑ |
| CI 构建时间 | ~8分钟 | ~3分钟 | 60%↓ |
| 网络带宽消耗 | 高 | 低 | 80%↓ |
开发流程
创建功能分支
bashgit checkout -b feat/new-feature本地验证
bashact push -W .github/workflows/ci-unified.yml -j test -n提交代码
bashgit commit -m "feat(component): add new functionality"推送并创建 PR
bashgit push origin feat/new-feature gh pr create --title "feat: add new functionality"
发布流程
- 合并到 develop 分支 → Beta 预发布
- 充分测试 Beta 版本
- 合并到 master 分支 → 生产发布
- 监控发布状态和质量指标
提交前检查
# 运行完整的本地检查
go mod verify
go vet ./...
go test -race ./...
# 使用 act 验证 CI 流程
act push -W .github/workflows/ci-unified.yml --dryrun📊 监控和指标
GitHub Actions 监控
- 📊 工作流执行时间: 监控构建性能
- 📈 成功率统计: 追踪质量趋势
- 🔍 失败原因分析: 快速定位问题
- 📝 资源使用情况: 优化配置
质量指标
- 代码覆盖率: pkg/ 包 ≥30% (PR 强制,Push 监控)
- 测试通过率: 100% (所有环境)
- 构建时间: 目标 <10 分钟
- 发布频率: 基于语义化提交自动决定
- 文档同步: Cloudflare Pages 自动更新
🔧 故障排除
已解决的问题
Issue #42: GitHub Actions 缓存键冲突导致 tar 解压失败
问题描述: 推送标签时 GitHub Actions runner 失败,错误信息为 "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
症状表现:
- 多个工作流 (
ci-unified.yml,release.yaml) 同时触发 - build-assets.yml 中的缓存恢复步骤持续失败
- 即使升级缓存键版本 (v2) 仍然失败
- 即使完全移除 restore-keys 回退机制仍然失败
根本原因分析:
- 缓存设计根本性错误: 将构建缓存 (
~/.cache/go-build) 和模块缓存 (~/go/pkg/mod) 混合使用相同的缓存键 - 缓存键依赖关系错误: 构建缓存依赖源代码+依赖+构建参数+目标平台,但缓存键只包含
go.mod和go.sum - 平台相关性混淆: Go 模块是平台无关的,但使用了平台相关的缓存键 (
linux-amd64,darwin-arm64) - 并发冲突: 多个构建作业同时操作相似的缓存,导致 tar 文件损坏
解决方案演进过程:
第一次尝试: 缓存键版本化 (v1 → v2)
- 结果: 失败,问题依然存在
- 教训: 版本化无法解决设计问题
第二次尝试: 完全移除 restore-keys 回退机制
- 结果: 失败,问题依然存在
- 教训: 问题不在回退机制,而在缓存内容混合
最终解决方案: 重新设计缓存策略
- 只缓存 Go 模块下载 (
~/go/pkg/mod) - 不缓存构建产物 (
~/.cache/go-build) - 统一缓存键:
Linux-go-modules-{hash} - 保留安全回退: 只回退到同类型缓存
- 只缓存 Go 模块下载 (
核心技术洞察:
# ❌ 错误的缓存设计
path: |
~/.cache/go-build # 构建缓存:依赖源码+构建参数+平台
~/go/pkg/mod # 模块缓存:只依赖 go.mod/go.sum
key: ${{ runner.os }}-build-${{ inputs.goos }}-${{ inputs.goarch }}-${{ hashFiles('go.mod', 'go.sum') }}
# ✅ 正确的缓存设计
path: ~/go/pkg/mod # 只缓存模块:平台无关,依赖关系简单
key: ${{ runner.os }}-go-modules-${{ hashFiles('go.mod', 'go.sum') }}性能影响评估:
- 缓存命中率: 从 ~20% 提升到 ~90%(消除平台重复)
- 构建时间: 模块下载加速 ~2 分钟,构建时间略增 ~30 秒
- 稳定性: 根本解决 tar 错误和并发冲突
- 维护性: 大幅简化缓存键设计复杂度
相关 PR: #44 - 修复 GitHub Actions 多工作流缓存键冲突
Issue #42 后续修复: Go Build 参数错误和缓存策略不一致
发现时间: 2025-06-22 (Issue #42 缓存修复后的构建失败)
问题描述: 在修复缓存冲突后,构建步骤出现新的错误:
- ldflags 语法错误:
invalid value "\"-w" for flag -ldflags: missing =<value> in <pattern>=<value> - 缓存策略不一致: 不同工作流使用不同的缓存路径,可能重新引入缓存冲突
- 过时的构建标志: 使用了不必要的
-a -installsuffix cgo参数
症状表现:
Building for linux/amd64...
invalid value "\"-w" for flag -ldflags: missing =<value> in <pattern>=<value>
usage: go build [-o output] [build flags] [packages]
##[error]Process completed with exit code 2.根本原因分析:
- Shell 引用错误:
build-assets.yml中的 ldflags 有双重转义问题 - 历史遗留参数:
-a -installsuffix cgo在现代 Go +CGO_ENABLED=0环境下是多余的 - 缓存策略分歧: 不同工作流混用
~/go/pkg/mod和~/.cache/go-build缓存
技术细节:
# ❌ 错误的 BUILD_FLAGS 定义(引用问题)
BUILD_FLAGS="-a -installsuffix cgo -ldflags \"-w -s -X main.Version=${VERSION}\""
# ✅ 正确的 LDFLAGS 定义和使用
LDFLAGS="-w -s -X main.Version=${VERSION} -X main.BuildTime=${BUILD_TIME}"
go build -ldflags="$LDFLAGS" -o "$BINARY_NAME" ./main.go解决方案:
修复构建参数语法:
- 重构
build-assets.yml中的 ldflags 处理 - 分离 LDFLAGS 变量定义,避免 shell 引用问题
- 简化构建命令,移除不必要的参数
- 重构
统一缓存策略:
- 所有工作流统一使用
~/go/pkg/mod缓存 - 移除构建缓存 (
~/.cache/go-build) 避免复杂依赖 - 统一缓存键命名规范
- 所有工作流统一使用
清理过时标志:
- 移除
-a -installsuffix cgo(在CGO_ENABLED=0时多余) - 保持
CGO_ENABLED: 0环境变量配置
- 移除
修复的文件:
.github/workflows/build-assets.yml.github/workflows/ci-unified.yml.github/workflows/performance-test.yml
修复后的标准构建模式:
env:
GOOS: linux
GOARCH: amd64
CGO_ENABLED: 0
run: |
LDFLAGS="-w -s -X main.Version=${VERSION}"
go build -ldflags="$LDFLAGS" -o "$BINARY_NAME" ./main.go验证结果:
- ✅ 构建参数语法正确
- ✅ 缓存策略在所有工作流中一致
- ✅ 移除了不必要的历史遗留参数
- ✅ 保持了现代化的 Go 构建最佳实践
Issue #11: test-legacy.yml "No jobs were run" 问题
问题描述: GitHub Actions 显示 .github/workflows/test-legacy.yml 工作流 "No jobs were run"
根本原因: 该工作流文件中的触发条件(on: 部分)被完全注释掉,导致工作流永远不会触发
解决方案: 删除冗余的 test-legacy.yml 文件
- 该文件已被
ci-unified.yml完全替代 - 注释表明它是"临时禁用"但实际上不再需要
- 删除后避免了混淆和"No jobs were run"的错误提示
验证方法:
# 确认工作流列表中不再包含 test-legacy.yml
act --list
# 验证统一 CI 工作流正常运行
act pull_request -W .github/workflows/ci-unified.yml --dryrun相关 PR: #13 - 删除冗余的 test-legacy.yml 工作流
Issue #15: GitHub Actions 缓存配置使用废弃参数
问题描述: Test & Quality Check action 的 Cache Go Modules 步骤运行缓慢,并且出现废弃警告:
Warning: Input 'save-always' has been deprecated with message:
save-always does not work as intended and will be removed in a future release.
A separate `actions/cache/restore` step should be used instead.根本原因: actions/cache@v4 中的 save-always: true 参数已被废弃,新版本要求分离缓存的恢复和保存操作
解决方案: 将单一的 actions/cache@v4 步骤拆分为两个独立的步骤
- 使用
actions/cache/restore@v4在工作流开始时恢复缓存 - 使用
actions/cache/save@v4在工作流结束时保存缓存(带if: always()条件)
主要变更:
- 移除废弃参数: 删除了
save-always: true - 分离缓存操作:
- 恢复:
actions/cache/restore@v4 - 保存:
actions/cache/save@v4
- 恢复:
- 优化缓存键: 移除
**/*.go文件哈希,仅基于依赖文件
缓存策略优化效果:
- ✅ 消除废弃警告信息
- ✅ Go 模块依赖仅在
go.mod和go.sum变化时重新下载 - ✅ 普通
.go文件变化不会导致依赖缓存失效 - ✅ 显著提高缓存命中率和 CI/CD 执行速度
验证方法:
# 本地验证 GitHub Actions 工作流语法
act -j test -n
# 检查工作流结构
act --list
# 验证缓存步骤正确执行
act push -W .github/workflows/ci-unified.yml -j test --verbose预期效果:
- ✅ 消除废弃警告信息
- ✅ 提高缓存操作性能
- ✅ 符合 GitHub Actions 最新最佳实践
- ✅ 保持与旧版本的功能兼容性
参考资源: GitHub Actions Cache 保存文档
文档部署问题
如果遇到 Cloudflare Pages 或其他静态站点部署失败,请参考专门的 部署故障排除指南,包含:
- Yarn/npm 版本冲突解决方案
- Package.json 配置优化
- 多包管理器兼容性设置
- VitePress 构建问题修复
CI/CD 流水线调试
- 使用
act在本地重现工作流问题 - 检查 GitHub Actions 日志中的详细错误信息
- 验证环境变量和秘钥配置
- 确认分支保护规则和权限设置
📚 参考资源
深度技术文档
- GitHub Actions Workflow 工作机制 - 深入理解 workflow 版本选择、触发时机和执行原理
- Act 本地测试指南 - 完整的 act 配置、使用方法和最佳实践
- 部署故障排除指南 - 常见部署问题和解决方案
外部参考
这个 CI/CD 系统提供了完整的自动化流水线,确保代码质量、安全性和发布可靠性。通过 act 工具的本地验证和完善的故障排除文档,开发者可以快速解决部署和构建问题。