blob: 7bc7762bb585fcd91189e8d49b8ac2e86934b66d [file] [log] [blame]
#!/usr/bin/python -u
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""This allows easy execution of a recipe (scripts/slave/recipes, etc.)
without buildbot.
This is currently useful for testing recipes locally while developing them.
Example:
./run_recipe.py run_presubmit repo_name=build issue=12345 patchset=1 \
description="this is a cool description" blamelist=['dude@chromium.org'] \
rietveld=https://codereview.chromium.org
Alternatively, the properties can be specified as a Python dict using a
--properties-file, which can optionally be read in from stdin. For example:
./run_recipe.py run_presubmit --properties-file - <<EOF
{
'repo_name': 'build',
'issue': 12345,
'patchset': 1,
'description': 'this is a cool description',
'blamelist': ['dude@chromium.org'],
'rietveld': 'https://codereview.chromium.org',
}
EOF
This would execute the run_presubmit recipe, passing
{'repo_name':'build', 'issue':'12345' ...} as properties.
This script can be run from any directory.
See scripts/slave/annotated_run.py for more information about recipes.
"""
import argparse
import ast
import json
import os
import subprocess
import sys
SCRIPT_PATH = os.path.abspath(os.path.dirname(__file__))
ROOT_PATH = os.path.abspath(os.path.join(SCRIPT_PATH, os.pardir, os.pardir))
SLAVE_DIR = os.path.join(ROOT_PATH, 'slave', 'fake_slave', 'build')
RUNIT = os.path.join(SCRIPT_PATH, 'runit.py')
ANNOTATED_RUN = os.path.join(ROOT_PATH, 'scripts', 'slave', 'annotated_run.py')
USAGE = """
%(prog)s <recipe_name [<property=value>*]
%(prog)s <recipe_name> --properties-file <filename>
If specified, the properties file should contain a Python dictionary. If the
filename "-" is used, then the dictionary is read from stdin, for example:
%(prog)s recipe_name --properties-file - <<EOF
{
'property1: 'value1',
'property2: 'value2',
}
EOF
This could also be specified as:
%(prog)s <recipe_name> property1=value1 property2=value2
"""
def parse_args(args):
"""Parses the command line arguments and returns type-scrubbed properties."""
parser = argparse.ArgumentParser(usage=USAGE)
parser.add_argument('recipe')
parser.add_argument('--properties-file')
parser.add_argument('--master-overrides-slave', action='store_true')
known_args, extra_args = parser.parse_known_args(args)
if known_args.properties_file:
properties = get_properties_from_file(known_args.properties_file)
else:
# If properties were given as command line arguments, make sure that
# they are all prop=value pairs.
bad_params = [x for x in extra_args if '=' not in x]
if bad_params:
parser.error('Error: Got bad arguments: %s' % bad_params)
properties = get_properties_from_args(extra_args)
assert type(properties) is dict
properties['recipe'] = known_args.recipe
return properties, known_args.master_overrides_slave
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, silently ignore it.
return properties
def get_properties_from_file(filename):
properties_file = sys.stdin if filename == '-' else open(filename)
return ast.literal_eval(properties_file.read())
def main(args):
"""Gets the recipe name and properties and runs an annotated run."""
properties, master_overrides_slave = parse_args(args)
properties.setdefault('use_mirror', False)
if not os.path.exists(SLAVE_DIR):
os.makedirs(SLAVE_DIR)
env = os.environ.copy()
env['RUN_SLAVE_UPDATED_SCRIPTS'] = '1'
env['PYTHONUNBUFFERED'] = '1'
env['PYTHONIOENCODING'] = 'UTF-8'
cmd = ['python', '-u', RUNIT, 'python', '-u', ANNOTATED_RUN,
'--keep-stdin', # so that pdb works for local execution
'--factory-properties', json.dumps(properties),
'--build-properties', json.dumps(properties)]
if master_overrides_slave:
cmd.append('--master-overrides-slave')
return subprocess.call(cmd, cwd=SLAVE_DIR, env=env)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))