Daniel Fütterer
27.04.21 b07d2e00d40692f9076e65cfe41140d4515b7b32
commit | author | age
61574e 1 import os.path
DF 2
56795e 3 ## To Do
DF 4 # - Breaking Changes
5 # - Merge Commits
6 # - Error Handling
7
61574e 8 def getSeparatedGitLog(repo):
DF 9     try:
10         stream = os.popen("git -C {} log --format=%B--SEP--%H--SEP--%d--END--".format(repo))
11     except:
12         raise ValueError("Not a valid git-repository!")
13     else:
14         gitLog = stream.read()
15         commitList = gitLog.split("--END--")
16         del commitList[-1]
17         return commitList
18      
19
20 class RawCommit:
21     def __init__(self, completeCommit):
22         self.raw = completeCommit.split("--SEP--")
23         self.body = self.raw[0].strip()
24         self.hash = self.raw[1].strip()
25         self.tag = self.raw[2].strip()
26
27
28 class CommitBody:
29     commitTypes = ("build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "style", "test")
30     def __init__(self, completeMessage):
31         self.completeMessage = completeMessage
32         #self.subject = None
33         #self.body = None
34         #self.scope = None
35         #self.commitType = None
36
37         self.setSubjectAndBody()
38         self.setScope()
39         self.setCommitType()
40     
41     def setSubjectAndBody(self):
56795e 42         try:
DF 43             subStart = self.completeMessage.index(": ")+2
44         except:
45             try:
46                 subStart = self.completeMessage.index("\n")
47             except:
48                 subStart = 0
61574e 49         try:
DF 50             subEnd = self.completeMessage.index("\n")
51         except:
52             self.subject = self.completeMessage[subStart:]
53             self.body = None
54         else:
55             self.subject = self.completeMessage[subStart:subEnd]
56             self.body = self.completeMessage[subEnd:].strip()
57     
58     def setScope(self):
59         try:
60             start = self.completeMessage.index("(")+1
61             end = self.completeMessage.index(")")
62         except:
63             self.scope = None
64         else:
65             self.scope = self.completeMessage[start:end]
66     
67     def setCommitType(self):
68         for commitType in self.commitTypes:
81313f 69             if (self.completeMessage.startswith((commitType + ": "))) or (self.completeMessage.startswith((commitType + "("))) or (self.completeMessage.startswith((commitType + " ("))):
61574e 70                 self.commitType = commitType
DF 71                 break
56795e 72             else:
DF 73                 self.commitType = "nonconform"
74     
75     def getCommitMessageWithType(self):
76         return self.commitType + ": " + self.subject
61574e 77         
DF 78
79 class CommitTag:
80     def __init__(self, completeTag):
81         if not ("tag: " in completeTag):
82             self.completeTag = None
83             self.tagAsString = None
84             self.major = None
85             self.minor = None
86             self.bugfix = None
87         else:
88             self.completeTag = completeTag
89             self.setTagAsString()
90             self.setMajorMinorBugfix()
91
92     def setTagAsString(self):
93         try: ## this one is temporary
94             self.tagAsString = self.completeTag[(self.completeTag.rindex(": v.")+4):-1]
95         except:
96             try:
97                 self.tagAsString = self.completeTag[(self.completeTag.rindex(": v")+3):-1]
98             except:
99                 self.tagAsString = self.completeTag[(self.completeTag.rindex(": ")+2):-1]
100
101     def setMajorMinorBugfix(self):
102         versionList = self.tagAsString.split(".")
103         self.major = versionList[0]
104         self.minor = versionList[1]
105         self.bugfix = versionList[2]
106     
107     @staticmethod
108     def getUpdateType(newTag, previousTag):
109         if newTag.major > previousTag.major:
110             return "major"
111         elif newTag.minor > previousTag.minor:
112             return "minor"
113         elif newTag.bugfix > previousTag.bugfix:
114             return "bugfix"
115
116
117 class Commit:
118     def __init__(self, rawCommit):
119         self.body = CommitBody(rawCommit.body)
120         self.tag = CommitTag(rawCommit.tag)
121         self.hash = rawCommit.hash
56795e 122     
DF 123     def appendShortHash(self):
124         return " (" + self.hash[:6] + ")"
61574e 125
DF 126
127
128 #### Main ####
129
6dff09 130 pathToRepo = "/Users/daniel/Developer/Repos/HfM/schumacher/Prisma-Binauralize"
DF 131 #pathToRepo = "/Users/daniel/Desktop/testrepo"
132
133 commitList = getSeparatedGitLog(pathToRepo)
61574e 134
DF 135 # Create a list of commits
136 commitHistory = []
137 for commit in commitList:
138     commitHistory.append(Commit(RawCommit(commit)))
139
140 # Create a two-dimensional list by tags: [[tag, [commits]],[tag, [commits]],...]
141 taggedHistory = []
142 for commit in commitHistory:
143     if commit.tag.tagAsString:
144         taggedHistory.append([commit.tag, commit])
145     else:
146         if len(taggedHistory) == 0:
147             taggedHistory.append([None, commit])
148         else:
149             taggedHistory[-1].append(commit)
150
151
152 # Construction of the changelog-file
153 fileTemplate = ["# Changelog"]
154 for tag in taggedHistory:
155     # A Dictionairy to store grouped commits
81313f 156     commitsByType = {"fix":[], "feat":[], "Other":[], "Nonconform":[]}
61574e 157
DF 158     # If latest commit has no tag:
159     if not tag[0]:
81313f 160         fileTemplate.append("\n## No version number yet")
61574e 161     else:
DF 162         fileTemplate.append("\n## Version " + tag[0].tagAsString)
163
164     # Grouping by CommitTypes
165     for commit in tag[1:]:
81313f 166         if commit.body.commitType == CommitBody.commitTypes[5]: # fix
DF 167             commitsByType["fix"].append(commit)
168         elif commit.body.commitType == CommitBody.commitTypes[4]: # feat
169             commitsByType["feat"].append(commit)
170         elif commit.body.commitType == "nonconform":
171             commitsByType["Nonconform"].append(commit)
61574e 172         else:
81313f 173             commitsByType["Other"].append(commit)
61574e 174     
81313f 175     if len(commitsByType["feat"]) != 0:
61574e 176         fileTemplate.append("### Features")
81313f 177         for feature in commitsByType["feat"]:
DF 178             if feature.body.scope:
179                 fileTemplate.append("- **" + feature.body.scope + "**: " + feature.body.subject + feature.appendShortHash())
180             else:
181                 fileTemplate.append("- " + feature.body.subject + feature.appendShortHash())
182     if len(commitsByType["fix"]) != 0:
61574e 183         fileTemplate.append("### Fixes")
81313f 184         for fix in commitsByType["fix"]:
DF 185             if fix.body.scope:
186                 fileTemplate.append("- **" + fix.body.scope  + "**: " + fix.body.scope + fix.appendShortHash())
187             else:
188                 fileTemplate.append("- " + fix.body.subject + fix.appendShortHash())
61574e 189     if len(commitsByType["Other"]) != 0:
DF 190         fileTemplate.append("### Other")
191         for other in commitsByType["Other"]:
81313f 192             if other.body.scope:
DF 193                 fileTemplate.append("- **" + other.body.scope + "**: " +  other.body.getCommitMessageWithType() + other.appendShortHash())
194             else:
195                 fileTemplate.append("- " + other.body.getCommitMessageWithType() + other.appendShortHash())
56795e 196     if len(commitsByType["Nonconform"]) != 0:
DF 197         fileTemplate.append("### Non-conventional")
198         for nonconform in commitsByType["Nonconform"]:
199             fileTemplate.append("- " + nonconform.body.subject + nonconform.appendShortHash())
6dff09 200
61574e 201
DF 202 # write into changelog
6dff09 203 with open(pathToRepo + "/changelog.md", "w") as file:
61574e 204     for line in fileTemplate:
DF 205         file.write(line + "\n")