Skip to content

How to support RC builds and keep a grouped changelog for the final release? #1440

@cookielevanna5

Description

@cookielevanna5

How to support RC builds and keep a grouped changelog for the final release?

I’m struggling to design a correct CI/release workflow using python-semantic-release together with Woodpecker CI, and I feel like I’m mixing concepts incorrectly.

My goals are:

  • Accumulate multiple commits (feat/fix/chore/etc.)
  • Have them appear under a single grouped version in the changelog (e.g. v2.1.0)
  • Still be able to generate and use versioned RC builds (v2.1.0-rc.1, rc.2, etc.) for testing before release
  • Maintain a clean and predictable CI/CD flow

What I tried

1. dev + main with semantic-release on both

  • Feature branches → squash merge into dev
  • Run semantic-release on dev → generates vX.Y.Z-rc.N
  • Then merge dev → main and run semantic-release again

Problem:

  • Each RC bumps version and updates the changelog
  • When merging to main, commits are already “consumed”
  • Final changelog is fragmented across multiple RC entries instead of grouped

2. Rebase + squash workflow

  • Squash commits into dev
  • Rebase dev onto main

Problem:

  • Breaks git history
  • semantic-release cannot properly detect previous tags
  • Version calculation becomes incorrect (falls back to older versions)

3. Adding a release/* branch

  • dev → release/x.y (merge commit)
  • Run semantic-release on release/x.y → RC versions
  • Merge release/x.y → main

Problem:

  • RC releases still update changelog and consume commits
  • Final release on main does not regroup all changes under one version
  • Changelog remains split across multiple RC entries

Core issue

It seems like running semantic-release on prerelease branches (dev or release/*):

  • “Consumes” commits by generating versions and changelog entries
  • Prevents those commits from being grouped later in the final release on main

At the same time, I still need:

  • Versioned artifacts for testing (e.g. Docker images for a FastAPI app)
  • Ability to test individual features or combinations before a stable release

Additional constraint

If I stop using RC versions on dev:

  • I lose structured versioning for test deployments
  • I can only rely on commit SHA-based builds
  • This makes version tracking less clear compared to semantic versions

What I want to achieve

An ideal workflow would:

  • Allow continuous feature integration on dev
  • Allow creation of versioned RC builds for testing (e.g. v2.1.0-rc.1)
  • Ensure that when releasing to main, the changelog is fully grouped:
## v2.1.0

### Features
- feature A
- feature B

### Fixes
- fix C

and not split across multiple RC entries.

Questions

  1. What is the correct way to structure branches and CI with python-semantic-release to support both:
  • RC builds
  • grouped final changelog?
  1. Should semantic-release be:
  • run only on main?
  • or also on prerelease branches but with limited plugins (e.g. no changelog)?
  1. How should test deployments be handled properly?
  • Use commit SHA-based images?
  • Or rely on RC versions?
  1. Is there a recommended pattern for this (especially with Woodpecker CI), or am I fundamentally misusing semantic-release?

Tech stack

  • python-semantic-release
  • Woodpecker CI
  • FastAPI app (Dockerized)
  • Conventional commits

Any guidance or real-world examples would be highly appreciated 🙏

Configuration

Pretty basic config for now, I'm open to changes, perhaps I did something wrong

Semantic Release Configuration
[semantic_release]
commit_message = "{version} [skip ci]\n\nAutomatically generated by python-semantic-release"
commit_parser = "conventional"
tag_format = "v{version}"

[semantic_release.branches.main]
match = "(main|master)"
prerelease = false

[semantic_release.branches.dev]
match = "dev"
prerelease_token = "rc"
prerelease = true
.woodpecker.yaml
when:
  event: push
  branch:
    - dev
    - main

skip_clone: true

steps:
  semantic-release:
    image: python:3.14.3-slim
    environment:
      GH_TOKEN:
        from_secret: cookie-github-token
    commands:
      - apt-get update && apt-get install -y git
      - git clone "$CI_REPO_CLONE_URL" && cd "$CI_REPO_NAME" && git checkout "$CI_COMMIT_BRANCH"
      - pip install python-semantic-release
      - semantic-release -vv --config releaserc.toml version --skip-build --no-vcs-release

Additional context

I tried lots of things before but right now it looks like this

git log --oneline --decorate --graph --all -n 50
* 46896df (tag: v2.2.0, origin/main, origin/HEAD, main) 2.2.0 [skip ci]
*   33af00a Dev (#33)
|\
| * effc434 (HEAD -> feat/letssee, tag: v2.2.0-rc.2, origin/dev, dev) 2.2.0-rc.2 [skip ci]
| * 44893d2 fix: huh what the heck why not (#32)
| * e468fb7 (tag: v2.2.0-rc.1) 2.2.0-rc.1 [skip ci]
| * 0212f35 feat: trying something interesting (#30)
| | * 18a82a8 (origin/feature/diff, feature/diff) huh what the heck why not
| |/
| | * 0223404 (origin/feature/newone, feature/newone) lets try
| |/
| *   7682309 finale? [skip ci]
| |\
| |/
|/|
* | 1f992d4 (tag: v2.1.0) 2.1.0 [skip ci]
* | 8084339 feat: finale supposedly lmao (#28)
* | 13bea6f (tag: v2.0.1) 2.0.1 [skip ci]
* | 7c8651e fix: try fixie fix (#25)
* | df53a68 (tag: v2.0.0) 2.0.0 [skip ci]
* |   df54475 Dev (#24)
|\ \
* | | db66d0c (tag: v1.0.0) 1.0.0 [skip ci]
* | |   d3c9292 Dev (#19)
|\ \ \
| | | *   4f0ebbe Merge branch 'dev' of github.com:cookielevanna5/computer-networks into dev
| | | |\
| | | | * 8d6d7f8 (tag: v1.0.0-rc.3) feat: finale supposedly lmao (#28)
| | | | | * cab06a4 (origin/feature/finale, feature/finale) finale?
| | | | |/
| | | |/|
| | | * | 76003e1 Merge branch 'dev' of github.com:cookielevanna5/computer-networks into dev
| | | |\|
| | | | * bf46a02 lets see [skip ci] (#27)
| | | | * 6f491b5 (tag: v1.0.0-rc.2) fix: try fixie fix (#25)
| | | |/
| | |/|
| | | | * ef9b155 (origin/feature/ohno, feature/ohno) alalal
| | | |/
| | | *   743a365 Merge branch 'dev' of github.com:cookielevanna5/computer-networks into dev
| | | |\
| | | |/
| | |/|
| | * | 9ad36ac (tag: v1.0.0-rc.1) feat: completely oops (#23)
| | * | fcc6e5d fix: oops (#22)
| | | | * 947dc92 (origin/feature/oops, feature/oops) princewwwwww
| | | | * 26d1138 prince
| | | |/
| | | *   ed883b7 Merge branch 'dev' of github.com:cookielevanna5/computer-networks into dev
| | | |\
| | | |/
| | |/|
| | * | f74d7f5 feat(wow)!: amazing wowowow (#21)
| | | | *   8c6ebe2 (origin/feature/another) Merge branch 'dev' into feature/another
| | | | |\
| | | |_|/
| | |/| |
| | * | | fd0a973 fix(semantic-release): lets see (#20)
| |/ / /
| | | * 7d01a1a (feature/another) fadiha rezah
| | |/
| | | * 2851a4b (origin/feature/see, feature/see) major_on_zero ssstrue
| | |/
| | *   019824b Merge branch 'dev' of github.com:cookielevanna5/computer-networks into dev
| | |\
| | |/
| |/|
| * | 57dfcf2 (tag: v0.1.0-rc.7) feat(conf): major_on_zero set to true (#18)
| | | * d5ee345 (origin/feature/major) major_on_zero true
| | |/
| | *   05f2d53 Merge branch 'dev' of github.com:cookielevanna5/computer-networks into dev
| | |\
| | |/
| |/|
| * | 6351115 (tag: v0.1.0-rc.6) feat(api)!: something incredibly different wowowow (#17)
| * | 4776420 (tag: v0.1.0-rc.5) feat(trying): interesting (#16)
| | | * 9d8a477 (origin/feature/sheraton) lolol 67
| | |/
| | | * 9fed372 (origin/feature/lol) lolol
| | |/
| | *   3226331 Merge branch 'dev' of github.com:cookielevanna5/computer-networks into dev
| | |\
| | |/
| |/|
| * | 80c90e7 (tag: v0.1.0-rc.4) fix(api): found something great in api wow (#15)
| | | * 33f14d0 (origin/feature/try) added distinguishing between stuff
| | | *   55a413e Merge branch 'dev' into feature/try
| | | |\
| | | |/
| | |/|
| | * |   d0700ec Merge branch 'main' into dev
| | |\ \
| |_|/ /
|/| | |
| | * | 1cfb03a try lololo 67
| |/ /
| | *   ad636e1 Merge branch 'main' into feature/try
| | |\
| |_|/
|/| |

Metadata

Metadata

Assignees

No one assigned

    Labels

    duplicateThis issue or pull request already existsquestion

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions