diff --git a/recipes/README.recipes.md b/recipes/README.recipes.md
index ac1828b82..694c931b0 100644
--- a/recipes/README.recipes.md
+++ b/recipes/README.recipes.md
@@ -21,6 +21,7 @@
* [bot_update:tests/do_not_retry_patch_failures_in_cq](#recipes-bot_update_tests_do_not_retry_patch_failures_in_cq)
* [bot_update:tests/download_topics](#recipes-bot_update_tests_download_topics)
* [bot_update:tests/ensure_checkout](#recipes-bot_update_tests_ensure_checkout)
+ * [bot_update:tests/ensure_checkout_return_custom_result](#recipes-bot_update_tests_ensure_checkout_return_custom_result)
* [depot_tools:examples/full](#recipes-depot_tools_examples_full)
* [gclient:examples/full](#recipes-gclient_examples_full)
* [gclient:tests/diff_deps](#recipes-gclient_tests_diff_deps)
@@ -51,23 +52,23 @@
### *recipe_modules* / [bot\_update](/recipes/recipe_modules/bot_update)
-[DEPS](/recipes/recipe_modules/bot_update/__init__.py#3): [depot\_tools](#recipe_modules-depot_tools), [gclient](#recipe_modules-gclient), [gerrit](#recipe_modules-gerrit), [gitiles](#recipe_modules-gitiles), [gsutil](#recipe_modules-gsutil), [tryserver](#recipe_modules-tryserver), [recipe\_engine/archive][recipe_engine/recipe_modules/archive], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/commit\_position][recipe_engine/recipe_modules/commit_position], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/cv][recipe_engine/recipe_modules/cv], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/led][recipe_engine/recipe_modules/led], [recipe\_engine/milo][recipe_engine/recipe_modules/milo], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+[DEPS](/recipes/recipe_modules/bot_update/__init__.py#3): [depot\_tools](#recipe_modules-depot_tools), [gclient](#recipe_modules-gclient), [gerrit](#recipe_modules-gerrit), [gitiles](#recipe_modules-gitiles), [gsutil](#recipe_modules-gsutil), [tryserver](#recipe_modules-tryserver), [recipe\_engine/archive][recipe_engine/recipe_modules/archive], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/commit\_position][recipe_engine/recipe_modules/commit_position], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/cv][recipe_engine/recipe_modules/cv], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/led][recipe_engine/recipe_modules/led], [recipe\_engine/milo][recipe_engine/recipe_modules/milo], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/step][recipe_engine/recipe_modules/step], [recipe\_engine/warning][recipe_engine/recipe_modules/warning]
Recipe module to ensure a checkout is consistent on a bot.
-#### **class [BotUpdateApi](/recipes/recipe_modules/bot_update/api.py#12)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
+#### **class [BotUpdateApi](/recipes/recipe_modules/bot_update/api.py#93)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
-— **def [\_\_call\_\_](/recipes/recipe_modules/bot_update/api.py#20)(self, name, cmd, \*\*kwargs):**
+— **def [\_\_call\_\_](/recipes/recipe_modules/bot_update/api.py#101)(self, name, cmd, \*\*kwargs):**
Wrapper for easy calling of bot_update.
-— **def [deapply\_patch](/recipes/recipe_modules/bot_update/api.py#599)(self, bot_update_step):**
+— **def [deapply\_patch](/recipes/recipe_modules/bot_update/api.py#693)(self, bot_update_result):**
Deapplies a patch, taking care of DEPS and solution revisions properly.
-— **def [ensure\_checkout](/recipes/recipe_modules/bot_update/api.py#128)(self, gclient_config=None, suffix=None, patch=True, update_presentation=True, patch_root=None, with_branch_heads=False, with_tags=False, no_fetch_tags=False, refs=None, clobber=False, root_solution_revision=None, gerrit_no_reset=False, gerrit_no_rebase_patch_ref=False, assert_one_gerrit_change=True, patch_refs=None, ignore_input_commit=False, add_blamelists=False, set_output_commit=False, step_test_data=None, enforce_fetch=False, download_topics=False, recipe_revision_overrides=None, \*\*kwargs):**
+— **def [ensure\_checkout](/recipes/recipe_modules/bot_update/api.py#209)(self, gclient_config=None, suffix=None, patch=True, update_presentation=True, patch_root=None, with_branch_heads=False, with_tags=False, no_fetch_tags=False, refs=None, clobber=False, root_solution_revision=None, gerrit_no_reset=False, gerrit_no_rebase_patch_ref=False, assert_one_gerrit_change=True, patch_refs=None, ignore_input_commit=False, add_blamelists=False, set_output_commit=False, step_test_data=None, enforce_fetch=False, download_topics=False, recipe_revision_overrides=None, \*\*kwargs):**
Args:
* gclient_config: The gclient configuration to use when running bot_update.
@@ -103,7 +104,7 @@ Args:
change's commit message to get this revision override requested by the
author.
-— **def [get\_project\_revision\_properties](/recipes/recipe_modules/bot_update/api.py#576)(self, project_name, gclient_config=None):**
+— **def [get\_project\_revision\_properties](/recipes/recipe_modules/bot_update/api.py#670)(self, project_name, gclient_config=None):**
Returns all property names used for storing the checked-out revision of
a given project.
@@ -117,9 +118,9 @@ Args:
Returns (list of str): All properties that'll hold the checked-out revision
of the given project. An empty list if no such properties exist.
- **@property**
— **def [last\_returned\_properties](/recipes/recipe_modules/bot_update/api.py#39)(self):**
+ **@property**
— **def [last\_returned\_properties](/recipes/recipe_modules/bot_update/api.py#120)(self):**
-— **def [resolve\_fixed\_revision](/recipes/recipe_modules/bot_update/api.py#527)(self, bot_update_json, name):**
+— **def [resolve\_fixed\_revision](/recipes/recipe_modules/bot_update/api.py#621)(self, bot_update_json, name):**
Sets a fixed revision for a single dependency using project revision
properties.
@@ -1031,10 +1032,10 @@ Raises:
### *recipes* / [bot\_update:examples/full](/recipes/recipe_modules/bot_update/examples/full.py)
-[DEPS](/recipes/recipe_modules/bot_update/examples/full.py#9): [bot\_update](#recipe_modules-bot_update), [gclient](#recipe_modules-gclient), [gerrit](#recipe_modules-gerrit), [tryserver](#recipe_modules-tryserver), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime]
+[DEPS](/recipes/recipe_modules/bot_update/examples/full.py#10): [bot\_update](#recipe_modules-bot_update), [gclient](#recipe_modules-gclient), [gerrit](#recipe_modules-gerrit), [tryserver](#recipe_modules-tryserver), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/json][recipe_engine/recipe_modules/json], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime]
-— **def [RunSteps](/recipes/recipe_modules/bot_update/examples/full.py#27)(api):**
+ **@recipe_api.ignore_warnings('^depot_tools/BOT_UPDATE_CUSTOM_RESULT_ATTRIBUTES$')**
— **def [RunSteps](/recipes/recipe_modules/bot_update/examples/full.py#32)(api):**
### *recipes* / [bot\_update:tests/do\_not\_retry\_patch\_failures\_in\_cq](/recipes/recipe_modules/bot_update/tests/do_not_retry_patch_failures_in_cq.py)
[DEPS](/recipes/recipe_modules/bot_update/tests/do_not_retry_patch_failures_in_cq.py#9): [bot\_update](#recipe_modules-bot_update), [gclient](#recipe_modules-gclient), [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cv][recipe_engine/recipe_modules/cv], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
@@ -1053,6 +1054,12 @@ Raises:
— **def [RunSteps](/recipes/recipe_modules/bot_update/tests/ensure_checkout.py#16)(api):**
+### *recipes* / [bot\_update:tests/ensure\_checkout\_return\_custom\_result](/recipes/recipe_modules/bot_update/tests/ensure_checkout_return_custom_result.py)
+
+[DEPS](/recipes/recipe_modules/bot_update/tests/ensure_checkout_return_custom_result.py#10): [bot\_update](#recipe_modules-bot_update), [gclient](#recipe_modules-gclient), [recipe\_engine/assertions][recipe_engine/recipe_modules/assertions], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/properties][recipe_engine/recipe_modules/properties]
+
+
+ **@recipe_api.ignore_warnings('^depot_tools/BOT_UPDATE_CUSTOM_RESULT_ATTRIBUTES$')**
— **def [RunSteps](/recipes/recipe_modules/bot_update/tests/ensure_checkout_return_custom_result.py#29)(api, expected_checkout_dir, expected_source_root_name, expected_patch_root_name):**
### *recipes* / [depot\_tools:examples/full](/recipes/recipe_modules/depot_tools/examples/full.py)
[DEPS](/recipes/recipe_modules/depot_tools/examples/full.py#7): [depot\_tools](#recipe_modules-depot_tools), [recipe\_engine/path][recipe_engine/recipe_modules/path], [recipe\_engine/platform][recipe_engine/recipe_modules/platform], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/step][recipe_engine/recipe_modules/step]
@@ -1234,4 +1241,5 @@ Move things around in a loop!
[recipe_engine/recipe_modules/time]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/61d032078b9e8f1a9a512bbaed668224deedca9d/README.recipes.md#recipe_modules-time
[recipe_engine/recipe_modules/url]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/61d032078b9e8f1a9a512bbaed668224deedca9d/README.recipes.md#recipe_modules-url
[recipe_engine/recipe_modules/version]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/61d032078b9e8f1a9a512bbaed668224deedca9d/README.recipes.md#recipe_modules-version
+[recipe_engine/recipe_modules/warning]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/61d032078b9e8f1a9a512bbaed668224deedca9d/README.recipes.md#recipe_modules-warning
[recipe_engine/wkt/RecipeApi]: https://chromium.googlesource.com/infra/luci/recipes-py.git/+/61d032078b9e8f1a9a512bbaed668224deedca9d/recipe_engine/recipe_api.py#433
diff --git a/recipes/recipe.warnings b/recipes/recipe.warnings
new file mode 100644
index 000000000..33080dd4c
--- /dev/null
+++ b/recipes/recipe.warnings
@@ -0,0 +1,36 @@
+google_issue_default {
+ host: "crbug.com"
+}
+warning {
+ name: "BOT_UPDATE_CUSTOM_RESULT_ATTRIBUTES"
+ description: "The custom result type returned from"
+ description: "`bot_update.ensure_checkout` provides attributes that give easy"
+ description: "access to the information that is often accessed via the"
+ description: "existing step result's `presentation` or `json.output` values."
+ description: "These uses should be updated as follows:"
+ description: ""
+ description: "1) `result.presentation.properties` should be replaced with"
+ description: "`result.properties`."
+ description: ""
+ description: "2) `result.json.output['properties']`,"
+ description: "`result.json.output['manifest']` and"
+ description: "`result.json.output['fixed_revisions']` should be replaced"
+ description: "with `result.properties`, `result.manifest` and"
+ description: "`result.fixed_revisions`, respectively."
+ description: ""
+ description: "3) Getting the source root and the patch root should no longer"
+ description: "be done via `result.json.output['root']` and"
+ description: "`result.json.output['patch_root']`. `result.source_root` and"
+ description: "`result.patch_root` should be used instead. These objects have"
+ description: "`path` and `name` attributes. The `path` attribute gives a"
+ description: "`Path` for the location of the repo. The `name` attribute is"
+ description: "the gclient \"name\" of the repo, i.e. the path relative to the"
+ description: "directory where the checkout was performed. This removes the"
+ description: "need to do path manipulation in the common cases. If no patch"
+ description: "was applied, `result.patch_root` will be `None`."
+ description: ""
+ description: "4) `result.json.output['did_run']` does not need to be"
+ description: "accessed, it is always guaranteed to be True if a result was"
+ description: "returned."
+ google_issue { id: 339472834 }
+}
\ No newline at end of file
diff --git a/recipes/recipe_modules/bot_update/__init__.py b/recipes/recipe_modules/bot_update/__init__.py
index 4b3fa114b..74efdbc11 100644
--- a/recipes/recipe_modules/bot_update/__init__.py
+++ b/recipes/recipe_modules/bot_update/__init__.py
@@ -20,6 +20,7 @@ DEPS = [
'recipe_engine/raw_io',
'recipe_engine/runtime',
'recipe_engine/step',
+ 'recipe_engine/warning',
'tryserver',
]
@@ -36,3 +37,6 @@ PROPERTIES = {
default={},
),
}
+
+# Forward these types so that they can be used without importing api
+from .api import RelativeRoot, Result
\ No newline at end of file
diff --git a/recipes/recipe_modules/bot_update/api.py b/recipes/recipe_modules/bot_update/api.py
index 5e34318f3..5961cebc9 100644
--- a/recipes/recipe_modules/bot_update/api.py
+++ b/recipes/recipe_modules/bot_update/api.py
@@ -4,11 +4,92 @@
"""Recipe module to ensure a checkout is consistent on a bot."""
+import dataclasses
+import typing
+
from recipe_engine import recipe_api
+from recipe_engine.config_types import Path
+from recipe_engine.engine_types import StepPresentation
from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
+@dataclasses.dataclass(kw_only=True, frozen=True)
+class RelativeRoot:
+ """A root that is relative to the checkout root.
+
+ Attributes:
+ name: The name of the root/the path to the root relative to the checkout
+ directory.
+ path: The absolute path to the root.
+ """
+ name: str
+ path: Path
+
+ @classmethod
+ def create(cls, checkout_dir: Path, name: str) -> typing.Self:
+ return cls(name=name, path=checkout_dir / name)
+
+
+@dataclasses.dataclass(kw_only=True, frozen=True)
+class Json:
+ output: dict[str, typing.Any]
+
+
+class ManifestRepo(typing.TypedDict):
+ repository: str
+ revision: str
+
+
+@dataclasses.dataclass(kw_only=True, frozen=True)
+class Result:
+ """The noteworthy paths for a source checkout.
+
+ Attributes:
+ checkout_dir: The directory where the checkout was performed.
+ source_root: The root for the repo identified by the first gclient solution.
+ patch_root: The root for the repo that was patched, if a patch was applied.
+ Otherwise, None.
+ presentation: DEPRECATED. The presentation of the bot_update step. This is
+ used by some code to get the properties. This is provided for backwards
+ compatibility, code should access the properties attribute instead.
+ json: DEPRECATED. The result of json outputs for the bot_update step. This
+ is provided for backwards compatibility, attributes on this object are
+ provided for accessing the contents of json.output.
+ properties: The properties set by the bot_update execution.
+ manifest: The manifest mapping the checkout_dir-relative path to the
+ repository and revision that was checked out.
+ fixed_revisions: The explicitly requested revisions; a mapping from the
+ checkout_dir-relative path to the requested revision.
+ """
+ # Directories relevant to the checkout
+ checkout_dir: Path
+ source_root: RelativeRoot
+ patch_root: RelativeRoot | None = None
+
+ # Details about the revisions that were checked out
+ properties: dict[str, str]
+ manifest: dict[str, ManifestRepo]
+ fixed_revisions: dict[str, str]
+
+ # TODO: crbug.com/339472834 - Once all downstream users are switched to use
+ # the above fields, these attributes and the property methods can be removed,
+ # as well as the Json type
+ _api: 'BotUpdateApi'
+ _presentation: StepPresentation
+ _json: Json
+
+ @property
+ def presentation(self):
+ self._api.m.warning.issue('BOT_UPDATE_CUSTOM_RESULT_ATTRIBUTES')
+ return self._presentation
+
+ @property
+ def json(self):
+ self._api.m.warning.issue('BOT_UPDATE_CUSTOM_RESULT_ATTRIBUTES')
+ return self._json
+
+
class BotUpdateApi(recipe_api.RecipeApi):
def __init__(self, properties, deps_revision_overrides, *args, **kwargs):
@@ -495,7 +576,20 @@ class BotUpdateApi(recipe_api.RecipeApi):
cwd = self.m.context.cwd or self.m.path.start_dir
self.m.path.checkout_dir = cwd / co_root
- return step_result
+ assert result.get('did_run') and result.get('root')
+ checkout_dir = self.m.context.cwd or self.m.path.start_dir
+ return Result(
+ checkout_dir=checkout_dir,
+ source_root=RelativeRoot.create(checkout_dir, result['root']),
+ patch_root=(RelativeRoot.create(checkout_dir, result['patch_root'])
+ if result['patch_root'] is not None else None),
+ properties=result.get('properties', {}),
+ manifest=result.get('manifest', {}),
+ fixed_revisions=result.get('fixed_revisions', {}),
+ _api=self,
+ _presentation=step_result.presentation,
+ _json=Json(output=result),
+ )
def _destination_ref(self, cfg, path):
"""Returns the ref branch of a CL for the matching project if available or
@@ -533,7 +627,7 @@ class BotUpdateApi(recipe_api.RecipeApi):
name: bot_update_json['properties'][rev_properties[0]]
}
- def _resolve_fixed_revisions(self, bot_update_json):
+ def _resolve_fixed_revisions(self, bot_update_result):
"""Sets all fixed revisions from the first sync to their respective
got_X_revision values.
@@ -564,12 +658,12 @@ class BotUpdateApi(recipe_api.RecipeApi):
bot_update.py --revision src@abc
When deapplying the patch, v8 will be synced to v8_before.
"""
- for name in bot_update_json.get('fixed_revisions', {}):
+ for name in bot_update_result.fixed_revisions:
rev_properties = self.get_project_revision_properties(name)
- if (rev_properties and
- bot_update_json['properties'].get(rev_properties[0])):
+ if (rev_properties
+ and bot_update_result.properties.get(rev_properties[0])):
self.m.gclient.c.revisions[name] = str(
- bot_update_json['properties'][rev_properties[0]])
+ bot_update_result.properties[rev_properties[0]])
# TODO(machenbach): Replace usages of this method eventually by direct calls
# to the manifest output.
@@ -596,10 +690,9 @@ class BotUpdateApi(recipe_api.RecipeApi):
if project == project_name
)
- def deapply_patch(self, bot_update_step):
+ def deapply_patch(self, bot_update_result):
"""Deapplies a patch, taking care of DEPS and solution revisions properly.
"""
- bot_update_json = bot_update_step.json.output
# We only override first solution here to make sure that we correctly revert
# changes to DEPS file, which is particularly important for auto-rolls. It
# is also imporant that we do not assume that corresponding revision is
@@ -608,8 +701,8 @@ class BotUpdateApi(recipe_api.RecipeApi):
first_solution_name = self.m.gclient.c.solutions[0].name
rev_property = self.get_project_revision_properties(first_solution_name)[0]
self.m.gclient.c.revisions[first_solution_name] = str(
- bot_update_json['properties'][rev_property])
- self._resolve_fixed_revisions(bot_update_json)
+ bot_update_result.properties[rev_property])
+ self._resolve_fixed_revisions(bot_update_result)
self.ensure_checkout(
patch=False, no_fetch_tags=True, update_presentation=False)
diff --git a/recipes/recipe_modules/bot_update/examples/full.py b/recipes/recipe_modules/bot_update/examples/full.py
index 826956a16..566ba8317 100644
--- a/recipes/recipe_modules/bot_update/examples/full.py
+++ b/recipes/recipe_modules/bot_update/examples/full.py
@@ -3,6 +3,7 @@
# found in the LICENSE file.
from recipe_engine import post_process
+from recipe_engine import recipe_api
PYTHON_VERSION_COMPATIBILITY = 'PY3'
@@ -24,6 +25,11 @@ from recipe_engine import engine_types
from RECIPE_MODULES.depot_tools import gclient
from PB.go.chromium.org.luci.buildbucket.proto.build import Build
+
+# TODO: crbug.com/339472834 - Once all downstream uses of presentation and
+# json.output have been removed, this test can be updated to not reference them
+# and the decorator can be removed
+@recipe_api.ignore_warnings('^depot_tools/BOT_UPDATE_CUSTOM_RESULT_ATTRIBUTES$')
def RunSteps(api):
api.gclient.use_mirror = True
commit = api.buildbucket.build.input.gitiles_commit
diff --git a/recipes/recipe_modules/bot_update/tests/ensure_checkout_return_custom_result.py b/recipes/recipe_modules/bot_update/tests/ensure_checkout_return_custom_result.py
new file mode 100644
index 000000000..c2b2fe586
--- /dev/null
+++ b/recipes/recipe_modules/bot_update/tests/ensure_checkout_return_custom_result.py
@@ -0,0 +1,76 @@
+# Copyright 2018 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.
+
+from recipe_engine import post_process
+from recipe_engine import recipe_api
+
+PYTHON_VERSION_COMPATIBILITY = 'PY3'
+
+DEPS = [
+ 'bot_update',
+ 'gclient',
+ 'recipe_engine/assertions',
+ 'recipe_engine/buildbucket',
+ 'recipe_engine/path',
+ 'recipe_engine/properties',
+]
+
+PROPERTIES = {
+ 'expected_checkout_dir': recipe_api.Property(),
+ 'expected_source_root_name': recipe_api.Property(),
+ 'expected_patch_root_name': recipe_api.Property(default=None),
+}
+
+
+# TODO: crbug.com/339472834 - Once all downstream uses of presentation and
+# json.output have been removed, this test can be updated to not reference them
+# and the decorator can be removed
+@recipe_api.ignore_warnings('^depot_tools/BOT_UPDATE_CUSTOM_RESULT_ATTRIBUTES$')
+def RunSteps(api, expected_checkout_dir, expected_source_root_name,
+ expected_patch_root_name):
+ api.gclient.set_config('depot_tools')
+ result = api.bot_update.ensure_checkout()
+
+ api.assertions.assertEqual(result.checkout_dir, expected_checkout_dir)
+
+ api.assertions.assertEqual(result.source_root.name, expected_source_root_name)
+ api.assertions.assertEqual(result.source_root.path,
+ expected_checkout_dir / expected_source_root_name)
+
+ if expected_patch_root_name is not None:
+ api.assertions.assertEqual(result.patch_root.name, expected_patch_root_name)
+ api.assertions.assertEqual(result.patch_root.path,
+ expected_checkout_dir / expected_patch_root_name)
+ else:
+ api.assertions.assertIsNone(result.patch_root)
+
+ api.assertions.assertEqual(result.properties, result.presentation.properties)
+ api.assertions.assertEqual(result.manifest,
+ result.json.output.get('manifest', {}))
+ api.assertions.assertEqual(result.fixed_revisions,
+ result.json.output.get('fixed_revisions', {}))
+
+
+def GenTests(api):
+ yield api.test(
+ 'basic',
+ api.properties(
+ expected_checkout_dir=api.path.start_dir,
+ expected_source_root_name='depot_tools',
+ ),
+ api.expect_status('SUCCESS'),
+ api.post_process(post_process.DropExpectation),
+ )
+
+ yield api.test(
+ 'patch',
+ api.buildbucket.try_build(),
+ api.properties(
+ expected_checkout_dir=api.path.start_dir,
+ expected_source_root_name='depot_tools',
+ expected_patch_root_name='depot_tools',
+ ),
+ api.expect_status('SUCCESS'),
+ api.post_process(post_process.DropExpectation),
+ )
diff --git a/recipes/recipe_modules/presubmit/api.py b/recipes/recipe_modules/presubmit/api.py
index e6b015352..1eb6807a4 100644
--- a/recipes/recipe_modules/presubmit/api.py
+++ b/recipes/recipe_modules/presubmit/api.py
@@ -116,8 +116,7 @@ class PresubmitApi(recipe_api.RecipeApi):
# as the delimiter. This breaks on windows otherwise.
self._relative_root.replace(self.m.path.sep, '/'),
self.m.gclient.c)
- upstream = bot_update_step.json.output['properties'].get(
- got_revision_properties[0])
+ upstream = bot_update_step.properties.get(got_revision_properties[0])
presubmit_args = []
if self.m.tryserver.is_tryserver: