Skip to content

Commit d3a9fd6

Browse files
committed
Fail the release when docstrfmt verifies nothing
The 1.6.0 release job ran docstrfmt over the changelog, was told "0 files was checked", and treated the zero as success. The pre-commit docstrfmt hook agreed, so the fail-closed check at the end of the release passed and the commit went to main past the ruleset bypass, where pre-commit.ci then rejected it. Drop the bespoke docstrfmt call so the hooks are the only formatter, and verify the result: the changelog check must both succeed and report the file it inspected. A run that formats nothing now stops the release instead of shipping.
1 parent 58b7aae commit d3a9fd6

1 file changed

Lines changed: 25 additions & 5 deletions

File tree

tasks/release.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
from __future__ import annotations
44

5+
import re
6+
57
from pathlib import Path
6-
from subprocess import call, check_call
8+
from subprocess import call, check_call, run
79

810
from git import Commit, Remote, Repo, TagReference
911
from packaging.version import Version
@@ -15,6 +17,7 @@
1517
CHANGELOG_FRAGMENTS_DIR = ROOT_SRC_DIR / 'docs' / 'changelog'
1618
MAJOR_FRAGMENT_TYPES = frozenset({'removal'})
1719
MINOR_FRAGMENT_TYPES = frozenset({'feature', 'deprecation'})
20+
CHECKED_FILE_COUNT = re.compile(r'(\d+) files? was checked')
1821

1922

2023
def main(version_str: str, *, push: bool) -> None:
@@ -80,20 +83,37 @@ def create_release_commit(repo: Repo, version: Version) -> Commit:
8083
update_version_file(version)
8184
print('build changelog from fragments with towncrier')
8285
check_call(['towncrier', 'build', '--yes', '--version', version.public], cwd=str(ROOT_SRC_DIR)) # noqa: S603
83-
# towncrier appends the issue reference past docstrfmt's width budget, so its raw output can run over the
84-
# limit; reflow it here with a pinned docstrfmt instead of trusting the release job's isolated hook env,
85-
# which passed over-long lines into 1.5.1 and left a changelog that failed pre-commit everywhere else.
86-
check_call(['docstrfmt', '--line-length', '120', 'CHANGELOG.rst'], cwd=str(ROOT_SRC_DIR))
8786
call(['pre-commit', 'run', '--all-files'], cwd=str(ROOT_SRC_DIR))
8887
call(['pre-commit', 'run', '--all-files'], cwd=str(ROOT_SRC_DIR))
8988
repo.git.add('src/build/__init__.py', 'CHANGELOG.rst', 'docs/changelog/*')
9089
check_call(['pre-commit', 'run', '--all-files', '--show-diff-on-failure'], cwd=str(ROOT_SRC_DIR))
9190
if repo.is_dirty(index=False, working_tree=True, untracked_files=False):
9291
msg = 'Pre-commit hooks modified files after final run. This indicates an environment inconsistency.'
9392
raise RuntimeError(msg)
93+
verify_changelog_formatted()
9494
return repo.index.commit(f'chore: prepare for {version}')
9595

9696

97+
def verify_changelog_formatted() -> None:
98+
# 1.6.0 shipped a changelog that every other environment reformats: the release runner's docstrfmt inspected no
99+
# files and still exited 0, so the hook reported success. Require the check to name a file it actually looked at.
100+
# The width is the one the docstrfmt hook passes in .pre-commit-config.yaml.
101+
result = run(
102+
['docstrfmt', '--check', '--ignore-cache', '--line-length', '120', 'CHANGELOG.rst'],
103+
capture_output=True,
104+
check=False,
105+
cwd=str(ROOT_SRC_DIR),
106+
encoding='utf-8',
107+
)
108+
report = (result.stderr or result.stdout).strip()
109+
if result.returncode:
110+
msg = f'CHANGELOG.rst is not formatted the way the docstrfmt hook expects:\n{report}'
111+
raise RuntimeError(msg)
112+
if (checked := CHECKED_FILE_COUNT.search(report)) is None or not int(checked[1]):
113+
msg = f'docstrfmt inspected no files, leaving CHANGELOG.rst unverified:\n{report}'
114+
raise RuntimeError(msg)
115+
116+
97117
def update_version_file(version: Version) -> None:
98118
content = VERSION_FILE.read_text(encoding='utf-8')
99119
lines = content.splitlines(keepends=True)

0 commit comments

Comments
 (0)