Created
November 16, 2021 06:29
-
-
Save homebysix/875542cf68288167bda1e7dde33283e8 to your computer and use it in GitHub Desktop.
update_changelog_diffs.py
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
#!/usr/bin/env python3 | |
"""Given the path to a Markdown-formatted change log, this script will update | |
the diff links for each version at the bottom of the document. For an example | |
of the diff links, see: https://keepachangelog.com | |
Currently only supports GitHub, GitLab, and Bitbucket diff links. | |
""" | |
import argparse | |
import os | |
import re | |
import sys | |
from distutils.version import LooseVersion | |
from urllib import parse | |
__author__ = "Elliot Jordan" | |
__version__ = "1.0" | |
def build_argument_parser(): | |
"""Build and return the argument parser.""" | |
parser = argparse.ArgumentParser( | |
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter | |
) | |
parser.add_argument( | |
"changelog", | |
help="Path to the Markdown-formatted change log to update.", | |
) | |
parser.add_argument( | |
"--prefix", | |
default="v", | |
help="The character or string that is added to the beginning of each tag. Defaults to 'v'.", | |
) | |
return parser | |
def main(): | |
"""Main process.""" | |
# Parse command line arguments. | |
argparser = build_argument_parser() | |
args = argparser.parse_args() | |
# Bail out if the file doesn't exist. | |
if not os.path.isfile(args.changelog): | |
sys.exit("ERROR: No such file: %s" % args.changelog) | |
# Read the lines of the change log. | |
with open(args.changelog, "r") as openfile: | |
content = openfile.readlines() | |
# Create a list to store versions. | |
version_pattern = r"## \[?([\d\.]+)\]? - " | |
found_versions = [] | |
# Keep track of where the diff links start. | |
diff_pattern = r"^\[Unreleased\]: (https?:\/\/\S+\/\S+)" | |
diff_start_index = -1 | |
# Need to know in order to create the diff link. | |
repo_info = {} | |
# Iterate through change log content, keeping track of the H2 headings | |
# with versions we find along the way. Stop when we get to the diff links. | |
for idx, line in enumerate(content): | |
version_m = re.match(version_pattern, line) | |
if version_m: | |
found_versions.append(version_m.group(1)) | |
diff_m = re.match(diff_pattern, line) | |
if diff_m: | |
diff_start_index = idx | |
repo_info["found_url"] = diff_m.group(1) | |
# No need to read any further lines, since we'll be | |
# regenerating them anyway. | |
break | |
# Bail out if any of these errors prevent continuing. | |
if diff_start_index < 0: | |
sys.exit("ERROR: No existing diff links found.") | |
if not found_versions: | |
sys.exit("ERROR: No H2 version headings found in change log.") | |
if "found_url" not in repo_info: | |
sys.exit("ERROR: No Git URL found in existing diff links.") | |
# Link templates based on Git host. | |
link_templates = { | |
"github.com": "[%s]: https://github.com/%s/compare/%s...%s\n", | |
"gitlab.com": "[%s]: https://gitlab.com/%s/-/compare/%s...%s\n", | |
"bitbucket.org": "[%s]: https://bitbucket.org/%s/branches/compare/%s..%s\n", | |
} | |
# Based on Git URL, extrapolate information about the project that we can | |
# use to create diff URLs. | |
parsed_url = parse.urlparse(repo_info["found_url"]) | |
repo_info["scheme"] = parsed_url.scheme | |
repo_info["hostname"] = parsed_url.hostname | |
repo_info["project"] = "/".join(parsed_url.path.split("/")[1:3]) | |
# Bail out if we don't know how to create a diff URL for this host. | |
if repo_info["hostname"] not in link_templates: | |
sys.exit("ERROR: %s diff links are not yet supported." % repo_info["hostname"]) | |
# Sort versions in reverse. | |
found_versions = sorted(found_versions, key=LooseVersion, reverse=True) | |
# Start diff output with an Unreleased link comparing most recent version to HEAD. | |
diff_output = [ | |
link_templates[repo_info["hostname"]] | |
% ("Unreleased", repo_info["project"], args.prefix + found_versions[0], "HEAD") | |
] | |
# Iterate (backwards) through the versions and add each link to the output. | |
found_versions = sorted(found_versions, key=LooseVersion, reverse=True) | |
for idx, vers in enumerate(found_versions): | |
# For initial (highest index) version, there's nothing to compare to. | |
if idx == len(found_versions) - 1: | |
continue | |
# For other versions, use diff template. | |
diff_output.append( | |
link_templates[repo_info["hostname"]] | |
% ( | |
vers, | |
repo_info["project"], | |
args.prefix + found_versions[idx + 1], | |
args.prefix + vers, | |
) | |
) | |
# Write the updated change log. | |
new_content = content[:diff_start_index] + diff_output | |
with open(args.changelog, "w") as openfile: | |
openfile.write("".join(new_content)) | |
print("Updated: %s" % args.changelog) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment