OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 | 2 |
3 # Copyright 2016 The LUCI Authors. All rights reserved. | 3 # Copyright 2016 The LUCI Authors. All rights reserved. |
4 # Use of this source code is governed under the Apache License, Version 2.0 | 4 # Use of this source code is governed under the Apache License, Version 2.0 |
5 # that can be found in the LICENSE file. | 5 # that can be found in the LICENSE file. |
6 | 6 |
7 """Bootstrap script to clone and forward to the recipe engine tool. | 7 """Bootstrap script to clone and forward to the recipe engine tool. |
8 | 8 |
9 *********************************************************************** | 9 *********************************************************************** |
10 ** DO NOT MODIFY EXCEPT IN THE PER-REPO CONFIGURATION SECTION BELOW. ** | 10 ** DO NOT MODIFY EXCEPT IN THE PER-REPO CONFIGURATION SECTION BELOW. ** |
11 *********************************************************************** | 11 *********************************************************************** |
12 | 12 |
13 This is a copy of https://github.com/luci/recipes-py/blob/master/doc/recipes.py. | 13 This is a copy of https://github.com/luci/recipes-py/blob/master/doc/recipes.py. |
14 To fix bugs, fix in the github repo then copy it back to here and fix the | 14 To fix bugs, fix in the github repo then copy it back to here and fix the |
15 PER-REPO CONFIGURATION section to look like this one. | 15 PER-REPO CONFIGURATION section to look like this one. |
16 """ | 16 """ |
17 | 17 |
18 import os | 18 import os |
19 | 19 |
20 # IMPORTANT: Do not alter the header or footer line for the | |
21 # "PER-REPO CONFIGURATION" section below, or the autoroller will not be able | |
22 # to automatically update this file! All lines between the header and footer | |
23 # lines will be retained verbatim by the autoroller. | |
24 | |
20 #### PER-REPO CONFIGURATION (editable) #### | 25 #### PER-REPO CONFIGURATION (editable) #### |
21 # The root of the repository relative to the directory of this file. | 26 # The root of the repository relative to the directory of this file. |
22 REPO_ROOT = None # os.path.join(os.pardir, os.pardir) | 27 REPO_ROOT = None # os.path.join(os.pardir, os.pardir) |
23 # The path of the recipes.cfg file relative to the root of the repository. | 28 # The path of the recipes.cfg file relative to the root of the repository. |
24 RECIPES_CFG = None # os.path.join('infra', 'config', 'recipes.cfg') | 29 RECIPES_CFG = None # os.path.join('infra', 'config', 'recipes.cfg') |
25 #### END PER-REPO CONFIGURATION #### | 30 #### END PER-REPO CONFIGURATION #### |
26 | 31 |
27 BOOTSTRAP_VERSION = 1 | 32 BOOTSTRAP_VERSION = 1 |
28 | 33 |
29 import argparse | 34 import argparse |
30 import ast | 35 import ast |
31 import json | 36 import json |
32 import logging | 37 import logging |
33 import random | 38 import random |
34 import re | 39 import re |
35 import subprocess | 40 import subprocess |
36 import sys | 41 import sys |
37 import time | 42 import time |
38 import traceback | 43 import traceback |
44 import urlparse | |
39 | 45 |
40 from cStringIO import StringIO | 46 from cStringIO import StringIO |
41 | 47 |
42 | 48 |
43 def parse(repo_root, recipes_cfg_path): | 49 def parse(repo_root, recipes_cfg_path): |
44 """Parse is transitional code which parses a recipes.cfg file as either jsonpb | 50 """Parse is transitional code which parses a recipes.cfg file as either jsonpb |
45 or as textpb. | 51 or as textpb. |
46 | 52 |
47 Args: | 53 Args: |
48 repo_root (str) - native path to the root of the repo we're trying to run | 54 repo_root (str) - native path to the root of the repo we're trying to run |
(...skipping 16 matching lines...) Expand all Loading... | |
65 data = fh.read() | 71 data = fh.read() |
66 | 72 |
67 if data.lstrip().startswith('{'): | 73 if data.lstrip().startswith('{'): |
68 pb = json.loads(data) | 74 pb = json.loads(data) |
69 engine = next( | 75 engine = next( |
70 (d for d in pb['deps'] if d['project_id'] == 'recipe_engine'), None) | 76 (d for d in pb['deps'] if d['project_id'] == 'recipe_engine'), None) |
71 if engine is None: | 77 if engine is None: |
72 raise ValueError('could not find recipe_engine dep in %r' | 78 raise ValueError('could not find recipe_engine dep in %r' |
73 % recipes_cfg_path) | 79 % recipes_cfg_path) |
74 engine_url = engine['url'] | 80 engine_url = engine['url'] |
75 engine_revision = engine['revision'] | 81 engine_revision = engine.get('revision', '') |
iannucci
2017/03/16 05:19:55
these changes are needed because the autoroller te
| |
76 engine_subpath = engine.get('path_override', '') | 82 engine_subpath = engine.get('path_override', '') |
77 recipes_path = pb.get('recipes_path', '') | 83 recipes_path = pb.get('recipes_path', '') |
78 else: | 84 else: |
79 def get_unique(things): | 85 def get_unique(things): |
80 if len(things) == 1: | 86 if len(things) == 1: |
81 return things[0] | 87 return things[0] |
82 elif len(things) == 0: | 88 elif len(things) == 0: |
83 raise ValueError("Expected to get one thing, but dinna get none.") | 89 raise ValueError("Expected to get one thing, but dinna get none.") |
84 else: | 90 else: |
85 logging.warn('Expected to get one thing, but got a bunch: %s\n%s' % | 91 logging.warn('Expected to get one thing, but got a bunch: %s\n%s' % |
86 (things, traceback.format_stack())) | 92 (things, traceback.format_stack())) |
87 return things[0] | 93 return things[0] |
88 | 94 |
89 protobuf = parse_textpb(StringIO(data)) | 95 protobuf = parse_textpb(StringIO(data)) |
90 | 96 |
91 engine_buf = get_unique([ | 97 engine_buf = get_unique([ |
92 b for b in protobuf.get('deps', []) | 98 b for b in protobuf.get('deps', []) |
93 if b.get('project_id') == ['recipe_engine'] ]) | 99 if b.get('project_id') == ['recipe_engine'] ]) |
94 engine_url = get_unique(engine_buf['url']) | 100 engine_url = get_unique(engine_buf['url']) |
95 engine_revision = get_unique(engine_buf['revision']) | 101 engine_revision = get_unique(engine_buf.get('revision', [''])) |
96 engine_subpath = (get_unique(engine_buf.get('path_override', [''])) | 102 engine_subpath = (get_unique(engine_buf.get('path_override', [''])) |
97 .replace('/', os.path.sep)) | 103 .replace('/', os.path.sep)) |
98 recipes_path = get_unique(protobuf.get('recipes_path', [''])) | 104 recipes_path = get_unique(protobuf.get('recipes_path', [''])) |
99 | 105 |
100 recipes_path = os.path.join(repo_root, recipes_path.replace('/', os.path.sep)) | 106 recipes_path = os.path.join(repo_root, recipes_path.replace('/', os.path.sep)) |
101 return engine_url, engine_revision, engine_subpath, recipes_path | 107 return engine_url, engine_revision, engine_subpath, recipes_path |
102 | 108 |
103 | 109 |
104 def parse_textpb(fh): | 110 def parse_textpb(fh): |
105 """Parse the protobuf text format just well enough to understand recipes.cfg. | 111 """Parse the protobuf text format just well enough to understand recipes.cfg. |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
195 git = 'git' | 201 git = 'git' |
196 | 202 |
197 # Find the repository and config file to operate on. | 203 # Find the repository and config file to operate on. |
198 repo_root = os.path.abspath( | 204 repo_root = os.path.abspath( |
199 os.path.join(os.path.dirname(__file__), REPO_ROOT)) | 205 os.path.join(os.path.dirname(__file__), REPO_ROOT)) |
200 recipes_cfg_path = os.path.join(repo_root, RECIPES_CFG) | 206 recipes_cfg_path = os.path.join(repo_root, RECIPES_CFG) |
201 | 207 |
202 engine_url, engine_revision, engine_subpath, recipes_path = parse( | 208 engine_url, engine_revision, engine_subpath, recipes_path = parse( |
203 repo_root, recipes_cfg_path) | 209 repo_root, recipes_cfg_path) |
204 | 210 |
205 deps_path = os.path.join(recipes_path, '.recipe_deps') | |
206 engine_path = find_engine_override(sys.argv[1:]) | 211 engine_path = find_engine_override(sys.argv[1:]) |
212 if not engine_path and engine_url.startswith('file://'): | |
iannucci
2017/03/16 05:19:55
this recipes.py file now works correctly with file
| |
213 engine_path = urlparse.urlparse(engine_url).path | |
214 | |
207 if not engine_path: | 215 if not engine_path: |
216 deps_path = os.path.join(recipes_path, '.recipe_deps') | |
208 # Ensure that we have the recipe engine cloned. | 217 # Ensure that we have the recipe engine cloned. |
209 engine_root_path = os.path.join(deps_path, 'recipe_engine') | 218 engine_root_path = os.path.join(deps_path, 'recipe_engine') |
210 engine_path = os.path.join(engine_root_path, engine_subpath) | 219 engine_path = os.path.join(engine_root_path, engine_subpath) |
211 def ensure_engine(): | 220 def ensure_engine(): |
212 if not os.path.exists(deps_path): | 221 if not os.path.exists(deps_path): |
213 os.makedirs(deps_path) | 222 os.makedirs(deps_path) |
214 if not os.path.exists(engine_root_path): | 223 if not os.path.exists(engine_root_path): |
215 _subprocess_check_call([git, 'clone', engine_url, engine_root_path]) | 224 _subprocess_check_call([git, 'clone', engine_url, engine_root_path]) |
216 | 225 |
217 needs_fetch = _subprocess_call( | 226 needs_fetch = _subprocess_call( |
(...skipping 13 matching lines...) Expand all Loading... | |
231 time.sleep(random.uniform(2,5)) | 240 time.sleep(random.uniform(2,5)) |
232 ensure_engine() | 241 ensure_engine() |
233 | 242 |
234 args = ['--package', recipes_cfg_path] + sys.argv[1:] | 243 args = ['--package', recipes_cfg_path] + sys.argv[1:] |
235 return _subprocess_call([ | 244 return _subprocess_call([ |
236 sys.executable, '-u', | 245 sys.executable, '-u', |
237 os.path.join(engine_path, 'recipes.py')] + args) | 246 os.path.join(engine_path, 'recipes.py')] + args) |
238 | 247 |
239 if __name__ == '__main__': | 248 if __name__ == '__main__': |
240 sys.exit(main()) | 249 sys.exit(main()) |
OLD | NEW |