lucene/dev-tools/scripts/scriptutil.py

189 lines
6.3 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You 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 argparse
import re
import subprocess
import sys
import os
from enum import Enum
import time
import urllib.request, urllib.error, urllib.parse
import urllib.parse
class Version(object):
def __init__(self, major, minor, bugfix, prerelease):
self.major = major
self.minor = minor
self.bugfix = bugfix
self.prerelease = prerelease
self.previous_dot_matcher = self.make_previous_matcher()
self.dot = '%d.%d.%d' % (self.major, self.minor, self.bugfix)
self.constant = 'LUCENE_%d_%d_%d' % (self.major, self.minor, self.bugfix)
@classmethod
def parse(cls, value):
match = re.search(r'(\d+)\.(\d+).(\d+)(.1|.2)?', value)
if match is None:
raise argparse.ArgumentTypeError('Version argument must be of format x.y.z(.1|.2)?')
parts = [int(v) for v in match.groups()[:-1]]
parts.append({ None: 0, '.1': 1, '.2': 2 }[match.groups()[-1]])
return Version(*parts)
def __str__(self):
return self.dot
def make_previous_matcher(self, prefix='', suffix='', sep='\\.'):
if self.is_bugfix_release():
pattern = '%s%s%s%s%d' % (self.major, sep, self.minor, sep, self.bugfix - 1)
elif self.is_minor_release():
pattern = '%s%s%d%s\\d+' % (self.major, sep, self.minor - 1, sep)
else:
pattern = '%d%s\\d+%s\\d+' % (self.major - 1, sep, sep)
return re.compile(prefix + '(' + pattern + ')' + suffix)
def is_bugfix_release(self):
return self.bugfix != 0
def is_minor_release(self):
return self.bugfix == 0 and self.minor != 0
def is_major_release(self):
return self.bugfix == 0 and self.minor == 0
def on_or_after(self, other):
return (self.major > other.major or self.major == other.major and
(self.minor > other.minor or self.minor == other.minor and
(self.bugfix > other.bugfix or self.bugfix == other.bugfix and
self.prerelease >= other.prerelease)))
def gt(self, other):
return (self.major > other.major or
(self.major == other.major and self.minor > other.minor) or
(self.major == other.major and self.minor == other.minor and self.bugfix > other.bugfix))
def is_back_compat_with(self, other):
if not self.on_or_after(other):
raise Exception('Back compat check disallowed for newer version: %s < %s' % (self, other))
return other.major + 1 >= self.major
def run(cmd, cwd=None):
try:
output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, cwd=cwd)
except subprocess.CalledProcessError as e:
print(e.output.decode('utf-8'))
raise e
return output.decode('utf-8')
def update_file(filename, line_re, edit, append=None):
infile = open(filename, 'r')
buffer = []
changed = False
for line in infile:
if not changed:
match = line_re.search(line)
if match:
changed = edit(buffer, match, line)
if changed is None:
return False
continue
buffer.append(line)
if append:
changed = append(buffer, changed) # in the case did not change in edit but have an append function
if not changed:
raise Exception('Could not find %s in %s' % (line_re, filename))
with open(filename, 'w') as f:
f.write(''.join(buffer))
return True
# branch types are "release", "stable" and "unstable"
class BranchType(Enum):
unstable = 1
stable = 2
release = 3
def find_branch_type():
output = subprocess.check_output('git status', shell=True)
for line in output.split(b'\n'):
if line.startswith(b'On branch '):
branchName = line.split(b' ')[-1]
break
else:
raise Exception('git status missing branch name')
if branchName == b'main':
return BranchType.unstable
if re.match(r'branch_(\d+)x', branchName.decode('UTF-8')):
return BranchType.stable
if re.match(r'branch_(\d+)_(\d+)', branchName.decode('UTF-8')):
return BranchType.release
raise Exception('Cannot run %s on feature branch' % sys.argv[0].rsplit('/', 1)[-1])
def download(name, urlString, tmpDir, quiet=False, force_clean=True):
if not quiet:
print("Downloading %s" % urlString)
startTime = time.time()
fileName = '%s/%s' % (tmpDir, name)
if not force_clean and os.path.exists(fileName):
if not quiet and fileName.find('.asc') == -1:
print(' already done: %.1f MB' % (os.path.getsize(fileName)/1024./1024.))
return
try:
attemptDownload(urlString, fileName)
except Exception as e:
print('Retrying download of url %s after exception: %s' % (urlString, e))
try:
attemptDownload(urlString, fileName)
except Exception as e:
raise RuntimeError('failed to download url "%s"' % urlString) from e
if not quiet and fileName.find('.asc') == -1:
t = time.time()-startTime
sizeMB = os.path.getsize(fileName)/1024./1024.
print(' %.1f MB in %.2f sec (%.1f MB/sec)' % (sizeMB, t, sizeMB/t))
def attemptDownload(urlString, fileName):
fIn = urllib.request.urlopen(urlString)
fOut = open(fileName, 'wb')
success = False
try:
while True:
s = fIn.read(65536)
if s == b'':
break
fOut.write(s)
fOut.close()
fIn.close()
success = True
finally:
fIn.close()
fOut.close()
if not success:
os.remove(fileName)
version_prop_re = re.compile(r'baseVersion\s*=\s*([\'"])(.*)\1')
def find_current_version():
script_path = os.path.dirname(os.path.realpath(__file__))
top_level_dir = os.path.join(os.path.abspath("%s/" % script_path), os.path.pardir, os.path.pardir)
return version_prop_re.search(open('%s/build.gradle' % top_level_dir).read()).group(2).strip()
if __name__ == '__main__':
print('This is only a support module, it cannot be run')
sys.exit(1)