Skip to content

Instantly share code, notes, and snippets.

@homebysix
Created November 16, 2021 06:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save homebysix/875542cf68288167bda1e7dde33283e8 to your computer and use it in GitHub Desktop.
Save homebysix/875542cf68288167bda1e7dde33283e8 to your computer and use it in GitHub Desktop.
update_changelog_diffs.py
#!/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