187c131baSJan Høydahl#!/usr/bin/env python3 287c131baSJan Høydahl# -*- coding: utf-8 -*- 31db06937SRyan Ernst# Licensed to the Apache Software Foundation (ASF) under one or more 41db06937SRyan Ernst# contributor license agreements. See the NOTICE file distributed with 51db06937SRyan Ernst# this work for additional information regarding copyright ownership. 61db06937SRyan Ernst# The ASF licenses this file to You under the Apache License, Version 2.0 71db06937SRyan Ernst# (the "License"); you may not use this file except in compliance with 81db06937SRyan Ernst# the License. You may obtain a copy of the License at 91db06937SRyan Ernst# 101db06937SRyan Ernst# http://www.apache.org/licenses/LICENSE-2.0 111db06937SRyan Ernst# 121db06937SRyan Ernst# Unless required by applicable law or agreed to in writing, software 131db06937SRyan Ernst# distributed under the License is distributed on an "AS IS" BASIS, 141db06937SRyan Ernst# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 151db06937SRyan Ernst# See the License for the specific language governing permissions and 161db06937SRyan Ernst# limitations under the License. 171db06937SRyan Ernst 181db06937SRyan Ernstimport os 191db06937SRyan Ernstimport sys 201db06937SRyan Ernstsys.path.append(os.path.dirname(__file__)) 2103f49700SAnshum Guptafrom scriptutil import * 221db06937SRyan Ernst 231db06937SRyan Ernstimport argparse 241db06937SRyan Ernstimport re 25843adfb7SSteve Rowefrom configparser import ConfigParser, ExtendedInterpolation 26843adfb7SSteve Rowefrom textwrap import dedent 271db06937SRyan Ernst 28742e6b7eSDavid Smileydef update_changes(filename, new_version, init_changes, headers): 291db06937SRyan Ernst print(' adding new section to %s...' % filename, end='', flush=True) 301db06937SRyan Ernst matcher = re.compile(r'\d+\.\d+\.\d+\s+===') 311db06937SRyan Ernst def edit(buffer, match, line): 321db06937SRyan Ernst if new_version.dot in line: 331db06937SRyan Ernst return None 341db06937SRyan Ernst match = new_version.previous_dot_matcher.search(line) 351db06937SRyan Ernst if match is not None: 361db06937SRyan Ernst buffer.append(line.replace(match.group(0), new_version.dot)) 37843adfb7SSteve Rowe buffer.append(init_changes) 38742e6b7eSDavid Smiley for header in headers: 39742e6b7eSDavid Smiley buffer.append('%s\n---------------------\n(No changes)\n\n' % header) 401db06937SRyan Ernst buffer.append(line) 411db06937SRyan Ernst return match is not None 421db06937SRyan Ernst 4303f49700SAnshum Gupta changed = update_file(filename, matcher, edit) 441db06937SRyan Ernst print('done' if changed else 'uptodate') 451db06937SRyan Ernst 461db06937SRyan Ernstdef add_constant(new_version, deprecate): 471db06937SRyan Ernst filename = 'lucene/core/src/java/org/apache/lucene/util/Version.java' 481db06937SRyan Ernst print(' adding constant %s...' % new_version.constant, end='', flush=True) 491db06937SRyan Ernst constant_prefix = 'public static final Version LUCENE_' 501db06937SRyan Ernst matcher = re.compile(constant_prefix) 511db06937SRyan Ernst prev_matcher = new_version.make_previous_matcher(prefix=constant_prefix, sep='_') 521db06937SRyan Ernst 531db06937SRyan Ernst def ensure_deprecated(buffer): 541db06937SRyan Ernst last = buffer[-1] 551db06937SRyan Ernst if last.strip() != '@Deprecated': 561db06937SRyan Ernst spaces = ' ' * (len(last) - len(last.lstrip()) - 1) 5704a133f9SSteve Rowe del buffer[-1] # Remove comment closer line 5804a133f9SSteve Rowe if (len(buffer) >= 4 and re.search('for Lucene.\s*$', buffer[-1]) != None): 5904a133f9SSteve Rowe del buffer[-3:] # drop the trailing lines '<p> / Use this to get the latest ... / ... for Lucene.' 6004a133f9SSteve Rowe buffer.append(( '{0} * @deprecated ({1}) Use latest\n' 6104a133f9SSteve Rowe + '{0} */\n' 6204a133f9SSteve Rowe + '{0}@Deprecated\n').format(spaces, new_version)) 631db06937SRyan Ernst 641db06937SRyan Ernst def buffer_constant(buffer, line): 651db06937SRyan Ernst spaces = ' ' * (len(line) - len(line.lstrip())) 6604a133f9SSteve Rowe buffer.append(( '\n{0}/**\n' 6704a133f9SSteve Rowe + '{0} * Match settings and bugs in Lucene\'s {1} release.\n') 6804a133f9SSteve Rowe .format(spaces, new_version)) 691db06937SRyan Ernst if deprecate: 7004a133f9SSteve Rowe buffer.append('%s * @deprecated Use latest\n' % spaces) 7104a133f9SSteve Rowe else: 72*f9be01d5SAdrien Grand buffer.append(( '{0} * <p>Use this to get the latest & greatest settings, bug fixes, etc, for Lucene.\n').format(spaces)) 7304a133f9SSteve Rowe buffer.append('%s */\n' % spaces) 741db06937SRyan Ernst if deprecate: 7504a133f9SSteve Rowe buffer.append('%s@Deprecated\n' % spaces) 7604a133f9SSteve Rowe buffer.append('{0}public static final Version {1} = new Version({2}, {3}, {4});\n'.format 7704a133f9SSteve Rowe (spaces, new_version.constant, new_version.major, new_version.minor, new_version.bugfix)) 781db06937SRyan Ernst 791db06937SRyan Ernst class Edit(object): 801db06937SRyan Ernst found = -1 811db06937SRyan Ernst def __call__(self, buffer, match, line): 821db06937SRyan Ernst if new_version.constant in line: 831db06937SRyan Ernst return None # constant already exists 8404a133f9SSteve Rowe # outer match is just to find lines declaring version constants 851db06937SRyan Ernst match = prev_matcher.search(line) 861db06937SRyan Ernst if match is not None: 871db06937SRyan Ernst ensure_deprecated(buffer) # old version should be deprecated 881db06937SRyan Ernst self.found = len(buffer) + 1 # extra 1 for buffering current line below 891db06937SRyan Ernst elif self.found != -1: 901db06937SRyan Ernst # we didn't match, but we previously had a match, so insert new version here 911db06937SRyan Ernst # first find where to insert (first empty line before current constant) 921db06937SRyan Ernst c = [] 931db06937SRyan Ernst buffer_constant(c, line) 941db06937SRyan Ernst tmp = buffer[self.found:] 951db06937SRyan Ernst buffer[self.found:] = c 961db06937SRyan Ernst buffer.extend(tmp) 971db06937SRyan Ernst buffer.append(line) 981db06937SRyan Ernst return True 991db06937SRyan Ernst 1001db06937SRyan Ernst buffer.append(line) 1011db06937SRyan Ernst return False 1021db06937SRyan Ernst 10303f49700SAnshum Gupta changed = update_file(filename, matcher, Edit()) 1041db06937SRyan Ernst print('done' if changed else 'uptodate') 1051db06937SRyan Ernst 1061db06937SRyan Ernstdef update_build_version(new_version): 10708e38d34SMike Drob print(' changing baseVersion...', end='', flush=True) 10808e38d34SMike Drob filename = 'build.gradle' 1091db06937SRyan Ernst def edit(buffer, match, line): 1101db06937SRyan Ernst if new_version.dot in line: 1111db06937SRyan Ernst return None 112674b66ddSJan Høydahl buffer.append(' String baseVersion = \'' + new_version.dot + '\'\n') 1131db06937SRyan Ernst return True 1141db06937SRyan Ernst 115674b66ddSJan Høydahl version_prop_re = re.compile(r'baseVersion\s*=\s*([\'"])(.*)\1') 116674b66ddSJan Høydahl changed = update_file(filename, version_prop_re, edit) 1171db06937SRyan Ernst print('done' if changed else 'uptodate') 1181db06937SRyan Ernst 1191db06937SRyan Ernstdef update_latest_constant(new_version): 1201db06937SRyan Ernst print(' changing Version.LATEST to %s...' % new_version.constant, end='', flush=True) 1211db06937SRyan Ernst filename = 'lucene/core/src/java/org/apache/lucene/util/Version.java' 1221db06937SRyan Ernst matcher = re.compile('public static final Version LATEST') 1231db06937SRyan Ernst def edit(buffer, match, line): 1241db06937SRyan Ernst if new_version.constant in line: 1251db06937SRyan Ernst return None 1261db06937SRyan Ernst buffer.append(line.rpartition('=')[0] + ('= %s;\n' % new_version.constant)) 1271db06937SRyan Ernst return True 1281db06937SRyan Ernst 12903f49700SAnshum Gupta changed = update_file(filename, matcher, edit) 1301db06937SRyan Ernst print('done' if changed else 'uptodate') 1311db06937SRyan Ernst 1321e83e339STimothy Potterdef onerror(x): 1331e83e339STimothy Potter raise x 1341e83e339STimothy Potter 1351db06937SRyan Ernstdef check_lucene_version_tests(): 1361db06937SRyan Ernst print(' checking lucene version tests...', end='', flush=True) 137178d83d6SJason Gerlowski run('./gradlew -p lucene/core test --tests TestVersion') 1381db06937SRyan Ernst print('ok') 1391db06937SRyan Ernst 14046c827e3SSteve Rowedef read_config(current_version): 141674b66ddSJan Høydahl parser = argparse.ArgumentParser(description='Add a new version to CHANGES, to Version.java and build.gradle files') 1421db06937SRyan Ernst parser.add_argument('version', type=Version.parse) 143cd8592c8SSteve Rowe newconf = parser.parse_args() 1441db06937SRyan Ernst 145cd8592c8SSteve Rowe newconf.branch_type = find_branch_type() 146cd8592c8SSteve Rowe newconf.is_latest_version = newconf.version.on_or_after(current_version) 1471db06937SRyan Ernst 148cd8592c8SSteve Rowe print ("branch_type is %s " % newconf.branch_type) 1491db06937SRyan Ernst 150cd8592c8SSteve Rowe return newconf 1511db06937SRyan Ernst 152843adfb7SSteve Rowe# Hack ConfigParser, designed to parse INI files, to parse & interpolate Java .properties files 153843adfb7SSteve Rowedef parse_properties_file(filename): 154843adfb7SSteve Rowe contents = open(filename, encoding='ISO-8859-1').read().replace('%', '%%') # Escape interpolation metachar 155843adfb7SSteve Rowe parser = ConfigParser(interpolation=ExtendedInterpolation()) # Handle ${property-name} interpolation 156843adfb7SSteve Rowe parser.read_string("[DUMMY_SECTION]\n" + contents) # Add required section 157843adfb7SSteve Rowe return dict(parser.items('DUMMY_SECTION')) 158843adfb7SSteve Rowe 159843adfb7SSteve Rowe 1601db06937SRyan Ernstdef main(): 16108e38d34SMike Drob if not os.path.exists('build.gradle'): 16287c131baSJan Høydahl sys.exit("Tool must be run from the root of a source checkout.") 16346c827e3SSteve Rowe current_version = Version.parse(find_current_version()) 164cd8592c8SSteve Rowe newconf = read_config(current_version) 165742e6b7eSDavid Smiley is_bugfix = newconf.version.is_bugfix_release() 1661db06937SRyan Ernst 167cd8592c8SSteve Rowe print('\nAdding new version %s' % newconf.version) 168742e6b7eSDavid Smiley # See LUCENE-8883 for some thoughts on which categories to use 169742e6b7eSDavid Smiley update_changes('lucene/CHANGES.txt', newconf.version, '\n', 170742e6b7eSDavid Smiley ['Bug Fixes'] if is_bugfix else ['API Changes', 'New Features', 'Improvements', 'Optimizations', 'Bug Fixes', 'Other']) 17146c827e3SSteve Rowe 172cd8592c8SSteve Rowe latest_or_backcompat = newconf.is_latest_version or current_version.is_back_compat_with(newconf.version) 173cd8592c8SSteve Rowe if latest_or_backcompat: 174cd8592c8SSteve Rowe add_constant(newconf.version, not newconf.is_latest_version) 17546c827e3SSteve Rowe else: 176cd8592c8SSteve Rowe print('\nNot adding constant for version %s because it is no longer supported' % newconf.version) 1771db06937SRyan Ernst 178cd8592c8SSteve Rowe if newconf.is_latest_version: 1791db06937SRyan Ernst print('\nUpdating latest version') 180cd8592c8SSteve Rowe update_build_version(newconf.version) 181cd8592c8SSteve Rowe update_latest_constant(newconf.version) 1821db06937SRyan Ernst 183cd8592c8SSteve Rowe if newconf.version.is_major_release(): 1841db06937SRyan Ernst print('\nTODO: ') 1851db06937SRyan Ernst print(' - Move backcompat oldIndexes to unsupportedIndexes in TestBackwardsCompatibility') 1861db06937SRyan Ernst print(' - Update IndexFormatTooOldException throw cases') 187cd8592c8SSteve Rowe elif latest_or_backcompat: 1881db06937SRyan Ernst print('\nTesting changes') 1891db06937SRyan Ernst check_lucene_version_tests() 1901db06937SRyan Ernst 1911db06937SRyan Ernst print() 1921db06937SRyan Ernst 1931db06937SRyan Ernstif __name__ == '__main__': 1941db06937SRyan Ernst try: 1951db06937SRyan Ernst main() 1961db06937SRyan Ernst except KeyboardInterrupt: 1971db06937SRyan Ernst print('\nReceived Ctrl-C, exiting early') 198