-
Notifications
You must be signed in to change notification settings - Fork 11
[DOCS-GEN] Add scripts for making release documentation #9
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| # | ||
| # PROJECT: ReactOS Release Document Scripts | ||
| # LICENSE: MIT (https://spdx.org/licenses/MIT) | ||
| # PURPOSE: Creates a commit history mediawiki document for a release | ||
| # COPYRIGHT: Copyright 2026 Carl Bialorucki <carl.bialorucki@reactos.org> | ||
| # | ||
|
|
||
| import subprocess | ||
| import sys | ||
| import re | ||
| import unicodedata | ||
| from collections import defaultdict | ||
|
|
||
| MISC_TITLE = "Miscellaneous" | ||
| MAX_MSG_LENGTH = 100 | ||
| skip_prefixes = [] | ||
|
|
||
| class Commit: | ||
| def __init__(self, sha, author, message): | ||
| self.sha = sha | ||
| # Normalize and strip author, message | ||
| self.author = unicodedata.normalize("NFKD", author.strip()).encode("ascii", "ignore").decode("ascii") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't you use utf8 for some of our authors?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did try making it utf-8 at first, but pasting that into our wiki broke on all the extended characters. |
||
| self.message = unicodedata.normalize("NFKD", message.strip()).encode("ascii", "ignore").decode("ascii") | ||
|
|
||
| def truncate_msg(s): | ||
| return s if len(s) <= MAX_MSG_LENGTH else s[:MAX_MSG_LENGTH - 3] + "..." | ||
|
|
||
| def run(cmd): | ||
| return subprocess.run(cmd, text=True, capture_output=True, encoding="utf-8").stdout.strip() | ||
|
|
||
| def parse_message_prefix(msg): | ||
| # Skip over some prefixes | ||
| for prefix in skip_prefixes: | ||
| if msg.startswith(f"[{prefix}]"): | ||
| msg = msg[msg.find("]") + 1:].lstrip() | ||
|
|
||
| match = re.match(r"\[([^\]:]+)(?::([^\]]+))?\]", msg) | ||
| if match: | ||
| group = match.group(1).upper() | ||
| subgroup = match.group(2) or None | ||
| return group, subgroup | ||
| return MISC_TITLE, None | ||
|
|
||
| def main(): | ||
| if len(sys.argv) < 2: | ||
| print("Usage: python commit-history.py <parent_branch>") | ||
| sys.exit(1) | ||
|
|
||
| parent = sys.argv[1] | ||
| branch = run("git rev-parse --abbrev-ref HEAD") | ||
| raw_commits = run(f'git log {parent}..{branch} --pretty=format:"%h|%an|%s" --reverse').split("\n") | ||
| current_version = branch.split("releases/", 1)[1] | ||
| # Add current version as a skipped prefix | ||
| skip_prefixes.append(current_version) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this work?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does work. See this page for example, which was made using this script: https://reactos.org/wiki/0.4.16_Commit_History#BOOTDATA |
||
|
|
||
| # groups[group][subgroup] = list of commits | ||
| groups = defaultdict(lambda: defaultdict(list)) | ||
|
|
||
| for entry in raw_commits: | ||
| if not entry.strip(): | ||
| continue | ||
|
|
||
| sha, author, message = entry.split("|", 2) | ||
| commit = Commit(sha, author, message) | ||
| group, subgroup = parse_message_prefix(commit.message) | ||
| groups[group][subgroup].append(commit) | ||
|
|
||
| print(f"''Note: commits prefixed with the version number (i.e. [{current_version}]) are specific to this release. Backported commits from after the release was branched will also have this prefix.''\n") | ||
| # All properly formatted commits first, then misc | ||
| for group in sorted(groups.keys(), key=lambda g: (g == MISC_TITLE, g)): | ||
| print(f"== {group} ==") | ||
| # None subgroup first, then alphabetical | ||
| for subgroup in sorted(groups[group].keys(), key=lambda x: (x is not None, x or "")): | ||
| if subgroup is not None: | ||
| print(f"=== {subgroup} ===") | ||
|
|
||
| for commit in groups[group][subgroup]: | ||
| print(f"* [https://git.reactos.org/?p=reactos.git;a=commit;h={commit.sha} <nowiki>{truncate_msg(commit.message)}</nowiki>] ({commit.author})") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might make sense to link to github instead?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer Github too, but I figured someone browsing our wiki is more likely to want to use our git mirror. Maybe I am wrong 😄 |
||
|
|
||
| print() | ||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| # | ||
| # PROJECT: ReactOS Release Document Scripts | ||
| # LICENSE: MIT (https://spdx.org/licenses/MIT) | ||
| # PURPOSE: Creates a resolved issues mediawiki document for a release | ||
| # COPYRIGHT: Copyright 2026 Carl Bialorucki <carl.bialorucki@reactos.org> | ||
| # | ||
|
|
||
| import sys | ||
| import unicodedata | ||
| import requests | ||
|
|
||
| JIRA_URL = "https://jira.reactos.org/rest/api/2/search" | ||
| MAX_SUMMARY_LENGTH = 100 | ||
|
|
||
| def normalize_summary(s): | ||
| s = unicodedata.normalize("NFKD", s).encode("ascii", "ignore").decode("ascii") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. utf8 is probably better fitting here as well |
||
| return s if len(s) <= MAX_SUMMARY_LENGTH else s[:MAX_SUMMARY_LENGTH - 3] + "..." | ||
|
|
||
| def fetch_tickets(version): | ||
| jql = f'fixVersion = "{version}"' | ||
|
|
||
| # Largest valid maxResults value is 1000 | ||
| # See: https://developer.atlassian.com/cloud/jira/platform/change-notice-for-get-search-max-results | ||
| params = { | ||
| "jql": jql, | ||
| "maxResults": 1000, | ||
| "fields": "key,summary,issuetype" | ||
| } | ||
|
|
||
| response = requests.get(JIRA_URL, params=params) | ||
| response.raise_for_status() | ||
| data = response.json() | ||
| return data["issues"] | ||
|
|
||
| def main(): | ||
| if len(sys.argv) < 2: | ||
| print("Usage: python resolved-issues.py <version>") | ||
| sys.exit(1) | ||
|
|
||
| version = sys.argv[1] | ||
| issues = fetch_tickets(version) | ||
|
|
||
| groups = { | ||
| "Bug Fixes": [], | ||
| "Epics": [], | ||
| "New Features": [], | ||
| "Stories": [], | ||
| "Tasks": [], | ||
| "Improvements": [], | ||
| "Sub-tasks": [], | ||
| } | ||
|
|
||
| for issue in issues: | ||
| itype = issue["fields"]["issuetype"]["name"] | ||
|
|
||
| if itype == "Bug": | ||
| groups["Bug Fixes"].append(issue) | ||
| elif itype == "Epic": | ||
| groups["Epics"].append(issue) | ||
| elif itype == "New Feature": | ||
| groups["New Features"].append(issue) | ||
| elif itype == "Story": | ||
| groups["Stories"].append(issue) | ||
| elif itype == "Task": | ||
| groups["Tasks"].append(issue) | ||
| elif itype == "Improvement": | ||
| groups["Improvements"].append(issue) | ||
| elif itype == "Sub-task": | ||
| groups["Sub-tasks"].append(issue) | ||
|
|
||
| for group_name, issues in groups.items(): | ||
| print(f"== {group_name} ==") | ||
| if not issues: | ||
| print("''No issues were resolved in this category.''") | ||
| print() | ||
| continue | ||
|
|
||
| for issue in issues: | ||
| key = issue["key"] | ||
| summary = issue["fields"]["summary"] | ||
| print(f"* [https://jira.reactos.org/browse/{key} <nowiki>{key}</nowiki>] {normalize_summary(summary)}") | ||
| print() | ||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might wanna look into dataclasses (https://docs.python.org/3/library/dataclasses.html), that has some nice things like automatic pretty-printing etc.