xref: /Lucene/dev-tools/scripts/scriptutil.py (revision 57524c6a5e724088d97cb8ae0e81bd43625083f0)
18d885e4fSRyan Ernst# Licensed to the Apache Software Foundation (ASF) under one or more
28d885e4fSRyan Ernst# contributor license agreements.  See the NOTICE file distributed with
38d885e4fSRyan Ernst# this work for additional information regarding copyright ownership.
48d885e4fSRyan Ernst# The ASF licenses this file to You under the Apache License, Version 2.0
58d885e4fSRyan Ernst# (the "License"); you may not use this file except in compliance with
68d885e4fSRyan Ernst# the License.  You may obtain a copy of the License at
78d885e4fSRyan Ernst#
88d885e4fSRyan Ernst#     http://www.apache.org/licenses/LICENSE-2.0
98d885e4fSRyan Ernst#
108d885e4fSRyan Ernst# Unless required by applicable law or agreed to in writing, software
118d885e4fSRyan Ernst# distributed under the License is distributed on an "AS IS" BASIS,
128d885e4fSRyan Ernst# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138d885e4fSRyan Ernst# See the License for the specific language governing permissions and
148d885e4fSRyan Ernst# limitations under the License.
158d885e4fSRyan Ernst
168d885e4fSRyan Ernstimport argparse
178d885e4fSRyan Ernstimport re
188d885e4fSRyan Ernstimport subprocess
198d885e4fSRyan Ernstimport sys
2044287d42SJan Høydahlimport os
2110c77578SJan Høydahlfrom enum import Enum
2244287d42SJan Høydahlimport time
2344287d42SJan Høydahlimport urllib.request, urllib.error, urllib.parse
2444287d42SJan Høydahlimport urllib.parse
258d885e4fSRyan Ernst
268d885e4fSRyan Ernstclass Version(object):
278d885e4fSRyan Ernst  def __init__(self, major, minor, bugfix, prerelease):
288d885e4fSRyan Ernst    self.major = major
298d885e4fSRyan Ernst    self.minor = minor
308d885e4fSRyan Ernst    self.bugfix = bugfix
318d885e4fSRyan Ernst    self.prerelease = prerelease
328d885e4fSRyan Ernst    self.previous_dot_matcher = self.make_previous_matcher()
338d885e4fSRyan Ernst    self.dot = '%d.%d.%d' % (self.major, self.minor, self.bugfix)
348d885e4fSRyan Ernst    self.constant = 'LUCENE_%d_%d_%d' % (self.major, self.minor, self.bugfix)
358d885e4fSRyan Ernst
368d885e4fSRyan Ernst  @classmethod
378d885e4fSRyan Ernst  def parse(cls, value):
388d885e4fSRyan Ernst    match = re.search(r'(\d+)\.(\d+).(\d+)(.1|.2)?', value)
398d885e4fSRyan Ernst    if match is None:
408d885e4fSRyan Ernst      raise argparse.ArgumentTypeError('Version argument must be of format x.y.z(.1|.2)?')
418d885e4fSRyan Ernst    parts = [int(v) for v in match.groups()[:-1]]
428d885e4fSRyan Ernst    parts.append({ None: 0, '.1': 1, '.2': 2 }[match.groups()[-1]])
438d885e4fSRyan Ernst    return Version(*parts)
448d885e4fSRyan Ernst
458d885e4fSRyan Ernst  def __str__(self):
468d885e4fSRyan Ernst    return self.dot
478d885e4fSRyan Ernst
488d885e4fSRyan Ernst  def make_previous_matcher(self, prefix='', suffix='', sep='\\.'):
498d885e4fSRyan Ernst    if self.is_bugfix_release():
508d885e4fSRyan Ernst      pattern = '%s%s%s%s%d' % (self.major, sep, self.minor, sep, self.bugfix - 1)
518d885e4fSRyan Ernst    elif self.is_minor_release():
528d885e4fSRyan Ernst      pattern = '%s%s%d%s\\d+' % (self.major, sep, self.minor - 1, sep)
538d885e4fSRyan Ernst    else:
548d885e4fSRyan Ernst      pattern = '%d%s\\d+%s\\d+' % (self.major - 1, sep, sep)
558d885e4fSRyan Ernst
568d885e4fSRyan Ernst    return re.compile(prefix + '(' + pattern + ')' + suffix)
578d885e4fSRyan Ernst
588d885e4fSRyan Ernst  def is_bugfix_release(self):
598d885e4fSRyan Ernst    return self.bugfix != 0
608d885e4fSRyan Ernst
618d885e4fSRyan Ernst  def is_minor_release(self):
628d885e4fSRyan Ernst    return self.bugfix == 0 and self.minor != 0
638d885e4fSRyan Ernst
648d885e4fSRyan Ernst  def is_major_release(self):
658d885e4fSRyan Ernst    return self.bugfix == 0 and self.minor == 0
668d885e4fSRyan Ernst
678d885e4fSRyan Ernst  def on_or_after(self, other):
688d885e4fSRyan Ernst    return (self.major > other.major or self.major == other.major and
698d885e4fSRyan Ernst           (self.minor > other.minor or self.minor == other.minor and
708d885e4fSRyan Ernst           (self.bugfix > other.bugfix or self.bugfix == other.bugfix and
718d885e4fSRyan Ernst           self.prerelease >= other.prerelease)))
728d885e4fSRyan Ernst
7344287d42SJan Høydahl  def gt(self, other):
7444287d42SJan Høydahl    return (self.major > other.major or
7544287d42SJan Høydahl           (self.major == other.major and self.minor > other.minor) or
7644287d42SJan Høydahl           (self.major == other.major and self.minor == other.minor and self.bugfix > other.bugfix))
7744287d42SJan Høydahl
780018003bSSteve Rowe  def is_back_compat_with(self, other):
790018003bSSteve Rowe    if not self.on_or_after(other):
800018003bSSteve Rowe      raise Exception('Back compat check disallowed for newer version: %s < %s' % (self, other))
810018003bSSteve Rowe    return other.major + 1 >= self.major
820018003bSSteve Rowe
8344287d42SJan Høydahldef run(cmd, cwd=None):
848d885e4fSRyan Ernst  try:
8544287d42SJan Høydahl    output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, cwd=cwd)
868d885e4fSRyan Ernst  except subprocess.CalledProcessError as e:
878d885e4fSRyan Ernst    print(e.output.decode('utf-8'))
888d885e4fSRyan Ernst    raise e
898d885e4fSRyan Ernst  return output.decode('utf-8')
908d885e4fSRyan Ernst
918d885e4fSRyan Ernstdef update_file(filename, line_re, edit):
928d885e4fSRyan Ernst  infile = open(filename, 'r')
938d885e4fSRyan Ernst  buffer = []
948d885e4fSRyan Ernst
958d885e4fSRyan Ernst  changed = False
968d885e4fSRyan Ernst  for line in infile:
978d885e4fSRyan Ernst    if not changed:
988d885e4fSRyan Ernst      match = line_re.search(line)
998d885e4fSRyan Ernst      if match:
1008d885e4fSRyan Ernst        changed = edit(buffer, match, line)
1018d885e4fSRyan Ernst        if changed is None:
1028d885e4fSRyan Ernst          return False
1038d885e4fSRyan Ernst        continue
1048d885e4fSRyan Ernst    buffer.append(line)
1058d885e4fSRyan Ernst  if not changed:
1068d885e4fSRyan Ernst    raise Exception('Could not find %s in %s' % (line_re, filename))
1078d885e4fSRyan Ernst  with open(filename, 'w') as f:
1088d885e4fSRyan Ernst    f.write(''.join(buffer))
1098d885e4fSRyan Ernst  return True
1108d885e4fSRyan Ernst
11144287d42SJan Høydahl
112a9cc7b63SSteve Rowe# branch types are "release", "stable" and "unstable"
11310c77578SJan Høydahlclass BranchType(Enum):
114a9cc7b63SSteve Rowe  unstable = 1
11510c77578SJan Høydahl  stable   = 2
11610c77578SJan Høydahl  release  = 3
11710c77578SJan Høydahl
1188d885e4fSRyan Ernstdef find_branch_type():
11970e61fd9SMike McCandless  output = subprocess.check_output('git status', shell=True)
1208d885e4fSRyan Ernst  for line in output.split(b'\n'):
12170e61fd9SMike McCandless    if line.startswith(b'On branch '):
12270e61fd9SMike McCandless      branchName = line.split(b' ')[-1]
1238d885e4fSRyan Ernst      break
1248d885e4fSRyan Ernst  else:
12570e61fd9SMike McCandless    raise Exception('git status missing branch name')
1268d885e4fSRyan Ernst
127*57524c6aSChristine Poerschke  if branchName == b'main':
128a9cc7b63SSteve Rowe    return BranchType.unstable
1296e446c0bSJan Høydahl  if re.match(r'branch_(\d+)x', branchName.decode('UTF-8')):
13010c77578SJan Høydahl    return BranchType.stable
1316e446c0bSJan Høydahl  if re.match(r'branch_(\d+)_(\d+)', branchName.decode('UTF-8')):
13210c77578SJan Høydahl    return BranchType.release
1331ec6a886SSteve Rowe  raise Exception('Cannot run %s on feature branch' % sys.argv[0].rsplit('/', 1)[-1])
1348d885e4fSRyan Ernst
13544287d42SJan Høydahl
13644287d42SJan Høydahldef download(name, urlString, tmpDir, quiet=False, force_clean=True):
13744287d42SJan Høydahl  if not quiet:
13844287d42SJan Høydahl      print("Downloading %s" % urlString)
13944287d42SJan Høydahl  startTime = time.time()
14044287d42SJan Høydahl  fileName = '%s/%s' % (tmpDir, name)
14144287d42SJan Høydahl  if not force_clean and os.path.exists(fileName):
14244287d42SJan Høydahl    if not quiet and fileName.find('.asc') == -1:
14344287d42SJan Høydahl      print('    already done: %.1f MB' % (os.path.getsize(fileName)/1024./1024.))
14444287d42SJan Høydahl    return
14544287d42SJan Høydahl  try:
14644287d42SJan Høydahl    attemptDownload(urlString, fileName)
14744287d42SJan Høydahl  except Exception as e:
14844287d42SJan Høydahl    print('Retrying download of url %s after exception: %s' % (urlString, e))
14944287d42SJan Høydahl    try:
15044287d42SJan Høydahl      attemptDownload(urlString, fileName)
15144287d42SJan Høydahl    except Exception as e:
15244287d42SJan Høydahl      raise RuntimeError('failed to download url "%s"' % urlString) from e
15344287d42SJan Høydahl  if not quiet and fileName.find('.asc') == -1:
15444287d42SJan Høydahl    t = time.time()-startTime
15544287d42SJan Høydahl    sizeMB = os.path.getsize(fileName)/1024./1024.
15644287d42SJan Høydahl    print('    %.1f MB in %.2f sec (%.1f MB/sec)' % (sizeMB, t, sizeMB/t))
15744287d42SJan Høydahl
15844287d42SJan Høydahl
15944287d42SJan Høydahldef attemptDownload(urlString, fileName):
16044287d42SJan Høydahl  fIn = urllib.request.urlopen(urlString)
16144287d42SJan Høydahl  fOut = open(fileName, 'wb')
16244287d42SJan Høydahl  success = False
16344287d42SJan Høydahl  try:
16444287d42SJan Høydahl    while True:
16544287d42SJan Høydahl      s = fIn.read(65536)
16644287d42SJan Høydahl      if s == b'':
16744287d42SJan Høydahl        break
16844287d42SJan Høydahl      fOut.write(s)
16944287d42SJan Høydahl    fOut.close()
17044287d42SJan Høydahl    fIn.close()
17144287d42SJan Høydahl    success = True
17244287d42SJan Høydahl  finally:
17344287d42SJan Høydahl    fIn.close()
17444287d42SJan Høydahl    fOut.close()
17544287d42SJan Høydahl    if not success:
17644287d42SJan Høydahl      os.remove(fileName)
17744287d42SJan Høydahl
17808e38d34SMike Drobversion_prop_re = re.compile(r'baseVersion\s*=\s*([\'"])(.*)\1')
1798d885e4fSRyan Ernstdef find_current_version():
18008e38d34SMike Drob  script_path = os.path.dirname(os.path.realpath(__file__))
18108e38d34SMike Drob  top_level_dir = os.path.join(os.path.abspath("%s/" % script_path), os.path.pardir, os.path.pardir)
18208e38d34SMike Drob  return version_prop_re.search(open('%s/build.gradle' % top_level_dir).read()).group(2).strip()
1838d885e4fSRyan Ernst
1848d885e4fSRyan Ernstif __name__ == '__main__':
1858d885e4fSRyan Ernst  print('This is only a support module, it cannot be run')
1868d885e4fSRyan Ernst  sys.exit(1)
187