Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Side by Side Diff: recipes.py

Issue 2839423002: [recipes.py] move common arg parsing to separate module. (Closed)
Patch Set: put back missing arg Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « recipe_engine/common_args.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2015 The LUCI Authors. All rights reserved. 2 # Copyright 2017 The LUCI Authors. All rights reserved.
3 # Use of this source code is governed under the Apache License, Version 2.0 3 # Use of this source code is governed under the Apache License, Version 2.0
4 # that can be found in the LICENSE file. 4 # that can be found in the LICENSE file.
5 5
6 """Tool to interact with recipe repositories. 6 """Tool to interact with recipe repositories.
7 7
8 This tool operates on the nearest ancestor directory containing an 8 This tool operates on the nearest ancestor directory containing an
9 infra/config/recipes.cfg. 9 infra/config/recipes.cfg.
10 """ 10 """
11 11
12 import json
13 import logging 12 import logging
14 import os 13 import os
15 import shutil 14 import shutil
16 import subprocess 15 import subprocess
17 import sys 16 import sys
18 import tempfile 17 import tempfile
19 18
20 # This is necessary to ensure that str literals are by-default assumed to hold 19 # This is necessary to ensure that str literals are by-default assumed to hold
21 # utf-8. It also makes the implicit str(unicode(...)) act like 20 # utf-8. It also makes the implicit str(unicode(...)) act like
22 # unicode(...).encode('utf-8'), rather than unicode(...).encode('ascii') . 21 # unicode(...).encode('utf-8'), rather than unicode(...).encode('ascii') .
23 reload(sys) 22 reload(sys)
24 sys.setdefaultencoding('UTF8') 23 sys.setdefaultencoding('UTF8')
25 24
26 ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) 25 ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
27 sys.path.insert(0, ROOT_DIR) 26 sys.path.insert(0, ROOT_DIR)
28 27
29 from recipe_engine import env 28 from recipe_engine import env
30 29
30 from recipe_engine import common_args
31
31 import argparse # this is vendored 32 import argparse # this is vendored
32 from recipe_engine import arguments_pb2
33 from google.protobuf import json_format as jsonpb
34
35 33
36 from recipe_engine import fetch, lint_test, bundle, depgraph, autoroll 34 from recipe_engine import fetch, lint_test, bundle, depgraph, autoroll
37 from recipe_engine import remote, refs, doc, test, run 35 from recipe_engine import remote, refs, doc, test, run
38 36
39 37
40 # Each of these subcommands has a method: 38 # Each of these subcommands has a method:
41 # 39 #
42 # def add_subparsers(argparse._SubParsersAction): ... 40 # def add_subparsers(argparse._SubParsersAction): ...
43 # 41 #
44 # which is expected to add a subparser by calling .add_parser on it. In 42 # which is expected to add a subparser by calling .add_parser on it. In
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
76 bundle, 74 bundle,
77 depgraph, 75 depgraph,
78 doc, 76 doc,
79 fetch, 77 fetch,
80 lint_test, 78 lint_test,
81 refs, 79 refs,
82 remote, 80 remote,
83 ] 81 ]
84 82
85 83
86 def add_common_args(parser):
87 from recipe_engine import package_io
88
89 class ProjectOverrideAction(argparse.Action):
90 def __call__(self, parser, namespace, values, option_string=None):
91 p = values.split('=', 2)
92 if len(p) != 2:
93 raise ValueError('Override must have the form: repo=path')
94 project_id, path = p
95
96 v = getattr(namespace, self.dest, None)
97 if v is None:
98 v = {}
99 setattr(namespace, self.dest, v)
100
101 if v.get(project_id):
102 raise ValueError('An override is already defined for [%s] (%s)' % (
103 project_id, v[project_id]))
104 path = os.path.abspath(os.path.expanduser(path))
105 if not os.path.isdir(path):
106 raise ValueError('Override path [%s] is not a directory' % (path,))
107 v[project_id] = path
108
109 def package_type(value):
110 if not os.path.isfile(value):
111 raise argparse.ArgumentTypeError(
112 'Given recipes config file %r does not exist.' % (value,))
113 return package_io.PackageFile(value)
114
115 parser.add_argument(
116 '--package',
117 type=package_type,
118 help='Path to recipes.cfg of the recipe package to operate on'
119 ', usually in infra/config/recipes.cfg')
120 parser.add_argument(
121 '--deps-path',
122 type=os.path.abspath,
123 help='Path where recipe engine dependencies will be extracted. Specify '
124 '"-" to use a temporary directory for deps, which will be cleaned '
125 'up on exit.')
126 parser.add_argument(
127 '--verbose', '-v', action='count',
128 help='Increase logging verboisty')
129 # TODO(phajdan.jr): Figure out if we need --no-fetch; remove if not.
130 parser.add_argument(
131 '--no-fetch', action='store_true',
132 help='Disable automatic fetching')
133 parser.add_argument('-O', '--project-override', metavar='ID=PATH',
134 action=ProjectOverrideAction,
135 help='Override a project repository path with a local one.')
136 parser.add_argument(
137 # Use 'None' as default so that we can recognize when none of the
138 # bootstrap options were passed.
139 '--use-bootstrap', action='store_true', default=None,
140 help='Use bootstrap/bootstrap.py to create a isolated python virtualenv'
141 ' with required python dependencies.')
142 parser.add_argument(
143 '--bootstrap-vpython-path', metavar='PATH',
144 help='Specify the `vpython` executable path to use when bootstrapping ('
145 'requires --use-bootstrap).')
146 parser.add_argument(
147 '--disable-bootstrap', action='store_false', dest='use_bootstrap',
148 help='Disables bootstrap (see --use-bootstrap)')
149
150 def operational_args_type(value):
151 with open(value) as fd:
152 return jsonpb.ParseDict(json.load(fd), arguments_pb2.Arguments())
153
154 parser.set_defaults(
155 operational_args=arguments_pb2.Arguments(),
156 bare_command=False, # don't call postprocess_func, don't use package_deps
157 postprocess_func=lambda parser, args: None,
158 )
159
160 parser.add_argument(
161 '--operational-args-path',
162 dest='operational_args',
163 type=operational_args_type,
164 help='The path to an operational Arguments file. If provided, this file '
165 'must contain a JSONPB-encoded Arguments protobuf message, and will '
166 'be integrated into the runtime parameters.')
167
168 def post_process_args(parser, args):
169 if args.bare_command:
170 # TODO(iannucci): this is gross, and only for the remote subcommand;
171 # remote doesn't behave like ANY other commands. A way to solve this will
172 # be to allow --package to take a remote repo and then simply remove the
173 # remote subcommand entirely.
174 if args.package is not None:
175 parser.error('%s forbids --package' % args.command)
176 else:
177 if not args.package:
178 parser.error('%s requires --package' % args.command)
179
180 return post_process_args
181
182
183 def main(): 84 def main():
184 parser = argparse.ArgumentParser( 85 parser = argparse.ArgumentParser(
185 description='Interact with the recipe system.') 86 description='Interact with the recipe system.')
186 87
187 common_postprocess_func = add_common_args(parser) 88 common_postprocess_func = common_args.add_common_args(parser)
188 89
189 subp = parser.add_subparsers() 90 subp = parser.add_subparsers()
190 for module in _SUBCOMMANDS: 91 for module in _SUBCOMMANDS:
191 module.add_subparser(subp) 92 module.add_subparser(subp)
192 93
193 args = parser.parse_args() 94 args = parser.parse_args()
194 common_postprocess_func(parser, args) 95 common_postprocess_func(parser, args)
195 args.postprocess_func(parser, args) 96 args.postprocess_func(parser, args)
196 97
197 # TODO(iannucci): We should always do logging.basicConfig() (probably with
198 # logging.WARNING), even if no verbose is passed. However we need to be
199 # careful as this could cause issues with spurious/unexpected output. I think
200 # it's risky enough to do in a different CL.
201
202 if args.verbose > 0:
203 logging.basicConfig()
204 logging.getLogger().setLevel(logging.INFO)
205 if args.verbose > 1:
206 logging.getLogger().setLevel(logging.DEBUG)
207
208 # If we're bootstrapping, construct our bootstrap environment. If we're 98 # If we're bootstrapping, construct our bootstrap environment. If we're
209 # using a custom deps path, install our enviornment there too. 99 # using a custom deps path, install our enviornment there too.
210 if args.use_bootstrap and not env.USING_BOOTSTRAP: 100 if args.use_bootstrap and not env.USING_BOOTSTRAP:
211 logging.debug('Bootstrapping recipe engine through vpython...') 101 logging.debug('Bootstrapping recipe engine through vpython...')
212 102
213 bootstrap_env = os.environ.copy() 103 bootstrap_env = os.environ.copy()
214 bootstrap_env[env.BOOTSTRAP_ENV_KEY] = '1' 104 bootstrap_env[env.BOOTSTRAP_ENV_KEY] = '1'
215 105
216 cmd = [ 106 cmd = [
217 sys.executable, 107 sys.executable,
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
273 163
274 if not isinstance(ret, int): 164 if not isinstance(ret, int):
275 if ret is None: 165 if ret is None:
276 ret = 0 166 ret = 0
277 else: 167 else:
278 print >> sys.stderr, ret 168 print >> sys.stderr, ret
279 ret = 1 169 ret = 1
280 sys.stdout.flush() 170 sys.stdout.flush()
281 sys.stderr.flush() 171 sys.stderr.flush()
282 os._exit(ret) 172 os._exit(ret)
OLDNEW
« no previous file with comments | « recipe_engine/common_args.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698