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