chore(tools): validate date format in changelogs
What changed, and why it matters
This commit is a routine tooling change. It adds a date-format check to the script that manages changelog files and removes empty subsections from generated changelogs. There is no security relevance: it does not touch firmware, cryptography, wallet handling, or any user-facing device behavior.
No security action needed. Treat as normal repository maintenance.
Security signals we found
No strong security signals were identified.
Evidence from the diff
tools/changelog.py is a developer-side helper for formatting release changelogs. The patch introduces validate_date() to enforce a specific human-readable date style (e.g., ‘18th March 2026’) and a helper for correct English ordinal suffixes. It also unifies current_date() to always use that format and adds a regex step to strip empty ‘###’ subsections. No code running on the Trezor device is changed.
Changed components
tools/changelog.pyInspect captured patch +47 / −15
diff --git a/tools/changelog.py b/tools/changelog.py
index f827812e..1ca0cd81 100755
--- a/tools/changelog.py
+++ b/tools/changelog.py
@@ -105,23 +105,49 @@ def linkify_gh_diff(changelog_file: Path, tag_prefix: str) -> None:
linkified = True
+def _expected_day_suffix(day: int) -> str:
+ if 11 <= day <= 13:
+ return "th"
+ return {1: "st", 2: "nd", 3: "rd"}.get(day % 10, "th")
+
+
+def validate_date(date: str, project: Path) -> None:
+ _MONTHS = (
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+ )
+ _DATE_DAY_SUFFIX_RE = re.compile(
+ r"^([1-9]|[12][0-9]|3[01])(st|nd|rd|th) (" + "|".join(_MONTHS) + r") \d{4}$"
+ )
+ m = _DATE_DAY_SUFFIX_RE.match(date)
+ if not m:
+ raise click.BadParameter(
+ "Expected format: '18th March 2026'.", param_hint="--date"
+ )
+ day = int(m.group(1))
+ suffix = m.group(2)
+ expected_suffix = _expected_day_suffix(day)
+ if suffix != expected_suffix:
+ raise click.BadParameter(
+ f"Invalid day suffix '{day}{suffix}', expected '{day}{expected_suffix}'.",
+ param_hint="--date",
+ )
+
+
def current_date(project: Path) -> str:
- parts = project.parts
today = datetime.datetime.now()
-
- if (
- parts[-3:] == ("core", "embed", "boardloader")
- or parts[-3:] == ("core", "embed", "bootloader")
- or parts[-3:] == ("core", "embed", "bootloader_ci")
- or parts[-2:] == ("legacy", "bootloader")
- or parts[-2:] == ("legacy", "intermediate_fw")
- ):
- return today.strftime("%B %Y")
- elif parts[-1] == "python":
- return today.strftime("%Y-%m-%d")
- else:
- daysuffix = {1: "st", 2: "nd", 3: "rd"}.get(today.day % 10, "th")
- return today.strftime(f"%-d{daysuffix} %B %Y")
+ daysuffix = _expected_day_suffix(today.day)
+ return today.strftime(f"%-d{daysuffix} %B %Y")
def filter_changelog(changelog_file: Path, internal_name: str) -> None:
@@ -151,6 +177,10 @@ def filter_changelog(changelog_file: Path, internal_name: str) -> None:
res = filter_line(line)
if res is not None:
destination.write(res)
+
+ # Drop empty sub-sections
+ destination_file.write_text(re.sub(r"### .*\n\n", "", destination_file.read_text()))
+
# Ensure issue links are present even if we truncated before the link block
linkify_changelog(destination_file)
@@ -282,6 +312,8 @@ def generate(
if date is None:
date = current_date(project)
+ else:
+ validate_date(date, project)
if only_models:
generate_filtered(project, changelog)
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.