xref: /Lucene/dev-tools/scripts/buildAndPushRelease.py (revision daf149817ab2f4bb8265d0209fe30945dd3854d2)
187c131baSJan Høydahl#!/usr/bin/env python3
287c131baSJan Høydahl# -*- coding: utf-8 -*-
3d6ab323dSMichael McCandless# Licensed to the Apache Software Foundation (ASF) under one or more
4d6ab323dSMichael McCandless# contributor license agreements.  See the NOTICE file distributed with
5d6ab323dSMichael McCandless# this work for additional information regarding copyright ownership.
6d6ab323dSMichael McCandless# The ASF licenses this file to You under the Apache License, Version 2.0
7d6ab323dSMichael McCandless# (the "License"); you may not use this file except in compliance with
8d6ab323dSMichael McCandless# the License.  You may obtain a copy of the License at
9d6ab323dSMichael McCandless#
10d6ab323dSMichael McCandless#     http://www.apache.org/licenses/LICENSE-2.0
11d6ab323dSMichael McCandless#
12d6ab323dSMichael McCandless# Unless required by applicable law or agreed to in writing, software
13d6ab323dSMichael McCandless# distributed under the License is distributed on an "AS IS" BASIS,
14d6ab323dSMichael McCandless# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15d6ab323dSMichael McCandless# See the License for the specific language governing permissions and
16d6ab323dSMichael McCandless# limitations under the License.
17d6ab323dSMichael McCandless
1880e60002SRyan Ernstimport argparse
19d6ab323dSMichael McCandlessimport datetime
20d6ab323dSMichael McCandlessimport re
219c56dccfSMichael McCandlessimport time
22d6ab323dSMichael McCandlessimport os
23d6ab323dSMichael McCandlessimport sys
249c56dccfSMichael McCandlessimport subprocess
25df1775ffSJan Høydahlfrom subprocess import TimeoutExpired
2644287d42SJan Høydahlfrom scriptutil import check_ant
2780e60002SRyan Ernstimport textwrap
28d60849f3SSteve Roweimport urllib.request, urllib.error, urllib.parse
29d60849f3SSteve Roweimport xml.etree.ElementTree as ET
30d6ab323dSMichael McCandless
31d6ab323dSMichael McCandlessLOG = '/tmp/release.log'
32d6ab323dSMichael McCandless
33d6ab323dSMichael McCandlessdef log(msg):
340178e8e6SSteven Rowe  f = open(LOG, mode='ab')
350178e8e6SSteven Rowe  f.write(msg.encode('utf-8'))
36d6ab323dSMichael McCandless  f.close()
37d6ab323dSMichael McCandless
38d6ab323dSMichael McCandlessdef run(command):
39d6ab323dSMichael McCandless  log('\n\n%s: RUN: %s\n' % (datetime.datetime.now(), command))
40d6ab323dSMichael McCandless  if os.system('%s >> %s 2>&1' % (command, LOG)):
41d6ab323dSMichael McCandless    msg = '    FAILED: %s [see log %s]' % (command, LOG)
420178e8e6SSteven Rowe    print(msg)
43d6ab323dSMichael McCandless    raise RuntimeError(msg)
44d6ab323dSMichael McCandless
459c56dccfSMichael McCandlessdef runAndSendGPGPassword(command, password):
46fdff37f3SShalin Shekhar Mangar  p = subprocess.Popen(command, shell=True, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE)
479c56dccfSMichael McCandless  f = open(LOG, 'ab')
489c56dccfSMichael McCandless  while True:
49d0b01a2bSShalin Shekhar Mangar    p.stdout.flush()
509c56dccfSMichael McCandless    line = p.stdout.readline()
519c56dccfSMichael McCandless    if len(line) == 0:
529c56dccfSMichael McCandless      break
539c56dccfSMichael McCandless    f.write(line)
549c56dccfSMichael McCandless    if line.find(b'Enter GPG keystore password:') != -1:
559c56dccfSMichael McCandless      time.sleep(1.0)
569c56dccfSMichael McCandless      p.stdin.write((password + '\n').encode('UTF-8'))
579c56dccfSMichael McCandless      p.stdin.write('\n'.encode('UTF-8'))
589c56dccfSMichael McCandless
592a13e783SSteve Rowe  try:
602a13e783SSteve Rowe    result = p.wait(timeout=120)
612a13e783SSteve Rowe    if result != 0:
629c56dccfSMichael McCandless      msg = '    FAILED: %s [see log %s]' % (command, LOG)
639c56dccfSMichael McCandless      print(msg)
649c56dccfSMichael McCandless      raise RuntimeError(msg)
652a13e783SSteve Rowe  except TimeoutExpired:
662a13e783SSteve Rowe    msg = '    FAILED: %s [timed out after 2 minutes; see log %s]' % (command, LOG)
672a13e783SSteve Rowe    print(msg)
682a13e783SSteve Rowe    raise RuntimeError(msg)
699c56dccfSMichael McCandless
70982ee393SJan Høydahldef load(urlString, encoding="utf-8"):
71d60849f3SSteve Rowe  try:
72982ee393SJan Høydahl    content = urllib.request.urlopen(urlString).read().decode(encoding)
73d60849f3SSteve Rowe  except Exception as e:
74d60849f3SSteve Rowe    print('Retrying download of url %s after exception: %s' % (urlString, e))
75982ee393SJan Høydahl    content = urllib.request.urlopen(urlString).read().decode(encoding)
76d60849f3SSteve Rowe  return content
77d60849f3SSteve Rowe
78f8be973bSMike McCandlessdef getGitRev():
79f8be973bSMike McCandless  status = os.popen('git status').read().strip()
80a6e14ec6SIshan Chattopadhyaya  if 'nothing to commit, working directory clean' not in status and 'nothing to commit, working tree clean' not in status:
81de9d4ac3SMike McCandless    raise RuntimeError('git clone is dirty:\n\n%s' % status)
8251f7574dSSteve Rowe  branch = os.popen('git rev-parse --abbrev-ref HEAD').read().strip()
836c85a1d7SSteve Rowe  command = 'git log origin/%s..' % branch
845b96f89dSJan Høydahl  p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
855b96f89dSJan Høydahl  stdout, stderr = p.communicate()
865b96f89dSJan Høydahl  if len(stdout.strip()) > 0:
875b96f89dSJan Høydahl    raise RuntimeError('There are unpushed commits - "%s" output is:\n\n%s' % (command, stdout.decode('utf-8')))
885b96f89dSJan Høydahl  if len(stderr.strip()) > 0:
895b96f89dSJan Høydahl    raise RuntimeError('Command "%s" failed:\n\n%s' % (command, stderr.decode('utf-8')))
90d6ab323dSMichael McCandless
917509b9c9SMike McCandless  print('  git clone is clean')
927509b9c9SMike McCandless  return os.popen('git rev-parse HEAD').read().strip()
93d6ab323dSMichael McCandless
949cba8eb4SUwe Schindlerdef prepare(root, version, gpgKeyID, gpgPassword):
950178e8e6SSteven Rowe  print()
960178e8e6SSteven Rowe  print('Prepare release...')
97d6ab323dSMichael McCandless  if os.path.exists(LOG):
98d6ab323dSMichael McCandless    os.remove(LOG)
99d6ab323dSMichael McCandless
100d6ab323dSMichael McCandless  os.chdir(root)
1017263491dSnknize  print('  git pull...')
1027263491dSnknize  run('git pull')
103d6ab323dSMichael McCandless
104f8be973bSMike McCandless  rev = getGitRev()
105f8be973bSMike McCandless  print('  git rev: %s' % rev)
106f8be973bSMike McCandless  log('\nGIT rev: %s\n' % rev)
107d6ab323dSMichael McCandless
108d60849f3SSteve Rowe  print('  Check DOAP files')
109d60849f3SSteve Rowe  checkDOAPfiles(version)
110d60849f3SSteve Rowe
111c074b97eSAdrien Grand  print('  ant -Dtests.badapples=false clean test validate documentation-lint')
112c074b97eSAdrien Grand  run('ant -Dtests.badapples=false clean test validate documentation-lint')
113d6ab323dSMichael McCandless
1140178e8e6SSteven Rowe  open('rev.txt', mode='wb').write(rev.encode('UTF-8'))
115d6ab323dSMichael McCandless
1160178e8e6SSteven Rowe  print('  lucene prepare-release')
117d6ab323dSMichael McCandless  os.chdir('lucene')
11836c653aaSSteven Rowe  cmd = 'ant -Dversion=%s' % version
119879e8250SMichael McCandless  if gpgKeyID is not None:
120879e8250SMichael McCandless    cmd += ' -Dgpg.key=%s prepare-release' % gpgKeyID
121879e8250SMichael McCandless  else:
122879e8250SMichael McCandless    cmd += ' prepare-release-no-sign'
1239c56dccfSMichael McCandless
1249c56dccfSMichael McCandless  if gpgPassword is not None:
1259c56dccfSMichael McCandless    runAndSendGPGPassword(cmd, gpgPassword)
1269c56dccfSMichael McCandless  else:
127879e8250SMichael McCandless    run(cmd)
128879e8250SMichael McCandless
1290178e8e6SSteven Rowe  print('  solr prepare-release')
130d6ab323dSMichael McCandless  os.chdir('../solr')
13136c653aaSSteven Rowe  cmd = 'ant -Dversion=%s' % version
132879e8250SMichael McCandless  if gpgKeyID is not None:
133879e8250SMichael McCandless    cmd += ' -Dgpg.key=%s prepare-release' % gpgKeyID
134879e8250SMichael McCandless  else:
135879e8250SMichael McCandless    cmd += ' prepare-release-no-sign'
1369c56dccfSMichael McCandless
1379c56dccfSMichael McCandless  if gpgPassword is not None:
1389c56dccfSMichael McCandless    runAndSendGPGPassword(cmd, gpgPassword)
1399c56dccfSMichael McCandless  else:
140879e8250SMichael McCandless    run(cmd)
1419c56dccfSMichael McCandless
1420178e8e6SSteven Rowe  print('  done!')
1430178e8e6SSteven Rowe  print()
144d6ab323dSMichael McCandless  return rev
145d6ab323dSMichael McCandless
146d60849f3SSteve RowereVersion1 = re.compile(r'\>(\d+)\.(\d+)\.(\d+)(-alpha|-beta)?/\<', re.IGNORECASE)
147d60849f3SSteve RowereVersion2 = re.compile(r'-(\d+)\.(\d+)\.(\d+)(-alpha|-beta)?\.zip<', re.IGNORECASE)
148d60849f3SSteve RowereDoapRevision = re.compile(r'(\d+)\.(\d+)(?:\.(\d+))?(-alpha|-beta)?', re.IGNORECASE)
149d60849f3SSteve Rowedef checkDOAPfiles(version):
150d60849f3SSteve Rowe  # In Lucene and Solr DOAP files, verify presence of all releases less than the one being produced.
151d60849f3SSteve Rowe  errorMessages = []
152d60849f3SSteve Rowe  for product in 'lucene', 'solr':
153d60849f3SSteve Rowe    url = 'https://archive.apache.org/dist/lucene/%s' % ('java' if product == 'lucene' else product)
154d60849f3SSteve Rowe    distpage = load(url)
155d60849f3SSteve Rowe    releases = set()
156d60849f3SSteve Rowe    for regex in reVersion1, reVersion2:
157d60849f3SSteve Rowe      for tup in regex.findall(distpage):
158d60849f3SSteve Rowe        if tup[0] in ('1', '2'):                    # Ignore 1.X and 2.X releases
159d60849f3SSteve Rowe          continue
160d60849f3SSteve Rowe        releases.add(normalizeVersion(tup))
161d60849f3SSteve Rowe    doapNS = '{http://usefulinc.com/ns/doap#}'
162d60849f3SSteve Rowe    xpathRevision = '{0}Project/{0}release/{0}Version/{0}revision'.format(doapNS)
163d60849f3SSteve Rowe    doapFile = "dev-tools/doap/%s.rdf" % product
164d60849f3SSteve Rowe    treeRoot = ET.parse(doapFile).getroot()
165d60849f3SSteve Rowe    doapRevisions = set()
166d60849f3SSteve Rowe    for revision in treeRoot.findall(xpathRevision):
167d60849f3SSteve Rowe      match = reDoapRevision.match(revision.text)
168d60849f3SSteve Rowe      if (match is not None):
169d60849f3SSteve Rowe        if (match.group(1) not in ('0', '1', '2')): # Ignore 0.X, 1.X and 2.X revisions
170d60849f3SSteve Rowe          doapRevisions.add(normalizeVersion(match.groups()))
171d60849f3SSteve Rowe      else:
172d60849f3SSteve Rowe        errorMessages.append('ERROR: Failed to parse revision: %s in %s' % (revision.text, doapFile))
173d60849f3SSteve Rowe    missingDoapRevisions = set()
174d60849f3SSteve Rowe    for release in releases:
175d60849f3SSteve Rowe      if release not in doapRevisions and release < version: # Ignore releases greater than the one being produced
176d60849f3SSteve Rowe        missingDoapRevisions.add(release)
177d60849f3SSteve Rowe    if len(missingDoapRevisions) > 0:
178d60849f3SSteve Rowe      errorMessages.append('ERROR: Missing revision(s) in %s: %s' % (doapFile, ', '.join(sorted(missingDoapRevisions))))
179d60849f3SSteve Rowe  if (len(errorMessages) > 0):
180d60849f3SSteve Rowe    raise RuntimeError('\n%s\n(Hint: copy/paste from the stable branch version of the file(s).)'
181d60849f3SSteve Rowe                       % '\n'.join(errorMessages))
182d60849f3SSteve Rowe
183d60849f3SSteve Rowedef normalizeVersion(tup):
184d60849f3SSteve Rowe  suffix = ''
185d60849f3SSteve Rowe  if tup[-1] is not None and tup[-1].lower() == '-alpha':
186d60849f3SSteve Rowe    tup = tup[:(len(tup) - 1)]
187d60849f3SSteve Rowe    suffix = '-ALPHA'
188d60849f3SSteve Rowe  elif tup[-1] is not None and tup[-1].lower() == '-beta':
189d60849f3SSteve Rowe    tup = tup[:(len(tup) - 1)]
190d60849f3SSteve Rowe    suffix = '-BETA'
191d60849f3SSteve Rowe  while tup[-1] in ('', None):
192d60849f3SSteve Rowe    tup = tup[:(len(tup) - 1)]
193d60849f3SSteve Rowe  while len(tup) < 3:
194d60849f3SSteve Rowe    tup = tup + ('0',)
195d60849f3SSteve Rowe  return '.'.join(tup) + suffix
196d60849f3SSteve Rowe
197879e8250SMichael McCandlessdef pushLocal(version, root, rev, rcNum, localDir):
1980178e8e6SSteven Rowe  print('Push local [%s]...' % localDir)
199879e8250SMichael McCandless  os.makedirs(localDir)
200879e8250SMichael McCandless
201879e8250SMichael McCandless  dir = 'lucene-solr-%s-RC%d-rev%s' % (version, rcNum, rev)
202879e8250SMichael McCandless  os.makedirs('%s/%s/lucene' % (localDir, dir))
203879e8250SMichael McCandless  os.makedirs('%s/%s/solr' % (localDir, dir))
2040178e8e6SSteven Rowe  print('  Lucene')
205879e8250SMichael McCandless  os.chdir('%s/lucene/dist' % root)
2060178e8e6SSteven Rowe  print('    zip...')
207879e8250SMichael McCandless  if os.path.exists('lucene.tar.bz2'):
208879e8250SMichael McCandless    os.remove('lucene.tar.bz2')
209879e8250SMichael McCandless  run('tar cjf lucene.tar.bz2 *')
210879e8250SMichael McCandless
211879e8250SMichael McCandless  os.chdir('%s/%s/lucene' % (localDir, dir))
2120178e8e6SSteven Rowe  print('    unzip...')
213879e8250SMichael McCandless  run('tar xjf "%s/lucene/dist/lucene.tar.bz2"' % root)
214879e8250SMichael McCandless  os.remove('%s/lucene/dist/lucene.tar.bz2' % root)
215879e8250SMichael McCandless
2160178e8e6SSteven Rowe  print('  Solr')
217879e8250SMichael McCandless  os.chdir('%s/solr/package' % root)
2180178e8e6SSteven Rowe  print('    zip...')
219879e8250SMichael McCandless  if os.path.exists('solr.tar.bz2'):
220879e8250SMichael McCandless    os.remove('solr.tar.bz2')
221879e8250SMichael McCandless  run('tar cjf solr.tar.bz2 *')
2220178e8e6SSteven Rowe  print('    unzip...')
223879e8250SMichael McCandless  os.chdir('%s/%s/solr' % (localDir, dir))
224879e8250SMichael McCandless  run('tar xjf "%s/solr/package/solr.tar.bz2"' % root)
225879e8250SMichael McCandless  os.remove('%s/solr/package/solr.tar.bz2' % root)
226879e8250SMichael McCandless
2270178e8e6SSteven Rowe  print('  chmod...')
228879e8250SMichael McCandless  os.chdir('..')
229879e8250SMichael McCandless  run('chmod -R a+rX-w .')
230879e8250SMichael McCandless
2310178e8e6SSteven Rowe  print('  done!')
232879e8250SMichael McCandless  return 'file://%s/%s' % (os.path.abspath(localDir), dir)
233d6ab323dSMichael McCandless
23480e60002SRyan Ernstdef read_version(path):
23580e60002SRyan Ernst  version_props_file = os.path.join(path, 'lucene', 'version.properties')
23680e60002SRyan Ernst  return re.search(r'version\.base=(.*)', open(version_props_file).read()).group(1)
237879e8250SMichael McCandless
23880e60002SRyan Ernstdef parse_config():
23980e60002SRyan Ernst  epilogue = textwrap.dedent('''
24080e60002SRyan Ernst    Example usage for a Release Manager:
2418cb2773dSSteve Rowe    python3 -u dev-tools/scripts/buildAndPushRelease.py --push-local /tmp/releases/6.0.1 --sign 6E68DA61 --rc-num 1
24280e60002SRyan Ernst  ''')
24380e60002SRyan Ernst  description = 'Utility to build, push, and test a release.'
24480e60002SRyan Ernst  parser = argparse.ArgumentParser(description=description, epilog=epilogue,
24580e60002SRyan Ernst                                   formatter_class=argparse.RawDescriptionHelpFormatter)
24680e60002SRyan Ernst  parser.add_argument('--no-prepare', dest='prepare', default=True, action='store_false',
24780e60002SRyan Ernst                      help='Use the already built release in the provided checkout')
248982ee393SJan Høydahl  parser.add_argument('--local-keys', metavar='PATH',
249982ee393SJan Høydahl                      help='Uses local KEYS file to validate presence of RM\'s gpg key')
25080e60002SRyan Ernst  parser.add_argument('--push-local', metavar='PATH',
25180e60002SRyan Ernst                      help='Push the release to the local path')
25280e60002SRyan Ernst  parser.add_argument('--sign', metavar='KEYID',
25380e60002SRyan Ernst                      help='Sign the release with the given gpg key')
25480e60002SRyan Ernst  parser.add_argument('--rc-num', metavar='NUM', type=int, default=1,
255dbb1fc68SSteve Rowe                      help='Release Candidate number.  Default: 1')
256dbb1fc68SSteve Rowe  parser.add_argument('--root', metavar='PATH', default='.',
257dbb1fc68SSteve Rowe                      help='Root of Git working tree for lucene-solr.  Default: "." (the current directory)')
258df1775ffSJan Høydahl  parser.add_argument('--logfile', metavar='PATH',
259df1775ffSJan Høydahl                      help='Specify log file path (default /tmp/release.log)')
26080e60002SRyan Ernst  config = parser.parse_args()
261879e8250SMichael McCandless
26280e60002SRyan Ernst  if not config.prepare and config.sign:
26380e60002SRyan Ernst    parser.error('Cannot sign already built release')
26480e60002SRyan Ernst  if config.push_local is not None and os.path.exists(config.push_local):
26580e60002SRyan Ernst    parser.error('Cannot push to local path that already exists')
26680e60002SRyan Ernst  if config.rc_num <= 0:
26780e60002SRyan Ernst    parser.error('Release Candidate number must be a positive integer')
26880e60002SRyan Ernst  if not os.path.isdir(config.root):
2698cb2773dSSteve Rowe    parser.error('Root path "%s" is not a directory' % config.root)
270982ee393SJan Høydahl  if config.local_keys is not None and not os.path.exists(config.local_keys):
271982ee393SJan Høydahl    parser.error('Local KEYS file "%s" not found' % config.local_keys)
2728cb2773dSSteve Rowe  cwd = os.getcwd()
2738cb2773dSSteve Rowe  os.chdir(config.root)
2748cb2773dSSteve Rowe  config.root = os.getcwd() # Absolutize root dir
27554143f5dSSteve Rowe  if os.system('git rev-parse') or 3 != len([d for d in ('dev-tools','lucene','solr') if os.path.isdir(d)]):
2768cb2773dSSteve Rowe    parser.error('Root path "%s" is not a valid lucene-solr checkout' % config.root)
2778cb2773dSSteve Rowe  os.chdir(cwd)
278df1775ffSJan Høydahl  global LOG
279df1775ffSJan Høydahl  if config.logfile:
280df1775ffSJan Høydahl    LOG = config.logfile
281879e8250SMichael McCandless
28280e60002SRyan Ernst  config.version = read_version(config.root)
28380e60002SRyan Ernst  print('Building version: %s' % config.version)
284879e8250SMichael McCandless
28580e60002SRyan Ernst  return config
286d6ab323dSMichael McCandless
2878cb2773dSSteve Rowedef check_cmdline_tools():  # Fail fast if there are cmdline tool problems
288532d07f1SSteve Rowe  if os.system('git --version >/dev/null 2>/dev/null'):
2898cb2773dSSteve Rowe    raise RuntimeError('"git --version" returned a non-zero exit code.')
290f2e3b109SAlan Woodward  check_ant()
291f2e3b109SAlan Woodward
292982ee393SJan Høydahldef check_key_in_keys(gpgKeyID, local_keys):
293982ee393SJan Høydahl  if gpgKeyID is not None:
294982ee393SJan Høydahl    print('  Verify your gpg key is in the main KEYS file')
295982ee393SJan Høydahl    if local_keys is not None:
296982ee393SJan Høydahl      print("    Using local KEYS file %s" % local_keys)
297982ee393SJan Høydahl      keysFileText = open(local_keys, encoding='iso-8859-1').read()
298982ee393SJan Høydahl      keysFileLocation = local_keys
299982ee393SJan Høydahl    else:
300982ee393SJan Høydahl      keysFileURL = "https://archive.apache.org/dist/lucene/KEYS"
301982ee393SJan Høydahl      keysFileLocation = keysFileURL
302982ee393SJan Høydahl      print("    Using online KEYS file %s" % keysFileURL)
303982ee393SJan Høydahl      keysFileText = load(keysFileURL, encoding='iso-8859-1')
304982ee393SJan Høydahl    if len(gpgKeyID) > 2 and gpgKeyID[0:2] == '0x':
305982ee393SJan Høydahl      gpgKeyID = gpgKeyID[2:]
306982ee393SJan Høydahl    if len(gpgKeyID) > 40:
307982ee393SJan Høydahl      gpgKeyID = gpgKeyID.replace(" ", "")
308982ee393SJan Høydahl    if len(gpgKeyID) == 8:
3095b96f89dSJan Høydahl      gpgKeyID8Char = "%s %s" % (gpgKeyID[0:4], gpgKeyID[4:8])
310*daf14981SAlan Woodward      re_to_match = r"^pub .*\n\s+(\w{4} \w{4} \w{4} \w{4} \w{4}  \w{4} \w{4} \w{4} %s|\w{32}%s)" % (gpgKeyID8Char, gpgKeyID)
311982ee393SJan Høydahl    elif len(gpgKeyID) == 40:
312982ee393SJan Høydahl      gpgKeyID40Char = "%s %s %s %s %s  %s %s %s %s %s" % \
313982ee393SJan Høydahl                       (gpgKeyID[0:4], gpgKeyID[4:8], gpgKeyID[8:12], gpgKeyID[12:16], gpgKeyID[16:20],
314982ee393SJan Høydahl                       gpgKeyID[20:24], gpgKeyID[24:28], gpgKeyID[28:32], gpgKeyID[32:36], gpgKeyID[36:])
31555c06177SAlan Woodward      re_to_match = r"^pub .*\n\s+(%s|%s)" % (gpgKeyID40Char, gpgKeyID)
316982ee393SJan Høydahl    else:
31726a32d79SAlan Woodward      print('Invalid gpg key id format [%s]. Must be 8 byte short ID or 40 byte fingerprint, with or without 0x prefix, no spaces.' % gpgKeyID)
318982ee393SJan Høydahl      exit(2)
319982ee393SJan Høydahl    if re.search(re_to_match, keysFileText, re.MULTILINE):
320982ee393SJan Høydahl      print('    Found key %s in KEYS file at %s' % (gpgKeyID, keysFileLocation))
321982ee393SJan Høydahl    else:
322982ee393SJan Høydahl      print('    ERROR: Did not find your key %s in KEYS file at %s. Please add it and try again.' % (gpgKeyID, keysFileLocation))
323982ee393SJan Høydahl      if local_keys is not None:
324982ee393SJan Høydahl        print('           You are using a local KEYS file. Make sure it is up to date or validate against the online version')
325982ee393SJan Høydahl      exit(2)
326982ee393SJan Høydahl
327982ee393SJan Høydahl
32880e60002SRyan Ernstdef main():
3298cb2773dSSteve Rowe  check_cmdline_tools()
3308cb2773dSSteve Rowe
33180e60002SRyan Ernst  c = parse_config()
33280e60002SRyan Ernst
3335b96f89dSJan Høydahl  if c.sign:
3345b96f89dSJan Høydahl    sys.stdout.flush()
3355b96f89dSJan Høydahl    c.key_id = c.sign
336982ee393SJan Høydahl    check_key_in_keys(c.key_id, c.local_keys)
3375b96f89dSJan Høydahl    import getpass
3385b96f89dSJan Høydahl    c.key_password = getpass.getpass('Enter GPG keystore password: ')
3395b96f89dSJan Høydahl  else:
3405b96f89dSJan Høydahl    c.key_id = None
3415b96f89dSJan Høydahl    c.key_password = None
342982ee393SJan Høydahl
34380e60002SRyan Ernst  if c.prepare:
3449cba8eb4SUwe Schindler    rev = prepare(c.root, c.version, c.key_id, c.key_password)
345d6ab323dSMichael McCandless  else:
346b8e50f38SShalin Shekhar Mangar    os.chdir(c.root)
3470178e8e6SSteven Rowe    rev = open('rev.txt', encoding='UTF-8').read()
348d6ab323dSMichael McCandless
3498cb2773dSSteve Rowe  if c.push_local:
350753fd599SAnshum Gupta    url = pushLocal(c.version, c.root, rev, c.rc_num, c.push_local)
351879e8250SMichael McCandless  else:
3520178e8e6SSteven Rowe    url = None
353879e8250SMichael McCandless
354879e8250SMichael McCandless  if url is not None:
3550178e8e6SSteven Rowe    print('  URL: %s' % url)
3568cb2773dSSteve Rowe    print('Next run the smoker tester:')
3578cb2773dSSteve Rowe    p = re.compile(".*/")
35822a358ffSAnshum Gupta    m = p.match(sys.argv[0])
359a4de634aSSteve Rowe    print('%s -u %ssmokeTestRelease.py %s' % (sys.executable, m.group(), url))
360d6ab323dSMichael McCandless
361d6ab323dSMichael McCandlessif __name__ == '__main__':
3620178e8e6SSteven Rowe  try:
363d6ab323dSMichael McCandless    main()
36480e60002SRyan Ernst  except KeyboardInterrupt:
36580e60002SRyan Ernst    print('Keyboard interrupt...exiting')
36680e60002SRyan Ernst
367