blob: 21fbac58054a8d45103d19491235c9f52340fb45 [file] [log] [blame]
Rob Mohr038bf992021-06-18 13:03:35 -07001# Copyright 2021 The Pigweed Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7# https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Utility functions common to multiple recipes that don't fit elsewhere."""
15
Rob Mohr2f31ceb2024-08-13 19:17:44 +000016from __future__ import annotations
17
Rob Mohr34a1f8e2021-08-19 08:37:43 -070018import json
Rob Mohr038bf992021-06-18 13:03:35 -070019import re
Rob Mohr86531542024-08-13 19:33:21 +000020from typing import TYPE_CHECKING
Rob Mohr038bf992021-06-18 13:03:35 -070021
Rob Mohraab51662023-11-10 22:45:09 +000022import attrs
Rob Mohr34a1f8e2021-08-19 08:37:43 -070023from google.protobuf import json_format
Rob Mohr038bf992021-06-18 13:03:35 -070024from recipe_engine import recipe_api
25
Rob Mohr86531542024-08-13 19:33:21 +000026if TYPE_CHECKING: # pragma: no cover
27 from typing import Any, Sequence
28 from PB.go.chromium.org.luci.buildbucket.proto import (
29 build as build_pb2,
30 common as common_pb2,
31 )
32
Rob Mohr038bf992021-06-18 13:03:35 -070033
Rob Mohraab51662023-11-10 22:45:09 +000034@attrs.define
Rob Mohr82c0e352022-07-27 20:57:40 +000035class ChangeWithComments:
Rob Mohraab51662023-11-10 22:45:09 +000036 change: common_pb2.GerritChange
37 details: dict[str, Any]
38 commit_message: str
39 comments: Sequence[str]
Rob Mohr038bf992021-06-18 13:03:35 -070040
41
42class UtilApi(recipe_api.RecipeApi):
Rob Mohr298f5b32023-11-17 20:16:51 +000043 ChangeWithComments = ChangeWithComments
44
Rob Mohraab51662023-11-10 22:45:09 +000045 def get_change_with_comments(self) -> ChangeWithComments:
Rob Mohr298f5b32023-11-17 20:16:51 +000046 input_: build_pb2.Build.Input = self.m.buildbucket.build.input
47 change: common_pb2.GerritChange = input_.gerrit_changes[0]
48 change_id: str = str(change.change)
49 details: dict[str, Any] = self.m.gerrit.change_details(
Rob Mohr038bf992021-06-18 13:03:35 -070050 'change details',
51 change_id=change_id,
52 host=input_.gerrit_changes[0].host,
Rob Mohr807f3ce2021-09-08 11:16:04 -070053 query_params=['ALL_COMMITS', 'ALL_REVISIONS', 'ALL_FILES'],
Rob Mohr038bf992021-06-18 13:03:35 -070054 test_data=self.m.json.test_api.output(
55 {
Rob Mohr336d3352024-02-14 19:27:51 +000056 'owner': {
57 'email': 'coder@example.com',
58 },
Rob Mohr038bf992021-06-18 13:03:35 -070059 'current_revision': 'a' * 40,
60 'revisions': {
Rob Mohr5a79c0f2021-07-26 12:27:35 -070061 'a'
62 * 40: {
63 'files': [],
Rob Mohr336d3352024-02-14 19:27:51 +000064 'commit': {
65 'message': '',
66 },
Rob Mohr5a79c0f2021-07-26 12:27:35 -070067 'description': 'description',
68 }
Rob Mohr038bf992021-06-18 13:03:35 -070069 },
70 'revert_of': 0,
71 }
72 ),
73 ).json.output
74
Rob Mohr336d3352024-02-14 19:27:51 +000075 current_revision: str = details['revisions'][
76 details['current_revision']
77 ]
Rob Mohr298f5b32023-11-17 20:16:51 +000078 commit_message: str = current_revision['commit']['message']
Rob Mohr5acf39e2021-11-11 08:10:58 -080079
Rob Mohr298f5b32023-11-17 20:16:51 +000080 comments: list[str] = []
Rob Mohr038bf992021-06-18 13:03:35 -070081
Rob Mohr753e1832021-07-30 15:04:45 -070082 for revision in details['revisions'].values():
Rob Mohr5a79c0f2021-07-26 12:27:35 -070083 if revision.get('description'):
84 comments.append(revision['description'])
85
Rob Mohr298f5b32023-11-17 20:16:51 +000086 comments_result: dict[str, Any] = self.m.gerrit.list_change_comments(
Rob Mohr038bf992021-06-18 13:03:35 -070087 "list change comments",
88 change_id,
89 test_data=self.m.json.test_api.output(
Rob Mohr336d3352024-02-14 19:27:51 +000090 {
91 '/PATCHSET_LEVEL': [{'message': ''}],
92 }
Rob Mohr038bf992021-06-18 13:03:35 -070093 ),
94 ).json.output
95
Rob Mohr34453952021-08-04 06:46:54 -070096 for _, comment_data in comments_result.items():
97 comments.extend(x['message'] for x in comment_data)
Rob Mohr038bf992021-06-18 13:03:35 -070098
Rob Mohr5acf39e2021-11-11 08:10:58 -080099 return ChangeWithComments(change, details, commit_message, comments)
Rob Mohr038bf992021-06-18 13:03:35 -0700100
Rob Mohraab51662023-11-10 22:45:09 +0000101 def find_matching_comment(
102 self, rx: re.Pattern, comments: Sequence[str]
Rob Mohr1457cf92024-03-06 20:39:59 +0000103 ) -> str | None:
Rob Mohr038bf992021-06-18 13:03:35 -0700104 """Find a comment in comments that matches regex object rx."""
Rob Mohr1457cf92024-03-06 20:39:59 +0000105 result: str | None = None
Rob Mohr038bf992021-06-18 13:03:35 -0700106 with self.m.step.nest('checking comments'):
107 for i, comment in enumerate(comments):
Rob Mohr4806b6b2023-02-03 18:03:32 +0000108 with self.m.step.nest(f'comment ({i})') as pres:
Rob Mohr038bf992021-06-18 13:03:35 -0700109 pres.step_summary_text = comment
Rob Mohraab51662023-11-10 22:45:09 +0000110 match: re.Match = re.search(rx, comment)
Rob Mohr038bf992021-06-18 13:03:35 -0700111 if match:
Rob Mohr4806b6b2023-02-03 18:03:32 +0000112 pres.step_summary_text = f'MATCH: {comment}'
Rob Mohraab51662023-11-10 22:45:09 +0000113 result: str = match.group(0)
Rob Mohr038bf992021-06-18 13:03:35 -0700114 break
115
116 if result:
117 with self.m.step.nest('found'):
118 pass
119
120 return result
Rob Mohr34a1f8e2021-08-19 08:37:43 -0700121
Rob Mohraab51662023-11-10 22:45:09 +0000122 def build_metadata(self) -> dict[str, Any]:
Rob Mohr34a1f8e2021-08-19 08:37:43 -0700123 return {
124 'bb_id': self.m.buildbucket.build.id,
125 'swarming_id': self.m.swarming.task_id,
126 'builder': self.m.buildbucket_util.full_builder_name(),
Rob Mohr9865cec2024-10-17 18:13:50 +0000127 'url': self.m.buildbucket.build_url(),
Rob Mohr34a1f8e2021-08-19 08:37:43 -0700128 'triggers': [
129 json.loads(json_format.MessageToJson(x))
130 for x in self.m.scheduler.triggers
131 ],
132 }