df89
08.06.21 0d53612520622307cbf6a7b9d783de18c373cb15
commit | author | age
b07d2e 1 import os.path
DF 2
3 ## To Do
4 # - Breaking Changes
5 # - Merge Commits
6 # - Error Handling
7
8 def getSeparatedGitLog(repo):
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):
42         try:
43             subStart = self.completeMessage.index(": ")+2
44         except:
45             try:
46                 subStart = self.completeMessage.index("\n")
47             except:
48                 subStart = 0
49         try:
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
d03d64 61             end = self.completeMessage.index("):")
b07d2e 62         except:
DF 63             self.scope = None
64         else:
65             self.scope = self.completeMessage[start:end].strip().lower()
66     
67     def setCommitType(self):
68         for commitType in self.commitTypes:
69             if (self.completeMessage.startswith((commitType + ": "))) or (self.completeMessage.startswith((commitType + "("))) or (self.completeMessage.startswith((commitType + " ("))):
70                 self.commitType = commitType
71                 break
72             else:
73                 self.commitType = "nonconform"
74     
75     def getCommitMessageWithType(self):
76         return self.commitType + ": " + self.subject
77         
78 class Scope:
79     def __init__(self, name):
80         self.name = name.strip().lower()
81     
82     @staticmethod
83     def createScope(name):
84         return Scope(name)
85
86 class CommitTag:
87     def __init__(self, completeTag):
88         if not ("tag: " in completeTag):
89             self.completeTag = None
90             self.tagAsString = None
91             self.major = None
92             self.minor = None
93             self.bugfix = None
94         else:
95             self.completeTag = completeTag
96             self.setTagAsString()
97             self.setMajorMinorBugfix()
98
99     def setTagAsString(self):
100         try: ## this one is temporary
101             self.tagAsString = self.completeTag[(self.completeTag.rindex(": v.")+4):-1]
102         except:
103             try:
104                 self.tagAsString = self.completeTag[(self.completeTag.rindex(": v")+3):-1]
105             except:
106                 self.tagAsString = self.completeTag[(self.completeTag.rindex(": ")+2):-1]
107
108     def setMajorMinorBugfix(self):
109         versionList = self.tagAsString.split(".")
110         self.major = versionList[0]
111         self.minor = versionList[1]
112         self.bugfix = versionList[2]
113     
114     @staticmethod
115     def getUpdateType(newTag, previousTag):
116         if newTag.major > previousTag.major:
117             return "major"
118         elif newTag.minor > previousTag.minor:
119             return "minor"
120         elif newTag.bugfix > previousTag.bugfix:
121             return "bugfix"
122
123
124 class Commit:
125     def __init__(self, rawCommit):
126         self.body = CommitBody(rawCommit.body)
127         self.tag = CommitTag(rawCommit.tag)
128         self.hash = rawCommit.hash
129     
130     def appendShortHash(self):
131         return " (" + self.hash[:6] + ")"
132
133
134
135 #### Main ####
136
137 pathToRepo = "/Users/daniel/Developer/Repos/HfM/schumacher/Prisma-Binauralize"
138 #pathToRepo = "/Users/daniel/Desktop/testrepo"
139
140 commitList = getSeparatedGitLog(pathToRepo)
141
142 # Create a list of commits
143 commitHistory = []
144 for commit in commitList:
145     commitHistory.append(Commit(RawCommit(commit)))
146
147 # Create a two-dimensional list by tags: [[tag, [commits]],[tag, [commits]],...]
148 taggedHistory = []
149 for commit in commitHistory:
150     if commit.tag.tagAsString:
151         taggedHistory.append([commit.tag, commit])
152     else:
153         if len(taggedHistory) == 0:
154             taggedHistory.append([None, commit])
155         else:
156             taggedHistory[-1].append(commit)
157
158
159 # Construction of the changelog-file
160 fileTemplate = ["# Changelog"]
161 for tag in taggedHistory:
162     # If latest commit has no tag:
163     if not tag[0]:
164         fileTemplate.append("\n## Without version number")
165     else:
166         fileTemplate.append("\n## Version " + tag[0].tagAsString)
167
168
169     # Grouping by Type
0d5361 170     featType = ["Features"]
D 171     fixType = ["Fixes"]
172     otherType = ["Other"]
b07d2e 173     nonconformCommits = []
0d5361 174     
b07d2e 175     for commit in tag[1:]:
DF 176         if commit.body.commitType == CommitBody.commitTypes[5]: # fix
0d5361 177             fixType.append(commit)
b07d2e 178         elif commit.body.commitType == CommitBody.commitTypes[4]: # feat
0d5361 179             featType.append(commit)
b07d2e 180         elif commit.body.commitType == "nonconform":
DF 181             nonconformCommits.append(commit)
182         else:
0d5361 183             otherType.append(commit)
b07d2e 184
DF 185     # Sub-Grouping by Scopes within Types    
0d5361 186     commitlistByType = [featType, fixType, otherType]
D 187     for commitsByType in commitlistByType:
188         if len(commitsByType) == 1:
b07d2e 189             continue
0d5361 190         commitlistByScope = {}
D 191         noScope = []
192         for commit in commitsByType:
193             if type(commit) == str:
194                 continue
b07d2e 195             if commit.body.scope == None:
b2e728 196                 noScope.append(commit)
0d5361 197             elif commit.body.scope in commitlistByScope:
D 198                 commitlistByScope[commit.body.scope].append(commit)
b07d2e 199             else:
0d5361 200                 commitlistByScope[commit.body.scope] = [commit]
b07d2e 201         
0d5361 202         fileTemplate.append("\n### " + commitsByType[0])
D 203         while len(commitlistByScope) > 0:
204             scope, commits = commitlistByScope.popitem()
b07d2e 205             fileTemplate.append("- *" + str(scope) + "*")
DF 206             for commit in commits:
0d5361 207                 if commitsByType[0] == "Other":
b07d2e 208                     fileTemplate.append("    - (" + commit.body.commitType + ") " + commit.body.subject + commit.appendShortHash())
DF 209                 else:
210                     fileTemplate.append("    - " + commit.body.subject + commit.appendShortHash())
b2e728 211         if len(noScope) > 0:
DF 212             fileTemplate.append("- *no scope*")
213         for commit in noScope:
214             fileTemplate.append("    - " + commit.body.subject + commit.appendShortHash())
b07d2e 215     
DF 216     # nonconform commits
217     if len(nonconformCommits) > 0:
218         fileTemplate.append("\n### Non-conform commits")
219         for commit in nonconformCommits:
220             fileTemplate.append("- " + commit.body.subject + commit.appendShortHash())
221
222
223 # write into changelog
224 with open(pathToRepo + "/changelog.md", "w") as file:
225     for line in fileTemplate:
226         file.write(line + "\n")