| 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 326 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 def _op_properties_to_dict(pmap): | 337 def _op_properties_to_dict(pmap): |
| 338 """Creates a properties dictionary from an arguments_pb2.PropertyMap entry. | 338 """Creates a properties dictionary from an arguments_pb2.PropertyMap entry. |
| 339 | 339 |
| 340 Args: | 340 Args: |
| 341 pmap (arguments_pb2.PropertyMap): Map to convert to dictionary form. | 341 pmap (arguments_pb2.PropertyMap): Map to convert to dictionary form. |
| 342 Returns (dict): A dictionary derived from the properties in "pmap". | 342 Returns (dict): A dictionary derived from the properties in "pmap". |
| 343 """ | 343 """ |
| 344 return dict((k, _op_property_value(pmap[k])) for k in pmap) | 344 return dict((k, _op_property_value(pmap[k])) for k in pmap) |
| 345 | 345 |
| 346 | 346 |
| 347 def main(): | 347 def add_common_args(parser): |
| 348 parser = argparse.ArgumentParser( | 348 from recipe_engine import package_io |
| 349 description='Interact with the recipe system.') | 349 |
| 350 def package_type(value): |
| 351 if not os.path.isfile(value): |
| 352 raise argparse.ArgumentTypeError( |
| 353 'Given recipes config file %r does not exist.' % (value,)) |
| 354 return package_io.PackageFile(value) |
| 350 | 355 |
| 351 parser.add_argument( | 356 parser.add_argument( |
| 352 '--package', | 357 '--package', |
| 353 type=os.path.abspath, | 358 type=package_type, |
| 354 help='Path to recipes.cfg of the recipe package to operate on' | 359 help='Path to recipes.cfg of the recipe package to operate on' |
| 355 ', usually in infra/config/recipes.cfg') | 360 ', usually in infra/config/recipes.cfg') |
| 356 parser.add_argument( | 361 parser.add_argument( |
| 357 '--deps-path', | 362 '--deps-path', |
| 358 type=os.path.abspath, | 363 type=os.path.abspath, |
| 359 help='Path where recipe engine dependencies will be extracted. Specify ' | 364 help='Path where recipe engine dependencies will be extracted. Specify ' |
| 360 '"-" to use a temporary directory for deps, which will be cleaned ' | 365 '"-" to use a temporary directory for deps, which will be cleaned ' |
| 361 'up on exit.') | 366 'up on exit.') |
| 362 parser.add_argument( | 367 parser.add_argument( |
| 363 '--verbose', '-v', action='count', | 368 '--verbose', '-v', action='count', |
| (...skipping 14 matching lines...) Expand all Loading... |
| 378 parser.add_argument( | 383 parser.add_argument( |
| 379 '--disable-bootstrap', action='store_false', dest='use_bootstrap', | 384 '--disable-bootstrap', action='store_false', dest='use_bootstrap', |
| 380 help='Disables bootstrap (see --use-bootstrap)') | 385 help='Disables bootstrap (see --use-bootstrap)') |
| 381 parser.add_argument( | 386 parser.add_argument( |
| 382 '--operational-args-path', action='store', | 387 '--operational-args-path', action='store', |
| 383 type=os.path.abspath, | 388 type=os.path.abspath, |
| 384 help='The path to an operational Arguments file. If provided, this file ' | 389 help='The path to an operational Arguments file. If provided, this file ' |
| 385 'must contain a JSONPB-encoded Arguments protobuf message, and will ' | 390 'must contain a JSONPB-encoded Arguments protobuf message, and will ' |
| 386 'be integrated into the runtime parameters.') | 391 'be integrated into the runtime parameters.') |
| 387 | 392 |
| 393 |
| 394 def post_process_common_args(parser, args): |
| 395 if args.command == "remote": |
| 396 # TODO(iannucci): this is a hack; remote doesn't behave like ANY other |
| 397 # commands. A way to solve this will be to allow --package to take a remote |
| 398 # repo and then simply remove the remote subcommand entirely. |
| 399 return |
| 400 |
| 401 if not args.package: |
| 402 parser.error('%s requires --package' % args.command) |
| 403 |
| 404 |
| 405 def main(): |
| 406 parser = argparse.ArgumentParser( |
| 407 description='Interact with the recipe system.') |
| 408 |
| 409 add_common_args(parser) |
| 410 |
| 388 subp = parser.add_subparsers() | 411 subp = parser.add_subparsers() |
| 389 | 412 |
| 390 fetch_p = subp.add_parser( | 413 fetch_p = subp.add_parser( |
| 391 'fetch', | 414 'fetch', |
| 392 description='Fetch and update dependencies.') | 415 description='Fetch and update dependencies.') |
| 393 fetch_p.set_defaults(command='fetch') | 416 fetch_p.set_defaults(command='fetch') |
| 394 | 417 |
| 395 test_p = subp.add_parser( | 418 test_p = subp.add_parser( |
| 396 'test', | 419 'test', |
| 397 description='Generate or check expectations by simulation') | 420 description='Generate or check expectations by simulation') |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 564 'doc', | 587 'doc', |
| 565 description='List all known modules reachable from the current package, ' | 588 description='List all known modules reachable from the current package, ' |
| 566 'with their documentation') | 589 'with their documentation') |
| 567 doc_p.add_argument('recipe', nargs='?', | 590 doc_p.add_argument('recipe', nargs='?', |
| 568 help='Restrict documentation to this recipe') | 591 help='Restrict documentation to this recipe') |
| 569 doc_p.add_argument('--kind', default='jsonpb', choices=doc_kinds, | 592 doc_p.add_argument('--kind', default='jsonpb', choices=doc_kinds, |
| 570 help='Output this kind of documentation') | 593 help='Output this kind of documentation') |
| 571 doc_p.set_defaults(command='doc') | 594 doc_p.set_defaults(command='doc') |
| 572 | 595 |
| 573 args = parser.parse_args() | 596 args = parser.parse_args() |
| 597 post_process_common_args(parser, args) |
| 574 | 598 |
| 575 # Load/parse operational arguments. | 599 # Load/parse operational arguments. |
| 576 op_args = arguments_pb2.Arguments() | 600 op_args = arguments_pb2.Arguments() |
| 577 if args.operational_args_path is not None: | 601 if args.operational_args_path is not None: |
| 578 with open(args.operational_args_path) as fd: | 602 with open(args.operational_args_path) as fd: |
| 579 data = fd.read() | 603 data = fd.read() |
| 580 jsonpb.Parse(data, op_args) | 604 jsonpb.Parse(data, op_args) |
| 581 | 605 |
| 582 # TODO(iannucci): We should always do logging.basicConfig() (probably with | 606 # TODO(iannucci): We should always do logging.basicConfig() (probably with |
| 583 # logging.WARNING), even if no verbose is passed. However we need to be | 607 # logging.WARNING), even if no verbose is passed. However we need to be |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 667 | 691 |
| 668 | 692 |
| 669 def _real_main(args, op_args): | 693 def _real_main(args, op_args): |
| 670 from recipe_engine import package, package_io | 694 from recipe_engine import package, package_io |
| 671 | 695 |
| 672 # Commands which do not require config_file, package_deps, and other objects | 696 # Commands which do not require config_file, package_deps, and other objects |
| 673 # initialized later. | 697 # initialized later. |
| 674 if args.command == 'remote': | 698 if args.command == 'remote': |
| 675 return remote(args) | 699 return remote(args) |
| 676 | 700 |
| 677 assert args.package, 'No recipe config (--package) given.' | 701 config_file = args.package |
| 678 assert os.path.isfile(args.package), ( | 702 repo_root = package.InfraRepoConfig().from_recipes_cfg(args.package.path) |
| 679 'Given recipes config file %r does not exist.' % (args.package,)) | |
| 680 repo_root = package.InfraRepoConfig().from_recipes_cfg(args.package) | |
| 681 config_file = package_io.PackageFile(args.package) | |
| 682 | 703 |
| 683 try: | 704 try: |
| 684 # TODO(phajdan.jr): gracefully handle inconsistent deps when rolling. | 705 # TODO(phajdan.jr): gracefully handle inconsistent deps when rolling. |
| 685 # This fails if the starting point does not have consistent dependency | 706 # This fails if the starting point does not have consistent dependency |
| 686 # graph. When performing an automated roll, it'd make sense to attempt | 707 # graph. When performing an automated roll, it'd make sense to attempt |
| 687 # to automatically find a consistent state, rather than bailing out. | 708 # to automatically find a consistent state, rather than bailing out. |
| 688 # Especially that only some subcommands refer to package_deps. | 709 # Especially that only some subcommands refer to package_deps. |
| 689 package_deps = package.PackageDeps.create( | 710 package_deps = package.PackageDeps.create( |
| 690 repo_root, config_file, allow_fetch=not args.no_fetch, | 711 repo_root, config_file, allow_fetch=not args.no_fetch, |
| 691 deps_path=args.deps_path, overrides=args.project_override) | 712 deps_path=args.deps_path, overrides=args.project_override) |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 746 | 767 |
| 747 if not isinstance(ret, int): | 768 if not isinstance(ret, int): |
| 748 if ret is None: | 769 if ret is None: |
| 749 ret = 0 | 770 ret = 0 |
| 750 else: | 771 else: |
| 751 print >> sys.stderr, ret | 772 print >> sys.stderr, ret |
| 752 ret = 1 | 773 ret = 1 |
| 753 sys.stdout.flush() | 774 sys.stdout.flush() |
| 754 sys.stderr.flush() | 775 sys.stderr.flush() |
| 755 os._exit(ret) | 776 os._exit(ret) |
| OLD | NEW |