From ac9239944000ba26c5c171c71f5fa3d4bd56c537 Mon Sep 17 00:00:00 2001 From: Robert Joseph Evans Date: Tue, 25 Sep 2012 21:08:40 +0000 Subject: [PATCH] svn merge -c 1390133 FIXES: HADOOP-8822. relnotes.py was deleted post mavenization (bobby) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1390138 13f79535-47bb-0310-9956-ffa450edef68 --- dev-support/relnotes.py | 274 ++++++++++++++++++ .../hadoop-common/CHANGES.txt | 2 + 2 files changed, 276 insertions(+) create mode 100644 dev-support/relnotes.py diff --git a/dev-support/relnotes.py b/dev-support/relnotes.py new file mode 100644 index 00000000000..9a3702340eb --- /dev/null +++ b/dev-support/relnotes.py @@ -0,0 +1,274 @@ +#!/usr/bin/python +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re +import sys +from optparse import OptionParser +import httplib +import urllib +import cgi +try: + import json +except ImportError: + import simplejson as json + + +namePattern = re.compile(r' \([0-9]+\)') + +def clean(str): + return quoteHtml(re.sub(namePattern, "", str)) + +def formatComponents(str): + str = re.sub(namePattern, '', str).replace("'", "") + if str != "": + ret = "(" + str + ")" + else: + ret = "" + return quoteHtml(ret) + +def quoteHtml(str): + return cgi.escape(str).encode('ascii', 'xmlcharrefreplace') + +def mstr(obj): + if (obj == None): + return "" + return unicode(obj) + +class Version: + """Represents a version number""" + def __init__(self, data): + self.mod = False + self.data = data + found = re.match('^((\d+)(\.\d+)*).*$', data) + if (found): + self.parts = [ int(p) for p in found.group(1).split('.') ] + else: + self.parts = [] + # backfill version with zeroes if missing parts + self.parts.extend((0,) * (3 - len(self.parts))) + + def decBugFix(self): + self.mod = True + self.parts[2] -= 1 + return self + + def __str__(self): + if (self.mod): + return '.'.join([ str(p) for p in self.parts ]) + return self.data + + def __cmp__(self, other): + return cmp(self.parts, other.parts) + +class Jira: + """A single JIRA""" + + def __init__(self, data, parent): + self.key = data['key'] + self.fields = data['fields'] + self.parent = parent + self.notes = None + + def getId(self): + return mstr(self.key) + + def getDescription(self): + return mstr(self.fields['description']) + + def getReleaseNote(self): + if (self.notes == None): + field = self.parent.fieldIdMap['Release Note'] + if (self.fields.has_key(field)): + self.notes=mstr(self.fields[field]) + else: + self.notes=self.getDescription() + return self.notes + + def getPriority(self): + ret = "" + pri = self.fields['priority'] + if(pri != None): + ret = pri['name'] + return mstr(ret) + + def getAssignee(self): + ret = "" + mid = self.fields['assignee'] + if(mid != None): + ret = mid['displayName'] + return mstr(ret) + + def getComponents(self): + return " , ".join([ comp['name'] for comp in self.fields['components'] ]) + + def getSummary(self): + return self.fields['summary'] + + def getType(self): + ret = "" + mid = self.fields['issuetype'] + if(mid != None): + ret = mid['name'] + return mstr(ret) + + def getReporter(self): + ret = "" + mid = self.fields['reporter'] + if(mid != None): + ret = mid['displayName'] + return mstr(ret) + + def getProject(self): + ret = "" + mid = self.fields['project'] + if(mid != None): + ret = mid['key'] + return mstr(ret) + + + +class JiraIter: + """An Iterator of JIRAs""" + + def __init__(self, versions): + self.versions = versions + + resp = urllib.urlopen("https://issues.apache.org/jira/rest/api/2/field") + data = json.loads(resp.read()) + + self.fieldIdMap = {} + for part in data: + self.fieldIdMap[part['name']] = part['id'] + + self.jiras = [] + at=0 + end=1 + count=100 + while (at < end): + params = urllib.urlencode({'jql': "project in (HADOOP,HDFS,MAPREDUCE,YARN) and fixVersion in ('"+"' , '".join(versions)+"') and resolution = Fixed", 'startAt':at+1, 'maxResults':count}) + resp = urllib.urlopen("https://issues.apache.org/jira/rest/api/2/search?%s"%params) + data = json.loads(resp.read()) + if (data.has_key('errorMessages')): + raise Exception(data['errorMessages']) + at = data['startAt'] + data['maxResults'] + end = data['total'] + self.jiras.extend(data['issues']) + + self.iter = self.jiras.__iter__() + + def __iter__(self): + return self + + def next(self): + data = self.iter.next() + j = Jira(data, self) + return j + +class Outputs: + """Several different files to output to at the same time""" + + def __init__(self, base_file_name, file_name_pattern, keys, params={}): + self.params = params + self.base = open(base_file_name%params, 'w') + self.others = {} + for key in keys: + both = dict(params) + both['key'] = key + self.others[key] = open(file_name_pattern%both, 'w') + + def writeAll(self, pattern): + both = dict(self.params) + both['key'] = '' + self.base.write(pattern%both) + for key in self.others.keys(): + both = dict(self.params) + both['key'] = key + self.others[key].write(pattern%both) + + def writeKeyRaw(self, key, str): + self.base.write(str) + if (self.others.has_key(key)): + self.others[key].write(str) + + def close(self): + self.base.close() + for fd in self.others.values(): + fd.close() + +def main(): + parser = OptionParser(usage="usage: %prog [options] [USER-ignored] [PASSWORD-ignored] [VERSION]") + parser.add_option("-v", "--version", dest="versions", + action="append", type="string", + help="versions in JIRA to include in releasenotes", metavar="VERSION") + parser.add_option("--previousVer", dest="previousVer", + action="store", type="string", + help="previous version to include in releasenotes", metavar="VERSION") + + (options, args) = parser.parse_args() + + if (options.versions == None): + options.versions = [] + + if (len(args) > 2): + options.versions.append(args[2]) + + if (len(options.versions) <= 0): + parser.error("At least one version needs to be supplied") + + versions = [ Version(v) for v in options.versions]; + versions.sort(); + + maxVersion = str(versions[-1]) + if(options.previousVer == None): + options.previousVer = str(versions[0].decBugFix()) + print >> sys.stderr, "WARNING: no previousVersion given, guessing it is "+options.previousVer + + list = JiraIter(options.versions) + version = maxVersion + outputs = Outputs("releasenotes.%(ver)s.html", + "releasenotes.%(key)s.%(ver)s.html", + ["HADOOP","HDFS","MAPREDUCE","YARN"], {"ver":maxVersion, "previousVer":options.previousVer}) + + head = '\n' \ + 'Hadoop %(key)s %(ver)s Release Notes\n' \ + '\n' \ + '\n' \ + '\n' \ + '

Hadoop %(key)s %(ver)s Release Notes

\n' \ + 'These release notes include new developer and user-facing incompatibilities, features, and major improvements. \n' \ + '\n' \ + '

Changes since Hadoop %(previousVer)s

\n' \ + '
\n\n") + outputs.close() + +if __name__ == "__main__": + main() + diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index 09e0c77453b..43b7a965428 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -736,6 +736,8 @@ Release 0.23.4 - UNRELEASED IMPROVEMENTS + HADOOP-8822. relnotes.py was deleted post mavenization (bobby) + OPTIMIZATIONS BUG FIXES