Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2015 The LUCI Authors. All rights reserved. | 2 # Copyright 2015 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 """ |
| (...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 213 step_runner.SubprocessStepRunner(stream_engine, engine_flags), | 213 step_runner.SubprocessStepRunner(stream_engine, engine_flags), |
| 214 universe_view, engine_flags=engine_flags, | 214 universe_view, engine_flags=engine_flags, |
| 215 emit_initial_properties=emit_initial_properties) | 215 emit_initial_properties=emit_initial_properties) |
| 216 finally: | 216 finally: |
| 217 os.chdir(old_cwd) | 217 os.chdir(old_cwd) |
| 218 | 218 |
| 219 return handle_recipe_return( | 219 return handle_recipe_return( |
| 220 ret, args.output_result_json, stream_engine, engine_flags) | 220 ret, args.output_result_json, stream_engine, engine_flags) |
| 221 | 221 |
| 222 | 222 |
| 223 def remote(args): | |
| 224 from recipe_engine import remote | |
| 225 | |
| 226 return remote.main(args) | |
| 227 | |
| 228 | |
| 229 class ProjectOverrideAction(argparse.Action): | 223 class ProjectOverrideAction(argparse.Action): |
| 230 def __call__(self, parser, namespace, values, option_string=None): | 224 def __call__(self, parser, namespace, values, option_string=None): |
| 231 p = values.split('=', 2) | 225 p = values.split('=', 2) |
| 232 if len(p) != 2: | 226 if len(p) != 2: |
| 233 raise ValueError('Override must have the form: repo=path') | 227 raise ValueError('Override must have the form: repo=path') |
| 234 project_id, path = p | 228 project_id, path = p |
| 235 | 229 |
| 236 v = getattr(namespace, self.dest, None) | 230 v = getattr(namespace, self.dest, None) |
| 237 if v is None: | 231 if v is None: |
| 238 v = {} | 232 v = {} |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 344 help='Use bootstrap/bootstrap.py to create a isolated python virtualenv' | 338 help='Use bootstrap/bootstrap.py to create a isolated python virtualenv' |
| 345 ' with required python dependencies.') | 339 ' with required python dependencies.') |
| 346 parser.add_argument( | 340 parser.add_argument( |
| 347 '--disable-bootstrap', action='store_false', dest='use_bootstrap', | 341 '--disable-bootstrap', action='store_false', dest='use_bootstrap', |
| 348 help='Disables bootstrap (see --use-bootstrap)') | 342 help='Disables bootstrap (see --use-bootstrap)') |
| 349 | 343 |
| 350 def operational_args_type(value): | 344 def operational_args_type(value): |
| 351 with open(value) as fd: | 345 with open(value) as fd: |
| 352 return jsonpb.ParseDict(json.load(fd), arguments_pb2.Arguments()) | 346 return jsonpb.ParseDict(json.load(fd), arguments_pb2.Arguments()) |
| 353 | 347 |
| 354 parser.set_defaults(operational_args=arguments_pb2.Arguments()) | 348 parser.set_defaults( |
| 349 operational_args=arguments_pb2.Arguments(), | |
| 350 bare_command=False, # don't call postprocess_func, don't use package_deps | |
|
dnj
2017/04/27 16:38:40
add a TODO here, remove this when "remote" command
| |
| 351 ) | |
| 355 | 352 |
| 356 parser.add_argument( | 353 parser.add_argument( |
| 357 '--operational-args-path', | 354 '--operational-args-path', |
| 358 dest='operational_args', | 355 dest='operational_args', |
| 359 type=operational_args_type, | 356 type=operational_args_type, |
| 360 help='The path to an operational Arguments file. If provided, this file ' | 357 help='The path to an operational Arguments file. If provided, this file ' |
| 361 'must contain a JSONPB-encoded Arguments protobuf message, and will ' | 358 'must contain a JSONPB-encoded Arguments protobuf message, and will ' |
| 362 'be integrated into the runtime parameters.') | 359 'be integrated into the runtime parameters.') |
| 363 | 360 |
| 364 def post_process_args(parser, args): | 361 def post_process_args(parser, args): |
| 365 if args.command == 'remote': | 362 if args.bare_command: |
| 366 # TODO(iannucci): this is a hack; remote doesn't behave like ANY other | 363 # TODO(iannucci): this is gross, and only for the remote subcommand; |
| 367 # commands. A way to solve this will be to allow --package to take | 364 # remote doesn't behave like ANY other commands. A way to solve this will |
| 368 # a remote repo and then simply remove the remote subcommand entirely. | 365 # be to allow --package to take a remote repo and then simply remove the |
| 369 return | 366 # remote subcommand entirely. |
| 370 | 367 if args.package is not None: |
| 371 if not args.package: | 368 parser.error('%s forbids --package' % args.command) |
| 372 parser.error('%s requires --package' % args.command) | 369 else: |
| 370 if not args.package: | |
| 371 parser.error('%s requires --package' % args.command) | |
| 373 | 372 |
| 374 return post_process_args | 373 return post_process_args |
| 375 | 374 |
| 376 | 375 |
| 377 def main(): | 376 def main(): |
| 378 parser = argparse.ArgumentParser( | 377 parser = argparse.ArgumentParser( |
| 379 description='Interact with the recipe system.') | 378 description='Interact with the recipe system.') |
| 380 | 379 |
| 381 common_postprocess_func = add_common_args(parser) | 380 common_postprocess_func = add_common_args(parser) |
| 382 | 381 |
| 383 from recipe_engine import fetch, lint_test, bundle, depgraph, autoroll | 382 from recipe_engine import fetch, lint_test, bundle, depgraph, autoroll |
| 384 to_add = [fetch, lint_test, bundle, depgraph, autoroll] | 383 from recipe_engine import remote |
|
dnj
2017/04/27 16:38:40
Since you're adding a lot of optional flags, can w
| |
| 384 to_add = [fetch, lint_test, bundle, depgraph, autoroll, remote] | |
| 385 | 385 |
| 386 subp = parser.add_subparsers() | 386 subp = parser.add_subparsers() |
| 387 for module in to_add: | 387 for module in to_add: |
| 388 module.add_subparser(subp) | 388 module.add_subparser(subp) |
| 389 | 389 |
| 390 test_p = subp.add_parser( | 390 test_p = subp.add_parser( |
| 391 'test', | 391 'test', |
| 392 description='Generate or check expectations by simulation') | 392 description='Generate or check expectations by simulation') |
| 393 test_p.set_defaults(command='test') | 393 test_p.set_defaults(command='test') |
| 394 test_p.add_argument('args', nargs=argparse.REMAINDER) | 394 test_p.add_argument('args', nargs=argparse.REMAINDER) |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 457 'recipe', | 457 'recipe', |
| 458 help='The recipe to execute') | 458 help='The recipe to execute') |
| 459 run_p.add_argument( | 459 run_p.add_argument( |
| 460 'props', | 460 'props', |
| 461 nargs=argparse.REMAINDER, | 461 nargs=argparse.REMAINDER, |
| 462 type=parse_prop, | 462 type=parse_prop, |
| 463 help='A list of property pairs; e.g. mastername=chromium.linux ' | 463 help='A list of property pairs; e.g. mastername=chromium.linux ' |
| 464 'issue=12345. The property value will be decoded as JSON, but if ' | 464 'issue=12345. The property value will be decoded as JSON, but if ' |
| 465 'this decoding fails the value will be interpreted as a string.') | 465 'this decoding fails the value will be interpreted as a string.') |
| 466 | 466 |
| 467 | |
| 468 remote_p = subp.add_parser( | |
| 469 'remote', | |
| 470 description='Invoke a recipe command from specified repo and revision') | |
| 471 remote_p.set_defaults(command='remote') | |
| 472 remote_p.add_argument( | |
| 473 '--repository', required=True, | |
| 474 help='URL of a git repository to fetch') | |
| 475 remote_p.add_argument( | |
| 476 '--revision', | |
| 477 help=( | |
| 478 'Git commit hash to check out; defaults to latest revision on master' | |
| 479 ' (refs/heads/master)' | |
| 480 )) | |
| 481 remote_p.add_argument( | |
| 482 '--workdir', | |
| 483 type=os.path.abspath, | |
| 484 help='The working directory of repo checkout') | |
| 485 remote_p.add_argument( | |
| 486 '--use-gitiles', action='store_true', | |
| 487 help='Use Gitiles-specific way to fetch repo (potentially cheaper for ' | |
| 488 'large repos)') | |
| 489 remote_p.add_argument( | |
| 490 'remote_args', nargs='*', | |
| 491 help='Arguments to pass to fetched repo\'s recipes.py') | |
| 492 | |
| 493 | |
| 494 refs_p = subp.add_parser( | 467 refs_p = subp.add_parser( |
| 495 'refs', | 468 'refs', |
| 496 description='List places referencing given recipe module(s).') | 469 description='List places referencing given recipe module(s).') |
| 497 refs_p.set_defaults(command='refs') | 470 refs_p.set_defaults(command='refs') |
| 498 refs_p.add_argument('modules', nargs='+', help='Module(s) to query for') | 471 refs_p.add_argument('modules', nargs='+', help='Module(s) to query for') |
| 499 refs_p.add_argument('--transitive', action='store_true', | 472 refs_p.add_argument('--transitive', action='store_true', |
| 500 help='Compute transitive closure of the references') | 473 help='Compute transitive closure of the references') |
| 501 | 474 |
| 502 doc_kinds=('binarypb', 'jsonpb', 'textpb', 'markdown(github)', | 475 doc_kinds=('binarypb', 'jsonpb', 'textpb', 'markdown(github)', |
| 503 'markdown(gitiles)') | 476 'markdown(gitiles)') |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 599 # wrong, log the error, but don't actually raise anything. | 572 # wrong, log the error, but don't actually raise anything. |
| 600 def on_error(_function, path, excinfo): | 573 def on_error(_function, path, excinfo): |
| 601 logging.error('Error cleaning up temporary deps file: %s', path, | 574 logging.error('Error cleaning up temporary deps file: %s', path, |
| 602 exc_info=excinfo) | 575 exc_info=excinfo) |
| 603 shutil.rmtree(temp_deps_dir, onerror=on_error) | 576 shutil.rmtree(temp_deps_dir, onerror=on_error) |
| 604 | 577 |
| 605 | 578 |
| 606 def _real_main(args): | 579 def _real_main(args): |
| 607 from recipe_engine import package | 580 from recipe_engine import package |
| 608 | 581 |
| 609 # Commands which do not require config_file, package_deps, and other objects | 582 if args.bare_command: |
| 610 # initialized later. | 583 return args.func(None, args) |
| 611 if args.command == 'remote': | |
| 612 return remote(args) | |
| 613 | 584 |
| 614 config_file = args.package | 585 config_file = args.package |
| 615 repo_root = package.InfraRepoConfig().from_recipes_cfg(args.package.path) | 586 repo_root = package.InfraRepoConfig().from_recipes_cfg(args.package.path) |
| 616 | 587 |
| 617 try: | 588 try: |
| 618 # TODO(phajdan.jr): gracefully handle inconsistent deps when rolling. | 589 # TODO(phajdan.jr): gracefully handle inconsistent deps when rolling. |
| 619 # This fails if the starting point does not have consistent dependency | 590 # This fails if the starting point does not have consistent dependency |
| 620 # graph. When performing an automated roll, it'd make sense to attempt | 591 # graph. When performing an automated roll, it'd make sense to attempt |
| 621 # to automatically find a consistent state, rather than bailing out. | 592 # to automatically find a consistent state, rather than bailing out. |
| 622 # Especially that only some subcommands refer to package_deps. | 593 # Especially that only some subcommands refer to package_deps. |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 671 | 642 |
| 672 if not isinstance(ret, int): | 643 if not isinstance(ret, int): |
| 673 if ret is None: | 644 if ret is None: |
| 674 ret = 0 | 645 ret = 0 |
| 675 else: | 646 else: |
| 676 print >> sys.stderr, ret | 647 print >> sys.stderr, ret |
| 677 ret = 1 | 648 ret = 1 |
| 678 sys.stdout.flush() | 649 sys.stdout.flush() |
| 679 sys.stderr.flush() | 650 sys.stderr.flush() |
| 680 os._exit(ret) | 651 os._exit(ret) |
| OLD | NEW |