| # Copyright 2025 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Pings an email address about any autogenerated CLs that need review.""" |
| |
| import argparse |
| import logging |
| from pathlib import Path |
| from typing import List |
| |
| from cros_utils import cros_paths |
| from cros_utils import email_sender |
| from cros_utils import git_utils |
| |
| |
| def count_outstanding_cls(chromeos_root: Path) -> int: |
| query = " ".join( |
| ( |
| "owner:mobiletc-prebuild@google.com", |
| "topic:crostc-auto-cl", |
| "status:open", |
| "-is:wip", |
| "-is:submittable", |
| ) |
| ) |
| cls = git_utils.query_gerrit( |
| chromeos_root, |
| query, |
| ) |
| logging.info("CLs matching query %r: %s", query, cls) |
| return len(cls) |
| |
| |
| def main(argv: List[str]) -> None: |
| # This is meant to be run from a ChromeOS tree for simplicity. |
| chromeos_root = cros_paths.script_chromiumos_checkout_or_exit() |
| |
| logging.basicConfig( |
| format=">> %(asctime)s: %(levelname)s: %(filename)s:%(lineno)d: " |
| "%(message)s", |
| level=logging.INFO, |
| ) |
| |
| parser = argparse.ArgumentParser( |
| description=__doc__, |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| ) |
| parser.add_argument( |
| "--email", |
| help="Email to send ping to", |
| ) |
| parser.add_argument( |
| "-n", |
| "--dry-run", |
| action="store_true", |
| help="Don't actually send an email", |
| ) |
| opts = parser.parse_args(argv) |
| |
| if not (opts.email or opts.dry_run): |
| parser.error("You must pass either --dry-run or --email") |
| |
| logging.info("Searching for CLs that need a review...") |
| num_outstanding_cls = count_outstanding_cls(chromeos_root) |
| if not num_outstanding_cls: |
| logging.info("No outstanding CLs found; exit") |
| return |
| |
| logging.info( |
| "Sending email notification of %d outstanding CLs", num_outstanding_cls |
| ) |
| |
| # NOTE: Please keep this subject constant, so Chat threads these |
| # notifications instead of posting new discussions. |
| subject = "Autogenerated CL(s) need review" |
| body = ( |
| f"http://go/crostc-unreviewed-auto-cls says {num_outstanding_cls} " |
| "need(s) review" |
| ) |
| if opts.dry_run: |
| logging.info("--dry-run specified; skipping sending email") |
| logging.info("Subject: %s", subject) |
| logging.info("Body: %s", body) |
| return |
| |
| email_sender.EmailSender().SendGSEmail( |
| subject=subject, |
| identifier="cl-pings", |
| text_body=body, |
| direct_recipients=(opts.email,), |
| ) |