Skip to content

feat: add changelog_skip_prereleases setting to omit prerelease entries from changelog - #1985

Closed
bearomorphism wants to merge 2 commits into
commitizen-tools:masterfrom
bearomorphism:fix/1218-changelog-skip-prereleases
Closed

feat: add changelog_skip_prereleases setting to omit prerelease entries from changelog#1985
bearomorphism wants to merge 2 commits into
commitizen-tools:masterfrom
bearomorphism:fix/1218-changelog-skip-prereleases

Conversation

@bearomorphism

Copy link
Copy Markdown
Collaborator

Description

Adds a new changelog_skip_prereleases configuration setting (default alse). When enabled, all prerelease release entries (tags whose version parses as a prerelease -
c, �lpha, �eta, dev, etc.) are omitted from the generated changelog entirely.

Closes #1218

Why

Currently, users who ship prereleases see cluttered changelogs like:

`

0.1.0-b0 (2024-08-20)

0.1.0-a0 (2024-08-20)

Feat

  • add onboarding modal

0.1.0 (2024-08-20)

`

The only existing option (--merge-prerelease) collapses prereleases into the next stable entry. There is no way to drop them silently. This PR adds that opt-in.

What changed

File Change
commitizen/defaults.py Added changelog_skip_prereleases: bool to Settings TypedDict and DEFAULT_SETTINGS (default alse)
commitizen/tags.py Added skip_prereleases: bool field to TagRules; updated include_in_changelog so that prerelease tags return False when the flag is set; updated rom_settings factory
commitizen/commands/changelog.py Passes skip_prereleases from config when constructing TagRules
ests/test_conf.py Added changelog_skip_prereleases to both expected-settings literals (avoids CI failure)
ests/test_changelog.py Two new unit tests for generate_tree_from_commits
ests/commands/test_changelog_command.py Two new end-to-end tests via cz changelog --dry-run
docs/commands/changelog.md New section next to --merge-prerelease

How it works

TagRules.include_in_changelog is the single gate that decides whether a tag produces a release block in the changelog tree. When skip_prereleases=True the method returns False for any tag whose parsed version satisfies �ersion.is_prerelease, before the merge-prerelease check runs.

Backward compatibility

  • Default is alse; no existing behaviour changes.
  • When both changelog_skip_prereleases = true and changelog_merge_prerelease = true are set, skip wins (documented in the docs and asserted in a test).

Checklist

Was generative AI tooling used to co-author this PR?

  • Yes (please specify the tool below)

Generated-by: Claude following the guidelines

Code Changes

  • Add test cases to all the changes you introduce
  • Run linters and tests locally
  • Manually test the changes
  • Update the documentation for the changes

Documentation Changes

  • Added changelog_skip_prereleases section in docs/commands/changelog.md next to --merge-prerelease

Expected Behavior

Scenario Before After
changelog_skip_prereleases = false (default) Prereleases appear in changelog Same - no change
changelog_skip_prereleases = true N/A Prerelease entries (
c, �lpha, �eta, dev) omitted
Both skip and merge set to rue merge wins skip wins (prereleases dropped, not collapsed)

Steps to Test This Pull Request

`�ash

Clone and set up

git clone https://github.com/bearomorphism/commitizen && cd commitizen
git checkout fix/1218-changelog-skip-prereleases
uv sync --frozen --group base --group test --group linters

Run targeted tests

uv run pytest tests/test_changelog.py::test_generate_tree_from_commits_skip_prereleases
uv run pytest tests/test_changelog.py::test_skip_prereleases_wins_over_merge_prereleases
uv run pytest tests/commands/test_changelog_command.py::test_changelog_config_flag_skip_prereleases
uv run pytest tests/commands/test_changelog_command.py::test_changelog_config_skip_prereleases_wins_over_merge_prerelease
`

Additional Context

Related to #1965 (merge-prerelease) which was the original implementation of prerelease handling in changelogs.

bearomorphism and others added 2 commits May 9, 2026 22:20
Users can now mark a cz_customize input question as required by setting
required = true in their config. The loader converts this into a questionary
validate= callable that rejects empty/whitespace-only answers with the
message 'This answer is required.' The required key is stripped before the
question dict reaches questionary, so questionary never sees the unknown key.

Closes commitizen-tools#1231

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When changelog_skip_prereleases = true is set, prerelease tags (rc,
alpha, beta, dev) are omitted entirely from the generated changelog.
Both cz changelog and cz bump --changelog honour the setting.

When both changelog_skip_prereleases and changelog_merge_prerelease
are set, skip takes precedence and prerelease entries are dropped.

Closes commitizen-tools#1218

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@codecov

codecov Bot commented May 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.10345% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 98.18%. Comparing base (4b93a50) to head (50d8156).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
commitizen/question.py 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1985      +/-   ##
==========================================
- Coverage   98.23%   98.18%   -0.06%     
==========================================
  Files          61       61              
  Lines        2779     2804      +25     
==========================================
+ Hits         2730     2753      +23     
- Misses         49       51       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new configuration option to omit prerelease tags from generated changelogs, and also extends the customize question system with a required field while refining changelog file path resolution behavior.

Changes:

  • Add changelog_skip_prereleases setting and plumb it through TagRules and cz changelog.
  • Add required = true support for type = "input" customize questions via a generated validate callback.
  • Adjust changelog output file path handling so CLI --file-name stays CWD-relative while config changelog_file is anchored to the config directory.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
commitizen/defaults.py Add changelog_skip_prereleases to settings + defaults.
commitizen/tags.py Add skip_prereleases to TagRules and filter prerelease tags.
commitizen/commands/changelog.py Wire skip_prereleases and change file-name path resolution logic.
commitizen/question.py Extend InputQuestion with validate and required.
commitizen/cz/customize/customize.py Implement required by injecting a validator and stripping the key.
tests/test_conf.py Update expected settings dictionaries with the new key.
tests/test_changelog.py Add unit tests for skip-prereleases + refactor file-name resolution test.
tests/commands/test_changelog_command.py Add E2E tests for skip-prereleases config behavior.
tests/test_cz_customize.py Add unit tests for required questions behavior.
docs/commands/changelog.md Document changelog_skip_prereleases and clarify path resolution.
docs/customization/config_file.md Document required for input questions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +65 to +83
file_name_from_args: str | None = arguments.get("file_name") or None
file_name_from_config: str | None = self.config.settings.get("changelog_file")
changelog_file_name = file_name_from_args or file_name_from_config
if not isinstance(changelog_file_name, str):
raise NotAllowed(
"Changelog file name is broken.\n"
"Check the flag `--file-name` in the terminal "
f"or the setting `changelog_file` in {self.config.path}"
)
self.file_name = (
Path(self.config.path.parent, changelog_file_name).as_posix()
if self.config.path is not None
else changelog_file_name
)
if file_name_from_args:
# Explicit CLI argument — keep relative to cwd (or use as-is if absolute).
self.file_name = file_name_from_args
elif self.config.path is not None:
# From configuration — anchor to the config file's directory.
self.file_name = Path(
self.config.path.parent, changelog_file_name
).as_posix()
else:
self.file_name = changelog_file_name
Comment on lines 121 to 132
self.tag_rules = TagRules(
scheme=self.scheme,
tag_format=arguments.get("tag_format")
or self.config.settings["tag_format"],
legacy_tag_formats=self.config.settings["legacy_tag_formats"],
ignored_tag_formats=self.config.settings["ignored_tag_formats"],
merge_prereleases=arguments.get("merge_prerelease")
or self.config.settings["changelog_merge_prerelease"],
skip_prereleases=bool(
self.config.settings.get("changelog_skip_prereleases")
),
)
Comment thread commitizen/tags.py
Comment on lines 164 to +176
def include_in_changelog(self, tag: GitTag) -> bool:
"""Check if a tag should be included in the changelog"""
try:
version = self.extract_version(tag)
except InvalidVersion:
return False
return not (self.merge_prereleases and version.is_prerelease)
if version.is_prerelease:
# skip_prereleases wins over merge_prereleases when both are set
if self.skip_prereleases:
return False
if self.merge_prereleases:
return False
return True
Comment thread tests/test_changelog.py
assert "v1.2.0" in versions
assert "v1.0.0" in versions


assert "more stuff" in out


@pytest.mark.usefixtures("tmp_commitizen_project")
Comment on lines +64 to +68
for raw in raw_questions:
q: dict[str, Any] = dict(raw)
if q.get("type") == "input" and q.pop("required", False):
q["validate"] = _required_validator
result.append(q) # type: ignore[arg-type]
@bearomorphism

Copy link
Copy Markdown
Collaborator Author

Closing this PR per maintainer-triage policy: feature-request issues should sit with the maintainers for design / scope review before any implementation lands. The issue's type: feature (or implicit equivalent) label means it's not on a "ready-to-implement" track yet.

The implementation itself is preserved on the branch (fix/1218-changelog-skip-prereleases) — if a maintainer decides this is the direction they want, the PR can be re-opened in one click, or the work can serve as a starting point for a maintainer-led design.

This PR is being closed so that #1218 reverts to "awaiting maintainer triage / decision" rather than "PR pending review", which is the correct state for a feature request.

Closed via the round-2 triage cleanup in #1965.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

2 participants