xref: /Lucene/dev-tools/scripts/addBackcompatIndexes.py (revision 71dfa9e9cd07df4e247895a53ed9b4422284c921)
187c131baSJan Høydahl#!/usr/bin/env python3
287c131baSJan Høydahl# -*- coding: utf-8 -*-
38d885e4fSRyan Ernst# Licensed to the Apache Software Foundation (ASF) under one or more
48d885e4fSRyan Ernst# contributor license agreements.  See the NOTICE file distributed with
58d885e4fSRyan Ernst# this work for additional information regarding copyright ownership.
68d885e4fSRyan Ernst# The ASF licenses this file to You under the Apache License, Version 2.0
78d885e4fSRyan Ernst# (the "License"); you may not use this file except in compliance with
88d885e4fSRyan Ernst# the License.  You may obtain a copy of the License at
98d885e4fSRyan Ernst#
108d885e4fSRyan Ernst#     http://www.apache.org/licenses/LICENSE-2.0
118d885e4fSRyan Ernst#
128d885e4fSRyan Ernst# Unless required by applicable law or agreed to in writing, software
138d885e4fSRyan Ernst# distributed under the License is distributed on an "AS IS" BASIS,
148d885e4fSRyan Ernst# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
158d885e4fSRyan Ernst# See the License for the specific language governing permissions and
168d885e4fSRyan Ernst# limitations under the License.
178d885e4fSRyan Ernst
1801a4c972SSteven Rowe
1901a4c972SSteven Rowe# For usage information, see:
2001a4c972SSteven Rowe#
2101a4c972SSteven Rowe#   http://wiki.apache.org/lucene-java/ReleaseTodo#Generate_Backcompat_Indexes
2201a4c972SSteven Rowe
2301a4c972SSteven Rowe
248d885e4fSRyan Ernstimport os
258d885e4fSRyan Ernstimport sys
268d885e4fSRyan Ernstsys.path.append(os.path.dirname(__file__))
278d885e4fSRyan Ernstimport scriptutil
288d885e4fSRyan Ernst
298d885e4fSRyan Ernstimport argparse
308d885e4fSRyan Ernstimport urllib.error
318d885e4fSRyan Ernstimport urllib.request
328d885e4fSRyan Ernstimport re
338d885e4fSRyan Ernstimport shutil
348d885e4fSRyan Ernst
350018003bSSteve Rowedef create_and_add_index(source, indextype, index_version, current_version, temp_dir):
365afd41deSSteve Rowe  if not current_version.is_back_compat_with(index_version):
375afd41deSSteve Rowe    prefix = 'unsupported'
385afd41deSSteve Rowe  else:
395afd41deSSteve Rowe    prefix = {
405afd41deSSteve Rowe      'cfs': 'index',
415afd41deSSteve Rowe      'nocfs': 'index',
425afd41deSSteve Rowe      'sorted': 'sorted',
435afd41deSSteve Rowe      'moreterms': 'moreterms',
445afd41deSSteve Rowe      'dvupdates': 'dvupdates',
455afd41deSSteve Rowe      'emptyIndex': 'empty'
465afd41deSSteve Rowe    }[indextype]
478d885e4fSRyan Ernst  if indextype in ('cfs', 'nocfs'):
488d885e4fSRyan Ernst    dirname = 'index.%s' % indextype
495afd41deSSteve Rowe    filename = '%s.%s-%s.zip' % (prefix, index_version, indextype)
508d885e4fSRyan Ernst  else:
518d885e4fSRyan Ernst    dirname = indextype
525afd41deSSteve Rowe    filename = '%s.%s.zip' % (prefix, index_version)
535afd41deSSteve Rowe
548d885e4fSRyan Ernst  print('  creating %s...' % filename, end='', flush=True)
559b112b58SRyan Ernst  module = 'backward-codecs'
56cfd0ccefSIgnacio Vera  index_dir = os.path.join('lucene', module, 'src/test/org/apache/lucene/backward_index')
578d885e4fSRyan Ernst  test_file = os.path.join(index_dir, filename)
588d885e4fSRyan Ernst  if os.path.exists(os.path.join(index_dir, filename)):
598d885e4fSRyan Ernst    print('uptodate')
608d885e4fSRyan Ernst    return
618d885e4fSRyan Ernst
628d885e4fSRyan Ernst  test = {
638d885e4fSRyan Ernst    'cfs': 'testCreateCFS',
645afd41deSSteve Rowe    'nocfs': 'testCreateNoCFS',
655afd41deSSteve Rowe    'sorted': 'testCreateSortedIndex',
665afd41deSSteve Rowe    'moreterms': 'testCreateMoreTermsIndex',
675afd41deSSteve Rowe    'dvupdates': 'testCreateIndexWithDocValuesUpdates',
685afd41deSSteve Rowe    'emptyIndex': 'testCreateEmptyIndex'
698d885e4fSRyan Ernst  }[indextype]
70*71dfa9e9SAdrien Grand  gradle_args = ' '.join([
71*71dfa9e9SAdrien Grand    '-Ptests.useSecurityManager=false',
72*71dfa9e9SAdrien Grand    '-p lucene/%s' % module,
73*71dfa9e9SAdrien Grand    'test',
74*71dfa9e9SAdrien Grand    '--tests TestBackwardsCompatibility.%s' % test,
758d885e4fSRyan Ernst    '-Dtests.bwcdir=%s' % temp_dir,
76*71dfa9e9SAdrien Grand    '-Dtests.codec=default'
778d885e4fSRyan Ernst  ])
788d885e4fSRyan Ernst  base_dir = os.getcwd()
798d885e4fSRyan Ernst  bc_index_dir = os.path.join(temp_dir, dirname)
808d885e4fSRyan Ernst  bc_index_file = os.path.join(bc_index_dir, filename)
818d885e4fSRyan Ernst
828d885e4fSRyan Ernst  if os.path.exists(bc_index_file):
838d885e4fSRyan Ernst    print('alreadyexists')
848d885e4fSRyan Ernst  else:
858d885e4fSRyan Ernst    if os.path.exists(bc_index_dir):
868d885e4fSRyan Ernst      shutil.rmtree(bc_index_dir)
87*71dfa9e9SAdrien Grand    os.chdir(source)
88*71dfa9e9SAdrien Grand    scriptutil.run('./gradlew %s' % gradle_args)
898d885e4fSRyan Ernst    os.chdir(bc_index_dir)
908d885e4fSRyan Ernst    scriptutil.run('zip %s *' % filename)
918d885e4fSRyan Ernst    print('done')
928d885e4fSRyan Ernst
938d885e4fSRyan Ernst  print('  adding %s...' % filename, end='', flush=True)
948d885e4fSRyan Ernst  scriptutil.run('cp %s %s' % (bc_index_file, os.path.join(base_dir, index_dir)))
958d885e4fSRyan Ernst  os.chdir(base_dir)
968d885e4fSRyan Ernst  scriptutil.run('rm -rf %s' % bc_index_dir)
978d885e4fSRyan Ernst  print('done')
988d885e4fSRyan Ernst
990018003bSSteve Rowedef update_backcompat_tests(types, index_version, current_version):
1005afd41deSSteve Rowe  print('  adding new indexes %s to backcompat tests...' % types, end='', flush=True)
1018d885e4fSRyan Ernst  module = 'lucene/backward-codecs'
102cfd0ccefSIgnacio Vera  filename = '%s/src/test/org/apache/lucene/backward_index/TestBackwardsCompatibility.java' % module
1035afd41deSSteve Rowe  if not current_version.is_back_compat_with(index_version):
1043698acf3SSteve Rowe    matcher = re.compile(r'final String\[\] unsupportedNames = {|};')
1055afd41deSSteve Rowe  elif 'sorted' in types:
106cfd0ccefSIgnacio Vera    matcher = re.compile(r'static final String\[\] oldSortedNames = {|};')
1075afd41deSSteve Rowe  else:
108cfd0ccefSIgnacio Vera    matcher = re.compile(r'static final String\[\] oldNames = {|};')
1095afd41deSSteve Rowe
1105afd41deSSteve Rowe  strip_dash_suffix_re = re.compile(r'-.*')
1118d885e4fSRyan Ernst
1128d885e4fSRyan Ernst  def find_version(x):
1138d885e4fSRyan Ernst    x = x.strip()
1145afd41deSSteve Rowe    x = re.sub(strip_dash_suffix_re, '', x) # remove the -suffix if any
1155afd41deSSteve Rowe    return scriptutil.Version.parse(x)
1168d885e4fSRyan Ernst
1178d885e4fSRyan Ernst  class Edit(object):
1188d885e4fSRyan Ernst    start = None
1198d885e4fSRyan Ernst    def __call__(self, buffer, match, line):
1208d885e4fSRyan Ernst      if self.start:
1210018003bSSteve Rowe        # find where this version should exist
1228d885e4fSRyan Ernst        i = len(buffer) - 1
1230e8d9411SSteve Rowe        previous_version_exists = not ('};' in line and buffer[-1].strip().endswith("{"))
1245afd41deSSteve Rowe        if previous_version_exists: # Only look if there is a version here
1258d885e4fSRyan Ernst          v = find_version(buffer[i])
1260018003bSSteve Rowe          while i >= self.start and v.on_or_after(index_version):
1278d885e4fSRyan Ernst            i -= 1
1288d885e4fSRyan Ernst            v = find_version(buffer[i])
1298d885e4fSRyan Ernst        i += 1 # readjust since we skipped past by 1
1308d885e4fSRyan Ernst
1318d885e4fSRyan Ernst        # unfortunately python doesn't have a range remove from list...
1328d885e4fSRyan Ernst        # here we want to remove any previous references to the version we are adding
1330018003bSSteve Rowe        while i < len(buffer) and index_version.on_or_after(find_version(buffer[i])):
1348d885e4fSRyan Ernst          buffer.pop(i)
1358d885e4fSRyan Ernst
1365afd41deSSteve Rowe        if i == len(buffer) and previous_version_exists and not buffer[-1].strip().endswith(","):
1378d885e4fSRyan Ernst          # add comma
1388d885e4fSRyan Ernst          buffer[-1] = buffer[-1].rstrip() + ",\n"
1398d885e4fSRyan Ernst
1405afd41deSSteve Rowe        if previous_version_exists:
1418d885e4fSRyan Ernst          last = buffer[-1]
1428d885e4fSRyan Ernst          spaces = ' ' * (len(last) - len(last.lstrip()))
1435afd41deSSteve Rowe        else:
1445afd41deSSteve Rowe          spaces = '    '
1458d885e4fSRyan Ernst        for (j, t) in enumerate(types):
1465afd41deSSteve Rowe          if t == 'sorted':
1475afd41deSSteve Rowe            newline = spaces + ('"sorted.%s"') % index_version
1485afd41deSSteve Rowe          else:
1490018003bSSteve Rowe            newline = spaces + ('"%s-%s"' % (index_version, t))
1508d885e4fSRyan Ernst          if j < len(types) - 1 or i < len(buffer):
1518d885e4fSRyan Ernst            newline += ','
1528d885e4fSRyan Ernst          buffer.insert(i, newline + '\n')
1538d885e4fSRyan Ernst          i += 1
1548d885e4fSRyan Ernst
1558d885e4fSRyan Ernst        buffer.append(line)
1568d885e4fSRyan Ernst        return True
1578d885e4fSRyan Ernst
1580018003bSSteve Rowe      if 'Names = {' in line:
1598d885e4fSRyan Ernst        self.start = len(buffer) # location of first index name
1608d885e4fSRyan Ernst      buffer.append(line)
1618d885e4fSRyan Ernst      return False
1628d885e4fSRyan Ernst
1638d885e4fSRyan Ernst  changed = scriptutil.update_file(filename, matcher, Edit())
1648d885e4fSRyan Ernst  print('done' if changed else 'uptodate')
1658d885e4fSRyan Ernst
1668d885e4fSRyan Ernstdef check_backcompat_tests():
1678d885e4fSRyan Ernst  print('  checking backcompat tests...', end='', flush=True)
1682a9d7040SJason Gerlowski  scriptutil.run('./gradlew -p lucene/backward-codecs test --tests TestBackwardsCompatibility')
1698d885e4fSRyan Ernst  print('ok')
1708d885e4fSRyan Ernst
17165296e5fSAdrien Granddef download_from_cdn(version, remotename, localname):
17265296e5fSAdrien Grand  url = 'http://dlcdn.apache.org/lucene/java/%s/%s' % (version, remotename)
1738d885e4fSRyan Ernst  try:
1748d885e4fSRyan Ernst    urllib.request.urlretrieve(url, localname)
1758d885e4fSRyan Ernst    return True
1768d885e4fSRyan Ernst  except urllib.error.URLError as e:
1778d885e4fSRyan Ernst    if e.code == 404:
1788d885e4fSRyan Ernst      return False
1798d885e4fSRyan Ernst    raise e
1808d885e4fSRyan Ernst
1818d885e4fSRyan Ernstdef download_from_archives(version, remotename, localname):
1828d885e4fSRyan Ernst  url = 'http://archive.apache.org/dist/lucene/java/%s/%s' % (version, remotename)
1838d885e4fSRyan Ernst  try:
1848d885e4fSRyan Ernst    urllib.request.urlretrieve(url, localname)
1858d885e4fSRyan Ernst    return True
1868d885e4fSRyan Ernst  except urllib.error.URLError as e:
1878d885e4fSRyan Ernst    if e.code == 404:
1888d885e4fSRyan Ernst      return False
1898d885e4fSRyan Ernst    raise e
1908d885e4fSRyan Ernst
1918d885e4fSRyan Ernstdef download_release(version, temp_dir, force):
1928d885e4fSRyan Ernst  print('  downloading %s source release...' % version, end='', flush=True)
1938d885e4fSRyan Ernst  source = os.path.join(temp_dir, 'lucene-%s' % version)
1948d885e4fSRyan Ernst  if os.path.exists(source):
1958d885e4fSRyan Ernst    if force:
1968d885e4fSRyan Ernst      shutil.rmtree(source)
1978d885e4fSRyan Ernst    else:
1988d885e4fSRyan Ernst      print('uptodate')
1998d885e4fSRyan Ernst      return source
2008d885e4fSRyan Ernst
2018d885e4fSRyan Ernst  filename = 'lucene-%s-src.tgz' % version
2028d885e4fSRyan Ernst  source_tgz = os.path.join(temp_dir, filename)
20365296e5fSAdrien Grand  if not download_from_cdn(version, filename, source_tgz) and \
2048d885e4fSRyan Ernst     not download_from_archives(version, filename, source_tgz):
20565296e5fSAdrien Grand    raise Exception('Could not find version %s in apache CDN or archives' % version)
2068d885e4fSRyan Ernst
2078d885e4fSRyan Ernst  olddir = os.getcwd()
2088d885e4fSRyan Ernst  os.chdir(temp_dir)
2098d885e4fSRyan Ernst  scriptutil.run('tar -xvzf %s' % source_tgz)
2108d885e4fSRyan Ernst  os.chdir(olddir)
2118d885e4fSRyan Ernst  print('done')
2128d885e4fSRyan Ernst  return source
2138d885e4fSRyan Ernst
2148d885e4fSRyan Ernstdef read_config():
21501a4c972SSteven Rowe  parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
21601a4c972SSteven Rowe                                   description='''\
21701a4c972SSteven RoweAdd backcompat index and test for new version.  See:
21801a4c972SSteven Rowehttp://wiki.apache.org/lucene-java/ReleaseTodo#Generate_Backcompat_Indexes
21901a4c972SSteven Rowe''')
2208d885e4fSRyan Ernst  parser.add_argument('--force', action='store_true', default=False,
2218d885e4fSRyan Ernst                      help='Redownload the version and rebuild, even if it already exists')
2228d885e4fSRyan Ernst  parser.add_argument('--no-cleanup', dest='cleanup', action='store_false', default=True,
2238d885e4fSRyan Ernst                      help='Do not cleanup the built indexes, so that they can be reused ' +
2248d885e4fSRyan Ernst                           'for adding to another branch')
2258d885e4fSRyan Ernst  parser.add_argument('--temp-dir', metavar='DIR', default='/tmp/lucenebwc',
2268d885e4fSRyan Ernst                      help='Temp directory to build backcompat indexes within')
2278d885e4fSRyan Ernst  parser.add_argument('version', type=scriptutil.Version.parse,
2288d885e4fSRyan Ernst                      help='Version to add, of the form X.Y.Z')
2298d885e4fSRyan Ernst  c = parser.parse_args()
2308d885e4fSRyan Ernst
2318d885e4fSRyan Ernst  return c
2328d885e4fSRyan Ernst
2338d885e4fSRyan Ernstdef main():
2348d885e4fSRyan Ernst  c = read_config()
2358d885e4fSRyan Ernst  if not os.path.exists(c.temp_dir):
2368d885e4fSRyan Ernst    os.makedirs(c.temp_dir)
2378d885e4fSRyan Ernst
2388d885e4fSRyan Ernst  print('\nCreating backwards compatibility indexes')
2398d885e4fSRyan Ernst  source = download_release(c.version, c.temp_dir, c.force)
2400018003bSSteve Rowe  current_version = scriptutil.Version.parse(scriptutil.find_current_version())
2410018003bSSteve Rowe  create_and_add_index(source, 'cfs', c.version, current_version, c.temp_dir)
2420018003bSSteve Rowe  create_and_add_index(source, 'nocfs', c.version, current_version, c.temp_dir)
2438388b195SSteve Rowe  should_make_sorted =     current_version.is_back_compat_with(c.version) \
2448388b195SSteve Rowe                       and (c.version.major > 6 or (c.version.major == 6 and c.version.minor >= 2))
2458388b195SSteve Rowe  if should_make_sorted:
2465afd41deSSteve Rowe    create_and_add_index(source, 'sorted', c.version, current_version, c.temp_dir)
247*71dfa9e9SAdrien Grand  if c.version.minor == 0 and c.version.bugfix == 0 and current_version.is_back_compat_with(c.version):
2485afd41deSSteve Rowe    create_and_add_index(source, 'moreterms', c.version, current_version, c.temp_dir)
2495afd41deSSteve Rowe    create_and_add_index(source, 'dvupdates', c.version, current_version, c.temp_dir)
2505afd41deSSteve Rowe    create_and_add_index(source, 'emptyIndex', c.version, current_version, c.temp_dir)
2515afd41deSSteve Rowe    print ('\nMANUAL UPDATE REQUIRED: edit TestBackwardsCompatibility to enable moreterms, dvupdates, and empty index testing')
2528d885e4fSRyan Ernst
2538d885e4fSRyan Ernst  print('\nAdding backwards compatibility tests')
2540018003bSSteve Rowe  update_backcompat_tests(['cfs', 'nocfs'], c.version, current_version)
2558388b195SSteve Rowe  if should_make_sorted:
2565afd41deSSteve Rowe    update_backcompat_tests(['sorted'], c.version, current_version)
2578d885e4fSRyan Ernst
2588d885e4fSRyan Ernst  print('\nTesting changes')
2598d885e4fSRyan Ernst  check_backcompat_tests()
2608d885e4fSRyan Ernst
2618d885e4fSRyan Ernst  if c.cleanup:
2628d885e4fSRyan Ernst    print('\nCleaning up')
2638d885e4fSRyan Ernst    print('  deleting %s...' % c.temp_dir, end='', flush=True)
2648d885e4fSRyan Ernst    shutil.rmtree(c.temp_dir)
2658d885e4fSRyan Ernst    print('done')
2668d885e4fSRyan Ernst
2678d885e4fSRyan Ernst  print()
2688d885e4fSRyan Ernst
2698d885e4fSRyan Ernstif __name__ == '__main__':
2708d885e4fSRyan Ernst  try:
2718d885e4fSRyan Ernst    main()
2728d885e4fSRyan Ernst  except KeyboardInterrupt:
2738d885e4fSRyan Ernst    print('\nRecieved Ctrl-C, exiting early')
274