2022-09-28 17:17:40 +01:00
|
|
|
import argparse
|
2023-11-22 18:26:03 +00:00
|
|
|
import logging
|
|
|
|
import os
|
2022-09-28 17:17:40 +01:00
|
|
|
import subprocess
|
2023-11-22 18:26:03 +00:00
|
|
|
import sys
|
|
|
|
from enum import Enum
|
|
|
|
|
|
|
|
# Setting up basic logging
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
|
|
|
|
|
|
|
|
|
class Section(Enum):
|
|
|
|
GENERAL = "General"
|
|
|
|
MULTINODE = "Multinode"
|
|
|
|
SATELLITE = "Satellite"
|
|
|
|
STORAGENODE = "Storagenode"
|
|
|
|
TEST = "Test"
|
|
|
|
UPLINK = "Uplink"
|
|
|
|
|
2022-09-28 17:17:40 +01:00
|
|
|
|
|
|
|
GITHUB_LINK = "[{0}](https://github.com/storj/storj/commit/{0})"
|
|
|
|
|
|
|
|
|
|
|
|
def git_ref_field(from_ref, to_ref):
|
2023-11-22 18:26:03 +00:00
|
|
|
"""
|
|
|
|
Executes a git command to find the difference in commits between two references.
|
|
|
|
Assumes 'from_ref' and 'to_ref' are valid Git references.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
from_ref (str): The source reference.
|
|
|
|
to_ref (str): The target reference.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
str: A string containing the git commit differences.
|
|
|
|
"""
|
|
|
|
cmd = ["git", "cherry", from_ref, to_ref, "-v"]
|
|
|
|
try:
|
|
|
|
result = subprocess.run(cmd, text=True, capture_output=True, check=True)
|
|
|
|
return result.stdout
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
logging.error(f"Error executing git command: {e.stderr}")
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
|
|
def validate_git_refs(from_ref, to_ref):
|
|
|
|
"""
|
|
|
|
Validates the provided Git references.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
from_ref (str): The source reference.
|
|
|
|
to_ref (str): The target reference.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: True if references are valid, False otherwise.
|
|
|
|
"""
|
|
|
|
for ref in [from_ref, to_ref]:
|
|
|
|
result = subprocess.run(["git", "rev-parse", "--verify", ref], text=True, capture_output=True)
|
|
|
|
if result.returncode != 0:
|
|
|
|
logging.error(f"Invalid Git reference: {ref}")
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def categorize_commit(commit, section_dict):
|
|
|
|
"""
|
|
|
|
Categorizes a single commit into the appropriate section.
|
|
|
|
Handles unexpected commit formats by logging a warning and defaulting to the GENERAL section.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
commit (str): A git commit message.
|
|
|
|
section_dict (dict): Dictionary of sections.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
"""
|
|
|
|
try:
|
|
|
|
commit_category = commit[42:].split(":")[0].lower()
|
|
|
|
for category in section_dict:
|
|
|
|
if category.name.lower() in commit_category:
|
|
|
|
section_dict[category].append(generate_line(commit))
|
|
|
|
return
|
|
|
|
section_dict[Section.GENERAL].append(generate_line(commit))
|
|
|
|
except IndexError:
|
|
|
|
logging.warning(f"Unexpected commit format: {commit}")
|
|
|
|
section_dict[Section.GENERAL].append(generate_line(commit))
|
2022-09-28 17:17:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
def generate_changelog(commits):
|
2023-11-22 18:26:03 +00:00
|
|
|
"""
|
|
|
|
Generates a formatted changelog from a string of commits.
|
|
|
|
Args:
|
|
|
|
commits (str): A string containing git commit messages.
|
|
|
|
Returns:
|
|
|
|
str: The formatted changelog.
|
|
|
|
"""
|
|
|
|
if not commits:
|
|
|
|
return "No new commits found or error occurred."
|
|
|
|
|
2022-09-28 17:17:40 +01:00
|
|
|
changelog = "# Changelog\n"
|
2023-11-22 18:26:03 +00:00
|
|
|
section_dict = {s: [] for s in Section}
|
2022-09-28 17:17:40 +01:00
|
|
|
|
|
|
|
for commit in commits.splitlines():
|
2023-11-22 18:26:03 +00:00
|
|
|
categorize_commit(commit, section_dict)
|
|
|
|
|
|
|
|
for title, lines in section_dict.items():
|
|
|
|
if lines:
|
|
|
|
changelog += f'### {title.value}\n' + ''.join(lines)
|
|
|
|
|
2022-09-28 17:17:40 +01:00
|
|
|
return changelog
|
|
|
|
|
|
|
|
|
|
|
|
def generate_line(commit):
|
2023-11-22 18:26:03 +00:00
|
|
|
"""
|
|
|
|
Formats a single commit line for the changelog.
|
|
|
|
Args:
|
|
|
|
commit (str): A git commit message.
|
|
|
|
Returns:
|
|
|
|
str: The formatted commit line.
|
|
|
|
"""
|
|
|
|
return f"- {GITHUB_LINK.format(commit[2:9])} {commit[42:]}\n"
|
|
|
|
|
|
|
|
|
|
|
|
def prompt_for_refs(args):
|
|
|
|
"""
|
|
|
|
Prompts user for 'from_ref' and 'to_ref' if not provided.
|
|
|
|
Args:
|
|
|
|
args: Parsed command-line arguments.
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
"""
|
|
|
|
if not args.from_ref:
|
|
|
|
args.from_ref = input("Enter the starting Git reference (from_ref): ")
|
|
|
|
if not args.to_ref:
|
|
|
|
args.to_ref = input("Enter the ending Git reference (to_ref): ")
|
2022-09-28 17:17:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
def main():
|
2023-11-22 18:26:03 +00:00
|
|
|
"""
|
|
|
|
Main function to parse arguments, validate them, and print the changelog.
|
|
|
|
If run interactively, prompts the user for input.
|
|
|
|
"""
|
|
|
|
parser = argparse.ArgumentParser(description="Generate a sorted changelog from Git commits.")
|
|
|
|
parser.add_argument("from_ref", nargs='?', help="The ref to show the path from")
|
|
|
|
parser.add_argument("to_ref", nargs='?', help="The ref to show the path to")
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
# Check if the script is running interactively
|
|
|
|
if os.isatty(sys.stdin.fileno()):
|
|
|
|
prompt_for_refs(args)
|
|
|
|
|
|
|
|
if not (args.from_ref and args.to_ref) or not validate_git_refs(args.from_ref, args.to_ref):
|
|
|
|
parser.print_help()
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
try:
|
|
|
|
commits = git_ref_field(args.from_ref, args.to_ref)
|
|
|
|
changelog = generate_changelog(commits)
|
|
|
|
print(changelog)
|
|
|
|
except Exception as e:
|
|
|
|
logging.error(f"An error occurred: {str(e)}")
|
2022-09-28 17:17:40 +01:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|