Stop gating releases on a dated changelog entry - #480
Conversation
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.
📝 WalkthroughWalkthroughThe 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. ChangesRelease tag gate
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (6)
.github/workflows/deploy-docs-stg.yml.github/workflows/release.ymlscripts/check_release_version.pyscripts/verify_ci_workflow.pyspec/process/production-readiness.mdtests/test_check_release_version.py
| # 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" |
There was a problem hiding this comment.
📐 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.
| 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" | ||
| ] |
There was a problem hiding this comment.
🗄️ 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.pyRepository: 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 || trueRepository: 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:
- 1: https://peps.python.org/pep-0440/
- 2: https://github.com/python/peps/blob/main/peps/pep-0440.rst
- 3: python-discord/peps@1dd991e
- 4: https://github.com/pypa/packaging.python.org/blob/main/source/specifications/version-specifiers.rst
- 5: https://packaging.python.org/en/latest/specifications/version-specifiers/?highlight=version
- 6: https://sethmlarson.dev/pep-440?featured_on=pythonbytes
- 7: Setuptools 8 strips leading zeros in packaging for date based releases pypa/setuptools#302
- 8: https://www.mail-archive.com/distutils-sig@python.org/msg28673.html
🏁 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)
PYRepository: 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.
| - 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. |
There was a problem hiding this comment.
📐 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
Merging this PR will not alter performance
Comparing Footnotes
|
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 "writeit 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.pynow checks the one thing a tag genuinely canget 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 builtunder, and are still refused before anything builds.
v0.0.7,v1.0.0rc2v0.0.6p1,v0.0.6.post1v0.0.3.dev4+g63c06972026.30.1(docs CalVer)Also in this diff:
release.yml/deploy-docs-stg.ymlclaimed atag == pyproject == CHANGELOGgate, which has not been true since theversion 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.mdkeeps 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 testsreplaced 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-filespasses both ruff hooks (
docs-app-codespellneeds docs deps not installed inmy worktree; no
docs/files changed).Summary by CodeRabbit
Release Process
Documentation