如何基于python-semantic-release与Woodpecker CI实现RC构建并保持最终版本的聚合式变更日志?
解决python-semantic-release + Woodpecker CI的RC发布与聚合变更日志问题
我完全懂你现在的纠结——既要用规范的RC版本做测试部署,又要保证正式发布时变更日志是整整齐齐聚合在一起的,确实很容易在semantic-release的提交"消耗"逻辑上踩坑。结合你的技术栈(python-semantic-release、Woodpecker CI、Docker化FastAPI)和需求,我给你拆解一套可行的落地方案:
核心思路:把「RC版本计算」和「正式版本发布」拆解开
semantic-release所谓的"消耗"提交,本质是因为它会把计算后的版本写入CHANGELOG、打标签并推送到仓库。所以我们要做的是:
- 预发布分支(dev/release/*)只负责生成RC版本的工件(比如Docker镜像),绝不修改CHANGELOG、绝不打正式标签
- 正式发布只在main分支运行,基于完整的提交历史计算版本,生成聚合式的CHANGELOG
1. 推荐的分支结构
我建议用「dev -> release/x.y -> main」的三层分支模式:
main:纯正式发布分支,只接受从release/*分支的普通合并(禁止直接提交、禁止squash merge)dev:日常开发分支,所有功能分支通过squash merge合并到这里,保证dev分支的提交都是规范的conventional commitsrelease/x.y:预发布准备分支,当dev上的功能足够发布一个新版本(比如v2.1.0)时,从dev创建该分支,专门用于生成RC版本和最终发布准备
2. Woodpecker CI配置示例
我把不同分支的CI流程拆成三个配置文件,清晰又好维护:
(1)dev分支:CI验证+临时RC镜像
dev分支只做测试和临时镜像,用提交短SHA做标记足够清晰:
# .woodpecker/dev.yml when: branch: dev steps: - name: run-tests image: python:3.11 commands: - pip install -r requirements.txt - pytest --cov=your_app - name: build-temp-rc-image image: woodpeckerci/plugin-docker-buildx settings: repo: your-docker-registry/your-fastapi-app tags: ${CI_COMMIT_SHA:0:7}-rc secrets: [docker_username, docker_password] - name: deploy-dev # 部署到开发环境的脚本,比如用docker-compose更新 commands: - ./deploy-dev.sh ${CI_COMMIT_SHA:0:7}-rc
(2)release/x.y分支:生成规范RC版本+预发布部署
这里用semantic-release的--noop模式计算RC版本,只输出版本号不修改仓库:
# .woodpecker/release.yml when: branch: release/* steps: - name: run-tests image: python:3.11 commands: - pip install -r requirements.txt - pytest --cov=your_app - name: calculate-rc-version image: python:3.11 commands: - pip install python-semantic-release # 计算RC版本,--noop表示只输出不修改仓库 - semantic-release version --prerelease rc --noop | grep "New version" | awk '{print $3}' > rc_version.txt - export SEMVER_RC=$(cat rc_version.txt) - name: build-rc-image image: woodpeckerci/plugin-docker-buildx settings: repo: your-docker-registry/your-fastapi-app tags: ${SEMVER_RC} secrets: [docker_username, docker_password] - name: deploy-staging # 部署到测试环境的脚本 commands: - ./deploy-staging.sh ${SEMVER_RC}
(3)main分支:正式发布+聚合CHANGELOG
这里运行完整的semantic-release流程,生成聚合的CHANGELOG并打正式标签:
# .woodpecker/main.yml when: branch: main steps: - name: run-tests image: python:3.11 commands: - pip install -r requirements.txt - pytest --cov=your_app - name: semantic-release-publish image: python:3.11 commands: - pip install python-semantic-release # 完整发布流程:计算版本、更新CHANGELOG、打标签、推送到仓库 - semantic-release publish secrets: [github_token] # 用于推送CHANGELOG和标签到Git仓库 - name: build-release-image image: woodpeckerci/plugin-docker-buildx settings: repo: your-docker-registry/your-fastapi-app tags: v${SEMANTIC_RELEASE_VERSION} secrets: [docker_username, docker_password] - name: deploy-production # 部署到生产环境的脚本 commands: - ./deploy-prod.sh v${SEMANTIC_RELEASE_VERSION}
3. python-semantic-release配置调整
在pyproject.toml里明确分支规则,让预发布分支只允许计算版本,禁止修改仓库:
[tool.semantic_release] version_variable = "your_app/__init__.py:__version__" commit_message_template = "chore(release): v{version}" prerelease_token = "rc" # 只有main分支允许完整发布流程 branches = ["main"] # 针对release分支的特殊配置:只生成RC版本,不推送变更 [tool.semantic_release.branches.release/*] prerelease = true allow_deployment = false # 禁止推送CHANGELOG和标签
4. 解答你的核心疑问
(1)如何设计分支与CI配置?
就是上面的三层分支模式:
- dev分支持续集成,用短SHA做临时RC标记,不影响正式发布
- release/x.y分支生成规范RC版本,方便测试团队追踪,且不修改仓库历史
- main分支基于release分支的完整提交历史,运行semantic-release生成聚合的CHANGELOG
(2)semantic-release该在哪些分支运行?
- main分支:必须运行完整的
publish流程,负责生成CHANGELOG、打正式标签 - release/x.y分支:只运行
version --prerelease rc --noop计算版本,绝不修改仓库 - dev分支:可以不用运行semantic-release,用短SHA做标记足够清晰,节省CI资源
(3)测试部署该用什么版本标记?
- 开发环境:用提交短SHA(如
abc123-rc),足够追溯且不会和正式RC冲突 - 预发布/测试环境:用release分支生成的规范RC版本(如
v2.1.0-rc.1),方便测试团队对应需求和bug - 这样既保证了可追溯性,又不会消耗提交影响正式发布的CHANGELOG
(4)是否误用了semantic-release?
完全没有!你之前的问题只是让预发布分支的semantic-release修改了仓库历史,导致提交被"消耗"。核心是要区分版本计算和**版本发布(修改仓库)**这两个步骤,预发布只做计算,正式发布才做修改。
额外小技巧
- 当需要更新RC版本时,只要把dev分支的最新代码merge到release/x.y分支,重新运行CI就能生成下一个RC版本(比如v2.1.0-rc.2),semantic-release会自动递增RC序号
- 合并release/x.y到main时,一定要用普通merge,不要squash,这样main分支会保留所有提交历史,semantic-release才能正确计算版本并聚合所有变更到一个版本条目里
- 如果担心release分支的临时文件,可以在正式发布后删除release分支,保持仓库整洁
内容的提问来源于stack exchange,提问作者AK-23
相关产品推荐
相关产品推荐

