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

Unified Diff: third_party/recipe_engine/recipes.py

Issue 1241323004: Cross-repo recipe package system. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Moved show_me_the_modules into recipe_engine Created 5 years, 4 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 side-by-side diff with in-line comments
Download patch
Index: third_party/recipe_engine/recipes.py
diff --git a/third_party/recipe_engine/recipes.py b/third_party/recipe_engine/recipes.py
new file mode 100755
index 0000000000000000000000000000000000000000..aa71a98f480da770e54f8277cc48ee6acab0f96c
--- /dev/null
+++ b/third_party/recipe_engine/recipes.py
@@ -0,0 +1,265 @@
+#!/usr/bin/env python
iannucci 2015/08/06 23:57:13 maybe call this main.py and rename main.py to run.
luqui 2015/08/20 22:45:25 Done.
+
+"""Tool to interact with recipe repositories.
+
+This tool operates on the nearest ancestor directory containing a
+recipe_package.pyl file.
luqui 2015/08/07 19:30:36 Update docstring
luqui 2015/08/20 22:45:24 Done.
+"""
+
+import argparse
+import json
+import logging
+import os
+import subprocess
+import sys
+
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
iannucci 2015/08/06 23:57:13 !!!!!!!
luqui 2015/08/20 22:45:25 !!!!!!!
+
+
+def get_package_config(args):
+ from recipe_engine import package
+
+ def try_path(path):
+ cand = os.path.realpath(
+ os.path.join(os.getcwd(), path, 'infra', 'config', 'recipes.cfg'))
+ if os.path.exists(cand):
+ return package.ProtoFile(cand)
+ else:
+ raise EnvironmentError(
+ 'Recipes config file %s does not exist' % cand)
+
+ if args.package:
+ return try_path(args.package)
+
+ cwd = os.path.realpath(os.getcwd())
+ while os.path.dirname(cwd) != cwd:
+ try:
+ return try_path(cwd)
+ except EnvironmentError:
+ if not os.path.isdir(os.path.join(cwd, '.git')):
+ cwd = os.path.dirname(cwd)
+ else:
+ raise EnvironmentError(
+ 'Will not cross git repository boundary %s when looking for '
+ 'recipe package spec' % cwd)
+ else:
+ raise EnvironmentError(
+ 'Reached root of filesystem when looking for recipe package spec')
+
+
+def simulation_test(package, args):
+ from recipe_engine import simulation_test
+ simulation_test.main(package, args=json.loads(args.args))
+
+
+def lint(package, args):
+ from recipe_engine import lint_test
+ lint_test.main(package)
+
+
+def run(package, args):
+ from recipe_engine import main as recipe_main
+ from recipe_engine import loader
+ from recipe_engine import package
+ from recipe_engine.third_party import annotator
+
+ def get_properties_from_args(args):
+ properties = dict(x.split('=', 1) for x in args)
+ for key, val in properties.iteritems():
+ try:
+ properties[key] = ast.literal_eval(val)
+ except (ValueError, SyntaxError):
+ pass # If a value couldn't be evaluated, keep the string version
+ return properties
+
+ def get_properties_from_file(filename):
+ properties_file = sys.stdin if filename == '-' else open(filename)
+ properties = ast.literal_eval(properties_file.read())
+ assert isinstance(properties, dict)
+
+ arg_properties = get_properties_from_args(args.properties)
+ if args.properties_file and arg_properties:
+ raise Exception(
+ 'Cannot specify both properties file and command-line properties')
+ elif args.properties_file:
+ properties = get_properties_from_file(args.properties_file)
+ else:
+ properties = arg_properties
+
+ properties['recipe'] = args.recipe
+
+ # TODO(luqui): Environment whitelist. Cf crbug.com/392992
+ os.environ['PYTHONUNBUFFERED'] = '1'
+ os.environ['PYTHONIOENCODING'] = 'UTF-8'
+
+ universe = loader.RecipeUniverse(package)
+
+ workdir = (args.workdir or
+ os.path.join(os.path.dirname(os.path.realpath(__file__)), 'workdir'))
+ logging.info('Using %s as work directory' % workdir)
+ if not os.path.exists(workdir):
+ os.makedirs(workdir)
+
+ old_cwd = os.getcwd()
+ os.chdir(workdir)
+ try:
+ ret = recipe_main.run_steps(
+ properties, annotator.StructuredAnnotationStream(), universe=universe)
+ return ret.status_code
+ finally:
+ os.chdir(old_cwd)
+
+
+def roll(args):
iannucci 2015/08/06 23:57:13 maybe have option to do this in a loop until it ca
luqui 2015/08/20 22:45:25 On second thought, no. The reason for one at a ti
+ from recipe_engine import package
+ config_file = get_package_config(args)
+ context = package.PackageContext.from_proto_file(config_file)
+ package_spec = package.PackageSpec.load_proto(config_file)
+
+ for update in package_spec.iterate_consistent_updates(context):
+ config_file.write(update.spec.dump())
+ print 'Wrote %s' % config_file.path
+
+ updated_deps = {
+ dep_id: dep.revision
+ for dep_id, dep in update.spec.deps.iteritems()
+ if dep.revision != package_spec.deps[dep_id].revision
+ }
+ print 'To commit this roll, run:'
+ print ' '.join([
+ 'git commit -a -m "Roll dependencies"',
+ ' '.join([ '-m "Roll %s to %s"' % (dep_id, rev)
+ for dep_id, rev in sorted(updated_deps.iteritems())]),
+ ])
+
+ break
+ else:
+ print 'No consistent rolls found'
+
+
+def show_me_the_modules(package):
+ from recipe_engine import show_me_the_modules
+ show_me_the_modules.main(package)
+
+
+def main():
+ from recipe_engine import package
+
+ # Super-annoyingly, we need to manually parse for simulation_test since
+ # argparse is bonkers and doesn't allow us to forward --help to subcommands.
+ if 'simulation_test' in sys.argv:
+ index = sys.argv.index('simulation_test')
+ sys.argv = sys.argv[:index+1] + [json.dumps(sys.argv[index+1:])]
+
+ parser = argparse.ArgumentParser(description='Do things with recipes.')
+
+ parser.add_argument(
+ '--package',
+ help='Package to operate on (directory containing '
+ 'infra/config/recipes.cfg)')
+ parser.add_argument(
+ '--verbose', '-v', action='store_true',
+ help='Increase logging verboisty')
+ parser.add_argument(
+ '--bootstrap', action='store_true',
luqui 2015/08/07 19:30:36 --no-fetch Don't checkout recipe engine at HEAD.
luqui 2015/08/20 22:45:24 Done.
+ help='Use current rather than pinned version of recipe engine')
+
+ subp = parser.add_subparsers()
+
+ fetch_p = subp.add_parser(
+ 'fetch',
+ help='Fetch and update dependencies.')
+ fetch_p.set_defaults(command='fetch')
+
+ simulation_test_p = subp.add_parser('simulation_test',
+ help='Generate or check expectations by simulating with mock actions')
+ simulation_test_p.set_defaults(command='simulation_test')
+ simulation_test_p.add_argument('args')
+
+ lint_p = subp.add_parser(
+ 'lint',
+ help='Check recipes for stylistic and hygenic issues')
+ lint_p.set_defaults(command='lint')
+
+ run_p = subp.add_parser(
+ 'run',
+ help='Run a recipe locally')
+ run_p.set_defaults(command='run')
+ run_p.add_argument(
+ '--properties-file',
+ help='A file containing a json blob of properties')
+ run_p.add_argument(
+ '--workdir',
+ help='The working directory of recipe execution')
+ run_p.add_argument(
+ 'recipe',
+ help='The recipe to execute')
+ run_p.add_argument(
+ 'properties', nargs=argparse.REMAINDER,
+ help='A list of property pairs; e.g. mastername=chromium.linux '
+ 'issue=12345')
+
+ roll_p = subp.add_parser(
+ 'roll',
+ help='Roll dependencies of a recipe package forward')
+ roll_p.set_defaults(command='roll')
+
+ show_me_the_modules_p = subp.add_parser(
+ 'show_me_the_modules',
iannucci 2015/08/06 23:57:13 maybe 'doc'? doc <module name> doc <recipe name>
luqui 2015/08/20 22:45:25 Done
+ help='List all known modules reachable from the current package with '
+ 'various info about each')
+ show_me_the_modules_p.set_defaults(command='show_me_the_modules')
+
+ args = parser.parse_args()
+ if args.verbose:
+ logging.getLogger().setLevel(logging.INFO)
+
+ if args.command == 'fetch' or args.command == 'roll':
+ package_deps = package.PackageDeps.create(
+ get_package_config(args), fetch=True)
+ else:
+ package_deps = package.PackageDeps.create(get_package_config(args))
iannucci 2015/08/06 23:57:13 let's have a `fetch = <condition>` line, and then
luqui 2015/08/20 22:45:24 Nope, because of error message stuff.
+
+ # We don't forward to pinned tool on rolling, because there might be new
+ # things to know about since then, who knows? Easier to look backward than
+ # forward.
+ if not args.bootstrap and args.command != 'roll':
+ tool = os.path.join(
+ package_deps.get_package('recipe_engine').recipes_dir, 'recipes.py')
+ cmd = [ sys.executable, '-u', tool, '--bootstrap' ] + sys.argv[1:]
+ logging.info(cmd)
+ return subprocess.call(cmd)
+
+ if args.command == 'fetch':
+ # We already did everything in the create() call above.
+ return 0
+ if args.command == 'simulation_test':
+ return simulation_test(package_deps, args)
+ elif args.command == 'lint':
+ return lint(package_deps, args)
+ elif args.command == 'run':
+ return run(package_deps, args)
+ elif args.command == 'roll':
+ return roll(args)
+ elif args.command == 'show_me_the_modules':
+ return show_me_the_modules(package_deps)
+ else:
+ print """Dear sir or madam,
+ It has come to my attention that a quite impossible condition has come
+ to pass in the specification you have issued a request for us to fulfill.
+ It is with a heavy heart that I inform you that, at the present juncture,
+ there is no conceivable next action to be taken upon your request, and as
+ such, we have decided to abort the request with a nonzero status code. We
+ hope that your larger goals have not been put at risk due to this
+ unfortunate circumstance, and wish you the best in deciding the next action
+ in your venture and larger life.
+
+ Warmly,
+ recipes.py
+ """
+ return 1
+
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())

Powered by Google App Engine
This is Rietveld 408576698