#!/usr/bin/env python3 # # Copyright 2021 The Mumble Developers. All rights reserved. # Use of this source code is governed by a BSD-style license # that can be found in the LICENSE file at the root of the # Mumble source tree or at . import argparse import platform import subprocess import re from commitMessage.CommitMessage import CommitMessage, CommitFormatError def cmd(args): shell = platform.system() == 'Windows' p = subprocess.Popen(args, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.returncode != 0: raise Exception('cmd(): {0} failed with status {1}: {2}'.format(args, p.returncode, stderr)) return stdout.decode('utf-8') def formatChangeLine(line, prNumber, target): if target == "github": return "- {} (#{})".format(line, prNumber) elif target == "website": return "- {} ({{{{< issue {} >}}}})".format(line, prNumber) else: return "- {} (https://github.com/mumble-voip/mumble/pull/{})".format(line, prNumber) def main(): parser = argparse.ArgumentParser() parser.add_argument("FROM_TAG", help="Tag or commit to start the diff from") parser.add_argument("TO_TAG", help="Tag or commit to end the diff at") parser.add_argument("--target", choices=["github", "website", "other"], help="Generate the Changelog in a format suitable for this target", default="other") args = parser.parse_args() mergeCommitPattern = re.compile("^([0-9a-f]+)\s*[Mm]erge\s.+?#(\d+):?\s*(.+)$", re.MULTILINE) commits = cmd(["git", "log" ,"--format=oneline", "--date=short", "{}..{}".format(args.FROM_TAG, args.TO_TAG)]).split("\n") serverChanges = [] clientChanges = [] sharedChanges = [] positionalAudioChanges = [] miscChanges = [] skipTypes = set(["FORMAT", "DOCS", "TEST", "MAINT", "CI", "REFAC", "BUILD", "TRANSLATION"]) for commitLine in commits: match = re.match(mergeCommitPattern, commitLine) if not match: # Commit doesn't match the expected pattern and is thus ignored continue commitHash = match.group(1) prNumber = match.group(2) commitTitle = match.group(3) try: commit = CommitMessage(commitTitle) if len(set(commit.m_types) & skipTypes) > 0: # We don't list these changes in the changelog continue targetGroups = [] if "client" in commit.m_scopes: targetGroups.append(clientChanges) if "server" in commit.m_scopes: targetGroups.append(serverChanges) if "shared" in commit.m_scopes: targetGroups.append(sharedChanges) if "positional-audio" in commit.m_scopes: targetGroups.append(positionalAudioChanges) if "ice" in commit.m_scopes and not "server" in commit.m_scopes: targetGroups.append(serverChanges) commit.m_summary = "Ice: " + commit.m_summary if "grpc" in commit.m_scopes and not "server" in commit.m_scopes: targetGroups.append(serverChanges) commit.m_summary = "gRPC: " + commit.m_summary if "audio" in commit.m_scopes and not "client" in commit.m_scopes: targetGroups.append(clientChanges) if len(targetGroups) == 0: targetGroups.append(miscChanges) prefix = "" if "FEAT" in commit.m_types: prefix = "Added: " elif "FIX" in commit.m_types: prefix = "Fixed: " else: prefix = "Unknown: " for currentTarget in targetGroups: currentTarget.append(formatChangeLine(prefix + commit.m_summary, prNumber, args.target)) except CommitFormatError: # We can't classify the change -> assume misc miscChanges.append(formatChangeLine("Unknown: " + commitTitle, prNumber, args.target)) if len(clientChanges) > 0: print("### Client") print() print("\n".join(sorted(clientChanges))) print() print() if len(serverChanges) > 0: print("### Server") print() print("\n".join(sorted(serverChanges))) print() print() if len(sharedChanges) > 0: print("### Both") print() print("\n".join(sorted(sharedChanges))) print() print() if len(positionalAudioChanges) > 0: print("### Positional audio plugins") print() print("\n".join(sorted(positionalAudioChanges))) print() print() if len(miscChanges): print("### Miscellaneous") print() print("\n".join(sorted(miscChanges))) print() if __name__ == "__main__": main()