df89
10.10.21 c4d692c98f3e06d39cf80bf8fa2035c2e398646b
refactor: separated script into different files
7 files added
2 files deleted
663 ■■■■■ changed files
commit.py 12 ●●●●● patch | view | raw | blame | history
commitBody.py 49 ●●●●● patch | view | raw | blame | history
commitTag.py 22 ●●●●● patch | view | raw | blame | history
functions.py 20 ●●●●● patch | view | raw | blame | history
main.py 107 ●●●●● patch | view | raw | blame | history
oop_changelog.py 205 ●●●●● patch | view | raw | blame | history
oop_changelog_scope.py 235 ●●●●● patch | view | raw | blame | history
rawcommit.py 6 ●●●●● patch | view | raw | blame | history
scope.py 7 ●●●●● patch | view | raw | blame | history
commit.py
New file
@@ -0,0 +1,12 @@
from commitBody import CommitBody
from commitTag import CommitTag
class Commit:
    def __init__(self, rawCommit):
        self.body = CommitBody(rawCommit.body)
        self.tag = CommitTag(rawCommit.tag)
        self.hash = rawCommit.hash
    def appendShortHash(self):
        """return the first digits from the hash as string"""
        return " (" + self.hash[:6] + ")"
commitBody.py
New file
@@ -0,0 +1,49 @@
class CommitBody:
    commitTypes = ("build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "style", "test")
    def __init__(self, completeMessage):
        self.completeMessage = completeMessage
        self.setSubjectAndBody()
        self.setScope()
        self.setCommitType()
    def setSubjectAndBody(self):
        """set commit message subject and body (not in use yet)"""
        try: # find start point of commit message
            subStart = self.completeMessage.index(": ")+2
        except:
            try:
                subStart = self.completeMessage.index("\n")
            except:
                subStart = 0
        try: # find end point of commit message
            subEnd = self.completeMessage.index("\n")
        except:
            self.subject = self.completeMessage[subStart:]
            self.body = None
        else:
            self.subject = self.completeMessage[subStart:subEnd]
            self.body = self.completeMessage[subEnd:].strip()
    def setScope(self):
        """set commit scope if contained in message"""
        try:
            start = self.completeMessage.index("(")+1
            end = self.completeMessage.index("):")
        except:
            self.scope = None
        else:
            self.scope = self.completeMessage[start:end].strip().lower()
    def setCommitType(self):
        """set commit type according to type list from conventional commits"""
        for commitType in self.commitTypes:
            if (self.completeMessage.startswith((commitType + ": "))) or (self.completeMessage.startswith((commitType + "("))) or (self.completeMessage.startswith((commitType + " ("))):
                self.commitType = commitType
                break
            else:
                self.commitType = "nonconform"
    def getCommitMessageWithType(self):
        """get commit type and message subject as concatenated string"""
        return self.commitType + ": " + self.subject
commitTag.py
New file
@@ -0,0 +1,22 @@
class CommitTag:
    def __init__(self, completeTag):
        if not ("tag: " in completeTag):
            self.completeTag = None
            self.tagAsString = None
            self.major = None
            self.minor = None
            self.bugfix = None
        else:
            self.completeTag = completeTag
            self.setTagAsString()
            self.setMajorMinorBugfix()
    def setTagAsString(self):
        """set only the version number from tag as string"""
        try:
            self.tagAsString = self.completeTag[(self.completeTag.rindex(": v.")+4):-1]
        except:
            try:
                self.tagAsString = self.completeTag[(self.completeTag.rindex(": v")+3):-1]
            except:
                self.tagAsString = self.completeTag[(self.completeTag.rindex(": ")+2):-1]
functions.py
New file
@@ -0,0 +1,20 @@
import os.path
from rawcommit import RawCommit
from commit import Commit
def getCommitList(repo):
    """returns a list of commits from a repository log"""
    try:
        stream = os.popen("git -C {} log --format=%B--SEP--%H--SEP--%d--END--".format(repo)) # formatted log
    except:
        raise ValueError("Not a valid git-repository!")
    else:
        gitLog = stream.read()
        commitList = gitLog.split("--END--") # separation of individual commits
        del commitList[-1] # deletes empty last element
        rawCommitList = []
        for commit in commitList: # convert every commit into Commit via RawCommit
            rawCommitList.append(Commit(RawCommit(commit)))
        return commitList
main.py
New file
@@ -0,0 +1,107 @@
import os.path
import sys
from functions import getCommitList
from rawcommit import RawCommit
from commit import Commit
from scope import Scope
from commitBody import CommitBody
# Compute user input of repository path via console
inputPath = input("Please enter the base path of the repository: ").strip()
userDecision = input("Should the generated changelog be stored in another location (y/n)? ").lower().strip()
if userDecision == "y":
    outputPath = input("Please enter the output path: ").strip()
elif userDecision == "n":
    print("The changelog will be stored in the same location as the repository.")
    outputPath = inputPath
else:
    print("invalid input")
    sys.exit(1)
# create a list of separate commits in chronological order (new to old)
commitHistory = getCommitList(inputPath)
# Create a two-dimensional list by tags: [[tag, [commits]],[tag, [commits]],...]
taggedHistory = []
for commit in commitHistory:
    if commit.tag.tagAsString:
        taggedHistory.append([commit.tag, commit])
    else:
        if len(taggedHistory) == 0:
            taggedHistory.append([None, commit])
        else:
            taggedHistory[-1].append(commit)
# Construction of the changelog-file
fileTemplate = ["# Changelog"]
for tag in taggedHistory:
    ## If latest commit has no tag:
    if not tag[0]:
        fileTemplate.append("\n## Without version number")
    else:
        fileTemplate.append("\n## Version " + tag[0].tagAsString)
    ## Grouping by Type
    featType = ["Features"]
    fixType = ["Fixes"]
    otherType = ["Other"]
    nonconformCommits = []
    for commit in tag[1:]:
        if commit.body.commitType == CommitBody.commitTypes[5]: # fix
            fixType.append(commit)
        elif commit.body.commitType == CommitBody.commitTypes[4]: # feat
            featType.append(commit)
        elif commit.body.commitType == "nonconform":
            nonconformCommits.append(commit)
        else:
            otherType.append(commit)
    ## Sub-Grouping by Scopes within Types
    commitlistByType = [featType, fixType, otherType]
    for commitsByType in commitlistByType:
        if len(commitsByType) == 1:
            continue
        commitlistByScope = {}
        noScope = []
        for commit in commitsByType:
            if type(commit) == str:
                continue
            if commit.body.scope == None:
                noScope.append(commit)
            elif commit.body.scope in commitlistByScope:
                commitlistByScope[commit.body.scope].append(commit)
            else:
                commitlistByScope[commit.body.scope] = [commit]
        fileTemplate.append("\n### " + commitsByType[0])
        while len(commitlistByScope) > 0:
            scope, commits = commitlistByScope.popitem()
            fileTemplate.append("- *" + str(scope) + "*")
            for commit in commits:
                if commitsByType[0] == "Other":
                    fileTemplate.append("    - (" + commit.body.commitType + ") " + commit.body.subject + commit.appendShortHash())
                else:
                    fileTemplate.append("    - " + commit.body.subject + commit.appendShortHash())
        if len(noScope) > 0:
            fileTemplate.append("- *no scope*")
        for commit in noScope:
            fileTemplate.append("    - " + commit.body.subject + commit.appendShortHash())
    ## nonconform commits
    if len(nonconformCommits) > 0:
        fileTemplate.append("\n### Non-conform commits")
        for commit in nonconformCommits:
            fileTemplate.append("- " + commit.body.subject + commit.appendShortHash())
# write into changelog.md file
with open(outputPath + "/changelog.md", "w") as file:
    for line in fileTemplate:
        file.write(line + "\n")
oop_changelog.py
File was deleted
oop_changelog_scope.py
File was deleted
rawcommit.py
New file
@@ -0,0 +1,6 @@
class RawCommit:
    def __init__(self, completeCommit):
        self.raw = completeCommit.split("--SEP--")
        self.body = self.raw[0].strip()
        self.hash = self.raw[1].strip()
        self.tag = self.raw[2].strip()
scope.py
New file
@@ -0,0 +1,7 @@
class Scope:
    def __init__(self, name):
        self.name = name.strip().lower()
    @staticmethod
    def createScope(name):
        return Scope(name)