blob: 332bca934f106851fb1d4247a9b3cd6213cd19ec [file] [log] [blame]
Nick Anthony7ea8ca92020-02-11 11:11:37 -05001#!/usr/bin/python3
2#
3# Copyright (C) 2020 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18from ReleaseNoteMarkdown import *
19from GitClient import CommitType, getTitleFromCommitType
20
21class CommitMarkdownList:
22 """Generates the markdown list of commits with sections defined by enum [CommitType], in the format:
23
24 **New Features**
25 - <[Commit.summary]> <[getChangeIdAOSPLink]> <[getBuganizerLink] 1> <[getBuganizerLink] 2>...
26 **API Changes**
27 - <[Commit.summary]> <[getChangeIdAOSPLink]> <[getBuganizerLink] 1> <[getBuganizerLink] 2>...
28 **Bug Fixes**
29 - <[Commit.summary]> <[getChangeIdAOSPLink]> <[getBuganizerLink] 1> <[getBuganizerLink] 2>...
30 **External Contribution**
31 - <[Commit.summary]> <[getChangeIdAOSPLink]> <[getBuganizerLink] 1> <[getBuganizerLink] 2>...
32 """
33 def __init__(self, commits=[], forceIncludeAllCommits=False):
34 self.forceIncludeAllCommits = forceIncludeAllCommits
35 self.commits = commits
36
37 def add(self, commit):
38 self.commits.append(commit)
39
40 def getListItemStr(self):
41 return "- "
42
43 def makeReleaseNotesSection(self, sectionCommitType):
44 sectionHeader = MarkdownBoldText(getTitleFromCommitType(sectionCommitType))
45 markdownStringSection = ""
46 for commit in self.commits:
47 if commit.changeType != sectionCommitType: continue
48 if commit.releaseNote != "":
49 commitString = self.getListItemStr() + commit.getReleaseNoteString()
50 else:
51 commitString = self.getListItemStr() + str(commit)
52 if self.forceIncludeAllCommits or commit.releaseNote != "":
53 markdownStringSection = markdownStringSection + commitString
54 if markdownStringSection[-1] != '\n':
55 markdownStringSection += '\n'
56 if markdownStringSection == "":
57 markdownStringSection = "\n%s\n\n%s" % (MarkdownComment(sectionHeader), markdownStringSection)
58 else:
59 markdownStringSection = "\n%s\n\n%s" % (sectionHeader, markdownStringSection)
60 return markdownStringSection
61
62 def __str__(self):
63 markdownString = ""
64 for commitType in CommitType:
65 markdownString += self.makeReleaseNotesSection(commitType)
66 return markdownString
67
68class GitilesDiffLogLink(MarkdownLink):
69 def __str__(self):
70 return "[%s](%s)" % (self.linkText, self.linkUrl)
71
72def getGitilesDiffLogLink(version, startSHA, endSHA, projectDir):
73 """
74 @param startSHA the SHA at which to start the diff log (exclusive)
75 @param endSHA the last SHA to include in the diff log (inclusive)
76 @param projectDir the local directory of the project, in relation to frameworks/support
77
78 @return A [MarkdownLink] to the public Gitiles diff log
79 """
80 baseGitilesUrl = "https://android.googlesource.com/platform/frameworks/support/+log/"
81 # The root project directory is already existent in the url path, so the directory here
82 # should be relative to frameworks/support/.
83 if ("frameworks/support" in projectDir):
84 print_e("Gitiles directory should be relative to frameworks/support; received incorrect directory: $projectDir")
85 exit(1)
Nick Anthony917014b2020-05-08 08:24:09 -040086 if startSHA != "":
87 return GitilesDiffLogLink("Version %s contains these commits." % version, "%s%s..%s/%s" % (baseGitilesUrl, startSHA, endSHA, projectDir))
88 else:
89 return GitilesDiffLogLink("Version %s contains these commits." % version, "%s%s/%s" % (baseGitilesUrl, endSHA, projectDir))
Nick Anthony7ea8ca92020-02-11 11:11:37 -050090
91class LibraryHeader(MarkdownHeader):
92 """
93 Markdown class for a Library Header in the format:
94
95 ### Version <version> {:#<artifactIdTag-version>}
96
97 An artifactId tag is required because artifactIds may be can be grouped by version, in which case the tag is not obvious
98 """
99 def __init__(self, groupId="", version="", artifactIdTag=""):
100 self.markdownType = HeaderType.H3
101 self.text = "%s Version %s {:#%s%s}" % (groupId, version, artifactIdTag, version)
102
103class LibraryReleaseNotes:
104 """ Structured release notes class, that connects all parts of the release notes. Creates release
105 notes in the format:
106 <pre>
107 <[LibraryHeader]>
108 <Date>
109
110 `androidx.<groupId>:<artifactId>:<version>` is released. The commits included in this version
111 can be found <[MarkdownLink]>.
112
113 <[CommitMarkdownList]>
114 </pre>
115 """
116 def __init__(self, groupId, artifactIds, version, releaseDate, fromSHA, untilSHA, projectDir, requiresSameVersion, commitList=[], forceIncludeAllCommits=False):
117 """
118 @property groupId Library GroupId.
119 @property artifactIds List of ArtifactIds included in these release notes.
120 @property version Version of the library, assuming all artifactIds have the same version.
121 @property releaseDate Date the release will go live. Defaults to the current date.
122 @property fromSHA The oldest SHA to which to query for release notes. It will be
123 excluded from release notes, but the next newer SHA will be included.
124 @property untilSHA The newest SHA to be included in the release notes.
125 @property projectDir The filepath relative to the parent directory of the .git directory.
126 @property requiresSameVersion True if the groupId of this module requires the same version for
127 all artifactIds in the groupId. When true, uses the GroupId for the release notes
128 header. When false, uses the list of artifactIds for the header.
129 @property commitList The initial list of Commits to include in these release notes. Defaults to an
130 empty list. Users can always add more commits with [LibraryReleaseNotes.addCommit]
131 @param forceIncludeAllCommits Set to true to include all commits, both with and without a
132 release note field in the commit message. Defaults to false, which means only commits
133 with a release note field are included in the release notes.
134 """
135 self.groupId = groupId
136 self.artifactIds = artifactIds
137 self.version = version
Nick Anthonyc687a4b2020-04-23 07:59:13 -0400138 self.releaseDate = MarkdownDate(releaseDate)
Nick Anthony7ea8ca92020-02-11 11:11:37 -0500139 self.fromSHA = fromSHA
140 self.untilSHA = untilSHA
141 self.projectDir = projectDir
142 self.commitList = commitList
143 self.requiresSameVersion = requiresSameVersion
144 self.forceIncludeAllCommits = forceIncludeAllCommits
145 self.diffLogLink = MarkdownLink()
146 self.commits = commitList
147 self.commitMarkdownList = CommitMarkdownList(commitList, forceIncludeAllCommits)
148 self.summary = ""
149 self.bugsFixed = []
150
151 if version == "" or groupId == "":
152 raise RuntimeError("Tried to create Library Release Notes Header without setting " +
153 "the groupId or version!")
154 if requiresSameVersion:
155 shortenedGroupId = groupId.replace("androidx.", "").capitalize()
156 self.header = LibraryHeader(shortenedGroupId, version)
157 else:
158 forrmattedArtifactIds = (" ".join(artifactIds) + " ").title()
159 artifactIdTag = ""
160 if len(artifactIds) == 1:
161 artifactIdTag = artifactIds[0] + "-"
162 self.header = LibraryHeader(forrmattedArtifactIds, version, artifactIdTag)
163 self.diffLogLink = getGitilesDiffLogLink(version, fromSHA, untilSHA, projectDir)
164
165 def getFormattedReleaseSummary(self):
166 numberArtifacts = len(self.artifactIds)
167 for i in range(0, numberArtifacts):
168 currentArtifactId = self.artifactIds[i]
169 if numberArtifacts == 1:
170 self.summary = "`%s:%s:%s` is released. " % (self.groupId, currentArtifactId, self.version)
171 elif numberArtifacts == 2:
172 if i == 0: self.summary = "`%s:%s:%s` and " % (self.groupId, currentArtifactId, self.version)
173 if i == 1: self.summary += "`%s:%s:%s` are released. " % (self.groupId, currentArtifactId, self.version)
174 else:
175 if (i < numberArtifacts - 1):
176 self.summary += "`%s:%s:%s`, " % (self.groupId, currentArtifactId, self.version)
177 else:
178 self.summary += "and `%s:%s:%s` are released. " % (self.groupId, currentArtifactId, self.version)
179
180 self.summary += "%s\n" % self.diffLogLink
181 return self.summary
182
183 def addCommit(self, newCommit):
184 for bug in newCommit.bugs:
185 bugsFixed.append(bug)
186 commits.append(newCommit)
187 commitMarkdownList.add(newCommit)
188
189 def __str__(self):
190 return "%s\n%s\n\n%s%s" % (self.header, self.releaseDate, self.getFormattedReleaseSummary(), self.commitMarkdownList)