Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a script that generates a changelog from recent PRs and their labels #1718

Merged
merged 7 commits into from
Mar 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Meanwhile, we did a bunch of improvements to our manual. If you had trouble runn
* Switch pre-release action to ncipollo/release-action [#1466](https://github.com/rerun-io/rerun/pull/1466)
* Disallow some methods and types via Clippy[#1411](https://github.com/rerun-io/rerun/pull/1411)

#### Other not user facing refactors
#### Other non-user-facing refactors
* Fix: don't create a dummy LogDb when opening the Rerun Menu [#1440](https://github.com/rerun-io/rerun/pull/1440)
* `re_renderer`
* `Draw Phases` in preparation of executing `Renderer` several times on different targets [#1419](https://github.com/rerun-io/rerun/pull/1419)
Expand Down
8 changes: 5 additions & 3 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ Copy this checklist to the the PR description, go through it from top to bottom,
* [ ] `./scripts/publish_crates.sh --dry-run`
* [ ] Bump version number in root `Cargo.toml`.
* [ ] Update `CHANGELOG.md` with the new version number and the summary and the gif
* Go through https://github.com/rerun-io/rerun/compare/latest...HEAD and manually add any important PR descriptions to the `CHANGELOG.md`, with a link to the PR (which should have a deeper explanation).
* You can use git log to quickly generate a list of commit headlines, use `git fetch --tags --force && git log --pretty=format:%s latest..HEAD` (fetch with `--force` is necessary to update the `latest` tag)
* [ ]Β Make sure to it includes instructions for handling any breaking changes
* [ ] Run `pip install GitPython && scripts/generate_changelog.py`
* [ ] Edit PR descriptions/labels to improve the generated changelog
* [ ] Copy-paste the results into `CHANGELOG.md`.
* [ ] Editorialize the changelog if necessary
* [ ]Β Make sure the changelog includes instructions for handling any breaking changes
* [ ] Get the PR reviewed
* [ ] Check that CI is green
* [ ] Publish the crates (see below)
Expand Down
172 changes: 172 additions & 0 deletions scripts/generate_changelog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#!/usr/bin/env python3

"""
Summarizes recent PRs based on their GitHub labels.

The result can be copy-pasted into CHANGELOG.md, though it often needs some manual editing too.
"""

import re
import sys
from typing import Any, List, Optional, Tuple

import requests
from git import Repo # pip install GitPython
from tqdm import tqdm


def get_github_token() -> str:
import os

token = os.environ.get("GH_ACCESS_TOKEN", "")
if token != "":
return token

home_dir = os.path.expanduser("~")
token_file = os.path.join(home_dir, ".githubtoken")

try:
with open(token_file, "r") as f:
token = f.read().strip()
return token
except Exception:
pass

print("ERROR: expected a GitHub token in the environment variable GH_ACCESS_TOKEN or in ~/.githubtoken")
sys.exit(1)


OWNER = "rerun-io"
REPO = "rerun"
COMMIT_RANGE = "latest..HEAD"
INCLUDE_LABELS = False # It adds quite a bit of visual noise


def pr_title_labels(pr_number: int) -> Tuple[Optional[str], List[str]]:
url = f"https://api.github.com/repos/{OWNER}/{REPO}/pulls/{pr_number}"
gh_access_token = get_github_token()
headers = {"Authorization": f"Token {gh_access_token}"}
response = requests.get(url, headers=headers)
json = response.json()

# Check if the request was successful (status code 200)
if response.status_code == 200:
labels = [label["name"] for label in json["labels"]]
return (json["title"], labels)
else:
print(f"ERROR: {response.status_code} - {json['message']}")
return (None, [])


def commit_title_pr_number(commit: Any) -> Tuple[str, Optional[int]]:
match = re.match(r"(.*) \(#(\d+)\)", commit.summary)
if match:
return (str(match.group(1)), int(match.group(2)))
else:
return (commit.summary, None)


def print_section(title: str, items: List[str]) -> None:
if 0 < len(items):
print(f"#### {title}")
for line in items:
print(f"- {line}")
print()


repo = Repo(".")
commits = list(repo.iter_commits(COMMIT_RANGE))
commits.reverse() # Most recent last

# Sections:
analytics = []
enhancement = []
bugs = []
dev_experience = []
docs = []
examples = []
misc = []
performance = []
python = []
renderer = []
rfc = []
rust = []
ui = []
viewer = []
web = []

for commit in tqdm(commits, desc="Processing commits"):
(title, pr_number) = commit_title_pr_number(commit)
if pr_number is None:
# Someone committed straight to main:
summary = f"{title} [{commit.hexsha}](https://github.com/{OWNER}/{REPO}/commit/{commit.hexsha})"
misc.append(summary)
else:
(pr_title, labels) = pr_title_labels(pr_number)
title = pr_title or title # We prefer the PR title if available
summary = f"{title} [#{pr_number}](https://github.com/{OWNER}/{REPO}/pull/{pr_number})"

if INCLUDE_LABELS and 0 < len(labels):
summary += f" ({', '.join(labels)})"

added = False

if labels == ["β›΄ release"]:
# Ignore release PRs
continue

# Some PRs can show up underm multiple sections:
if "🐍 python API" in labels:
python.append(summary)
added = True
if "πŸ¦€ rust SDK" in labels:
rust.append(summary)
added = True
if "πŸ“Š analytics" in labels:
analytics.append(summary)
added = True

if not added:
# Put the remaining PRs under just one section:
if "πŸͺ³ bug" in labels or "πŸ’£ crash" in labels:
bugs.append(summary)
elif "πŸ“‰ performance" in labels:
performance.append(summary)
elif "examples" in labels:
examples.append(summary)
elif "πŸ“– documentation" in labels:
docs.append(summary)
elif "ui" in labels:
ui.append(summary)
elif "πŸ“Ί re_viewer" in labels:
viewer.append(summary)
elif "πŸ”Ί re_renderer" in labels:
renderer.append(summary)
elif "πŸ•ΈοΈ web" in labels:
web.append(summary)
elif "enhancement" in labels:
enhancement.append(summary)
elif "πŸ§‘β€πŸ’» dev experience" in labels:
dev_experience.append(summary)
elif "πŸ’¬ discussion" in labels:
rfc.append(summary)
elif not added:
misc.append(summary)
Comment on lines +153 to +154
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we should exclude regression tagged PRs πŸ€”
depends obv. for how long a thing regressed

Copy link
Member Author

Choose a reason for hiding this comment

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

You mean add a separate category for regression fixes?


print()
# Most interesting first:
print_section("🐍 Python SDK", python)
print_section("πŸ¦€ Rust SDK", rust)
print_section("πŸͺ³ Bug Fixes", bugs)
print_section("πŸš€ Performance Improvements", performance)
print_section("πŸ§‘β€πŸ« Examples", examples)
print_section("πŸ“š Docs", docs)
print_section("πŸ–Ό UI Improvements", ui)
print_section("πŸ€·β€β™‚οΈ Other Viewer Improvements", viewer)
print_section("πŸ•ΈοΈ Web", web)
print_section("🎨 Renderer Improvements", renderer)
print_section("✨ Other Enhancement", enhancement)
print_section("πŸ“ˆ Analytics", analytics)
print_section("πŸ—£ Merged RFCs", rfc)
print_section("πŸ§‘β€πŸ’» Dev-experience", dev_experience)
print_section("πŸ€·β€β™‚οΈ Other", misc)