| OLD | NEW |
| 1 # Copyright 2013 The LUCI Authors. All rights reserved. | 1 # Copyright 2013 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import contextlib | 5 import contextlib |
| 6 import copy | 6 import copy |
| 7 import types |
| 7 | 8 |
| 8 from recipe_engine import recipe_api | 9 from recipe_engine import recipe_api |
| 10 from recipe_engine.config_types import Path |
| 11 from recipe_engine.util import Placeholder |
| 9 | 12 |
| 10 | 13 |
| 11 # Inherit from RecipeApiPlain because the only thing which is a step is | 14 # Inherit from RecipeApiPlain because the only thing which is a step is |
| 12 # run_from_dict() | 15 # run_from_dict() |
| 13 class StepApi(recipe_api.RecipeApiPlain): | 16 class StepApi(recipe_api.RecipeApiPlain): |
| 14 | 17 |
| 15 step_client = recipe_api.RequireClient('step') | 18 step_client = recipe_api.RequireClient('step') |
| 16 | 19 |
| 17 def __init__(self, step_properties, **kwargs): | 20 def __init__(self, step_properties, **kwargs): |
| 18 super(StepApi, self).__init__(**kwargs) | 21 super(StepApi, self).__init__(**kwargs) |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 in annotator's stderr. | 133 in annotator's stderr. |
| 131 stdin: Placeholder to read step stdin from. | 134 stdin: Placeholder to read step stdin from. |
| 132 step_test_data (func -> recipe_test_api.StepTestData): A factory which | 135 step_test_data (func -> recipe_test_api.StepTestData): A factory which |
| 133 returns a StepTestData object that will be used as the default test | 136 returns a StepTestData object that will be used as the default test |
| 134 data for this step. The recipe author can override/augment this object | 137 data for this step. The recipe author can override/augment this object |
| 135 in the GenTests function. | 138 in the GenTests function. |
| 136 | 139 |
| 137 Returns: | 140 Returns: |
| 138 Opaque step object produced and understood by recipe engine. | 141 Opaque step object produced and understood by recipe engine. |
| 139 """ | 142 """ |
| 140 kwargs = {} | |
| 141 if allow_subannotations is not None: | |
| 142 kwargs['allow_subannotations'] = allow_subannotations | |
| 143 if trigger_specs: | |
| 144 kwargs['trigger_specs'] = trigger_specs | |
| 145 if stdout: | |
| 146 kwargs['stdout'] = stdout | |
| 147 if stderr: | |
| 148 kwargs['stderr'] = stderr | |
| 149 if stdin: | |
| 150 kwargs['stdin'] = stdin | |
| 151 if step_test_data: | |
| 152 kwargs['step_test_data'] = step_test_data | |
| 153 assert cmd is None or isinstance(cmd, list) | |
| 154 if not ok_ret: | |
| 155 ok_ret = {0} | |
| 156 if ok_ret in ('any', 'all'): | |
| 157 ok_ret = set(range(-256, 256)) | |
| 158 | |
| 159 if cmd is not None: | |
| 160 command = list(wrapper) | |
| 161 command += cmd | |
| 162 kwargs['cmd'] = command | |
| 163 | |
| 164 kwargs['timeout'] = timeout | |
| 165 kwargs['ok_ret'] = ok_ret | |
| 166 | |
| 167 # Calculate our full step name. If a step already has that name, add an | 143 # Calculate our full step name. If a step already has that name, add an |
| 168 # index to the end of it. | 144 # index to the end of it. |
| 169 # | 145 # |
| 170 # Note that another step could exist with that index already added to it | 146 # Note that another step could exist with that index already added to it |
| 171 # by the user. If this happens, we'll continue appending indexes until we | 147 # by the user. If this happens, we'll continue appending indexes until we |
| 172 # have a unique step name. | 148 # have a unique step name. |
| 173 with self.m.context(name_prefix=name): | 149 with self.m.context(name_prefix=name): |
| 174 base_name = self.m.context.name_prefix | 150 base_name = self.m.context.name_prefix |
| 175 name_suffix = '' | 151 name_suffix = '' |
| 176 | 152 |
| 177 while True: | 153 while True: |
| 178 full_name = base_name + name_suffix | 154 full_name = base_name + name_suffix |
| 179 if full_name not in self._seen_steps: | 155 if full_name not in self._seen_steps: |
| 180 break | 156 break |
| 181 | 157 |
| 182 step_count = self._step_names.setdefault(full_name, 1) + 1 | 158 step_count = self._step_names.setdefault(full_name, 1) + 1 |
| 183 self._step_names[full_name] = step_count | 159 self._step_names[full_name] = step_count |
| 184 name_suffix = ' (%d)' % step_count | 160 name_suffix = ' (%d)' % step_count |
| 185 self._seen_steps.add(full_name) | 161 self._seen_steps.add(full_name) |
| 186 | 162 |
| 163 assert isinstance(cmd, (types.NoneType, list)) |
| 164 if cmd is not None: |
| 165 cmd = list(wrapper) + cmd |
| 166 assert all(isinstance(x, (int, long, basestring, Path, Placeholder)) |
| 167 for x in cmd) |
| 168 |
| 187 cwd = self.m.context.cwd | 169 cwd = self.m.context.cwd |
| 188 if cwd is not None and cwd != self.m.path['start_dir']: | 170 if cwd and cwd == self.m.path['start_dir']: |
| 189 kwargs['cwd'] = cwd | 171 cwd = None |
| 190 kwargs['env'] = self.m.context.env | 172 |
| 173 env = self.m.context.env |
| 191 if self._prefix_path: | 174 if self._prefix_path: |
| 192 ps = self.m.path.pathsep | 175 ps = self.m.path.pathsep |
| 193 prefix = ps.join(self._prefix_path) | 176 prefix = ps.join(self._prefix_path) |
| 194 suffix = kwargs['env'].get('PATH', '%(PATH)s') | 177 suffix = env.get('PATH', '%(PATH)s') |
| 195 kwargs['env']['PATH'] = '%s%s%s' % (prefix, ps, suffix) | 178 env['PATH'] = '%s%s%s' % (prefix, ps, suffix) |
| 196 kwargs['infra_step'] = self.m.context.infra_step or bool(infra_step) | |
| 197 kwargs['step_nest_level'] = self.m.context.nest_level | |
| 198 kwargs['name'] = full_name | |
| 199 kwargs['base_name'] = name | |
| 200 | 179 |
| 201 schema = self.make_config() | 180 if ok_ret in ('any', 'all'): |
| 202 schema.set_val(kwargs) | 181 ok_ret = range(-256, 256) |
| 203 return self.run_from_dict(schema.as_jsonish()) | |
| 204 | 182 |
| 205 # TODO(martiniss) delete, and make generator_script use **kwargs on step() | 183 return self.run_step( |
| 184 name=full_name, |
| 185 base_name=full_name or name, |
| 186 cmd=cmd, |
| 187 cwd=cwd, |
| 188 env=env, |
| 189 allow_subannotations=bool(allow_subannotations), |
| 190 trigger_specs=[self._make_trigger_spec(trig) |
| 191 for trig in (trigger_specs or ())], |
| 192 timeout=timeout, |
| 193 infra_step=self.m.context.infra_step or bool(infra_step), |
| 194 stdout=stdout, |
| 195 stderr=stderr, |
| 196 stdin=stdin, |
| 197 ok_ret=ok_ret, |
| 198 step_test_data=step_test_data, |
| 199 nest_level=self.m.context.nest_level, |
| 200 ) |
| 201 |
| 206 @recipe_api.composite_step | 202 @recipe_api.composite_step |
| 207 def run_from_dict(self, dct): | 203 def run_step(self, **kwargs): |
| 208 return self.step_client.run_step(dct) | 204 # TODO(dnj,iannucci): Remove this once "generator_script" is deleted. Make |
| 205 # sure we preserve "composite_step". |
| 206 return self.step_client.run_step(**kwargs) |
| 207 |
| 208 def _make_trigger_spec(self, trig): |
| 209 buildbot_changes = trig.get('buildbot_changes') |
| 210 assert isinstance(buildbot_changes, (types.NoneType, list)) |
| 211 |
| 212 critical = trig.get('critical') |
| 213 return self.step_client.make_trigger_spec( |
| 214 bucket=trig.get('bucket'), |
| 215 builder_name=trig['builder_name'], |
| 216 properties=trig.get('properties'), |
| 217 buildbot_changes=buildbot_changes, |
| 218 tags=trig.get('tags'), |
| 219 critical=bool(critical) if critical is not None else (True), |
| 220 ) |
| OLD | NEW |