CI/CD 与发布流程:GitHub Actions 驱动的自动化管线
CI/CD 与发布流程:GitHub Actions 驱动的自动化管线
学习目标
本章要解决什么问题:一个包含 20+ 独立包的 Monorepo 如何高效运行测试、Lint 和发布,而不浪费 CI 资源?
读者将学到:
- 变更检测驱动的 CI 管线设计
- 独立包的发布流程
- SHA 固定的 GitHub Actions 安全实践
前置知识
本章是 LangChain 特有的工程实践,不涉及通用知识原理。
项目实践
变更检测驱动的 CI
核心 CI workflow(check_diffs.yml)通过分析 Git 变更,只运行受影响包的测试和 Lint:
实现方式:
Ana06/get-changed-files获取变更文件列表- 运行
.github/scripts/check_diff.py分析哪些包被影响 - 输出 JSON 矩阵(lint/test/extended-tests 等),CI 只运行对应包的 job
效果:修改 libs/partners/openai/ 中的一个文件,不会触发 libs/partners/anthropic/ 的测试。
主 CI 工作流
check_diffs.yml 作为调度器,调用子 workflow:
| 子 Workflow | 职责 |
|---|---|
_lint.yml | Ruff Lint 检查 |
_test.yml | 单元测试 |
_test_pydantic.yml | Pydantic 兼容性测试 |
_compile_integration_test.yml | 集成测试编译检查 |
vcr_tests.yml | VCR HTTP 录制回放测试 |
并发控制
concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true同一 PR 的多次 push 只保留最新的 CI 运行,取消冗余的中间任务。
独立包发布流程
发布 workflow(_release.yml)支持每个包独立发版:
# 手动触发,选择要发布的包on: workflow_dispatch: inputs: working-directory: type: choice options: - core - langchain - langchain_v1 - text-splitters - standard-tests - model-profiles - anthropic - openai - ... release-version: type: string default: "0.1.0"发布流程:
- 选择目标包(通过
working-directory下拉菜单) - 输入新版本号
- Workflow 自动:
- 更新
pyproject.toml中的版本号 - 构建 wheel/sdist
- 发布到 PyPI
- 创建 Git tag
- 更新
命名映射:某些目录名与 PyPI 包名不一致:
langchain→langchain-classiclangchain_v1→langchainstandard-tests→langchain-tests
PR 标题 Lint
pr_lint.yml 强制 PR 标题遵循 Conventional Commits 格式,确保每个提交都可机器解析:
- type 必须是小写字母开头的预定义值(feat/fix/docs/style/refactor/perf/test/build/ci/chore/revert/release/hotfix)
- scope 必须是预定义列表中的值(core/langchain/各 partner 名等)
- Breaking changes 使用
!后缀
其他 CI 辅助流程
| Workflow | 职责 |
|---|---|
auto-label-by-package.yml | 根据变更包自动添加 PR 标签 |
pr_labeler.yml | 自动为 PR 分类标记 |
require_issue_link.yml | 要求 PR 描述关联 Issue |
block_fork_main_prs.yml | 阻止 Fork 向 main 分支创建 PR |
bump_uv_pin.yml | 自动更新 uv 版本锁定 |
check_agents_sync.yml | 检查 Agent 相关文件同步状态 |
middleware_evals.yml | 中间件评估测试 |
codspeed.yml | 性能基准测试 |
reopen_on_assignment.yml | Issue 被分配时自动重新打开 |
close_unchecked_issues.yml | 关闭未检查的 Issue |
tag-external-issues.yml | 标记外部 Issue |
SHA 固定的 Actions
所有 GitHub Actions 使用 SHA 固定而非 tag/branch 引用:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6uses: Ana06/get-changed-files@25f79e676e7ea1868813e21465014798211fad8c # v2.3.0安全意义:SHA 是不可变的,即使攻击者获取了 Action 仓库的写入权限,也无法将 SHA 指向恶意代码。
问题与规避
变更检测的准确性
问题:check_diff.py 可能漏掉间接影响(如 Core API 变更影响所有 Partner)。
对策:脚本通过分析依赖关系,将 Core 的变更传播到所有依赖包。修改 libs/core/ 会触发所有 partner 包的测试。
发布流程的人为错误
问题:手动输入版本号可能出错。
对策:发布 workflow 要求明确输入版本号,并自动生成 commit message:release(scope): x.y.z。CI 的 pr_lint 确保格式正确。
Pre-release 依赖解析
问题:发布 prelease 版本时,transitive prerelease 依赖可能无法正确解析。
对策:发布 workflow 提供 allow-prereleases 选项,传递 --prerelease=allow 给 wheel-install 步骤。
设计取舍
变更检测 vs 全量运行
| 方案 | 优势 | 代价 |
|---|---|---|
| 变更检测 | 大幅减少 CI 时间和资源 | 需要维护检测脚本、可能漏掉间接影响 |
| 全量运行 | 不会遗漏任何测试 | 每次 PR 都运行所有测试,CI 时间过长 |
LangChain 选择变更检测,因为 20+ 独立包全量运行可能耗时数十分钟。通过依赖传播分析,Core 变更仍能触发所有下游测试。
SHA 固定 vs Tag 引用
| 方案 | 优势 | 代价 |
|---|---|---|
| SHA 固定 | 不可变、防供应链攻击 | 需要手动更新 SHA、PR 难以判断版本新旧 |
| Tag 引用 | 自动跟随最新版本 | 攻击者可替换 tag 指向恶意代码 |
LangChain 选择 SHA 固定 + 注释标注版本(如 # v6),兼顾安全性与可读性。