New file |
| | |
| | | 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") |