| # Copyright 2020 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Recipe for triggering CQ+2 on changes which meet submitability criteria and have opted in via Auto-Submit+1.""" |
| |
| from PB.recipe_engine import result |
| from PB.go.chromium.org.luci.buildbucket.proto import common |
| from recipe_engine.recipe_api import Property |
| |
| DEPS = [ |
| "fuchsia/gerrit", |
| "fuchsia/status_check", |
| "recipe_engine/buildbucket", |
| "recipe_engine/cipd", |
| "recipe_engine/context", |
| "recipe_engine/json", |
| "recipe_engine/file", |
| "recipe_engine/path", |
| "recipe_engine/platform", |
| "recipe_engine/properties", |
| "recipe_engine/raw_io", |
| "recipe_engine/step", |
| "recipe_engine/url", |
| ] |
| |
| PROPERTIES = { |
| "auto_submit_label": Property( |
| kind=str, |
| help="Label to trigger auto-submission", |
| default="Fuchsia-Auto-Submit", |
| ), |
| "gerrit_host": Property( |
| kind=str, |
| help="Gerrit host to run against.", |
| default="fuchsia-review.googlesource.com", |
| ), |
| "dry_run": Property(kind=bool, help="", default=False), |
| } |
| |
| change_query_test_data = [ |
| { |
| "id": "myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940", |
| "project": "myProject", |
| "branch": "master", |
| "change_id": "I8473b95934b5732ac55d26311a706c9c2bde9940", |
| "subject": 'Revert "Implementing Feature X"', |
| "status": "NEW", |
| "created": "2013-02-01 09:59:32.126000000", |
| "updated": "2013-02-21 11:16:36.775000000", |
| "_number": 3965, |
| "owner": {"name": "John Doe"}, |
| } |
| ] |
| |
| |
| def _get_change_url(host, change): |
| return "https://%s/c/%s/+/%s" % (host, change["project"], change["_number"]) |
| |
| |
| def get_eligible_changes(api, host, auto_submit_label): |
| raw_query = ( |
| "is:submittable -is:wip -(label:Commit-Queue+1 OR label:Commit-Queue+2) " |
| "label:{}+1" |
| ).format(auto_submit_label) |
| changes = api.gerrit.change_query( |
| name="get changes for %s" % host, |
| query_string=raw_query, |
| host=host, |
| test_data=api.json.test_api.output(change_query_test_data), |
| ).json.output |
| # API returns None if there is no search results instead of []. |
| if not changes: |
| return [] |
| eligible_changes = [] |
| for change in changes: |
| with api.step.nest("get details for %s" % change["_number"]) as presentation: |
| presentation.links["change"] = _get_change_url(host, change) |
| other_changes_info = api.gerrit.changes_submitted_together( |
| "find dependent changes", |
| api.url.unquote(change["id"]), |
| query_params=["NON_VISIBLE_CHANGES"], |
| host=host, |
| ).json.output |
| if ( |
| len(other_changes_info["changes"]) > 1 |
| or other_changes_info.get("non_visible_changes", 0) != 0 |
| ): |
| continue |
| change_mergeability = api.gerrit.get_mergeable( |
| "get mergeable", change_id=api.url.unquote(change["id"]), host=host, |
| ).json.output |
| if not change_mergeability["mergeable"]: |
| continue |
| eligible_changes.append(change) |
| return eligible_changes |
| |
| |
| def set_commit_queue(api, host, change): |
| labels = {"Commit-Queue": 2} |
| with api.step.nest("%s" % change["_number"]) as presentation: |
| api.gerrit.set_review( |
| name="CQ", |
| change_id=api.url.unquote(change["id"]), |
| labels=labels, |
| host=host, |
| notify="NONE", |
| ) |
| presentation.links["change"] = _get_change_url(host, change) |
| |
| |
| def raw_result(host, changes, dry_run): |
| if changes: |
| contents = [ |
| "Submitted the following CLs{}:".format(" (dry run)" if dry_run else "") |
| ] |
| contents.extend(_get_change_url(host, change) for change in changes) |
| else: |
| contents = ["No CLs to submit."] |
| |
| return result.RawResult( |
| summary_markdown="\n".join(contents), status=common.SUCCESS, |
| ) |
| |
| |
| def RunSteps(api, gerrit_host, dry_run, auto_submit_label): |
| with api.step.nest("get eligible") as presentation: |
| changes = get_eligible_changes(api, gerrit_host, auto_submit_label) |
| if not changes: |
| presentation.step_text = "\nno eligible changes." |
| |
| if changes: |
| with api.step.nest("set cq+2") as presentation: |
| if dry_run: |
| for change in [ |
| _get_change_url(gerrit_host, change) for change in changes |
| ]: |
| presentation.links[change] = change |
| else: |
| for change in changes: |
| set_commit_queue(api, gerrit_host, change) |
| |
| return raw_result(gerrit_host, changes, dry_run) |
| |
| |
| def GenTests(api): |
| changes_submitted_together = { |
| "changes": [ |
| {"id": "myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9941",}, |
| ], |
| "non_visible_changes": 1, |
| } |
| no_changes_submitted_together = {"changes": [], "non_visible_changes": 0} |
| |
| mergeable_test_data = { |
| "mergeable": True, |
| } |
| unmergeable_test_data = { |
| "mergeable": False, |
| } |
| yield ( |
| api.status_check.test("default") |
| + api.step_data( |
| "get eligible.get details for 3965.find dependent changes", |
| api.json.output(no_changes_submitted_together), |
| ) |
| + api.step_data( |
| "get eligible.get details for 3965.get mergeable", |
| api.json.output(mergeable_test_data), |
| ) |
| ) |
| yield ( |
| api.status_check.test("no_changes") |
| + api.step_data( |
| "get eligible.get changes for fuchsia-review.googlesource.com", |
| api.json.output(None), |
| ) |
| ) |
| yield ( |
| api.status_check.test("changes_submitted_together") |
| + api.step_data( |
| "get eligible.get details for 3965.find dependent changes", |
| api.json.output(changes_submitted_together), |
| ) |
| ) |
| yield ( |
| api.status_check.test("unmergeable") |
| + api.step_data( |
| "get eligible.get details for 3965.find dependent changes", |
| api.json.output(no_changes_submitted_together), |
| ) |
| + api.step_data( |
| "get eligible.get details for 3965.get mergeable", |
| api.json.output(unmergeable_test_data), |
| ) |
| ) |
| |
| yield ( |
| api.status_check.test("dry_run") |
| + api.step_data( |
| "get eligible.get details for 3965.find dependent changes", |
| api.json.output(no_changes_submitted_together), |
| ) |
| + api.step_data( |
| "get eligible.get details for 3965.get mergeable", |
| api.json.output(mergeable_test_data), |
| ) |
| + api.properties(dry_run=True) |
| ) |