#!/usr/bin/env python # -*- coding: utf-8 -*- # Authors: # Thomas Woerner # # Copyright (C) 2020 Red Hat # see file 'COPYING' for use and warranty information # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import sys import argparse import subprocess class Ref: def __init__(self, commit): self.commit = commit def store(commits, prs, authors, commit, author, merge, msg): if commit is not None: if msg[0].startswith("Merge pull request #"): pr_ref = int(msg[0].split()[3][1:]) if len(msg) > 1: prs[pr_ref] = msg[1].strip() else: prs[pr_ref] = Ref(merge) else: commits[commit] = msg[0].strip() authors.setdefault(author, []).append(commit) def get_commit(commits, commit): _commits = [value for key, value in commits.items() if key.startswith(commit)] if len(_commits) == 1: return _commits[0] return commit def get_output(command): try: ret = subprocess.check_output(command, shell=True) ret = ret.decode("utf-8").strip() except subprocess.CalledProcessError: print("Command '%s' failed" % command) sys.exit(1) return ret def changelog(tag): prev_tag = None if tag is not None and tag != "": prev_tag = get_output( "git describe --tag --abbrev=0 --always '%s^'" % tag) else: tag = get_output("git describe --tags --abbrev=0 " "$(git rev-list --tags --max-count=1)") version = tag[1:] if prev_tag is not None: command = ["git", "log", "%s..%s" % (prev_tag, tag)] else: command = ["git", "log", "%s.." % tag] process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if process.returncode != 0: print("git log failed: %s" % process.stderr.decode("utf8").split("\n")[0]) sys.exit(1) lines = process.stdout.decode("utf-8").split("\n") commits = {} prs = {} authors = {} commit = None author = None merge = None msg = None for line in lines: line = line.rstrip() if line.startswith("commit "): store(commits, prs, authors, commit, author, merge, msg) author = None msg = [] commit = line[7:] elif line.startswith(" "): msg.append(line[4:]) else: try: key, value = line.split(":", 1) if key == "Author": author = value.split("<")[0].strip() elif key == "Merge": merge = value.split()[1].strip() # Ignore Date, .. except ValueError: pass # Add final commit if commit: store(commits, prs, authors, commit, author, merge, msg) if prev_tag is not None: line = "Changes for %s since %s" % (version, prev_tag[1:]) else: line = "Changes since %s" % version print("%s" % line) print("-" * len(line)) print() prs_sorted = sorted(prs.keys(), reverse=True) for pr_ref in prs_sorted: if isinstance(prs[pr_ref], Ref): msg = get_commit(commits, prs[pr_ref].commit) else: msg = prs[pr_ref] print(" - %s (#%d)" % (msg, pr_ref)) print() if prev_tag is not None: line = "Detailed changelog for %s since %s by author" % (version, prev_tag[1:]) else: line = "Detailed changelog since %s by author" % version print("%s" % line) print("-" * len(line)) print(" %d authors, %d commits" % (len(authors), len(commits))) print() authors_sorted = sorted(authors.keys()) for author in authors_sorted: print("%s (%d)\n" % (author, len(authors[author]))) for commit in authors[author]: print(" - %s" % commits[commit]) print() parser = argparse.ArgumentParser(usage="Usage: changelog [options]") parser.add_argument("--tag", dest="tag", help="git tag") parser.add_argument("--galaxy", dest="galaxy", action="store_true", help="Create changelog for galaxy") options, args = parser.parse_known_args() if len(args) != 0: parser.print_help() sys.exit(1) if options.galaxy: # Get latest tag tag = get_output("git describe --tag --abbrev=0") # get number of commits since latest tag count = get_output("git rev-list '%s'.. --count" % tag) if count != "0": changelog(None) changelog(tag) else: changelog(options.tag)