11ed2ae45f59c2864ea05838b4da2750b85472824borenet#!/usr/bin/env python 2f171e1625bd3ea46c2980c97143168598d99987dEric Boren 31ed2ae45f59c2864ea05838b4da2750b85472824borenet# Copyright 2016 The LUCI Authors. All rights reserved. 41ed2ae45f59c2864ea05838b4da2750b85472824borenet# Use of this source code is governed under the Apache License, Version 2.0 51ed2ae45f59c2864ea05838b4da2750b85472824borenet# that can be found in the LICENSE file. 6f171e1625bd3ea46c2980c97143168598d99987dEric Boren 71ed2ae45f59c2864ea05838b4da2750b85472824borenet"""Bootstrap script to clone and forward to the recipe engine tool. 8f171e1625bd3ea46c2980c97143168598d99987dEric Boren 91ed2ae45f59c2864ea05838b4da2750b85472824borenet*********************************************************************** 101ed2ae45f59c2864ea05838b4da2750b85472824borenet** DO NOT MODIFY EXCEPT IN THE PER-REPO CONFIGURATION SECTION BELOW. ** 111ed2ae45f59c2864ea05838b4da2750b85472824borenet*********************************************************************** 12f171e1625bd3ea46c2980c97143168598d99987dEric Boren 131ed2ae45f59c2864ea05838b4da2750b85472824borenetThis is a copy of https://github.com/luci/recipes-py/blob/master/doc/recipes.py. 141ed2ae45f59c2864ea05838b4da2750b85472824borenetTo fix bugs, fix in the github repo then copy it back to here and fix the 151ed2ae45f59c2864ea05838b4da2750b85472824borenetPER-REPO CONFIGURATION section to look like this one. 161ed2ae45f59c2864ea05838b4da2750b85472824borenet""" 17f171e1625bd3ea46c2980c97143168598d99987dEric Boren 181ed2ae45f59c2864ea05838b4da2750b85472824borenetimport os 191ed2ae45f59c2864ea05838b4da2750b85472824borenet 20342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci# IMPORTANT: Do not alter the header or footer line for the 21342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci# "PER-REPO CONFIGURATION" section below, or the autoroller will not be able 22342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci# to automatically update this file! All lines between the header and footer 23342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci# lines will be retained verbatim by the autoroller. 24342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci 25f171e1625bd3ea46c2980c97143168598d99987dEric Boren#### PER-REPO CONFIGURATION (editable) #### 261ed2ae45f59c2864ea05838b4da2750b85472824borenet# The root of the repository relative to the directory of this file. 271ed2ae45f59c2864ea05838b4da2750b85472824borenetREPO_ROOT = os.path.join(os.pardir, os.pardir) 281ed2ae45f59c2864ea05838b4da2750b85472824borenet# The path of the recipes.cfg file relative to the root of the repository. 291ed2ae45f59c2864ea05838b4da2750b85472824borenetRECIPES_CFG = os.path.join('infra', 'config', 'recipes.cfg') 301ed2ae45f59c2864ea05838b4da2750b85472824borenet#### END PER-REPO CONFIGURATION #### 31f171e1625bd3ea46c2980c97143168598d99987dEric Boren 321ed2ae45f59c2864ea05838b4da2750b85472824borenetBOOTSTRAP_VERSION = 1 33f171e1625bd3ea46c2980c97143168598d99987dEric Boren 34e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucciimport argparse 351ed2ae45f59c2864ea05838b4da2750b85472824borenetimport ast 362ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucciimport json 371ed2ae45f59c2864ea05838b4da2750b85472824borenetimport logging 381ed2ae45f59c2864ea05838b4da2750b85472824borenetimport random 391ed2ae45f59c2864ea05838b4da2750b85472824borenetimport re 401ed2ae45f59c2864ea05838b4da2750b85472824borenetimport subprocess 411ed2ae45f59c2864ea05838b4da2750b85472824borenetimport sys 421ed2ae45f59c2864ea05838b4da2750b85472824borenetimport time 431ed2ae45f59c2864ea05838b4da2750b85472824borenetimport traceback 44342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucciimport urlparse 45f171e1625bd3ea46c2980c97143168598d99987dEric Boren 462ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannuccifrom cStringIO import StringIO 47f171e1625bd3ea46c2980c97143168598d99987dEric Boren 482ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci 492ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannuccidef parse(repo_root, recipes_cfg_path): 502ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci """Parse is transitional code which parses a recipes.cfg file as either jsonpb 512ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci or as textpb. 522ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci 532ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci Args: 542ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci repo_root (str) - native path to the root of the repo we're trying to run 552ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci recipes for. 562ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci recipes_cfg_path (str) - native path to the recipes.cfg file to process. 572ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci 582ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci Returns (as tuple): 592ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_url (str) - the url to the engine repo we want to use. 602ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_revision (str) - the git revision for the engine to get. 612ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_subpath (str) - the subdirectory in the engine repo we should use to 622ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci find it's recipes.py entrypoint. This is here for completeness, but will 632ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci essentially always be empty. It would be used if the recipes-py repo was 642ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci merged as a subdirectory of some other repo and you depended on that 652ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci subdirectory. 662ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci recipes_path (str) - native path to where the recipes live inside of the 672ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci current repo (i.e. the folder containing `recipes/` and/or 682ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci `recipe_modules`) 692ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci """ 702ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci with open(recipes_cfg_path, 'rU') as fh: 712ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci data = fh.read() 722ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci 732ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci if data.lstrip().startswith('{'): 742ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci pb = json.loads(data) 752ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine = next( 762ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci (d for d in pb['deps'] if d['project_id'] == 'recipe_engine'), None) 772ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci if engine is None: 782ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci raise ValueError('could not find recipe_engine dep in %r' 792ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci % recipes_cfg_path) 802ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_url = engine['url'] 81342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci engine_revision = engine.get('revision', '') 822ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_subpath = engine.get('path_override', '') 832ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci recipes_path = pb.get('recipes_path', '') 842ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci else: 852ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci def get_unique(things): 862ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci if len(things) == 1: 872ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci return things[0] 882ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci elif len(things) == 0: 892ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci raise ValueError("Expected to get one thing, but dinna get none.") 902ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci else: 912ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci logging.warn('Expected to get one thing, but got a bunch: %s\n%s' % 922ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci (things, traceback.format_stack())) 932ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci return things[0] 942ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci 952ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci protobuf = parse_textpb(StringIO(data)) 962ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci 972ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_buf = get_unique([ 982ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci b for b in protobuf.get('deps', []) 992ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci if b.get('project_id') == ['recipe_engine'] ]) 1002ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_url = get_unique(engine_buf['url']) 101342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci engine_revision = get_unique(engine_buf.get('revision', [''])) 1022ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_subpath = (get_unique(engine_buf.get('path_override', [''])) 1032ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci .replace('/', os.path.sep)) 1042ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci recipes_path = get_unique(protobuf.get('recipes_path', [''])) 1052ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci 1062ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci recipes_path = os.path.join(repo_root, recipes_path.replace('/', os.path.sep)) 1072ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci return engine_url, engine_revision, engine_subpath, recipes_path 1082ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci 1092ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci 1102ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannuccidef parse_textpb(fh): 1111ed2ae45f59c2864ea05838b4da2750b85472824borenet """Parse the protobuf text format just well enough to understand recipes.cfg. 112f171e1625bd3ea46c2980c97143168598d99987dEric Boren 1131ed2ae45f59c2864ea05838b4da2750b85472824borenet We don't use the protobuf library because we want to be as self-contained 1141ed2ae45f59c2864ea05838b4da2750b85472824borenet as possible in this bootstrap, so it can be simply vendored into a client 1151ed2ae45f59c2864ea05838b4da2750b85472824borenet repo. 116f171e1625bd3ea46c2980c97143168598d99987dEric Boren 1171ed2ae45f59c2864ea05838b4da2750b85472824borenet We assume all fields are repeated since we don't have a proto spec to work 1181ed2ae45f59c2864ea05838b4da2750b85472824borenet with. 119f171e1625bd3ea46c2980c97143168598d99987dEric Boren 1201ed2ae45f59c2864ea05838b4da2750b85472824borenet Args: 1211ed2ae45f59c2864ea05838b4da2750b85472824borenet fh: a filehandle containing the text format protobuf. 1221ed2ae45f59c2864ea05838b4da2750b85472824borenet Returns: 1231ed2ae45f59c2864ea05838b4da2750b85472824borenet A recursive dictionary of lists. 1241ed2ae45f59c2864ea05838b4da2750b85472824borenet """ 125f171e1625bd3ea46c2980c97143168598d99987dEric Boren def parse_atom(field, text): 1261ed2ae45f59c2864ea05838b4da2750b85472824borenet if text == 'true': 1271ed2ae45f59c2864ea05838b4da2750b85472824borenet return True 1281ed2ae45f59c2864ea05838b4da2750b85472824borenet if text == 'false': 1291ed2ae45f59c2864ea05838b4da2750b85472824borenet return False 130f171e1625bd3ea46c2980c97143168598d99987dEric Boren 131f171e1625bd3ea46c2980c97143168598d99987dEric Boren # repo_type is an enum. Since it does not have quotes, 132f171e1625bd3ea46c2980c97143168598d99987dEric Boren # invoking literal_eval would fail. 133f171e1625bd3ea46c2980c97143168598d99987dEric Boren if field == 'repo_type': 134f171e1625bd3ea46c2980c97143168598d99987dEric Boren return text 135f171e1625bd3ea46c2980c97143168598d99987dEric Boren 1361ed2ae45f59c2864ea05838b4da2750b85472824borenet return ast.literal_eval(text) 137f171e1625bd3ea46c2980c97143168598d99987dEric Boren 1381ed2ae45f59c2864ea05838b4da2750b85472824borenet ret = {} 1391ed2ae45f59c2864ea05838b4da2750b85472824borenet for line in fh: 1401ed2ae45f59c2864ea05838b4da2750b85472824borenet line = line.strip() 1411ed2ae45f59c2864ea05838b4da2750b85472824borenet m = re.match(r'(\w+)\s*:\s*(.*)', line) 1421ed2ae45f59c2864ea05838b4da2750b85472824borenet if m: 143f171e1625bd3ea46c2980c97143168598d99987dEric Boren ret.setdefault(m.group(1), []).append(parse_atom(m.group(1), m.group(2))) 1441ed2ae45f59c2864ea05838b4da2750b85472824borenet continue 145f171e1625bd3ea46c2980c97143168598d99987dEric Boren 1461ed2ae45f59c2864ea05838b4da2750b85472824borenet m = re.match(r'(\w+)\s*{', line) 1471ed2ae45f59c2864ea05838b4da2750b85472824borenet if m: 1482ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci subparse = parse_textpb(fh) 1491ed2ae45f59c2864ea05838b4da2750b85472824borenet ret.setdefault(m.group(1), []).append(subparse) 1501ed2ae45f59c2864ea05838b4da2750b85472824borenet continue 151f171e1625bd3ea46c2980c97143168598d99987dEric Boren 1521ed2ae45f59c2864ea05838b4da2750b85472824borenet if line == '}': 1531ed2ae45f59c2864ea05838b4da2750b85472824borenet return ret 1541ed2ae45f59c2864ea05838b4da2750b85472824borenet if line == '': 1551ed2ae45f59c2864ea05838b4da2750b85472824borenet continue 156f171e1625bd3ea46c2980c97143168598d99987dEric Boren 1571ed2ae45f59c2864ea05838b4da2750b85472824borenet raise ValueError('Could not understand line: <%s>' % line) 158f171e1625bd3ea46c2980c97143168598d99987dEric Boren 1591ed2ae45f59c2864ea05838b4da2750b85472824borenet return ret 160f171e1625bd3ea46c2980c97143168598d99987dEric Boren 161f171e1625bd3ea46c2980c97143168598d99987dEric Boren 1621ed2ae45f59c2864ea05838b4da2750b85472824borenetdef _subprocess_call(argv, **kwargs): 1631ed2ae45f59c2864ea05838b4da2750b85472824borenet logging.info('Running %r', argv) 1641ed2ae45f59c2864ea05838b4da2750b85472824borenet return subprocess.call(argv, **kwargs) 165f171e1625bd3ea46c2980c97143168598d99987dEric Boren 166e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci 1671ed2ae45f59c2864ea05838b4da2750b85472824borenetdef _subprocess_check_call(argv, **kwargs): 1681ed2ae45f59c2864ea05838b4da2750b85472824borenet logging.info('Running %r', argv) 1691ed2ae45f59c2864ea05838b4da2750b85472824borenet subprocess.check_call(argv, **kwargs) 170f171e1625bd3ea46c2980c97143168598d99987dEric Boren 171f171e1625bd3ea46c2980c97143168598d99987dEric Boren 172e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannuccidef find_engine_override(argv): 173e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci """Since the bootstrap process attempts to defer all logic to the recipes-py 174e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci repo, we need to be aware if the user is overriding the recipe_engine 175e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci dependency. This looks for and returns the overridden recipe_engine path, if 176e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci any, or None if the user didn't override it.""" 177e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci PREFIX = 'recipe_engine=' 178e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci 179e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci p = argparse.ArgumentParser() 180e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci p.add_argument('-O', '--project-override', action='append') 181e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci args, _ = p.parse_known_args(argv) 182e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci for override in args.project_override or (): 183e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci if override.startswith(PREFIX): 184e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci return override[len(PREFIX):] 185e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci return None 186e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci 187e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci 1881ed2ae45f59c2864ea05838b4da2750b85472824borenetdef main(): 1891ed2ae45f59c2864ea05838b4da2750b85472824borenet if '--verbose' in sys.argv: 1901ed2ae45f59c2864ea05838b4da2750b85472824borenet logging.getLogger().setLevel(logging.INFO) 191f171e1625bd3ea46c2980c97143168598d99987dEric Boren 192e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci if REPO_ROOT is None or RECIPES_CFG is None: 193e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci logging.error( 194e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci 'In order to use this script, please copy it to your repo and ' 195e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci 'replace the REPO_ROOT and RECIPES_CFG values with approprite paths.') 196e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci sys.exit(1) 197e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci 1981ed2ae45f59c2864ea05838b4da2750b85472824borenet if sys.platform.startswith(('win', 'cygwin')): 1991ed2ae45f59c2864ea05838b4da2750b85472824borenet git = 'git.bat' 2001ed2ae45f59c2864ea05838b4da2750b85472824borenet else: 2011ed2ae45f59c2864ea05838b4da2750b85472824borenet git = 'git' 202f171e1625bd3ea46c2980c97143168598d99987dEric Boren 2031ed2ae45f59c2864ea05838b4da2750b85472824borenet # Find the repository and config file to operate on. 2041ed2ae45f59c2864ea05838b4da2750b85472824borenet repo_root = os.path.abspath( 2051ed2ae45f59c2864ea05838b4da2750b85472824borenet os.path.join(os.path.dirname(__file__), REPO_ROOT)) 2061ed2ae45f59c2864ea05838b4da2750b85472824borenet recipes_cfg_path = os.path.join(repo_root, RECIPES_CFG) 207f171e1625bd3ea46c2980c97143168598d99987dEric Boren 2082ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_url, engine_revision, engine_subpath, recipes_path = parse( 2092ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci repo_root, recipes_cfg_path) 2102ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci 211e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci engine_path = find_engine_override(sys.argv[1:]) 212342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci if not engine_path and engine_url.startswith('file://'): 213342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci engine_path = urlparse.urlparse(engine_url).path 214342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci 215e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci if not engine_path: 216342977ced701d06df2b3d2eedd8b64aeae1eb5c5Robert Iannucci deps_path = os.path.join(recipes_path, '.recipe_deps') 217e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci # Ensure that we have the recipe engine cloned. 2182ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_root_path = os.path.join(deps_path, 'recipe_engine') 2192ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci engine_path = os.path.join(engine_root_path, engine_subpath) 220e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci def ensure_engine(): 221e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci if not os.path.exists(deps_path): 222e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci os.makedirs(deps_path) 2232ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci if not os.path.exists(engine_root_path): 2242ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci _subprocess_check_call([git, 'clone', engine_url, engine_root_path]) 225e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci 226e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci needs_fetch = _subprocess_call( 227e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci [git, 'rev-parse', '--verify', '%s^{commit}' % engine_revision], 2282ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci cwd=engine_root_path, stdout=open(os.devnull, 'w')) 229e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci if needs_fetch: 2302ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci _subprocess_check_call([git, 'fetch'], cwd=engine_root_path) 231e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci _subprocess_check_call( 2322ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci [git, 'checkout', '--quiet', engine_revision], cwd=engine_root_path) 233e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci 234e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci try: 235e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci ensure_engine() 236e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci except subprocess.CalledProcessError: 237e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci logging.exception('ensure_engine failed') 238e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci 239e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci # Retry errors. 240e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci time.sleep(random.uniform(2,5)) 241e8b508556cdd1b18b7461301b35e8a20d3fe35e2Robert Iannucci ensure_engine() 242f171e1625bd3ea46c2980c97143168598d99987dEric Boren 243f171e1625bd3ea46c2980c97143168598d99987dEric Boren args = ['--package', recipes_cfg_path] + sys.argv[1:] 2441ed2ae45f59c2864ea05838b4da2750b85472824borenet return _subprocess_call([ 2451ed2ae45f59c2864ea05838b4da2750b85472824borenet sys.executable, '-u', 2462ba659e85ffeeb392f0dfd94dd5ee3c7d89787d6Robert Iannucci os.path.join(engine_path, 'recipes.py')] + args) 247f171e1625bd3ea46c2980c97143168598d99987dEric Boren 2481ed2ae45f59c2864ea05838b4da2750b85472824borenetif __name__ == '__main__': 2491ed2ae45f59c2864ea05838b4da2750b85472824borenet sys.exit(main()) 250