You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用python-semantic-release+Woodpecker CI实现RC构建与聚合变更日志?

使用python-semantic-release + Woodpecker CI构建支持RC版本与聚合日志的CI/CD工作流

需求回顾

  • 积累多个符合Conventional Commits规范的提交(feat/fix/chore等)
  • 正式发布到main分支时,所有变更聚合到单一版本(如v2.1.0)的变更日志中
  • 生成带版本号的RC构建(如v2.1.0-rc.1)用于预发布测试
  • 保持CI/CD流程清晰、可预测

核心问题分析

预发布分支运行semantic-release时,会将提交标记为“已纳入版本”(通过生成版本标签和更新日志),导致main分支合并这些提交后,semantic-release无法将其聚合到正式版本的变更日志中;同时需要带版本号的测试工件(如Docker镜像),仅依赖commit SHA会导致版本标识不清晰。

解决方案核心思路

采用dev(日常开发)→ release/vX.Y.Z(预发布准备)→ main(正式发布)的分支模型,配合分分支的semantic-release运行策略:

  1. dev分支:仅构建测试工件,不运行semantic-release的版本发布和日志更新逻辑
  2. release/vX.Y.Z分支:运行semantic-release生成RC版本,仅在分支内维护临时日志和标签,不影响main分支的变更聚合
  3. main分支:仅在合并release分支后,运行semantic-release聚合所有未被正式版本“消耗”的提交,生成正式版本和完整变更日志

疑问解答

1. 如何正确分支与配置CI,同时支持RC构建与聚合日志?

  • 分支结构
    • dev:日常开发分支,所有功能/fix通过PR合并到此分支
    • release/vX.Y.Z:预发布分支,从dev分支创建,命名对应预期的正式版本号(如v2.1.0)
    • main:正式发布分支,仅接受release分支的合并
  • CI配置策略
    • dev分支:触发Docker镜像构建,标签用dev-<commit-sha>或通过semantic-release计算的预期RC前缀版本
    • release分支:运行semantic-release生成RC版本,构建对应版本的Docker镜像推送至测试环境,仅推送RC标签到仓库,不更新全局CHANGELOG
    • main分支:合并release分支后,运行semantic-release完成正式版本发布,更新全局CHANGELOG,构建正式版本镜像

2. semantic-release应仅在main运行,还是在预发布分支运行但限制插件?

可以在预发布分支运行,但需限制为预发布模式

  • 配置semantic-release的prerelease_branches指向release/**prerelease_prefix设为rc
  • 预发布分支运行时,仅执行版本计算、RC标签生成和镜像构建,不将变更日志推送到main分支(仅在release分支内临时维护,或直接不在预发布分支更新日志,统一在main分支生成)
  • main分支运行完整的semantic-release流程,包括版本计算、CHANGELOG更新、正式标签推送和正式镜像发布

3. 测试部署该用commit SHA镜像还是依赖RC版本?

  • 日常dev环境测试:使用dev-<commit-sha>标签的镜像,方便快速定位提交对应的构建版本
  • 预发布环境测试:必须使用RC版本镜像(如v2.1.0-rc.1),该版本与正式发布版本的代码基线一致,测试结果更具参考性,且版本号清晰易追踪

4. 搭配Woodpecker CI是否有推荐模式,还是我误用了semantic-release?

Woodpecker CI支持按分支触发不同流水线任务,是适配此工作流的理想工具,你并未误用semantic-release,只是之前的分支策略和配置未匹配semantic-release的版本识别逻辑。推荐采用分分支触发任务的模式,针对dev、release、main分支分别配置对应的CI步骤。


配置示例

Woodpecker CI配置(.woodpecker.yml)

branches: [dev, release/**, main]

pipeline:
  # Dev分支:构建测试镜像
  build-dev:
    image: docker:dind
    commands:
      - docker build -t my-fastapi-app:dev-${CI_COMMIT_SHA:0:7} .
      - docker push my-fastapi-app:dev-${CI_COMMIT_SHA:0:7}
    when:
      branch: dev

  # Release分支:生成RC版本并构建镜像
  release-rc:
    image: python:3.11
    commands:
      - pip install python-semantic-release
      # 配置预发布模式,计算RC版本并更新本地版本文件
      - semantic-release version --prerelease rc --no-push
      - export RC_VERSION=$(semantic-release print-version --prerelease rc)
      # 构建并推送RC镜像
      - docker build -t my-fastapi-app:${RC_VERSION} .
      - docker push my-fastapi-app:${RC_VERSION}
      # 推送RC标签到Git仓库
      - git tag ${RC_VERSION}
      - git push origin ${RC_VERSION}
    secrets: [GIT_TOKEN]
    when:
      branch: release/**

  # Main分支:正式发布,聚合变更日志
  release-stable:
    image: python:3.11
    commands:
      - pip install python-semantic-release
      # 运行完整发布流程:版本计算、日志更新、标签推送
      - semantic-release publish
      - export STABLE_VERSION=$(semantic-release print-version)
      # 构建并推送正式版本镜像
      - docker build -t my-fastapi-app:${STABLE_VERSION} -t my-fastapi-app:latest .
      - docker push my-fastapi-app:${STABLE_VERSION}
      - docker push my-fastapi-app:latest
    secrets: [GIT_TOKEN, DOCKER_REGISTRY_TOKEN]
    when:
      branch: main
      event: push

python-semantic-release配置(pyproject.toml)

[tool.semantic_release]
# 指定版本号所在文件
version_variable = "my_app/__init__.py:__version__"
# 使用Angular风格的提交解析器(适配Conventional Commits)
commit_parser = "angular"
# 正式发布分支
release_branch = "main"
# 预发布分支匹配规则
prerelease_branches = ["release/**"]
# 预发布版本前缀
prerelease_prefix = "rc"
# 变更日志文件路径
changelog_file = "CHANGELOG.md"
# 禁用预发布分支的日志更新(可选,统一在main分支生成)
# changelog_update_on_prerelease = false

最佳实践

  1. 所有提交必须严格遵循Conventional Commits规范,确保semantic-release能正确计算版本升级类型
  2. release分支从dev分支创建后,若需修复预发布测试的bug,直接在release分支提交,再合并回dev分支,避免代码不一致
  3. 合并release分支到main分支时,采用普通合并(不要squash或rebase),保留完整提交历史,确保semantic-release能识别所有变更
  4. 正式发布完成后,删除对应的release分支,保持仓库分支整洁

内容的提问来源于stack exchange,提问作者AK-23

火山引擎 最新活动