Skip to content

Stop gating releases on a dated changelog entry - #480

Merged
Kastier1 merged 1 commit into
mainfrom
drop-changelog-release-gate
Aug 8, 2026
Merged

Stop gating releases on a dated changelog entry#480
Kastier1 merged 1 commit into
mainfrom
drop-changelog-release-gate

Conversation

@Kastier1

@Kastier1 Kastier1 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

The changelog leg of the release gate could only ever be satisfied by the
tagged tree, so a missing or undated ## [X.Y.Z] heading did not mean "write
it up before publishing" — it meant deleting the tag, committing a
documentation edit, and re-cutting the tag on the new commit. It blocked the
PyPI publish and the docs deploy on a file that a follow-up commit fixes.

scripts/check_release_version.py now checks the one thing a tag genuinely can
get wrong on its own: its shape. uv-dynamic-versioning derives the distribution
version from the tag, so .postN, .devN, local (+…), and non-canonical
(-alpha1) tags can never match the version their own artifacts are built
under, and are still refused before anything builds.

tag before after
v0.0.7, v1.0.0rc2 pass only with a dated changelog entry pass
v0.0.6p1, v0.0.6.post1 fail (shape) fail (shape)
v0.0.3.dev4+g63c0697 fail (shape) fail (shape)
2026.30.1 (docs CalVer) fail (shape) fail (shape)

Also in this diff:

  • Step names and comments in release.yml / deploy-docs-stg.yml claimed a
    tag == pyproject == CHANGELOG gate, which has not been true since the
    version became dynamic. They now describe the shape gate, and the docs-deploy
    pre-checkout regex is labelled as the loose ref/image-tag check it is.
  • spec/process/production-readiness.md keeps the dated-heading checklist item,
    now marked deliberately unenforced, and documents the release tag shape
    alongside it.
  • scripts/verify_ci_workflow.py: the publish-job description string.

Verification

  • pytest tests/test_check_release_version.py — 11 passed (changelog tests
    replaced with shape and CLI coverage, plus one asserting the changelog is
    ignored).
  • python scripts/verify_ci_workflow.py — OK.
  • ruff check . / ruff format --check . — clean; pre-commit run --all-files
    passes both ruff hooks (docs-app-codespell needs docs deps not installed in
    my worktree; no docs/ files changed).

Review in cubic

Summary by CodeRabbit

  • Release Process

    • Release validation now checks that tags use supported canonical version formats, including approved prereleases.
    • Development, post-release, local, dated, and non-canonical tag formats are rejected before building.
    • Releases are no longer blocked when the changelog lacks a matching dated entry.
  • Documentation

    • Release guidance and validation messages now clearly describe supported tag formats and changelog requirements.

The changelog leg of the release gate could only be satisfied by the tagged
tree, so a missing or undated `## [X.Y.Z]` heading did not mean "write it up
before publishing" — it meant deleting the tag, committing a documentation
edit, and re-cutting the tag on the new commit. That is a bad trade for a file
a follow-up commit fixes, and it blocked both the PyPI publish and the docs
deploy on it.

What survives is the leg a tag genuinely can get wrong on its own: the shape.
uv-dynamic-versioning derives the distribution version from the tag, so a
`.postN`, `.devN`, local, or non-canonical (`-alpha1`) tag can never match the
version its own artifacts are built under, and is still refused before anything
builds. Writing up the release stays a release-checklist item in
`spec/process/production-readiness.md`, now marked as deliberately unenforced.
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The release gate now validates canonical release-tag shapes and derived versions without checking dated changelog entries. Workflows, tests, CI validation, and release-readiness documentation reflect this behavior.

Changes

Release tag gate

Layer / File(s) Summary
Tag validator and command interface
scripts/check_release_version.py, tests/test_check_release_version.py
The validator removes changelog inputs and checks canonical release and prerelease tags. Tests cover accepted, rejected, missing, and ignored-changelog cases.
Workflow gates and release checklist
.github/workflows/*.yml, scripts/verify_ci_workflow.py, spec/process/production-readiness.md
Release workflows and CI validation describe tag-shape gating. The readiness checklist keeps the dated changelog entry as a non-blocking requirement.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • reflex-dev/xy#275: Modifies the shared release-tag validator, workflows, and tests.
  • reflex-dev/xy#276: Modifies the release-tag validation flow in the staging deployment workflow and validator.
  • reflex-dev/xy#406: Modifies release-tag validation and release workflow gating.

Suggested reviewers: alek99, masenf

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: removing the dated changelog entry as a release gate.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch drop-changelog-release-gate

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@masenf masenf 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.

Rip it

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/deploy-docs-stg.yml:
- Around line 45-55: Correct the comment near the VERSION validation regex by
changing “a pre-release tags” to “pre-release tags”, without altering the
validation logic.

In `@scripts/check_release_version.py`:
- Around line 62-68: Update the release-tag regex used by check_release,
specifically the _RELEASE pattern, so every numeric release and pre-release
component accepts only ASCII digits without leading zeros, while still allowing
the single value 0. Preserve valid canonical release-tag formats and ensure
aliases such as v01.02.003 and v1.2.3rc01 are rejected.

In `@spec/process/production-readiness.md`:
- Around line 355-358: Update the changelog guidance to show that the dated
heading uses the exact version being tagged, including its canonical pre-release
suffix when present (for example, v0.2.0rc1 maps to ## [0.2.0rc1] — YYYY-MM-DD).
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 20f884e0-e9d0-47cb-a5cd-56b02dbe580c

📥 Commits

Reviewing files that changed from the base of the PR and between f7c30d4 and 317ebd5.

📒 Files selected for processing (6)
  • .github/workflows/deploy-docs-stg.yml
  • .github/workflows/release.yml
  • scripts/check_release_version.py
  • scripts/verify_ci_workflow.py
  • spec/process/production-readiness.md
  • tests/test_check_release_version.py

Comment on lines +45 to +55
# Loose shape check only: this runs before the repo is on disk, so all
# it has to establish is that the value can resolve to a ref and is a
# legal image tag. check_release_version.py below is the authority on
# what a release tag is. Deliberately PEP 440, not strict SemVer — a
# pre-release tags as `v0.2.0rc1`, no dash. Local versions are
# excluded because `+` is not a legal Docker tag character and this
# value becomes the image tag, and the trailing class cannot end in a
# separator. 128 chars is the ceiling the reusable build/helm
# workflows enforce.
if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-]?[A-Za-z0-9]+)*$ ]]; then
echo "::error::version must be a release tag like v0.1.0, v0.2.0rc1 or v0.2.0.post1"
echo "::error::version must be a release tag like v0.1.0 or v0.2.0rc1"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the pre-release sentence.

Line 49 says “a pre-release tags”. Replace it with “pre-release tags”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy-docs-stg.yml around lines 45 - 55, Correct the
comment near the VERSION validation regex by changing “a pre-release tags” to
“pre-release tags”, without altering the validation logic.

Comment on lines +62 to 68
def check_release(tag: str, package: str = "xy") -> list[str]:
spec = PACKAGES[package]
match = spec.tag_re.match(tag)
if match is None:
if spec.tag_re.match(tag) is None:
return [
f"tag {tag!r} is not a release tag for {package} — expected "
f"{spec.tag_shape!r}, the shape the distribution version is derived from"
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

python -m pip install --quiet --disable-pip-version-check packaging

python - <<'PY'
from packaging.version import Version

for raw, expected in {
    "01.02.003": "1.2.3",
    "1.2.3rc01": "1.2.3rc1",
}.items():
    actual = str(Version(raw))
    assert actual == expected, (raw, actual, expected)
    print(f"{raw} -> {actual}")
PY

rg -n -C 3 'non_canonical|v01\.2\.3|rc01|check_release' \
  tests/test_check_release_version.py scripts/check_release_version.py

Repository: reflex-dev/xy

Length of output: 188


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "files:"
git ls-files | rg '(^|/)(scripts/check_release_version.py|tests/test_check_release_version.py|pyproject.toml|uv.lock|requirements|Pipfile|poetry.lock)$'
echo
echo "check_release_version outline:"
ast-grep outline scripts/check_release_version.py --view expanded
echo
echo "selected lines:"
cat -n scripts/check_release_version.py | sed -n '1,140p'
echo
echo "test file selected lines if present:"
if [ -f tests/test_check_release_version.py ]; then
  cat -n tests/test_check_release_version.py | sed -n '1,220p'
fi
echo
echo "version-related search:"
rg -n "check_release|PACKAGES|RELEASE|tag_re|distribution_version|release_tag|package_version|Version|tag" scripts tests pyproject.toml 2>/dev/null || true

Repository: reflex-dev/xy

Length of output: 50369


🌐 Web query:

PEP 440 canonical version format integer numbers ASCII digits leading zeroes normalization

💡 Result:

Under PEP 440, all numeric version components must be represented as sequences of ASCII digits (specifically 0-9) [1][2][3]. The specification requires that these components be interpreted as non-negative integers [1][4]. Regarding normalization and leading zeros: Integer Normalization: All numeric segments are normalized by interpreting them as integers via the Python int built-in [1][5]. Consequently, any leading zeros are stripped during the normalization process [1][6]. For example, a version segment of 00 or 09000 will normalize to 0 and 9000, respectively [1][2]. Local Versions Exception: This normalization behavior does not apply to integers contained within alphanumeric segments of a local version (e.g., 1.0+foo0100), which remain in their already normalized form [1][4]. Because the canonical format requires these components to be represented as integers, the presence of leading zeros is technically redundant and they are removed to ensure a consistent, standardized representation [1][7]. The requirement for ASCII digits specifically excludes arbitrary Unicode [Nd] code points, a clarification added to the specification to ensure consistent behavior across parsing tools [8][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import re
from scripts.check_release_version import check_release

for tag in [
    "v01.02.003",
    "v1.2.30",
    "v1.2.3rc01",
    "v1.2.3α1",
    "v1.2.3a1",
    "v0.2.0",
]:
    print(f"{tag!r}: {check_release(tag)!r}")

number = r"\d+"
release = rf"(?:{number}\.){{2}}{number}(?:(?:a|b|rc){number})?"
print("released tags accepted by current regex:", re.compile(re.escape("v") + release + r"$").match("v01.02.003") is not None)
print("release prerelease digit regex accepts non-ASCII Nd alpha:", re.search(r"[^\u0030-\u0039]", "α") is not None)
PY

Repository: reflex-dev/xy

Length of output: 514


Reject normalized numeric release aliases.

v01.02.003 and v1.2.3rc01 pass check_release() because _RELEASE uses \d+, even though PEP 440 canonicalizes release numbers and pre-release numbers with leading zeros. Match each release and pre-release component with ASCII digits, excluding leading zeros except for 0.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/check_release_version.py` around lines 62 - 68, Update the
release-tag regex used by check_release, specifically the _RELEASE pattern, so
every numeric release and pre-release component accepts only ASCII digits
without leading zeros, while still allowing the single value 0. Preserve valid
canonical release-tag formats and ensure aliases such as v01.02.003 and
v1.2.3rc01 are rejected.

Comment on lines 355 to +358
- Add a dated `## [X.Y.Z] — YYYY-MM-DD` heading to `CHANGELOG.md` for the
version being tagged. This is the one thing the tag cannot vouch for, and the
release gate blocks the publish without it.
version being tagged. Deliberately not gated: a missing or undated entry is
fixed with a follow-up commit, and blocking the publish on it only ever forced
a tag to be deleted and re-cut over a documentation edit.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Show the pre-release heading format.

A tag such as v0.2.0rc1 needs a changelog entry for 0.2.0rc1. The shown ## [X.Y.Z] format can imply ## [0.2.0] instead. State that the heading includes the canonical pre-release suffix when present.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@spec/process/production-readiness.md` around lines 355 - 358, Update the
changelog guidance to show that the dated heading uses the exact version being
tagged, including its canonical pre-release suffix when present (for example,
v0.2.0rc1 maps to ## [0.2.0rc1] — YYYY-MM-DD).

Source: Coding guidelines

@Kastier1
Kastier1 merged commit 659888c into main Aug 8, 2026
22 checks passed
@codspeed-hq

codspeed-hq Bot commented Aug 8, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 109 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing drop-changelog-release-gate (317ebd5) with main (f7c30d4)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants