blob: fca78a9238e213ae29b38f712834efe433bec64c [file] [log] [blame]
Josip Sokcevic4de5dea2022-03-23 21:15:14 +00001#!/usr/bin/env python3
maruel@chromium.org725f1c32011-04-01 20:24:54 +00002# Copyright (c) 2011 The Chromium Authors. All rights reserved.
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +00003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Watchlists
7
8Watchlists is a mechanism that allow a developer (a "watcher") to watch over
Chris Hallba97f602019-10-15 15:22:16 +00009portions of code that they are interested in. A "watcher" will be cc-ed to
10changes that modify that portion of code, thereby giving them an opportunity
James Zernfdd89462021-01-13 01:38:34 +000011to make comments on chromium-review.googlesource.com even before the change is
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000012committed.
James Zernfdd89462021-01-13 01:38:34 +000013Refer:
14https://chromium.googlesource.com/chromium/src/+/HEAD/docs/infra/watchlists.md
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000015
16When invoked directly from the base of a repository, this script lists out
17the watchers for files given on the command line. This is useful to verify
18changes to WATCHLISTS files.
19"""
20
Raul Tambre80ee78e2019-05-06 22:41:05 +000021from __future__ import print_function
22
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000023import logging
24import os
25import re
26import sys
27
28
29class Watchlists(object):
30 """Manage Watchlists.
31
32 This class provides mechanism to load watchlists for a repo and identify
33 watchers.
34 Usage:
35 wl = Watchlists("/path/to/repo/root")
36 watchers = wl.GetWatchersForPaths(["/path/to/file1",
37 "/path/to/file2",])
38 """
39
40 _RULES = "WATCHLISTS"
41 _RULES_FILENAME = _RULES
42 _repo_root = None
43 _defns = {} # Definitions
Raphael Kubo da Costaae979432017-11-02 19:49:57 +010044 _path_regexps = {} # Name -> Regular expression mapping
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000045 _watchlists = {} # name to email mapping
46
47 def __init__(self, repo_root):
48 self._repo_root = repo_root
49 self._LoadWatchlistRules()
50
51 def _GetRulesFilePath(self):
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000052 """Returns path to WATCHLISTS file."""
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000053 return os.path.join(self._repo_root, self._RULES_FILENAME)
54
55 def _HasWatchlistsFile(self):
56 """Determine if watchlists are available for this repo."""
57 return os.path.exists(self._GetRulesFilePath())
58
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000059 def _ContentsOfWatchlistsFile(self):
60 """Read the WATCHLISTS file and return its contents."""
61 try:
62 watchlists_file = open(self._GetRulesFilePath())
63 contents = watchlists_file.read()
64 watchlists_file.close()
65 return contents
Raul Tambre7c938462019-05-24 16:35:35 +000066 except IOError as e:
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000067 logging.error("Cannot read %s: %s" % (self._GetRulesFilePath(), e))
68 return ''
69
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000070 def _LoadWatchlistRules(self):
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000071 """Load watchlists from WATCHLISTS file. Does nothing if not present."""
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000072 if not self._HasWatchlistsFile():
73 return
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000074
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000075 contents = self._ContentsOfWatchlistsFile()
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000076 watchlists_data = None
77 try:
78 watchlists_data = eval(contents, {'__builtins__': None}, None)
Raul Tambre7c938462019-05-24 16:35:35 +000079 except SyntaxError as e:
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000080 logging.error("Cannot parse %s. %s" % (self._GetRulesFilePath(), e))
81 return
82
83 defns = watchlists_data.get("WATCHLIST_DEFINITIONS")
84 if not defns:
85 logging.error("WATCHLIST_DEFINITIONS not defined in %s" %
86 self._GetRulesFilePath())
87 return
88 watchlists = watchlists_data.get("WATCHLISTS")
89 if not watchlists:
90 logging.error("WATCHLISTS not defined in %s" % self._GetRulesFilePath())
91 return
92 self._defns = defns
93 self._watchlists = watchlists
94
Raphael Kubo da Costaae979432017-11-02 19:49:57 +010095 # Compile the regular expressions ahead of time to avoid creating them
96 # on-the-fly multiple times per file.
97 self._path_regexps = {}
Marc-Antoine Ruel8e57b4b2019-10-11 01:01:36 +000098 for name, rule in defns.items():
Raphael Kubo da Costaae979432017-11-02 19:49:57 +010099 filepath = rule.get('filepath')
100 if not filepath:
101 continue
102 self._path_regexps[name] = re.compile(filepath)
103
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000104 # Verify that all watchlist names are defined
105 for name in watchlists:
106 if name not in defns:
107 logging.error("%s not defined in %s" % (name, self._GetRulesFilePath()))
108
109 def GetWatchersForPaths(self, paths):
110 """Fetch the list of watchers for |paths|
111
112 Args:
113 paths: [path1, path2, ...]
114
115 Returns:
116 [u1@chromium.org, u2@gmail.com, ...]
117 """
118 watchers = set() # A set, to avoid duplicates
119 for path in paths:
nirnimesh@chromium.org76256af2009-06-18 22:52:12 +0000120 path = path.replace(os.sep, '/')
Marc-Antoine Ruel8e57b4b2019-10-11 01:01:36 +0000121 for name, rule in self._path_regexps.items():
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000122 if name not in self._watchlists:
123 continue
Raphael Kubo da Costaae979432017-11-02 19:49:57 +0100124 if rule.search(path):
Edward Lemur589d4562019-11-21 00:55:15 +0000125 for watchlist in self._watchlists[name]:
126 watchers.add(watchlist)
127 return sorted(watchers)
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000128
129
130def main(argv):
131 # Confirm that watchlists can be parsed and spew out the watchers
132 if len(argv) < 2:
Raul Tambre80ee78e2019-05-06 22:41:05 +0000133 print("Usage (from the base of repo):")
134 print(" %s [file-1] [file-2] ...." % argv[0])
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000135 return 1
136 wl = Watchlists(os.getcwd())
137 watchers = wl.GetWatchersForPaths(argv[1:])
Raul Tambre80ee78e2019-05-06 22:41:05 +0000138 print(watchers)
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000139
140
141if __name__ == '__main__':
142 main(sys.argv)