如何用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运行策略:
- dev分支:仅构建测试工件,不运行semantic-release的版本发布和日志更新逻辑
- release/vX.Y.Z分支:运行semantic-release生成RC版本,仅在分支内维护临时日志和标签,不影响main分支的变更聚合
- 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,构建正式版本镜像
- dev分支:触发Docker镜像构建,标签用
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
最佳实践
- 所有提交必须严格遵循Conventional Commits规范,确保semantic-release能正确计算版本升级类型
- release分支从dev分支创建后,若需修复预发布测试的bug,直接在release分支提交,再合并回dev分支,避免代码不一致
- 合并release分支到main分支时,采用普通合并(不要squash或rebase),保留完整提交历史,确保semantic-release能识别所有变更
- 正式发布完成后,删除对应的release分支,保持仓库分支整洁
内容的提问来源于stack exchange,提问作者AK-23




