commit.py | ●●●●● patch | view | raw | blame | history | |
commitBody.py | ●●●●● patch | view | raw | blame | history | |
commitTag.py | ●●●●● patch | view | raw | blame | history | |
functions.py | ●●●●● patch | view | raw | blame | history | |
main.py | ●●●●● patch | view | raw | blame | history | |
oop_changelog.py | ●●●●● patch | view | raw | blame | history | |
oop_changelog_scope.py | ●●●●● patch | view | raw | blame | history | |
rawcommit.py | ●●●●● patch | view | raw | blame | history | |
scope.py | ●●●●● 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)