blob: ee390983475c1b3c470d81c99acacdc533c95be8 [file] [log] [blame]
Tristan Honscheid52ba4d22023-02-09 11:59:29 -07001# Copyright 2023 The ChromiumOS Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Copybot Controller.
6
7Handles the endpoint for running copybot and generating the protobuf.
8"""
9
10from pathlib import Path
11import tempfile
12
13from chromite.third_party.google.protobuf import json_format
14
15from chromite.api import controller
16from chromite.api import faux
17from chromite.api import validate
18from chromite.api.gen.chromite.api import copybot_pb2
19from chromite.lib import constants
20from chromite.lib import cros_build_lib
21
22
23def _MockSuccess(_input_proto, _output_proto, _config_proto):
24 """Mock success output for the RunCopybot endpoint."""
25
26 # Successful response is the default protobuf, so no need to fill it out.
27
28
29@faux.success(_MockSuccess)
30@faux.empty_error
31@validate.validation_complete
32def RunCopybot(input_proto, output_proto, _config_proto):
33 """Run copybot. Translate all fields in the input protobuf to CLI args."""
34
35 cmd = [
Mike Frysinger903c1f72023-08-08 14:05:10 -040036 constants.SOURCE_ROOT / "src/platform/dev/contrib/copybot/copybot.py"
Tristan Honscheid52ba4d22023-02-09 11:59:29 -070037 ]
38
39 if input_proto.topic:
40 cmd.extend(["--topic", input_proto.topic])
41
42 for label in input_proto.labels:
43 cmd.extend(["--label", label.label])
44
45 for reviewer in input_proto.reviewers:
46 cmd.extend(["--re", reviewer.user])
47
48 for cc in input_proto.ccs:
49 cmd.extend(["--cc", cc.user])
50
51 if input_proto.prepend_subject:
52 cmd.extend(["--prepend-subject", input_proto.prepend_subject])
53
54 if (
55 input_proto.merge_conflict_behavior
56 == copybot_pb2.RunCopybotRequest.MERGE_CONFLICT_BEHAVIOR_SKIP
57 ):
58 cmd.extend(["--merge-conflict-behavior", "SKIP"])
59
60 if (
61 input_proto.merge_conflict_behavior
62 == copybot_pb2.RunCopybotRequest.MERGE_CONFLICT_BEHAVIOR_FAIL
63 ):
64 cmd.extend(["--merge-conflict-behavior", "FAIL"])
65
66 for exclude in input_proto.exclude_file_patterns:
67 cmd.extend(["--exclude-file-pattern", exclude.pattern])
68
69 for ph in input_proto.keep_pseudoheaders:
70 cmd.extend(["--keep-pseudoheader", ph.name])
71
72 if input_proto.add_signed_off_by:
73 cmd.append("--add-signed-off-by")
74
75 if input_proto.dry_run:
76 cmd.append("--dry-run")
77
Tristan Honscheid7a78af12023-02-16 16:36:56 -070078 for po in input_proto.push_options:
79 cmd.extend(["--push-option", po.opt])
80
Tristan Honscheid52ba4d22023-02-09 11:59:29 -070081 cmd.append(f"{input_proto.upstream.url}:{input_proto.upstream.branch}")
82 cmd.append(f"{input_proto.downstream.url}:{input_proto.downstream.branch}")
83
84 with tempfile.TemporaryDirectory() as temp_dir:
85 json_output_path = Path(temp_dir) / "copybot_output.json"
86 cmd.extend(["--json-out", json_output_path])
87
88 try:
89 cros_build_lib.run(cmd)
90 except cros_build_lib.RunCommandError:
Alex Kleinc2dc8f02023-03-14 09:57:23 -060091 # In case of failure, load details about the error from CopyBot's
92 # JSON output into the output protobuf. (If CopyBot ran
93 # successfully, the default values are simply used). CopyBot's
94 # output matches the JSON representation of the RunCopybotResponse
95 # protobuf.
Tristan Honscheid52ba4d22023-02-09 11:59:29 -070096
97 if not json_output_path.exists():
98 return controller.RETURN_CODE_UNRECOVERABLE
99
100 json_format.Parse(json_output_path.read_text(), output_proto)
101 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE