From b07d2e00d40692f9076e65cfe41140d4515b7b32 Mon Sep 17 00:00:00 2001
From: Daniel Fütterer <df89@outlook.com>
Date: Tue, 27 Apr 2021 14:12:29 +0200
Subject: [PATCH] feat: Added version with scopes

---
 oop_changelog_scope.py |  220 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 220 insertions(+), 0 deletions(-)

diff --git a/oop_changelog_scope.py b/oop_changelog_scope.py
new file mode 100644
index 0000000..bd28462
--- /dev/null
+++ b/oop_changelog_scope.py
@@ -0,0 +1,220 @@
+import os.path
+
+## To Do
+# - Breaking Changes
+# - Merge Commits
+# - Error Handling
+
+def getSeparatedGitLog(repo):
+    try:
+        stream = os.popen("git -C {} log --format=%B--SEP--%H--SEP--%d--END--".format(repo))
+    except:
+        raise ValueError("Not a valid git-repository!")
+    else:
+        gitLog = stream.read()
+        commitList = gitLog.split("--END--")
+        del commitList[-1]
+        return commitList
+     
+
+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()
+
+
+class CommitBody:
+    commitTypes = ("build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "style", "test")
+    def __init__(self, completeMessage):
+        self.completeMessage = completeMessage
+        #self.subject = None
+        #self.body = None
+        #self.scope = None
+        #self.commitType = None
+
+        self.setSubjectAndBody()
+        self.setScope()
+        self.setCommitType()
+    
+    def setSubjectAndBody(self):
+        try:
+            subStart = self.completeMessage.index(": ")+2
+        except:
+            try:
+                subStart = self.completeMessage.index("\n")
+            except:
+                subStart = 0
+        try:
+            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):
+        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):
+        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):
+        return self.commitType + ": " + self.subject
+        
+class Scope:
+    def __init__(self, name):
+        self.name = name.strip().lower()
+    
+    @staticmethod
+    def createScope(name):
+        return Scope(name)
+
+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):
+        try: ## this one is temporary
+            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]
+
+    def setMajorMinorBugfix(self):
+        versionList = self.tagAsString.split(".")
+        self.major = versionList[0]
+        self.minor = versionList[1]
+        self.bugfix = versionList[2]
+    
+    @staticmethod
+    def getUpdateType(newTag, previousTag):
+        if newTag.major > previousTag.major:
+            return "major"
+        elif newTag.minor > previousTag.minor:
+            return "minor"
+        elif newTag.bugfix > previousTag.bugfix:
+            return "bugfix"
+
+
+class Commit:
+    def __init__(self, rawCommit):
+        self.body = CommitBody(rawCommit.body)
+        self.tag = CommitTag(rawCommit.tag)
+        self.hash = rawCommit.hash
+    
+    def appendShortHash(self):
+        return " (" + self.hash[:6] + ")"
+
+
+
+#### Main ####
+
+pathToRepo = "/Users/daniel/Developer/Repos/HfM/schumacher/Prisma-Binauralize"
+#pathToRepo = "/Users/daniel/Desktop/testrepo"
+
+commitList = getSeparatedGitLog(pathToRepo)
+
+# Create a list of commits
+commitHistory = []
+for commit in commitList:
+    commitHistory.append(Commit(RawCommit(commit)))
+
+# 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
+    commitsByType = {"Other":[], "Features":[], "Fixes":[]}
+    nonconformCommits = []
+    for commit in tag[1:]:
+        if commit.body.commitType == CommitBody.commitTypes[5]: # fix
+            commitsByType["Fixes"].append(commit)
+        elif commit.body.commitType == CommitBody.commitTypes[4]: # feat
+            commitsByType["Features"].append(commit)
+        elif commit.body.commitType == "nonconform":
+            nonconformCommits.append(commit)
+        else:
+            commitsByType["Other"].append(commit)
+
+    # Sub-Grouping by Scopes within Types    
+    while len(commitsByType) > 0:
+        commitsByScope = {}
+        commitType, commits = commitsByType.popitem()
+        if len(commits) == 0:
+            continue
+
+        for commit in commits:
+            if commit.body.scope == None:
+                if "no scope" not in commitsByScope:
+                    commitsByScope["no scope"] = [commit]
+                else:
+                    commitsByScope["no scope"].append(commit)
+            elif commit.body.scope in commitsByScope:
+                commitsByScope[commit.body.scope].append(commit)
+            else:
+                commitsByScope[commit.body.scope] = [commit]
+        
+        fileTemplate.append("\n### " + str(commitType))
+        while len(commitsByScope) > 0:
+            scope, commits = commitsByScope.popitem()
+            fileTemplate.append("- *" + str(scope) + "*")
+            for commit in commits:
+                if commitType == "Other":
+                    fileTemplate.append("    - (" + commit.body.commitType + ") " + commit.body.subject + commit.appendShortHash())
+                else:
+                    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
+with open(pathToRepo + "/changelog.md", "w") as file:
+    for line in fileTemplate:
+        file.write(line + "\n")
\ No newline at end of file

--
Gitblit v1.9.1