Files
tactility/Buildscripts/gh-release-summary.py
T
Ken Van Hoeylandt 35fd7dd536 Post-release changes (#543)
- Update version to 0.8.0-dev
- Add new release scripts
- Update release docs
2026-07-02 20:52:26 +02:00

127 lines
3.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""Summarize commits between the last 2 pushed git tags into commits.md and pull-requests.md."""
import argparse
import re
import subprocess
import sys
from pathlib import Path
PR_REF_RE = re.compile(r"#(\d+)")
def get_repo(cli_repo: str | None) -> str:
if cli_repo:
return cli_repo
try:
result = subprocess.run(
["git", "remote", "get-url", "origin"],
capture_output=True,
text=True,
check=True,
)
url = result.stdout.strip()
except (FileNotFoundError, subprocess.CalledProcessError):
sys.exit("Could not detect repo from git remote. Pass --repo owner/name.")
if url.startswith("git@"):
path = url.split(":", 1)[1]
else:
path = url.split("github.com/", 1)[-1]
return path.removesuffix(".git")
def get_last_two_tags() -> tuple[str, str]:
result = subprocess.run(
["git", "tag", "--sort=-creatordate"],
capture_output=True,
text=True,
check=True,
)
tags = [t for t in result.stdout.splitlines() if t]
if len(tags) < 2:
sys.exit(f"Need at least 2 tags, found {len(tags)}.")
return tags[1], tags[0] # previous, latest
def get_commits(prev_tag: str, latest_tag: str) -> list[tuple[str, str]]:
result = subprocess.run(
[
"git",
"log",
"--pretty=format:%H\x1f%s",
f"{prev_tag}..{latest_tag}",
],
capture_output=True,
text=True,
check=True,
)
commits = []
for line in result.stdout.splitlines():
if not line:
continue
sha, subject = line.split("\x1f", 1)
commits.append((sha, subject))
return commits
def write_commits_md(path: Path, repo: str, prev_tag: str, latest_tag: str, commits: list[tuple[str, str]]):
lines = [f"# Commits: {prev_tag}..{latest_tag}", ""]
for sha, subject in commits:
link = f"https://github.com/{repo}/commit/{sha}"
lines.append(f"- [{subject}]({link})")
path.write_text("\n".join(lines) + "\n")
def write_pull_requests_md(path: Path, repo: str, commits: list[tuple[str, str]]):
pr_numbers = []
seen = set()
for _, subject in commits:
for match in PR_REF_RE.finditer(subject):
number = match.group(1)
if number not in seen:
seen.add(number)
pr_numbers.append(number)
lines = ["# Pull Requests", ""]
for number in pr_numbers:
lines.append(f"- https://github.com/{repo}/pull/{number}")
path.write_text("\n".join(lines) + "\n")
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--repo",
help="owner/repo (default: detected from git remote 'origin')",
)
parser.add_argument(
"--out",
default=".",
help="Output directory for commits.md and pull-requests.md (default: current directory)",
)
args = parser.parse_args()
repo = get_repo(args.repo)
prev_tag, latest_tag = get_last_two_tags()
commits = get_commits(prev_tag, latest_tag)
out_dir = Path(args.out)
out_dir.mkdir(parents=True, exist_ok=True)
commits_path = out_dir / "release-commits.md"
prs_path = out_dir / "release-pull-requests.md"
write_commits_md(commits_path, repo, prev_tag, latest_tag, commits)
write_pull_requests_md(prs_path, repo, commits)
print(f"Repo: {repo}")
print(f"Tags: {prev_tag}..{latest_tag}")
print(f"Commits: {len(commits)}")
print(f"Wrote {commits_path}")
print(f"Wrote {prs_path}")
if __name__ == "__main__":
main()