blob: 0df18bee8da59dab84ac6eeb09b62fb31a456d71 [file] [log] [blame]
Josip Sokcevic9686b2e2022-08-30 21:44:391#!/usr/bin/env vpython3
Avi Drissmandfd880852022-09-15 20:11:092# Copyright 2019 The Chromium Authors
Elly Fong-Jones4d461e92019-02-07 16:29:113# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
Elly Fong-Jones4d461e92019-02-07 16:29:115
Elly Fong-Jones2f2e4fd2019-03-03 01:34:286"""Emits a formatted, optionally filtered view of the list of flags.
7"""
8
Raul Tambref3d9412e2019-09-24 05:31:449from __future__ import print_function
10
Elly Fong-Jones2f2e4fd2019-03-03 01:34:2811import argparse
Elly Fong-Jones4d461e92019-02-07 16:29:1112import os
Elly Fong-Jones54d81cb2019-08-29 17:16:5913import re
Elly Fong-Jones4d461e92019-02-07 16:29:1114import sys
15
Elly12645ce12024-07-24 18:29:0816import flags_utils
Elly Fong-Jones2f2e4fd2019-03-03 01:34:2817
Elly12645ce12024-07-24 18:29:0818DEPOT_TOOLS_PATH = os.path.join(flags_utils.ROOT_PATH, 'third_party',
19 'depot_tools')
Josip Sokcevic9686b2e2022-08-30 21:44:3920
Elly Fong-Jones54d81cb2019-08-29 17:16:5921sys.path.append(DEPOT_TOOLS_PATH)
Elly Fong-Jones4d461e92019-02-07 16:29:1122
Josip Sokcevic9686b2e2022-08-30 21:44:3923import owners_client
Elly Fong-Jones2f2e4fd2019-03-03 01:34:2824
25
26def keep_never_expires(flags):
27 """Filter flags to contain only flags that never expire.
28
29 >>> keep_never_expires([{'expiry_milestone': -1}, {'expiry_milestone': 2}])
30 [{'expiry_milestone': -1}]
31 """
32 return [f for f in flags if f['expiry_milestone'] == -1]
33
34
Elly Fong-Jones54d81cb2019-08-29 17:16:5935def resolve_owners(flags):
36 """Resolves sets of owners for every flag in the provided list.
37
38 Given a list of flags, for each flag, resolves owners for that flag. Resolving
39 owners means, for each entry in a flag's owners list:
40 * Turning owners files references into the transitive set of owners listed in
41 those files
42 * Turning bare usernames into @chromium.org email addresses
43 * Passing any other type of entry through unmodified
44 """
45
Josip Sokcevic9686b2e2022-08-30 21:44:3946 owners_db = owners_client.GetCodeOwnersClient(
Josip Sokcevic9686b2e2022-08-30 21:44:3947 host="chromium-review.googlesource.com",
48 project="chromium/src",
49 branch="main")
Elly Fong-Jones54d81cb2019-08-29 17:16:5950
51 new_flags = []
52 for f in flags:
53 new_flag = f.copy()
Josip Sokcevic9686b2e2022-08-30 21:44:3954 new_owners = set()
Elly Fong-Jones54d81cb2019-08-29 17:16:5955 for o in f['owners']:
Josip Sokcevic9686b2e2022-08-30 21:44:3956 # Assume any filepath is to an OWNERS file.
57 if '/' in o:
58 new_owners.update(set(owners_db.ListBestOwners(re.sub('//', '', o))))
Elly Fong-Jones54d81cb2019-08-29 17:16:5959 elif '@' not in o:
Josip Sokcevic9686b2e2022-08-30 21:44:3960 new_owners.add(o + '@chromium.org')
Elly Fong-Jones54d81cb2019-08-29 17:16:5961 else:
Josip Sokcevic9686b2e2022-08-30 21:44:3962 new_owners.add(o)
63 new_flag['resolved_owners'] = sorted(new_owners)
Elly Fong-Jones54d81cb2019-08-29 17:16:5964 new_flags.append(new_flag)
65 return new_flags
66
67
Lei Zhang9d468c1c2020-09-18 19:30:5368def find_unused(flags):
69 FLAG_FILES = [
70 'chrome/browser/about_flags.cc',
71 'ios/chrome/browser/flags/about_flags.mm',
72 ]
Lei Zhang555c3b532021-09-22 21:18:1273 flag_files_data = [open(f, 'r', encoding='utf-8').read() for f in FLAG_FILES]
Lei Zhang9d468c1c2020-09-18 19:30:5374 unused_flags = []
75 for flag in flags:
76 # Search for the name in quotes.
77 needle = '"%s"' % flag['name']
78 if not any([needle in data for data in flag_files_data]):
79 unused_flags.append(flag)
80 return unused_flags
81
82
wenyu zhangabee3542023-06-08 22:44:3883def filter_by_owners(flags, owners):
84 """Given a list of owners, returns all flags which have any owner appearing
85 in the list. The `owners` arg is a list of owners.
wenyu zhang9c274952023-01-03 00:34:1286
87 Need exact match and need to include @google.com or @chromium.org in the
88 argument. This is because the owner with ldap only is extended with
89 @chromium.org automatically via resolve_owners function.
90 TODO(zhangwenyu): Support filter by ldap.
91
92 >>> f1 = {'name': 'f_1', 'owners': ['[email protected]']}
93 >>> f1['resolved_owners'] = ['[email protected]']
94 >>> f2 = {'name': 'f_2', 'owners': ['z']}
95 >>> f2['resolved_owners'] = ['[email protected]']
wenyu zhangabee3542023-06-08 22:44:3896 >>> f3 = {'name': 'f_3', 'owners': ['[email protected]', '[email protected]']}
97 >>> f3['resolved_owners'] = ['[email protected]', '[email protected]']
wenyu zhang9c274952023-01-03 00:34:1298
wenyu zhangabee3542023-06-08 22:44:3899 >>> filter_by_owners([f1, f2, f3], ['[email protected]'])
wenyu zhang9c274952023-01-03 00:34:12100 [{'name': 'f_1', 'owners': ['[email protected]'], 'resolved_owners': ['[email protected]']}]
wenyu zhangabee3542023-06-08 22:44:38101 >>> filter_by_owners([f1, f2, f3], ['[email protected]'])
wenyu zhang9c274952023-01-03 00:34:12102 [{'name': 'f_2', 'owners': ['z'], 'resolved_owners': ['[email protected]']}]
wenyu zhangabee3542023-06-08 22:44:38103 >>> filter_by_owners([f1, f2, f3], ['z']) # Filter by ldap not supported.
wenyu zhang9c274952023-01-03 00:34:12104 []
wenyu zhangabee3542023-06-08 22:44:38105 >>> filter_by_owners([f1, f2, f3], ['[email protected]']) # Need exact match.
wenyu zhang9c274952023-01-03 00:34:12106 []
wenyu zhangabee3542023-06-08 22:44:38107 >>> filter_by_owners([f1, f2, f3], ['[email protected]', '[email protected]'])
108 [{'name': 'f_1', 'owners': ['[email protected]'], 'resolved_owners': ['[email protected]']}, {'name': 'f_2', 'owners': ['z'], 'resolved_owners': ['[email protected]']}]
109 >>> filter_by_owners([f1, f2, f3], ['[email protected]', '[email protected]'])
110 [{'name': 'f_3', 'owners': ['[email protected]', '[email protected]'], 'resolved_owners': ['[email protected]', '[email protected]']}]
wenyu zhang9c274952023-01-03 00:34:12111 """
112
wenyu zhangabee3542023-06-08 22:44:38113 # A helper function to check if there is any intersection between flag's
114 # owners and targeted owners.
115 def matches_any_owner(flag):
116 return set(flag['resolved_owners']) & set(owners)
117
118 return list(filter(matches_any_owner, flags))
wenyu zhang9c274952023-01-03 00:34:12119
120
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28121def print_flags(flags, verbose):
122 """Prints the supplied list of flags.
123
124 In verbose mode, prints name, expiry, and owner list; in non-verbose mode,
Elly Fong-Jones552683f2022-04-05 15:34:20125 prints just the name. Verbose mode is actually tab-separated values, with
126 commas used as separators within individual fields - this is the format the
127 rest of the flags automation consumes most readily.
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28128
129 >>> f1 = {'name': 'foo', 'expiry_milestone': 73, 'owners': ['bar', 'baz']}
Avi Drissman4421c722022-08-16 16:35:52130 >>> f1['resolved_owners'] = ['[email protected]', '[email protected]']
Elly Fong-Jones0611795e2019-06-11 14:48:19131 >>> f2 = {'name': 'bar', 'expiry_milestone': 74, 'owners': ['//quxx/OWNERS']}
Avi Drissman4421c722022-08-16 16:35:52132 >>> f2['resolved_owners'] = ['[email protected]']
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28133 >>> print_flags([f1], False)
134 foo
Elly Fong-Jones552683f2022-04-05 15:34:20135 >>> print_flags([f1], True) # doctest: +NORMALIZE_WHITESPACE
136 foo 73 bar,baz [email protected],[email protected]
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28137 >>> print_flags([f2], False)
138 bar
Elly Fong-Jones552683f2022-04-05 15:34:20139 >>> print_flags([f2], True) # doctest: +NORMALIZE_WHITESPACE
140 bar 74 //quxx/OWNERS [email protected]
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28141 """
142 for f in flags:
143 if verbose:
Elly Fong-Jones552683f2022-04-05 15:34:20144 print('%s\t%d\t%s\t%s' % (f['name'], f['expiry_milestone'], ','.join(
145 f['owners']), ','.join(f['resolved_owners'])))
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28146 else:
Raul Tambref3d9412e2019-09-24 05:31:44147 print(f['name'])
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28148
149
150def main():
151 import doctest
152 doctest.testmod()
153
154 parser = argparse.ArgumentParser(description=__doc__)
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28155 group = parser.add_mutually_exclusive_group()
156 group.add_argument('-n', '--never-expires', action='store_true')
157 group.add_argument('-e', '--expired-by', type=int)
Lei Zhang9d468c1c2020-09-18 19:30:53158 group.add_argument('-u', '--find-unused', action='store_true')
wenyu zhangabee3542023-06-08 22:44:38159 # The -o argument could be a single owner or multiple owners joined by ','.
wenyu zhang9c274952023-01-03 00:34:12160 group.add_argument('-o', '--has-owner', type=str)
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28161 parser.add_argument('-v', '--verbose', action='store_true')
Elly Fong-Jones54d81cb2019-08-29 17:16:59162 parser.add_argument('--testonly', action='store_true')
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28163 args = parser.parse_args()
164
Elly Fong-Jones54d81cb2019-08-29 17:16:59165 if args.testonly:
166 return
167
Elly12645ce12024-07-24 18:29:08168 flags = flags_utils.load_metadata()
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28169 if args.expired_by:
Elly12645ce12024-07-24 18:29:08170 flags = flags_utils.keep_expired_by(flags, args.expired_by)
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28171 if args.never_expires:
172 flags = keep_never_expires(flags)
Lei Zhang9d468c1c2020-09-18 19:30:53173 if args.find_unused:
174 flags = find_unused(flags)
Elly Fong-Jones54d81cb2019-08-29 17:16:59175 flags = resolve_owners(flags)
wenyu zhang9c274952023-01-03 00:34:12176 # Filter by owner after resolving owners completed, so it understands
177 # owners file.
178 if args.has_owner:
wenyu zhangabee3542023-06-08 22:44:38179 owners = [o.strip() for o in args.has_owner.split(',')]
180 flags = filter_by_owners(flags, owners)
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28181 print_flags(flags, args.verbose)
Elly Fong-Jones4d461e92019-02-07 16:29:11182
183
184if __name__ == '__main__':
Elly Fong-Jones2f2e4fd2019-03-03 01:34:28185 main()