util: Update attrs and add type annotations

Change-Id: Ia916890d9a364d07fd77eaf13866eddaf8eb8d20
Reviewed-on: https://pigweed-review.googlesource.com/c/infra/recipes/+/180458
Pigweed-Auto-Submit: Rob Mohr <mohrr@google.com>
Presubmit-Verified: CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>
Commit-Queue: Auto-Submit <auto-submit@pigweed-service-accounts.iam.gserviceaccount.com>
Reviewed-by: Ted Pudlik <tpudlik@google.com>
diff --git a/recipe_modules/util/api.py b/recipe_modules/util/api.py
index c9a4dbe..f49d31e 100644
--- a/recipe_modules/util/api.py
+++ b/recipe_modules/util/api.py
@@ -15,22 +15,24 @@
 
 import json
 import re
+from typing import Any, Optional, Sequence
 
-import attr
+import attrs
 from google.protobuf import json_format
+from PB.go.chromium.org.luci.buildbucket.proto import common as common_pb2
 from recipe_engine import recipe_api
 
 
-@attr.s
+@attrs.define
 class ChangeWithComments:
-    change = attr.ib()
-    details = attr.ib()
-    commit_message = attr.ib()
-    comments = attr.ib()
+    change: common_pb2.GerritChange
+    details: dict[str, Any]
+    commit_message: str
+    comments: Sequence[str]
 
 
 class UtilApi(recipe_api.RecipeApi):
-    def get_change_with_comments(self):
+    def get_change_with_comments(self) -> ChangeWithComments:
         input_ = self.m.buildbucket.build.input
         change = input_.gerrit_changes[0]
         change_id = str(change.change)
@@ -78,17 +80,19 @@
 
         return ChangeWithComments(change, details, commit_message, comments)
 
-    def find_matching_comment(self, rx, comments):
+    def find_matching_comment(
+        self, rx: re.Pattern, comments: Sequence[str]
+    ) -> Optional[str]:
         """Find a comment in comments that matches regex object rx."""
         result = None
         with self.m.step.nest('checking comments'):
             for i, comment in enumerate(comments):
                 with self.m.step.nest(f'comment ({i})') as pres:
                     pres.step_summary_text = comment
-                    match = re.search(rx, comment)
+                    match: re.Match = re.search(rx, comment)
                     if match:
                         pres.step_summary_text = f'MATCH: {comment}'
-                        result = match
+                        result: str = match.group(0)
                         break
 
             if result:
@@ -97,7 +101,7 @@
 
         return result
 
-    def build_metadata(self):
+    def build_metadata(self) -> dict[str, Any]:
         return {
             'bb_id': self.m.buildbucket.build.id,
             'swarming_id': self.m.swarming.task_id,