blob: 6441156116499fc48e92e4f55a20a02257ec18e0 [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 Anthonyf721b5982020-02-27 11:38:08 -050086 return GitilesDiffLogLink("Version %s contains these commits." % version, "%s%s..%s/%s" % (baseGitilesUrl, startSHA, endSHA, projectDir))
Nick Anthony7ea8ca92020-02-11 11:11:37 -050087
88class LibraryHeader(MarkdownHeader):
89 """
90 Markdown class for a Library Header in the format:
91
92 ### Version <version> {:#<artifactIdTag-version>}
93
94 An artifactId tag is required because artifactIds may be can be grouped by version, in which case the tag is not obvious
95 """
96 def __init__(self, groupId="", version="", artifactIdTag=""):
97 self.markdownType = HeaderType.H3
98 self.text = "%s Version %s {:#%s%s}" % (groupId, version, artifactIdTag, version)
99
100class LibraryReleaseNotes:
101 """ Structured release notes class, that connects all parts of the release notes. Creates release
102 notes in the format:
103 <pre>
104 <[LibraryHeader]>
105 <Date>
106
107 `androidx.<groupId>:<artifactId>:<version>` is released. The commits included in this version
108 can be found <[MarkdownLink]>.
109
110 <[CommitMarkdownList]>
111 </pre>
112 """
113 def __init__(self, groupId, artifactIds, version, releaseDate, fromSHA, untilSHA, projectDir, requiresSameVersion, commitList=[], forceIncludeAllCommits=False):
114 """
115 @property groupId Library GroupId.
116 @property artifactIds List of ArtifactIds included in these release notes.
117 @property version Version of the library, assuming all artifactIds have the same version.
118 @property releaseDate Date the release will go live. Defaults to the current date.
119 @property fromSHA The oldest SHA to which to query for release notes. It will be
120 excluded from release notes, but the next newer SHA will be included.
121 @property untilSHA The newest SHA to be included in the release notes.
122 @property projectDir The filepath relative to the parent directory of the .git directory.
123 @property requiresSameVersion True if the groupId of this module requires the same version for
124 all artifactIds in the groupId. When true, uses the GroupId for the release notes
125 header. When false, uses the list of artifactIds for the header.
126 @property commitList The initial list of Commits to include in these release notes. Defaults to an
127 empty list. Users can always add more commits with [LibraryReleaseNotes.addCommit]
128 @param forceIncludeAllCommits Set to true to include all commits, both with and without a
129 release note field in the commit message. Defaults to false, which means only commits
130 with a release note field are included in the release notes.
131 """
132 self.groupId = groupId
133 self.artifactIds = artifactIds
134 self.version = version
Nick Anthonyc687a4b2020-04-23 07:59:13 -0400135 self.releaseDate = MarkdownDate(releaseDate)
Nick Anthony7ea8ca92020-02-11 11:11:37 -0500136 self.fromSHA = fromSHA
137 self.untilSHA = untilSHA
138 self.projectDir = projectDir
139 self.commitList = commitList
140 self.requiresSameVersion = requiresSameVersion
141 self.forceIncludeAllCommits = forceIncludeAllCommits
142 self.diffLogLink = MarkdownLink()
143 self.commits = commitList
144 self.commitMarkdownList = CommitMarkdownList(commitList, forceIncludeAllCommits)
145 self.summary = ""
146 self.bugsFixed = []
147
148 if version == "" or groupId == "":
149 raise RuntimeError("Tried to create Library Release Notes Header without setting " +
150 "the groupId or version!")
151 if requiresSameVersion:
152 shortenedGroupId = groupId.replace("androidx.", "").capitalize()
153 self.header = LibraryHeader(shortenedGroupId, version)
154 else:
155 forrmattedArtifactIds = (" ".join(artifactIds) + " ").title()
156 artifactIdTag = ""
157 if len(artifactIds) == 1:
158 artifactIdTag = artifactIds[0] + "-"
159 self.header = LibraryHeader(forrmattedArtifactIds, version, artifactIdTag)
160 self.diffLogLink = getGitilesDiffLogLink(version, fromSHA, untilSHA, projectDir)
161
162 def getFormattedReleaseSummary(self):
163 numberArtifacts = len(self.artifactIds)
164 for i in range(0, numberArtifacts):
165 currentArtifactId = self.artifactIds[i]
166 if numberArtifacts == 1:
167 self.summary = "`%s:%s:%s` is released. " % (self.groupId, currentArtifactId, self.version)
168 elif numberArtifacts == 2:
169 if i == 0: self.summary = "`%s:%s:%s` and " % (self.groupId, currentArtifactId, self.version)
170 if i == 1: self.summary += "`%s:%s:%s` are released. " % (self.groupId, currentArtifactId, self.version)
171 else:
172 if (i < numberArtifacts - 1):
173 self.summary += "`%s:%s:%s`, " % (self.groupId, currentArtifactId, self.version)
174 else:
175 self.summary += "and `%s:%s:%s` are released. " % (self.groupId, currentArtifactId, self.version)
176
177 self.summary += "%s\n" % self.diffLogLink
178 return self.summary
179
180 def addCommit(self, newCommit):
181 for bug in newCommit.bugs:
182 bugsFixed.append(bug)
183 commits.append(newCommit)
184 commitMarkdownList.add(newCommit)
185
186 def __str__(self):
187 return "%s\n%s\n\n%s%s" % (self.header, self.releaseDate, self.getFormattedReleaseSummary(), self.commitMarkdownList)