1# Licensed to the Apache Software Foundation (ASF) under one or more 2# contributor license agreements. See the NOTICE file distributed with 3# this work for additional information regarding copyright ownership. 4# The ASF licenses this file to You under the Apache License, Version 2.0 5# (the "License"); you may not use this file except in compliance with 6# the License. You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import argparse 17import re 18import subprocess 19import sys 20import os 21from enum import Enum 22import time 23import urllib.request, urllib.error, urllib.parse 24import urllib.parse 25 26class Version(object): 27 def __init__(self, major, minor, bugfix, prerelease): 28 self.major = major 29 self.minor = minor 30 self.bugfix = bugfix 31 self.prerelease = prerelease 32 self.previous_dot_matcher = self.make_previous_matcher() 33 self.dot = '%d.%d.%d' % (self.major, self.minor, self.bugfix) 34 self.constant = 'LUCENE_%d_%d_%d' % (self.major, self.minor, self.bugfix) 35 36 @classmethod 37 def parse(cls, value): 38 match = re.search(r'(\d+)\.(\d+).(\d+)(.1|.2)?', value) 39 if match is None: 40 raise argparse.ArgumentTypeError('Version argument must be of format x.y.z(.1|.2)?') 41 parts = [int(v) for v in match.groups()[:-1]] 42 parts.append({ None: 0, '.1': 1, '.2': 2 }[match.groups()[-1]]) 43 return Version(*parts) 44 45 def __str__(self): 46 return self.dot 47 48 def make_previous_matcher(self, prefix='', suffix='', sep='\\.'): 49 if self.is_bugfix_release(): 50 pattern = '%s%s%s%s%d' % (self.major, sep, self.minor, sep, self.bugfix - 1) 51 elif self.is_minor_release(): 52 pattern = '%s%s%d%s\\d+' % (self.major, sep, self.minor - 1, sep) 53 else: 54 pattern = '%d%s\\d+%s\\d+' % (self.major - 1, sep, sep) 55 56 return re.compile(prefix + '(' + pattern + ')' + suffix) 57 58 def is_bugfix_release(self): 59 return self.bugfix != 0 60 61 def is_minor_release(self): 62 return self.bugfix == 0 and self.minor != 0 63 64 def is_major_release(self): 65 return self.bugfix == 0 and self.minor == 0 66 67 def on_or_after(self, other): 68 return (self.major > other.major or self.major == other.major and 69 (self.minor > other.minor or self.minor == other.minor and 70 (self.bugfix > other.bugfix or self.bugfix == other.bugfix and 71 self.prerelease >= other.prerelease))) 72 73 def gt(self, other): 74 return (self.major > other.major or 75 (self.major == other.major and self.minor > other.minor) or 76 (self.major == other.major and self.minor == other.minor and self.bugfix > other.bugfix)) 77 78 def is_back_compat_with(self, other): 79 if not self.on_or_after(other): 80 raise Exception('Back compat check disallowed for newer version: %s < %s' % (self, other)) 81 return other.major + 1 >= self.major 82 83def run(cmd, cwd=None): 84 try: 85 output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, cwd=cwd) 86 except subprocess.CalledProcessError as e: 87 print(e.output.decode('utf-8')) 88 raise e 89 return output.decode('utf-8') 90 91def update_file(filename, line_re, edit): 92 infile = open(filename, 'r') 93 buffer = [] 94 95 changed = False 96 for line in infile: 97 if not changed: 98 match = line_re.search(line) 99 if match: 100 changed = edit(buffer, match, line) 101 if changed is None: 102 return False 103 continue 104 buffer.append(line) 105 if not changed: 106 raise Exception('Could not find %s in %s' % (line_re, filename)) 107 with open(filename, 'w') as f: 108 f.write(''.join(buffer)) 109 return True 110 111 112# branch types are "release", "stable" and "unstable" 113class BranchType(Enum): 114 unstable = 1 115 stable = 2 116 release = 3 117 118def find_branch_type(): 119 output = subprocess.check_output('git status', shell=True) 120 for line in output.split(b'\n'): 121 if line.startswith(b'On branch '): 122 branchName = line.split(b' ')[-1] 123 break 124 else: 125 raise Exception('git status missing branch name') 126 127 if branchName == b'main': 128 return BranchType.unstable 129 if re.match(r'branch_(\d+)x', branchName.decode('UTF-8')): 130 return BranchType.stable 131 if re.match(r'branch_(\d+)_(\d+)', branchName.decode('UTF-8')): 132 return BranchType.release 133 raise Exception('Cannot run %s on feature branch' % sys.argv[0].rsplit('/', 1)[-1]) 134 135 136def download(name, urlString, tmpDir, quiet=False, force_clean=True): 137 if not quiet: 138 print("Downloading %s" % urlString) 139 startTime = time.time() 140 fileName = '%s/%s' % (tmpDir, name) 141 if not force_clean and os.path.exists(fileName): 142 if not quiet and fileName.find('.asc') == -1: 143 print(' already done: %.1f MB' % (os.path.getsize(fileName)/1024./1024.)) 144 return 145 try: 146 attemptDownload(urlString, fileName) 147 except Exception as e: 148 print('Retrying download of url %s after exception: %s' % (urlString, e)) 149 try: 150 attemptDownload(urlString, fileName) 151 except Exception as e: 152 raise RuntimeError('failed to download url "%s"' % urlString) from e 153 if not quiet and fileName.find('.asc') == -1: 154 t = time.time()-startTime 155 sizeMB = os.path.getsize(fileName)/1024./1024. 156 print(' %.1f MB in %.2f sec (%.1f MB/sec)' % (sizeMB, t, sizeMB/t)) 157 158 159def attemptDownload(urlString, fileName): 160 fIn = urllib.request.urlopen(urlString) 161 fOut = open(fileName, 'wb') 162 success = False 163 try: 164 while True: 165 s = fIn.read(65536) 166 if s == b'': 167 break 168 fOut.write(s) 169 fOut.close() 170 fIn.close() 171 success = True 172 finally: 173 fIn.close() 174 fOut.close() 175 if not success: 176 os.remove(fileName) 177 178version_prop_re = re.compile(r'baseVersion\s*=\s*([\'"])(.*)\1') 179def find_current_version(): 180 script_path = os.path.dirname(os.path.realpath(__file__)) 181 top_level_dir = os.path.join(os.path.abspath("%s/" % script_path), os.path.pardir, os.path.pardir) 182 return version_prop_re.search(open('%s/build.gradle' % top_level_dir).read()).group(2).strip() 183 184if __name__ == '__main__': 185 print('This is only a support module, it cannot be run') 186 sys.exit(1) 187