Move build bisection code to its own module
Change-Id: I76f0ffcaa90cc9582940671189bce87fe9185f19
Reviewed-on: https://dart-review.googlesource.com/c/recipes/+/138505
Reviewed-by: Alexander Thomas <athom@google.com>
diff --git a/README.recipes.md b/README.recipes.md
index b292568..8cff48c 100644
--- a/README.recipes.md
+++ b/README.recipes.md
@@ -3,9 +3,11 @@
## Table of Contents
**[Recipe Modules](#Recipe-Modules)**
+ * [bisect_build](#recipe_modules-bisect_build)
* [dart](#recipe_modules-dart)
**[Recipes](#Recipes)**
+ * [bisect_build:tests/tests](#recipes-bisect_build_tests_tests)
* [dart/chocolatey](#recipes-dart_chocolatey)
* [dart/docker](#recipes-dart_docker)
* [dart/external](#recipes-dart_external)
@@ -19,6 +21,15 @@
* [dart:examples/example-get_secret](#recipes-dart_examples_example-get_secret)
## Recipe Modules
+### *recipe_modules* / [bisect\_build](/recipe_modules/bisect_build)
+
+[DEPS](/recipe_modules/bisect_build/__init__.py#5): [depot\_tools/gitiles][depot_tools/recipe_modules/gitiles], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+
+#### **class [BisectApi](/recipe_modules/bisect_build/api.py#11)([RecipeApi][recipe_engine/wkt/RecipeApi]):**
+
+— **def [is\_bisecting](/recipe_modules/bisect_build/api.py#16)(self):**
+
+— **def [schedule](/recipe_modules/bisect_build/api.py#19)(self, repo_url, reason):**
### *recipe_modules* / [dart](/recipe_modules/dart)
[DEPS](/recipe_modules/dart/__init__.py#5): [build/goma][build/recipe_modules/goma], [build/swarming\_client][build/recipe_modules/swarming_client], [depot\_tools/bot\_update][depot_tools/recipe_modules/bot_update], [depot\_tools/depot\_tools][depot_tools/recipe_modules/depot_tools], [depot\_tools/gclient][depot_tools/recipe_modules/gclient], [depot\_tools/gerrit][depot_tools/recipe_modules/gerrit], [depot\_tools/git][depot_tools/recipe_modules/git], [depot\_tools/gsutil][depot_tools/recipe_modules/gsutil], [depot\_tools/tryserver][depot_tools/recipe_modules/tryserver], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/cipd][recipe_engine/recipe_modules/cipd], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [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/python][recipe_engine/recipe_modules/python], [recipe\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/service\_account][recipe_engine/recipe_modules/service_account], [recipe\_engine/step][recipe_engine/recipe_modules/step], [recipe\_engine/swarming][recipe_engine/recipe_modules/swarming]
@@ -37,7 +48,7 @@
Checks out the dart code and prepares it for building.
-— **def [collect\_all](/recipe_modules/dart/api.py#282)(self, steps):**
+— **def [collect\_all](/recipe_modules/dart/api.py#283)(self, steps):**
Collects the results of a sharded test run.
@@ -49,11 +60,11 @@
Returns the path to the checked-in SDK dart executable.
-— **def [delete\_debug\_log](/recipe_modules/dart/api.py#564)(self):**
+— **def [delete\_debug\_log](/recipe_modules/dart/api.py#562)(self):**
Deletes the debug log file
-— **def [download\_browser](/recipe_modules/dart/api.py#835)(self, runtime, version):**
+— **def [download\_browser](/recipe_modules/dart/api.py#833)(self, runtime, version):**
— **def [download\_parent\_isolate](/recipe_modules/dart/api.py#155)(self):**
@@ -65,7 +76,7 @@
Kills leftover tasks from previous runs or steps.
-— **def [read\_debug\_log](/recipe_modules/dart/api.py#552)(self):**
+— **def [read\_debug\_log](/recipe_modules/dart/api.py#550)(self):**
Reads the debug log file
@@ -74,7 +85,7 @@
Runs test.py in the given isolate, sharded over several swarming tasks.
Returns the created tasks, which can be collected with collect_all().
-— **def [test](/recipe_modules/dart/api.py#570)(self, test_data):**
+— **def [test](/recipe_modules/dart/api.py#568)(self, test_data):**
Reads the test-matrix.json file in checkout and runs each step listed
in the file.
@@ -84,6 +95,11 @@
Builds an isolate
## Recipes
+### *recipes* / [bisect\_build:tests/tests](/recipe_modules/bisect_build/tests/tests.py)
+
+[DEPS](/recipe_modules/bisect_build/tests/tests.py#8): [bisect\_build](#recipe_modules-bisect_build), [depot\_tools/gitiles][depot_tools/recipe_modules/gitiles], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/properties][recipe_engine/recipe_modules/properties], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+
+— **def [RunSteps](/recipe_modules/bisect_build/tests/tests.py#25)(api, current_failure):**
### *recipes* / [dart/chocolatey](/recipes/dart/chocolatey.py)
[DEPS](/recipes/dart/chocolatey.py#7): [dart](#recipe_modules-dart), [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [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/step][recipe_engine/recipe_modules/step], [recipe\_engine/url][recipe_engine/recipe_modules/url]
@@ -101,39 +117,39 @@
— **def [RunSteps](/recipes/dart/external.py#13)(api):**
### *recipes* / [dart/flutter\_engine](/recipes/dart/flutter_engine.py)
-[DEPS](/recipes/dart/flutter_engine.py#15): [build/goma][build/recipe_modules/goma], [dart](#recipe_modules-dart), [depot\_tools/bot\_update][depot_tools/recipe_modules/bot_update], [depot\_tools/depot\_tools][depot_tools/recipe_modules/depot_tools], [depot\_tools/gclient][depot_tools/recipe_modules/gclient], [depot\_tools/gitiles][depot_tools/recipe_modules/gitiles], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [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/python][recipe_engine/recipe_modules/python], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/step][recipe_engine/recipe_modules/step]
+[DEPS](/recipes/dart/flutter_engine.py#11): [build/goma][build/recipe_modules/goma], [bisect\_build](#recipe_modules-bisect_build), [dart](#recipe_modules-dart), [depot\_tools/bot\_update][depot_tools/recipe_modules/bot_update], [depot\_tools/depot\_tools][depot_tools/recipe_modules/depot_tools], [depot\_tools/gclient][depot_tools/recipe_modules/gclient], [depot\_tools/gitiles][depot_tools/recipe_modules/gitiles], [recipe\_engine/buildbucket][recipe_engine/recipe_modules/buildbucket], [recipe\_engine/context][recipe_engine/recipe_modules/context], [recipe\_engine/file][recipe_engine/recipe_modules/file], [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/python][recipe_engine/recipe_modules/python], [recipe\_engine/runtime][recipe_engine/recipe_modules/runtime], [recipe\_engine/step][recipe_engine/recipe_modules/step]
-— **def [AnalyzeDartUI](/recipes/dart/flutter_engine.py#72)(api, checkout_dir):**
+— **def [AnalyzeDartUI](/recipes/dart/flutter_engine.py#69)(api, checkout_dir):**
-— **def [Build](/recipes/dart/flutter_engine.py#55)(api, checkout_dir, config, \*targets):**
+— **def [Build](/recipes/dart/flutter_engine.py#52)(api, checkout_dir, config, \*targets):**
-— **def [BuildAndTest](/recipes/dart/flutter_engine.py#351)(api, start_dir, checkout_dir, flutter_rev):**
+— **def [BuildAndTest](/recipes/dart/flutter_engine.py#339)(api, start_dir, checkout_dir, flutter_rev):**
-— **def [BuildLinux](/recipes/dart/flutter_engine.py#107)(api, checkout_dir):**
+— **def [BuildLinux](/recipes/dart/flutter_engine.py#104)(api, checkout_dir):**
-— **def [BuildLinuxAndroidArm](/recipes/dart/flutter_engine.py#89)(api, checkout_dir):**
+— **def [BuildLinuxAndroidArm](/recipes/dart/flutter_engine.py#86)(api, checkout_dir):**
-— **def [BuildLinuxAndroidx86](/recipes/dart/flutter_engine.py#82)(api, checkout_dir):**
+— **def [BuildLinuxAndroidx86](/recipes/dart/flutter_engine.py#79)(api, checkout_dir):**
-— **def [CopyArtifacts](/recipes/dart/flutter_engine.py#179)(api, engine_src, cached_dest, file_paths):**
+— **def [CopyArtifacts](/recipes/dart/flutter_engine.py#176)(api, engine_src, cached_dest, file_paths):**
-— **def [GetCheckout](/recipes/dart/flutter_engine.py#130)(api):**
+— **def [GetCheckout](/recipes/dart/flutter_engine.py#127)(api):**
-— **def [KillTasks](/recipes/dart/flutter_engine.py#47)(api, checkout_dir, ok_ret='any'):**
+— **def [KillTasks](/recipes/dart/flutter_engine.py#44)(api, checkout_dir, ok_ret='any'):**
Kills leftover tasks from previous runs or steps.
-— **def [RunGN](/recipes/dart/flutter_engine.py#64)(api, checkout_dir, \*args):**
+— **def [RunGN](/recipes/dart/flutter_engine.py#61)(api, checkout_dir, \*args):**
-— **def [RunSteps](/recipes/dart/flutter_engine.py#312)(api):**
+— **def [RunSteps](/recipes/dart/flutter_engine.py#309)(api):**
-— **def [TestEngine](/recipes/dart/flutter_engine.py#77)(api, checkout_dir):**
+— **def [TestEngine](/recipes/dart/flutter_engine.py#74)(api, checkout_dir):**
-— **def [TestFlutter](/recipes/dart/flutter_engine.py#266)(api, start_dir, just_built_dart_sdk):**
+— **def [TestFlutter](/recipes/dart/flutter_engine.py#263)(api, start_dir, just_built_dart_sdk):**
-— **def [TestObservatory](/recipes/dart/flutter_engine.py#119)(api, checkout_dir):**
+— **def [TestObservatory](/recipes/dart/flutter_engine.py#116)(api, checkout_dir):**
-— **def [UpdateCachedEngineArtifacts](/recipes/dart/flutter_engine.py#192)(api, flutter, engine_src):**
+— **def [UpdateCachedEngineArtifacts](/recipes/dart/flutter_engine.py#189)(api, flutter, engine_src):**
### *recipes* / [dart/forward\_branch](/recipes/dart/forward_branch.py)
[DEPS](/recipes/dart/forward_branch.py#8): [dart](#recipe_modules-dart), [depot\_tools/git][depot_tools/recipe_modules/git], [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\_engine/raw\_io][recipe_engine/recipe_modules/raw_io], [recipe\_engine/step][recipe_engine/recipe_modules/step]
diff --git a/recipe_modules/bisect_build/__init__.py b/recipe_modules/bisect_build/__init__.py
new file mode 100644
index 0000000..72028cb
--- /dev/null
+++ b/recipe_modules/bisect_build/__init__.py
@@ -0,0 +1,10 @@
+# Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+DEPS = [
+ 'depot_tools/gitiles',
+ 'recipe_engine/buildbucket',
+ 'recipe_engine/properties',
+ 'recipe_engine/step',
+]
diff --git a/recipe_modules/bisect_build/api.py b/recipe_modules/bisect_build/api.py
new file mode 100644
index 0000000..cdf26eb
--- /dev/null
+++ b/recipe_modules/bisect_build/api.py
@@ -0,0 +1,102 @@
+# Copyright (c) 2020, the Dart project 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 recipe_api
+
+from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
+from PB.go.chromium.org.luci.buildbucket.proto import rpc as rpc_pb2
+
+
+class BisectApi(recipe_api.RecipeApi):
+
+ def _get_reason(self):
+ return self.m.properties['bisect_reason']
+
+ def is_bisecting(self):
+ return 'bisect_reason' in self.m.properties
+
+ def schedule(self, repo_url, reason):
+ if self.is_bisecting():
+ bisect_reason = self._get_reason()
+ if bisect_reason == reason:
+ self._bisect_older(bisect_reason)
+ else:
+ self._bisect_newer(bisect_reason)
+ # The build failed for a different reason, fan out to find the root
+ # cause of that failure as well.
+ self._bisect_older(reason)
+ else:
+ self._start_bisection(repo_url, reason)
+
+ def _start_bisection(self, repo_url, reason):
+ current_rev = self.m.buildbucket.gitiles_commit.id
+ if not current_rev:
+ return
+
+ # Search for previous builds created by the Luci scheduler (to exclude
+ # bisection builds). The TimeRange includes all builds from start_time
+ # (defaults to 0) to end_time (exclusive). Because builds are ordered by
+ # create_time, the first result will be the previous build.
+ create_time = common_pb2.TimeRange(
+ end_time=self.m.buildbucket.build.create_time)
+ builder = self.m.buildbucket.build.builder
+ search_predicate = rpc_pb2.BuildPredicate(
+ builder=builder,
+ create_time=create_time,
+ tags=[common_pb2.StringPair(key='user_agent', value='luci-scheduler')])
+ result = self.m.buildbucket.search(
+ search_predicate, limit=1, step_name='fetch previous build')
+ if not result or len(result) == 0:
+ # There is no previous build: do not bisect on new builders.
+ return
+ previous_build = result[0]
+ if previous_build.status == common_pb2.FAILURE:
+ # Do not bisect if the previous build failed.
+ # TODO(athom): Check if the failure reason is the same by adding
+ # 'builds.*.summaryMarkdown' to the field mask.
+ return
+ previous_rev = previous_build.input.gitiles_commit.id
+ # We're intentionally not paging through the log to avoid bisecting an
+ # excessive number of commits.
+ commits, _ = self.m.gitiles.log(
+ url=repo_url, ref='%s..%s' % (previous_rev, current_rev))
+ commits = commits[1:] # The first commit is the current_rev
+ commits = [commit['commit'] for commit in commits]
+ self._bisect(commits, reason)
+
+ def _bisect(self, commits, reason):
+ if len(commits) == 0:
+ # Nothing more to bisect.
+ return
+
+ middle_index = len(commits) // 2
+ newer = commits[:middle_index]
+ middle = commits[middle_index]
+ older = commits[middle_index + 1:]
+
+ commit = self.m.buildbucket.gitiles_commit
+ middle_commit = common_pb2.GitilesCommit()
+ middle_commit.CopyFrom(commit)
+ middle_commit.id = middle
+ builder = self.m.buildbucket.build.builder
+ request = self.m.buildbucket.schedule_request(
+ builder=builder.builder,
+ project=builder.project,
+ bucket=builder.bucket,
+ properties={
+ 'bisect_newer': newer,
+ 'bisect_older': older,
+ 'bisect_reason': reason
+ },
+ gitiles_commit=middle_commit,
+ inherit_buildsets=False,
+ )
+ self.m.buildbucket.schedule([request],
+ step_name='schedule bisect (%s)' % middle)
+
+ def _bisect_newer(self, reason):
+ self._bisect(list(self.m.properties.get('bisect_newer', [])), reason)
+
+ def _bisect_older(self, reason):
+ self._bisect(list(self.m.properties.get('bisect_older', [])), reason)
diff --git a/recipe_modules/bisect_build/tests/tests.expected/basic.json b/recipe_modules/bisect_build/tests/tests.expected/basic.json
new file mode 100644
index 0000000..7647f3f
--- /dev/null
+++ b/recipe_modules/bisect_build/tests/tests.expected/basic.json
@@ -0,0 +1,26 @@
+[
+ {
+ "cmd": [
+ "bb",
+ "ls",
+ "-host",
+ "cr-buildbucket.appspot.com",
+ "-json",
+ "-nopage",
+ "-n",
+ "1",
+ "-fields",
+ "builder,create_time,created_by,critical,end_time,id,input,number,output,start_time,status,update_time",
+ "-predicate",
+ "{\"builder\": {\"bucket\": \"ci\", \"builder\": \"builder\", \"project\": \"project\"}, \"createTime\": {\"endTime\": \"2018-05-25T23:50:17Z\"}, \"tags\": [{\"key\": \"user_agent\", \"value\": \"luci-scheduler\"}]}"
+ ],
+ "infra_step": true,
+ "name": "fetch previous build",
+ "~followup_annotations": [
+ "@@@STEP_LOG_END@raw_io.output_text@@@"
+ ]
+ },
+ {
+ "name": "$result"
+ }
+]
\ No newline at end of file
diff --git a/recipes/dart/flutter_engine.expected/continue-bisect-on-failure.json b/recipe_modules/bisect_build/tests/tests.expected/continue-bisect-on-failure.json
similarity index 75%
rename from recipes/dart/flutter_engine.expected/continue-bisect-on-failure.json
rename to recipe_modules/bisect_build/tests/tests.expected/continue-bisect-on-failure.json
index e88b0af..74023c0 100644
--- a/recipes/dart/flutter_engine.expected/continue-bisect-on-failure.json
+++ b/recipe_modules/bisect_build/tests/tests.expected/continue-bisect-on-failure.json
@@ -8,7 +8,7 @@
],
"infra_step": true,
"name": "schedule bisect (d)",
- "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci\", \"builder\": \"flutter-engine-linux\", \"project\": \"project\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"d\", \"project\": \"linear_sdk_flutter_engine\", \"ref\": \"refs/heads/master\"}, \"priority\": 30, \"properties\": {\"bisect_newer\": [\"c\"], \"bisect_older\": [\"e\"], \"bisect_reason\": \"Infra Failure: Step('everything') (retcode: 1)\"}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
+ "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci\", \"builder\": \"builder\", \"project\": \"project\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"d\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"priority\": 30, \"properties\": {\"bisect_newer\": [\"c\"], \"bisect_older\": [\"e\"], \"bisect_reason\": \"failure\"}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"responses\": [@@@",
@@ -16,7 +16,7 @@
"@@@STEP_LOG_LINE@json.output@ \"scheduleBuild\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"builder\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"bucket\": \"ci\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"builder\": \"flutter-engine-linux\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"builder\": \"builder\", @@@",
"@@@STEP_LOG_LINE@json.output@ \"project\": \"project\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ \"id\": \"8922054662172514000\"@@@",
@@ -31,7 +31,7 @@
"@@@STEP_LOG_LINE@request@ \"scheduleBuild\": {@@@",
"@@@STEP_LOG_LINE@request@ \"builder\": {@@@",
"@@@STEP_LOG_LINE@request@ \"bucket\": \"ci\", @@@",
- "@@@STEP_LOG_LINE@request@ \"builder\": \"flutter-engine-linux\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"builder\": \"builder\", @@@",
"@@@STEP_LOG_LINE@request@ \"project\": \"project\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"experimental\": \"NO\", @@@",
@@ -39,7 +39,7 @@
"@@@STEP_LOG_LINE@request@ \"gitilesCommit\": {@@@",
"@@@STEP_LOG_LINE@request@ \"host\": \"dart.googlesource.com\", @@@",
"@@@STEP_LOG_LINE@request@ \"id\": \"d\", @@@",
- "@@@STEP_LOG_LINE@request@ \"project\": \"linear_sdk_flutter_engine\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"project\": \"sdk\", @@@",
"@@@STEP_LOG_LINE@request@ \"ref\": \"refs/heads/master\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"priority\": 30, @@@",
@@ -50,7 +50,7 @@
"@@@STEP_LOG_LINE@request@ \"bisect_older\": [@@@",
"@@@STEP_LOG_LINE@request@ \"e\"@@@",
"@@@STEP_LOG_LINE@request@ ], @@@",
- "@@@STEP_LOG_LINE@request@ \"bisect_reason\": \"Infra Failure: Step('everything') (retcode: 1)\"@@@",
+ "@@@STEP_LOG_LINE@request@ \"bisect_reason\": \"failure\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", @@@",
"@@@STEP_LOG_LINE@request@ \"tags\": [@@@",
diff --git a/recipes/dart/flutter_engine.expected/continue-bisect-on-failure.json b/recipe_modules/bisect_build/tests/tests.expected/continue-bisect-on-success.json
similarity index 71%
copy from recipes/dart/flutter_engine.expected/continue-bisect-on-failure.json
copy to recipe_modules/bisect_build/tests/tests.expected/continue-bisect-on-success.json
index e88b0af..7e7ab86 100644
--- a/recipes/dart/flutter_engine.expected/continue-bisect-on-failure.json
+++ b/recipe_modules/bisect_build/tests/tests.expected/continue-bisect-on-success.json
@@ -7,8 +7,8 @@
"cr-buildbucket.appspot.com"
],
"infra_step": true,
- "name": "schedule bisect (d)",
- "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci\", \"builder\": \"flutter-engine-linux\", \"project\": \"project\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"d\", \"project\": \"linear_sdk_flutter_engine\", \"ref\": \"refs/heads/master\"}, \"priority\": 30, \"properties\": {\"bisect_newer\": [\"c\"], \"bisect_older\": [\"e\"], \"bisect_reason\": \"Infra Failure: Step('everything') (retcode: 1)\"}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
+ "name": "schedule bisect (b)",
+ "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci\", \"builder\": \"builder\", \"project\": \"project\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"b\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"priority\": 30, \"properties\": {\"bisect_newer\": [\"a\"], \"bisect_older\": [\"c\"], \"bisect_reason\": \"failure\"}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"responses\": [@@@",
@@ -16,7 +16,7 @@
"@@@STEP_LOG_LINE@json.output@ \"scheduleBuild\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"builder\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"bucket\": \"ci\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"builder\": \"flutter-engine-linux\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"builder\": \"builder\", @@@",
"@@@STEP_LOG_LINE@json.output@ \"project\": \"project\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ \"id\": \"8922054662172514000\"@@@",
@@ -31,26 +31,26 @@
"@@@STEP_LOG_LINE@request@ \"scheduleBuild\": {@@@",
"@@@STEP_LOG_LINE@request@ \"builder\": {@@@",
"@@@STEP_LOG_LINE@request@ \"bucket\": \"ci\", @@@",
- "@@@STEP_LOG_LINE@request@ \"builder\": \"flutter-engine-linux\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"builder\": \"builder\", @@@",
"@@@STEP_LOG_LINE@request@ \"project\": \"project\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"experimental\": \"NO\", @@@",
"@@@STEP_LOG_LINE@request@ \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", @@@",
"@@@STEP_LOG_LINE@request@ \"gitilesCommit\": {@@@",
"@@@STEP_LOG_LINE@request@ \"host\": \"dart.googlesource.com\", @@@",
- "@@@STEP_LOG_LINE@request@ \"id\": \"d\", @@@",
- "@@@STEP_LOG_LINE@request@ \"project\": \"linear_sdk_flutter_engine\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"id\": \"b\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"project\": \"sdk\", @@@",
"@@@STEP_LOG_LINE@request@ \"ref\": \"refs/heads/master\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"priority\": 30, @@@",
"@@@STEP_LOG_LINE@request@ \"properties\": {@@@",
"@@@STEP_LOG_LINE@request@ \"bisect_newer\": [@@@",
- "@@@STEP_LOG_LINE@request@ \"c\"@@@",
+ "@@@STEP_LOG_LINE@request@ \"a\"@@@",
"@@@STEP_LOG_LINE@request@ ], @@@",
"@@@STEP_LOG_LINE@request@ \"bisect_older\": [@@@",
- "@@@STEP_LOG_LINE@request@ \"e\"@@@",
+ "@@@STEP_LOG_LINE@request@ \"c\"@@@",
"@@@STEP_LOG_LINE@request@ ], @@@",
- "@@@STEP_LOG_LINE@request@ \"bisect_reason\": \"Infra Failure: Step('everything') (retcode: 1)\"@@@",
+ "@@@STEP_LOG_LINE@request@ \"bisect_reason\": \"failure\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", @@@",
"@@@STEP_LOG_LINE@request@ \"tags\": [@@@",
diff --git a/recipe_modules/bisect_build/tests/tests.expected/do-not-start-bisect-if-previous-build-failed.json b/recipe_modules/bisect_build/tests/tests.expected/do-not-start-bisect-if-previous-build-failed.json
new file mode 100644
index 0000000..a6cc90b
--- /dev/null
+++ b/recipe_modules/bisect_build/tests/tests.expected/do-not-start-bisect-if-previous-build-failed.json
@@ -0,0 +1,28 @@
+[
+ {
+ "cmd": [
+ "bb",
+ "ls",
+ "-host",
+ "cr-buildbucket.appspot.com",
+ "-json",
+ "-nopage",
+ "-n",
+ "1",
+ "-fields",
+ "builder,create_time,created_by,critical,end_time,id,input,number,output,start_time,status,update_time",
+ "-predicate",
+ "{\"builder\": {\"bucket\": \"ci\", \"builder\": \"builder\", \"project\": \"project\"}, \"createTime\": {\"endTime\": \"2018-05-25T23:50:17Z\"}, \"tags\": [{\"key\": \"user_agent\", \"value\": \"luci-scheduler\"}]}"
+ ],
+ "infra_step": true,
+ "name": "fetch previous build",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@raw_io.output_text@{\"status\": \"FAILURE\", \"builder\": {\"project\": \"project\", \"builder\": \"builder\", \"bucket\": \"ci\"}, \"createTime\": \"2018-05-25T23:50:17Z\", \"createdBy\": \"user:luci-scheduler@appspot.gserviceaccount.com\", \"input\": {\"gitilesCommit\": {\"project\": \"project\", \"host\": \"chromium.googlesource.com\", \"ref\": \"refs/heads/master\", \"id\": \"2d72510e447ab60a9728aeea2362d8be2cbd7789\"}}, \"infra\": {\"swarming\": {\"priority\": 30}}, \"id\": \"8945511751514863184\"}@@@",
+ "@@@STEP_LOG_END@raw_io.output_text@@@",
+ "@@@STEP_LINK@8945511751514863184@https://cr-buildbucket.appspot.com/build/8945511751514863184@@@"
+ ]
+ },
+ {
+ "name": "$result"
+ }
+]
\ No newline at end of file
diff --git a/recipe_modules/bisect_build/tests/tests.expected/do-not-start-bisect-without-current-revision.json b/recipe_modules/bisect_build/tests/tests.expected/do-not-start-bisect-without-current-revision.json
new file mode 100644
index 0000000..b6042b6
--- /dev/null
+++ b/recipe_modules/bisect_build/tests/tests.expected/do-not-start-bisect-without-current-revision.json
@@ -0,0 +1,5 @@
+[
+ {
+ "name": "$result"
+ }
+]
\ No newline at end of file
diff --git a/recipe_modules/bisect_build/tests/tests.expected/do-not-start-bisect-without-previous-build.json b/recipe_modules/bisect_build/tests/tests.expected/do-not-start-bisect-without-previous-build.json
new file mode 100644
index 0000000..7647f3f
--- /dev/null
+++ b/recipe_modules/bisect_build/tests/tests.expected/do-not-start-bisect-without-previous-build.json
@@ -0,0 +1,26 @@
+[
+ {
+ "cmd": [
+ "bb",
+ "ls",
+ "-host",
+ "cr-buildbucket.appspot.com",
+ "-json",
+ "-nopage",
+ "-n",
+ "1",
+ "-fields",
+ "builder,create_time,created_by,critical,end_time,id,input,number,output,start_time,status,update_time",
+ "-predicate",
+ "{\"builder\": {\"bucket\": \"ci\", \"builder\": \"builder\", \"project\": \"project\"}, \"createTime\": {\"endTime\": \"2018-05-25T23:50:17Z\"}, \"tags\": [{\"key\": \"user_agent\", \"value\": \"luci-scheduler\"}]}"
+ ],
+ "infra_step": true,
+ "name": "fetch previous build",
+ "~followup_annotations": [
+ "@@@STEP_LOG_END@raw_io.output_text@@@"
+ ]
+ },
+ {
+ "name": "$result"
+ }
+]
\ No newline at end of file
diff --git a/recipes/dart/flutter_engine.expected/fan-out-on-distinct-failure.json b/recipe_modules/bisect_build/tests/tests.expected/fan-out-on-distinct-failure.json
similarity index 78%
rename from recipes/dart/flutter_engine.expected/fan-out-on-distinct-failure.json
rename to recipe_modules/bisect_build/tests/tests.expected/fan-out-on-distinct-failure.json
index bb97a19..42a9ee5 100644
--- a/recipes/dart/flutter_engine.expected/fan-out-on-distinct-failure.json
+++ b/recipe_modules/bisect_build/tests/tests.expected/fan-out-on-distinct-failure.json
@@ -8,7 +8,7 @@
],
"infra_step": true,
"name": "schedule bisect (b)",
- "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci\", \"builder\": \"flutter-engine-linux\", \"project\": \"project\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"b\", \"project\": \"linear_sdk_flutter_engine\", \"ref\": \"refs/heads/master\"}, \"priority\": 30, \"properties\": {\"bisect_newer\": [\"a\"], \"bisect_older\": [\"c\"], \"bisect_reason\": \"different failure\"}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
+ "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci\", \"builder\": \"builder\", \"project\": \"project\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"b\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"priority\": 30, \"properties\": {\"bisect_newer\": [\"a\"], \"bisect_older\": [\"c\"], \"bisect_reason\": \"failure\"}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"responses\": [@@@",
@@ -16,7 +16,7 @@
"@@@STEP_LOG_LINE@json.output@ \"scheduleBuild\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"builder\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"bucket\": \"ci\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"builder\": \"flutter-engine-linux\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"builder\": \"builder\", @@@",
"@@@STEP_LOG_LINE@json.output@ \"project\": \"project\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ \"id\": \"8922054662172514000\"@@@",
@@ -31,7 +31,7 @@
"@@@STEP_LOG_LINE@request@ \"scheduleBuild\": {@@@",
"@@@STEP_LOG_LINE@request@ \"builder\": {@@@",
"@@@STEP_LOG_LINE@request@ \"bucket\": \"ci\", @@@",
- "@@@STEP_LOG_LINE@request@ \"builder\": \"flutter-engine-linux\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"builder\": \"builder\", @@@",
"@@@STEP_LOG_LINE@request@ \"project\": \"project\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"experimental\": \"NO\", @@@",
@@ -39,7 +39,7 @@
"@@@STEP_LOG_LINE@request@ \"gitilesCommit\": {@@@",
"@@@STEP_LOG_LINE@request@ \"host\": \"dart.googlesource.com\", @@@",
"@@@STEP_LOG_LINE@request@ \"id\": \"b\", @@@",
- "@@@STEP_LOG_LINE@request@ \"project\": \"linear_sdk_flutter_engine\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"project\": \"sdk\", @@@",
"@@@STEP_LOG_LINE@request@ \"ref\": \"refs/heads/master\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"priority\": 30, @@@",
@@ -50,7 +50,7 @@
"@@@STEP_LOG_LINE@request@ \"bisect_older\": [@@@",
"@@@STEP_LOG_LINE@request@ \"c\"@@@",
"@@@STEP_LOG_LINE@request@ ], @@@",
- "@@@STEP_LOG_LINE@request@ \"bisect_reason\": \"different failure\"@@@",
+ "@@@STEP_LOG_LINE@request@ \"bisect_reason\": \"failure\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", @@@",
"@@@STEP_LOG_LINE@request@ \"tags\": [@@@",
@@ -76,7 +76,7 @@
],
"infra_step": true,
"name": "schedule bisect (d)",
- "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci\", \"builder\": \"flutter-engine-linux\", \"project\": \"project\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"d\", \"project\": \"linear_sdk_flutter_engine\", \"ref\": \"refs/heads/master\"}, \"priority\": 30, \"properties\": {\"bisect_newer\": [\"c\"], \"bisect_older\": [\"e\"], \"bisect_reason\": \"Infra Failure: Step('everything') (retcode: 1)\"}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-00000000133a\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
+ "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci\", \"builder\": \"builder\", \"project\": \"project\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"d\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"priority\": 30, \"properties\": {\"bisect_newer\": [\"c\"], \"bisect_older\": [\"e\"], \"bisect_reason\": \"different failure\"}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-00000000133a\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
"~followup_annotations": [
"@@@STEP_LOG_LINE@json.output@{@@@",
"@@@STEP_LOG_LINE@json.output@ \"responses\": [@@@",
@@ -84,7 +84,7 @@
"@@@STEP_LOG_LINE@json.output@ \"scheduleBuild\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"builder\": {@@@",
"@@@STEP_LOG_LINE@json.output@ \"bucket\": \"ci\", @@@",
- "@@@STEP_LOG_LINE@json.output@ \"builder\": \"flutter-engine-linux\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"builder\": \"builder\", @@@",
"@@@STEP_LOG_LINE@json.output@ \"project\": \"project\"@@@",
"@@@STEP_LOG_LINE@json.output@ }, @@@",
"@@@STEP_LOG_LINE@json.output@ \"id\": \"8922054662172514001\"@@@",
@@ -99,7 +99,7 @@
"@@@STEP_LOG_LINE@request@ \"scheduleBuild\": {@@@",
"@@@STEP_LOG_LINE@request@ \"builder\": {@@@",
"@@@STEP_LOG_LINE@request@ \"bucket\": \"ci\", @@@",
- "@@@STEP_LOG_LINE@request@ \"builder\": \"flutter-engine-linux\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"builder\": \"builder\", @@@",
"@@@STEP_LOG_LINE@request@ \"project\": \"project\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"experimental\": \"NO\", @@@",
@@ -107,7 +107,7 @@
"@@@STEP_LOG_LINE@request@ \"gitilesCommit\": {@@@",
"@@@STEP_LOG_LINE@request@ \"host\": \"dart.googlesource.com\", @@@",
"@@@STEP_LOG_LINE@request@ \"id\": \"d\", @@@",
- "@@@STEP_LOG_LINE@request@ \"project\": \"linear_sdk_flutter_engine\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"project\": \"sdk\", @@@",
"@@@STEP_LOG_LINE@request@ \"ref\": \"refs/heads/master\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"priority\": 30, @@@",
@@ -118,7 +118,7 @@
"@@@STEP_LOG_LINE@request@ \"bisect_older\": [@@@",
"@@@STEP_LOG_LINE@request@ \"e\"@@@",
"@@@STEP_LOG_LINE@request@ ], @@@",
- "@@@STEP_LOG_LINE@request@ \"bisect_reason\": \"Infra Failure: Step('everything') (retcode: 1)\"@@@",
+ "@@@STEP_LOG_LINE@request@ \"bisect_reason\": \"different failure\"@@@",
"@@@STEP_LOG_LINE@request@ }, @@@",
"@@@STEP_LOG_LINE@request@ \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-00000000133a\", @@@",
"@@@STEP_LOG_LINE@request@ \"tags\": [@@@",
diff --git a/recipe_modules/bisect_build/tests/tests.expected/starts bisection.json b/recipe_modules/bisect_build/tests/tests.expected/starts bisection.json
new file mode 100644
index 0000000..c3ee8a1
--- /dev/null
+++ b/recipe_modules/bisect_build/tests/tests.expected/starts bisection.json
@@ -0,0 +1,202 @@
+[
+ {
+ "cmd": [
+ "bb",
+ "ls",
+ "-host",
+ "cr-buildbucket.appspot.com",
+ "-json",
+ "-nopage",
+ "-n",
+ "1",
+ "-fields",
+ "builder,create_time,created_by,critical,end_time,id,input,number,output,start_time,status,update_time",
+ "-predicate",
+ "{\"builder\": {\"bucket\": \"ci\", \"builder\": \"builder\", \"project\": \"project\"}, \"createTime\": {\"endTime\": \"2018-05-25T23:50:17Z\"}, \"tags\": [{\"key\": \"user_agent\", \"value\": \"luci-scheduler\"}]}"
+ ],
+ "infra_step": true,
+ "name": "fetch previous build",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@raw_io.output_text@{\"status\": \"SUCCESS\", \"builder\": {\"project\": \"project\", \"builder\": \"builder\", \"bucket\": \"ci\"}, \"createTime\": \"2018-05-25T23:50:17Z\", \"createdBy\": \"user:luci-scheduler@appspot.gserviceaccount.com\", \"input\": {\"gitilesCommit\": {\"project\": \"project\", \"host\": \"chromium.googlesource.com\", \"ref\": \"refs/heads/master\", \"id\": \"2d72510e447ab60a9728aeea2362d8be2cbd7789\"}}, \"infra\": {\"swarming\": {\"priority\": 30}}, \"id\": \"8945511751514863184\"}@@@",
+ "@@@STEP_LOG_END@raw_io.output_text@@@",
+ "@@@STEP_LINK@8945511751514863184@https://cr-buildbucket.appspot.com/build/8945511751514863184@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "python",
+ "-u",
+ "RECIPE_MODULE[depot_tools::gitiles]/resources/gerrit_client.py",
+ "--json-file",
+ "/path/to/tmp/json",
+ "--url",
+ "https://dummy/repo/url/+log/2d72510e447ab60a9728aeea2362d8be2cbd7789..ffffffff",
+ "--format",
+ "json",
+ "--log-limit",
+ "0"
+ ],
+ "name": "gitiles log: 2d72510e447ab60a9728aeea2362d8be2cbd7789..ffffffff",
+ "~followup_annotations": [
+ "@@@STEP_TEXT@<br />3 commits fetched@@@",
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"log\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"author\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"email\": \"fake_master@fake_0.email.com\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"name\": \"fake_master\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"commit\": \"bda185dc04d062d391039867ae671ac9133e9801\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"committer\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"email\": \"fake_master@fake_0.email.com\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"name\": \"fake_master\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"message\": \"fake master msg 0\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"parents\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"faed5d85b02d45b884a6c84a69c198c120b36380\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ], @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"tree\": \"a32438c6a72030d92b00f5eb682e7745338e6668\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"tree_diff\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"new_id\": \"1f944b71d6fcc086596ced8938eff992cbe4e326\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"new_mode\": 33188, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"new_path\": \"a.py\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"old_id\": \"0000000000000000000000000000000000000000\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"old_mode\": 0, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"type\": \"add\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }, @@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"author\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"email\": \"fake_master@fake_1.email.com\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"name\": \"fake_master\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"commit\": \"8331c527346abecfe0ad081df241512bb4b7df50\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"committer\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"email\": \"fake_master@fake_1.email.com\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"name\": \"fake_master\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"message\": \"fake master msg 1\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"parents\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"c50e65308b4d20210b14a612d2afb6acb3e830d9\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ], @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"tree\": \"97cbb78571a53287bb1f64a5679f84c396df55ac\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"tree_diff\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"new_id\": \"5e881d213594ff0042014750f2ad397eaaeebee4\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"new_mode\": 33188, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"new_path\": \"b.py\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"old_id\": \"0000000000000000000000000000000000000000\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"old_mode\": 0, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"type\": \"add\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }, @@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"author\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"email\": \"fake_master@fake_2.email.com\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"name\": \"fake_master\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"commit\": \"f4d35da881f8fd329a4d3e01dd78b66a502d5c49\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"committer\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"email\": \"fake_master@fake_2.email.com\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"name\": \"fake_master\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"time\": \"Mon Jan 01 00:00:00 2015\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"message\": \"fake master msg 2\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"parents\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"73c504dc712dae16fdd38ba50db2a1c5a669cd22\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ ], @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"tree\": \"5ae2afc02495c1adee6a3233581f73dac4f7a76a\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"tree_diff\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"new_id\": \"38952d2a55008e5afca8d49d8dd78c448d1a7c6c\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"new_mode\": 33188, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"new_path\": \"c.py\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"old_id\": \"0000000000000000000000000000000000000000\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"old_mode\": 0, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"type\": \"add\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@"
+ ]
+ },
+ {
+ "cmd": [
+ "bb",
+ "batch",
+ "-host",
+ "cr-buildbucket.appspot.com"
+ ],
+ "infra_step": true,
+ "name": "schedule bisect (f4d35da881f8fd329a4d3e01dd78b66a502d5c49)",
+ "stdin": "{\"requests\": [{\"scheduleBuild\": {\"builder\": {\"bucket\": \"ci\", \"builder\": \"builder\", \"project\": \"project\"}, \"experimental\": \"NO\", \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", \"gitilesCommit\": {\"host\": \"dart.googlesource.com\", \"id\": \"f4d35da881f8fd329a4d3e01dd78b66a502d5c49\", \"project\": \"sdk\", \"ref\": \"refs/heads/master\"}, \"priority\": 30, \"properties\": {\"bisect_newer\": [\"8331c527346abecfe0ad081df241512bb4b7df50\"], \"bisect_older\": [], \"bisect_reason\": \"failure\"}, \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", \"tags\": [{\"key\": \"user_agent\", \"value\": \"recipe\"}]}}]}",
+ "~followup_annotations": [
+ "@@@STEP_LOG_LINE@json.output@{@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"responses\": [@@@",
+ "@@@STEP_LOG_LINE@json.output@ {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"scheduleBuild\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"builder\": {@@@",
+ "@@@STEP_LOG_LINE@json.output@ \"bucket\": \"ci\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"builder\": \"builder\", @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"project\": \"project\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }, @@@",
+ "@@@STEP_LOG_LINE@json.output@ \"id\": \"8922054662172514000\"@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ }@@@",
+ "@@@STEP_LOG_LINE@json.output@ ]@@@",
+ "@@@STEP_LOG_LINE@json.output@}@@@",
+ "@@@STEP_LOG_END@json.output@@@",
+ "@@@STEP_LOG_LINE@request@{@@@",
+ "@@@STEP_LOG_LINE@request@ \"requests\": [@@@",
+ "@@@STEP_LOG_LINE@request@ {@@@",
+ "@@@STEP_LOG_LINE@request@ \"scheduleBuild\": {@@@",
+ "@@@STEP_LOG_LINE@request@ \"builder\": {@@@",
+ "@@@STEP_LOG_LINE@request@ \"bucket\": \"ci\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"builder\": \"builder\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"project\": \"project\"@@@",
+ "@@@STEP_LOG_LINE@request@ }, @@@",
+ "@@@STEP_LOG_LINE@request@ \"experimental\": \"NO\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"fields\": \"builder,createTime,createdBy,critical,endTime,id,input,number,output,startTime,status,updateTime\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"gitilesCommit\": {@@@",
+ "@@@STEP_LOG_LINE@request@ \"host\": \"dart.googlesource.com\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"id\": \"f4d35da881f8fd329a4d3e01dd78b66a502d5c49\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"project\": \"sdk\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"ref\": \"refs/heads/master\"@@@",
+ "@@@STEP_LOG_LINE@request@ }, @@@",
+ "@@@STEP_LOG_LINE@request@ \"priority\": 30, @@@",
+ "@@@STEP_LOG_LINE@request@ \"properties\": {@@@",
+ "@@@STEP_LOG_LINE@request@ \"bisect_newer\": [@@@",
+ "@@@STEP_LOG_LINE@request@ \"8331c527346abecfe0ad081df241512bb4b7df50\"@@@",
+ "@@@STEP_LOG_LINE@request@ ], @@@",
+ "@@@STEP_LOG_LINE@request@ \"bisect_older\": [], @@@",
+ "@@@STEP_LOG_LINE@request@ \"bisect_reason\": \"failure\"@@@",
+ "@@@STEP_LOG_LINE@request@ }, @@@",
+ "@@@STEP_LOG_LINE@request@ \"requestId\": \"8945511751514863184-00000000-0000-0000-0000-000000001337\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"tags\": [@@@",
+ "@@@STEP_LOG_LINE@request@ {@@@",
+ "@@@STEP_LOG_LINE@request@ \"key\": \"user_agent\", @@@",
+ "@@@STEP_LOG_LINE@request@ \"value\": \"recipe\"@@@",
+ "@@@STEP_LOG_LINE@request@ }@@@",
+ "@@@STEP_LOG_LINE@request@ ]@@@",
+ "@@@STEP_LOG_LINE@request@ }@@@",
+ "@@@STEP_LOG_LINE@request@ }@@@",
+ "@@@STEP_LOG_LINE@request@ ]@@@",
+ "@@@STEP_LOG_LINE@request@}@@@",
+ "@@@STEP_LOG_END@request@@@",
+ "@@@STEP_LINK@8922054662172514000@https://cr-buildbucket.appspot.com/build/8922054662172514000@@@"
+ ]
+ },
+ {
+ "name": "$result"
+ }
+]
\ No newline at end of file
diff --git a/recipe_modules/bisect_build/tests/tests.py b/recipe_modules/bisect_build/tests/tests.py
new file mode 100644
index 0000000..33a6b68
--- /dev/null
+++ b/recipe_modules/bisect_build/tests/tests.py
@@ -0,0 +1,105 @@
+# Copyright (c) 2020, the Dart project 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.post_process import DropExpectation, DoesNotRunRE, Filter, MustRun
+from recipe_engine.recipe_api import Property
+
+DEPS = [
+ 'depot_tools/gitiles',
+ 'bisect_build',
+ 'recipe_engine/properties',
+ 'recipe_engine/step',
+ 'recipe_engine/buildbucket',
+]
+
+PROPERTIES = {
+ 'current_failure':
+ Property(
+ kind=str,
+ help="Reason for current failure or 'SUCCESS'",
+ default="SUCCESS"),
+}
+
+
+def RunSteps(api, current_failure):
+ api.m.bisect_build.schedule("https://dummy/repo/url", current_failure)
+
+
+def _test(api, name, failure=None):
+ data = api.test(
+ name,
+ api.buildbucket.ci_build(
+ builder='builder',
+ git_repo='https://dart.googlesource.com/sdk',
+ revision='f' * 8))
+ if failure:
+ data += api.m.properties(current_failure=failure)
+ return data
+
+
+def GenTests(api):
+ yield _test(api, 'basic')
+
+ yield (_test(api, 'starts bisection', failure='failure') + api.step_data(
+ 'gitiles log: 2d72510e447ab60a9728aeea2362d8be2cbd7789..ffffffff',
+ api.gitiles.make_log_test_data('master')) +
+ api.buildbucket.simulated_search_results(
+ [
+ api.buildbucket.ci_build_message(status='SUCCESS'),
+ ],
+ step_name='fetch previous build') + api.post_process(
+ MustRun,
+ 'schedule bisect (f4d35da881f8fd329a4d3e01dd78b66a502d5c49)'))
+
+ yield (_test(
+ api, 'do-not-start-bisect-if-previous-build-failed', failure="failure") +
+ api.buildbucket.simulated_search_results(
+ [
+ api.buildbucket.ci_build_message(status='FAILURE'),
+ ],
+ step_name='fetch previous build') + api.post_process(
+ DoesNotRunRE, r'schedule bisect.*'))
+
+ yield (_test(
+ api, 'do-not-start-bisect-without-previous-build', failure="failure") +
+ api.buildbucket.simulated_search_results(
+ [], step_name='fetch previous build') + api.post_process(
+ DoesNotRunRE, r'schedule bisect.*'))
+
+ yield api.test(
+ 'do-not-start-bisect-without-current-revision',
+ api.buildbucket.ci_build(
+ builder='builder',
+ git_repo='https://dart.googlesource.com/sdk',
+ revision=None), api.m.properties(current_failure="failure"),
+ api.post_process(DoesNotRunRE, r'schedule bisect.*'))
+
+ yield (_test(api, 'continue-bisect-on-failure', failure="failure") +
+ api.properties(
+ bisect_newer=['a', 'b', 'c'],
+ bisect_older=['c', 'd', 'e'],
+ bisect_reason="failure") + api.post_process(
+ MustRun, 'schedule bisect (d)') + api.post_process(
+ Filter('schedule bisect (d)')))
+
+ yield (_test(api, 'continue-bisect-on-success') + api.properties(
+ bisect_newer=['a', 'b', 'c'],
+ bisect_older=['c', 'd', 'e'],
+ bisect_reason="failure") + api.post_process(
+ MustRun, 'schedule bisect (b)') + api.post_process(
+ Filter('schedule bisect (b)')))
+
+ yield (_test(api, 'fan-out-on-distinct-failure', failure="different failure")
+ + api.properties(
+ bisect_newer=['a', 'b', 'c'],
+ bisect_older=['c', 'd', 'e'],
+ bisect_reason='failure') + api.post_process(
+ MustRun, 'schedule bisect (b)') + api.post_process(
+ MustRun, 'schedule bisect (d)') + api.post_process(
+ Filter().include_re(r'schedule bisect \(.*\)')))
+
+ yield (_test(api, 'stop-bisect') + api.properties(
+ bisect_newer=[], bisect_older=[], bisect_reason='failure') +
+ api.post_process(DoesNotRunRE, r'schedule bisect.*') +
+ api.post_process(DropExpectation))
diff --git a/recipe_modules/dart/api.py b/recipe_modules/dart/api.py
index f81915e..2afc635 100644
--- a/recipe_modules/dart/api.py
+++ b/recipe_modules/dart/api.py
@@ -273,6 +273,7 @@
self.m.buildbucket.builder_name.endswith('-dev') or
self.m.buildbucket.builder_name.endswith('-stable'))
+
def _try_builder(self):
"""Boolean that reports whether this a try builder.
Some steps are not run on the try builders."""
@@ -331,8 +332,6 @@
def _get_latest_tested_commit(self):
builder = self._get_builder_dir()
- # Note: The pre-approval script relies on this step being named
- # gsutil_find_latest_build inside the nested step download_previous_results.
latest_result = self.m.gsutil.download(
'dart-test-results',
'builders/%s/latest' % builder,
@@ -350,7 +349,6 @@
name='get revision for latest build',
ok_ret='any') # TODO(athom): succeed only if file does not exist
revision = revision_result.raw_io.output_texts.get('revision')
-
return (latest, revision)
@@ -1123,7 +1121,6 @@
class StepResults:
-
def __init__(self, m):
self.logs = ''
self.results = ''
diff --git a/recipes/dart/flutter_engine.expected/do-not-start-bisect-if-previous-build-failed.json b/recipes/dart/flutter_engine.expected/do-not-start-bisect-if-previous-build-failed.json
deleted file mode 100644
index 9171738..0000000
--- a/recipes/dart/flutter_engine.expected/do-not-start-bisect-if-previous-build-failed.json
+++ /dev/null
@@ -1,56 +0,0 @@
-[
- {
- "cmd": [
- "vpython",
- "-u",
- "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
- "--json-output",
- "/path/to/tmp/json",
- "rmcontents",
- "[CACHE]/builder"
- ],
- "cwd": "[CACHE]/builder",
- "infra_step": true,
- "name": "everything",
- "~followup_annotations": [
- "@@@STEP_EXCEPTION@@@"
- ]
- },
- {
- "cmd": [
- "bb",
- "ls",
- "-host",
- "cr-buildbucket.appspot.com",
- "-json",
- "-nopage",
- "-n",
- "1",
- "-fields",
- "builder,create_time,created_by,critical,end_time,id,input,number,output,start_time,status,update_time",
- "-predicate",
- "{\"builder\": {\"bucket\": \"ci\", \"builder\": \"flutter-engine-linux\", \"project\": \"project\"}, \"createTime\": {\"endTime\": \"2018-05-25T23:50:17Z\"}, \"tags\": [{\"key\": \"user_agent\", \"value\": \"luci-scheduler\"}]}"
- ],
- "infra_step": true,
- "name": "fetch previous build",
- "~followup_annotations": [
- "@@@STEP_LOG_LINE@raw_io.output_text@{\"status\": \"FAILURE\", \"builder\": {\"project\": \"project\", \"builder\": \"builder\", \"bucket\": \"ci\"}, \"createTime\": \"2018-05-25T23:50:17Z\", \"createdBy\": \"user:luci-scheduler@appspot.gserviceaccount.com\", \"input\": {\"gitilesCommit\": {\"project\": \"project\", \"host\": \"chromium.googlesource.com\", \"ref\": \"refs/heads/master\", \"id\": \"2d72510e447ab60a9728aeea2362d8be2cbd7789\"}}, \"infra\": {\"swarming\": {\"priority\": 30}}, \"id\": \"8945511751514863184\"}@@@",
- "@@@STEP_LOG_END@raw_io.output_text@@@",
- "@@@STEP_LINK@8945511751514863184@https://cr-buildbucket.appspot.com/build/8945511751514863184@@@"
- ]
- },
- {
- "cmd": [
- "python",
- "-u",
- "[CACHE]/builder/src/third_party/dart/tools/task_kill.py"
- ],
- "name": "kill processes"
- },
- {
- "failure": {
- "humanReason": "Infra Failure: Step('everything') (retcode: 1)"
- },
- "name": "$result"
- }
-]
\ No newline at end of file
diff --git a/recipes/dart/flutter_engine.expected/do-not-start-bisect-without-previous-build.json b/recipes/dart/flutter_engine.expected/do-not-start-bisect-without-previous-build.json
deleted file mode 100644
index f293d5c..0000000
--- a/recipes/dart/flutter_engine.expected/do-not-start-bisect-without-previous-build.json
+++ /dev/null
@@ -1,54 +0,0 @@
-[
- {
- "cmd": [
- "vpython",
- "-u",
- "RECIPE_MODULE[recipe_engine::file]/resources/fileutil.py",
- "--json-output",
- "/path/to/tmp/json",
- "rmcontents",
- "[CACHE]/builder"
- ],
- "cwd": "[CACHE]/builder",
- "infra_step": true,
- "name": "everything",
- "~followup_annotations": [
- "@@@STEP_EXCEPTION@@@"
- ]
- },
- {
- "cmd": [
- "bb",
- "ls",
- "-host",
- "cr-buildbucket.appspot.com",
- "-json",
- "-nopage",
- "-n",
- "1",
- "-fields",
- "builder,create_time,created_by,critical,end_time,id,input,number,output,start_time,status,update_time",
- "-predicate",
- "{\"builder\": {\"bucket\": \"ci\", \"builder\": \"flutter-engine-linux\", \"project\": \"project\"}, \"createTime\": {\"endTime\": \"2018-05-25T23:50:17Z\"}, \"tags\": [{\"key\": \"user_agent\", \"value\": \"luci-scheduler\"}]}"
- ],
- "infra_step": true,
- "name": "fetch previous build",
- "~followup_annotations": [
- "@@@STEP_LOG_END@raw_io.output_text@@@"
- ]
- },
- {
- "cmd": [
- "python",
- "-u",
- "[CACHE]/builder/src/third_party/dart/tools/task_kill.py"
- ],
- "name": "kill processes"
- },
- {
- "failure": {
- "humanReason": "Infra Failure: Step('everything') (retcode: 1)"
- },
- "name": "$result"
- }
-]
\ No newline at end of file
diff --git a/recipes/dart/flutter_engine.py b/recipes/dart/flutter_engine.py
index 85516cd..68e12cf 100644
--- a/recipes/dart/flutter_engine.py
+++ b/recipes/dart/flutter_engine.py
@@ -8,11 +8,8 @@
from recipe_engine.post_process import (
DoesNotRunRE, DropExpectation, Filter, MustRun)
-from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
-from PB.go.chromium.org.luci.buildbucket.proto import rpc as rpc_pb2
-
-
DEPS = [
+ 'bisect_build',
'dart',
'depot_tools/bot_update',
'depot_tools/depot_tools',
@@ -327,25 +324,15 @@
BuildAndTest(api, start_dir, checkout_dir, flutter_rev)
except recipe_api.StepFailure as failure:
- if _is_bisecting(api):
- bisect_reason = api.properties['bisect_reason']
- if bisect_reason == failure.reason:
- _bisect_older(api, bisect_reason)
- else:
- _bisect_newer(api, bisect_reason)
- # The build failed for a different reason, fan out to find the root
- # cause of that failure as well.
- _bisect_older(api, failure.reason)
- elif api.buildbucket.gitiles_commit.id:
- # Tryjobs don't have an input commit and can't be bisected.
- _start_bisection(api, failure.reason)
+ if api.buildbucket.gitiles_commit.id:
+ api.m.bisect_build.schedule(LINEARIZED_REPO_URL, failure.reason)
raise
finally:
# TODO(aam): Go back to `ok_ret={0}` once dartbug.com/35549 is fixed
KillTasks(api, checkout_dir, ok_ret='any')
- if _is_bisecting(api):
+ if api.m.bisect_build.is_bisecting():
# The build was successful, so search newer builds to find the root cause.
- _bisect_newer(api, api.properties['bisect_reason'])
+ api.m.bisect_build.schedule(LINEARIZED_REPO_URL, "SUCCESS")
def BuildAndTest(api, start_dir, checkout_dir, flutter_rev):
@@ -407,86 +394,6 @@
TestFlutter(api, start_dir, just_built_dart_sdk)
-def _is_bisecting(api):
- return 'bisect_reason' in api.properties
-
-
-def _start_bisection(api, reason):
- current_rev = api.buildbucket.gitiles_commit.id
-
- # Search for previous builds created by the Luci scheduler (to exclude
- # bisection builds). The TimeRange includes all builds from start_time
- # (defaults to 0) to end_time (exclusive). Because builds are ordered by
- # create_time, the first result will be the previous build.
- create_time = common_pb2.TimeRange(end_time=api.buildbucket.build.create_time)
- builder = api.buildbucket.build.builder
- search_predicate = rpc_pb2.BuildPredicate(
- builder=builder,
- create_time=create_time,
- tags=[common_pb2.StringPair(key='user_agent', value='luci-scheduler')])
- result = api.buildbucket.search(search_predicate, limit=1,
- step_name='fetch previous build')
- if not result or len(result) == 0:
- # There is no previous build: do not bisect on new builders.
- return
- previous_build = result[0]
- if previous_build.status == common_pb2.FAILURE:
- # Do not bisect if the previous build failed.
- # TODO(athom): Check if the failure reason is the same when
- # api.buildbucket.search supports adding
- # 'builds.*.summaryMarkdown' to the field mask.
- return
- previous_rev = previous_build.input.gitiles_commit.id
- # We're intentionally not paging through the log to avoid bisecting an
- # excessive number of commits.
- commits, _ = api.gitiles.log(
- url=LINEARIZED_REPO_URL,
- ref='%s..%s' % (previous_rev, current_rev)
- )
- commits = commits[1:] # The first commit is the current_rev
- commits = [commit['commit'] for commit in commits]
-
- _bisect(api, commits, reason)
-
-
-def _bisect(api, commits, reason):
- if len(commits) == 0:
- # Nothing more to bisect.
- return
-
- middle_index = len(commits)//2
- newer = commits[:middle_index]
- middle = commits[middle_index]
- older = commits[middle_index + 1:]
-
- commit = api.buildbucket.gitiles_commit
- middle_commit = common_pb2.GitilesCommit()
- middle_commit.CopyFrom(commit)
- middle_commit.id = middle
- builder = api.buildbucket.build.builder
- request = api.buildbucket.schedule_request(
- builder=builder.builder,
- project=builder.project,
- bucket=builder.bucket,
- properties={
- 'bisect_newer': newer,
- 'bisect_older': older,
- 'bisect_reason': reason
- },
- gitiles_commit = middle_commit,
- inherit_buildsets = False,
- )
- api.buildbucket.schedule([request], step_name='schedule bisect (%s)' % middle)
-
-
-def _bisect_newer(api, reason):
- _bisect(api, list(api.properties.get('bisect_newer', [])), reason)
-
-
-def _bisect_older(api, reason):
- _bisect(api, list(api.properties.get('bisect_older', [])), reason)
-
-
def _test(api, name, failure=False):
data = api.test(
name,
@@ -503,6 +410,7 @@
data += api.step_data('everything', retcode=1)
return data
+
def GenTests(api):
yield (_test(api, 'flutter-engine-linux')
+ api.post_process(DoesNotRunRE, r'schedule bisect.*'))
@@ -517,43 +425,9 @@
+ api.post_process(MustRun,
'schedule bisect (f4d35da881f8fd329a4d3e01dd78b66a502d5c49)'))
- yield (_test(api, 'do-not-start-bisect-if-previous-build-failed',
- failure=True)
- + api.buildbucket.simulated_search_results([
- api.buildbucket.ci_build_message(status='FAILURE'),
- ], step_name='fetch previous build')
- + api.post_process(DoesNotRunRE, r'schedule bisect.*'))
-
- yield (_test(api, 'do-not-start-bisect-without-previous-build', failure=True)
- + api.buildbucket.simulated_search_results([],
- step_name='fetch previous build')
- + api.post_process(DoesNotRunRE, r'schedule bisect.*'))
-
- yield (_test(api, 'continue-bisect-on-failure', failure=True)
- + api.properties(
- bisect_newer=['a', 'b', 'c'], bisect_older=['c', 'd', 'e'],
- bisect_reason="Infra Failure: Step('everything') (retcode: 1)")
- + api.post_process(MustRun, 'schedule bisect (d)')
- + api.post_process(Filter('schedule bisect (d)')))
-
- yield (_test(api, 'continue-bisect-on-success')
- + api.properties(
- bisect_newer=['a', 'b', 'c'], bisect_older=['c', 'd', 'e'],
- bisect_reason="Infra Failure: Step('everything') (retcode: 1)")
- + api.post_process(MustRun, 'schedule bisect (b)')
- + api.post_process(Filter('schedule bisect (b)')))
-
- yield (_test(api, 'fan-out-on-distinct-failure', failure=True)
- + api.properties(
- bisect_newer=['a', 'b', 'c'], bisect_older=['c', 'd', 'e'],
- bisect_reason='different failure')
- + api.post_process(MustRun, 'schedule bisect (b)')
- + api.post_process(MustRun, 'schedule bisect (d)')
- + api.post_process(Filter().include_re(r'schedule bisect \(.*\)')))
-
- yield (_test(api, 'stop-bisect')
- + api.properties(
- bisect_newer=[], bisect_older=[],
- bisect_reason='different failure')
- + api.post_process(DoesNotRunRE, r'schedule bisect.*')
- + api.post_process(DropExpectation))
+ yield (_test(api, 'continue-bisect-on-success') + api.properties(
+ bisect_newer=['a', 'b', 'c'],
+ bisect_older=['c', 'd', 'e'],
+ bisect_reason="Infra Failure: Step('everything') (retcode: 1)") +
+ api.post_process(MustRun, 'schedule bisect (b)') + api.post_process(
+ Filter('schedule bisect (b)')))