-
Notifications
You must be signed in to change notification settings - Fork 147
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add pyhton script for generate changelog
- Loading branch information
1 parent
99c6e33
commit 48080ae
Showing
1 changed file
with
171 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
# INSTALLATION: | ||
# Install Python https://www.python.org/downloads/windows/ | ||
# Install git https://git-scm.com/download/win | ||
# Open a terminal and verify that you can run python and git | ||
# Go to the directory where you want to install this to and type | ||
# python -m venv venv | ||
# venv\Scripts\activate.bat | ||
# To install the required libraries, type: | ||
# python -m pip install --upgrade pip | ||
# pip install requests | ||
# pip install lxml | ||
|
||
# Running the script | ||
# open a terminal and run activate.bat again (like above) | ||
# python changelog.py <from_version> <to_version> | ||
# For example: python changelog.py 2.0.8.8 2.0.9.0 | ||
# If you leave <to_version> out, it defaults to the latest git commit | ||
|
||
|
||
import os | ||
import re | ||
import sys | ||
import time | ||
|
||
from collections import defaultdict | ||
from subprocess import check_output | ||
|
||
import requests | ||
|
||
from lxml import html | ||
|
||
|
||
REPO_NAME = "NewHorizonsCoreMod" | ||
GITHUB_REPO_URL = "https://github.com/GTNewHorizons/" + REPO_NAME + ".git" | ||
GITHUB_ISSUE_URL = "https://github.com/GTNewHorizons/" + REPO_NAME + "/issues/" | ||
GITHUB_ISSUE_REGEX = re.compile("#\d+") | ||
|
||
|
||
def update_git_repo(): | ||
"""Updates the GT:NH repo from GITHUB_REPO_URL""" | ||
if os.path.exists(REPO_NAME): | ||
os.chdir(REPO_NAME) | ||
check_output("git fetch") | ||
os.chdir("..") | ||
else: | ||
check_output("git clone " + GITHUB_REPO_URL) | ||
|
||
|
||
def get_git_log(from_tag, to_tag="HEAD"): | ||
"""Gets the git log between two tags. If to_tag=None, defaults to HEAD""" | ||
os.chdir(REPO_NAME) | ||
git_command = "git log --oneline " + from_tag + ".." + to_tag | ||
return check_output(git_command).decode("utf-8").strip("\n") | ||
|
||
|
||
def format_raw_changelog(raw_log): | ||
"""Takes the raw changelog and formats it as follows: | ||
Find all github issues (by #<digits>) | ||
Fetch titles for github issues | ||
Output: | ||
- <Github issue title> <github issue #> | ||
-- commit 1 | ||
-- commit 2 | ||
- Commits without titles | ||
""" | ||
changelog_dict = make_changelog_dict(raw_log) | ||
changelog_with_github_titles = add_github_titles(changelog_dict) | ||
output_changelog = "" | ||
for issue_title, commits in changelog_with_github_titles.items(): | ||
if not issue_title: | ||
# No known GitHub title, just list these with one dash | ||
for commit_message in commits: | ||
output_changelog += "- " + commit_message + "\n" | ||
else: | ||
output_changelog += "- " + issue_title + "\n" | ||
for commit_message in commits: | ||
if commit_message == issue_title: | ||
continue | ||
if commit_message.startswith("Merge pull request "): | ||
continue | ||
if commit_message.startswith(" Merge pull request "): | ||
continue | ||
output_changelog += "-- " + commit_message + "\n" | ||
|
||
return output_changelog | ||
|
||
|
||
def make_changelog_dict(raw_log): | ||
"""Takes a raw git changelog and returns a dict from github issue # -> list of commits. | ||
If there is no github # mentioned, the key is the empty string | ||
This also strips out the commit hashes coming from git log --oneline | ||
""" | ||
changelog_dict = defaultdict(list) | ||
for line in raw_log.split('\n'): | ||
issue = get_github_issue_number(line) | ||
changelog_dict[issue].append(clean_commit_line(line)) | ||
return changelog_dict | ||
|
||
|
||
def get_github_issue_number(line): | ||
"""Returns the github issue number if it exists, otherwise returns the empty string""" | ||
matches = re.search(GITHUB_ISSUE_REGEX, line) | ||
if matches: | ||
return matches.group()[1:] | ||
else: | ||
return "" | ||
|
||
|
||
def clean_commit_line(line): | ||
"""Removes the commit hash from the front of the line (from the git log --oneline output)""" | ||
return line[9:] | ||
|
||
|
||
def add_github_titles(changelog_dict): | ||
"""Turns a defaultdict of <github issue #> -> list of commit messages into | ||
github issue title -> list of commit messages | ||
""" | ||
extended_dict = defaultdict(list) | ||
for issue, list_of_commits in changelog_dict.items(): | ||
if issue: | ||
github_title = get_github_issue_title(issue) | ||
if github_title: | ||
title = get_github_issue_title(issue) + " #" + issue | ||
else: | ||
# issue not found on Github | ||
title = "" | ||
else: | ||
title = "" | ||
existing_commits = extended_dict[title] | ||
if existing_commits: | ||
existing_commits.extend(list_of_commits) | ||
else: | ||
extended_dict[title] = list_of_commits | ||
return extended_dict | ||
|
||
|
||
def get_github_issue_title(issue): | ||
"""Fetches the issue title from GitHub""" | ||
response = requests.get(GITHUB_ISSUE_URL + issue) | ||
if response.status_code == 429: | ||
# We got rate-limited. Wait as long as they are asking for | ||
wait_time = int(response.headers["Retry-After"]) + 5 | ||
print("GitHub rate limiting. Sleeping for " + str(wait_time) + " seconds.") | ||
time.sleep(wait_time) | ||
response = requests.get(GITHUB_ISSUE_URL + issue) | ||
html_tree = html.fromstring(response.content) | ||
title_attribute = html_tree.find(".//title") | ||
if title_attribute is None: | ||
# Apparently someone referenced a non-existing Github issue | ||
return "" | ||
title = title_attribute.text | ||
separator = " · " | ||
separator_location = title.find(separator) | ||
return title[0:separator_location] | ||
|
||
|
||
if __name__ == "__main__": | ||
if len(sys.argv) < 2: | ||
print("Please provide at least a starting version or commit hash.") | ||
from_commit = sys.argv[1] | ||
to_commit = "HEAD" | ||
if len(sys.argv) == 3: | ||
to_commit = sys.argv[2] | ||
|
||
print("Changelog generator started. Fetching latest git repository...") | ||
update_git_repo() | ||
print("Generating raw changelog from git...") | ||
raw_log = get_git_log(from_commit, to_commit) | ||
print("Getting issue titles from Github (this takes a while for large change logs)...") | ||
formatted_log = format_raw_changelog(raw_log) | ||
print(formatted_log) |