blob: ecd3e69b7028a7d28ade7b53d53e4a191557cb13 [file] [log] [blame]
#!/usr/bin/env vpython3
# Copyright 2019 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Emits a formatted, optionally filtered view of the list of flags.
"""
from __future__ import print_function
import argparse
import os
import re
import sys
import utils
DEPOT_TOOLS_PATH = os.path.join(utils.ROOT_PATH, 'third_party', 'depot_tools')
sys.path.append(DEPOT_TOOLS_PATH)
import owners_client
def keep_never_expires(flags):
"""Filter flags to contain only flags that never expire.
>>> keep_never_expires([{'expiry_milestone': -1}, {'expiry_milestone': 2}])
[{'expiry_milestone': -1}]
"""
return [f for f in flags if f['expiry_milestone'] == -1]
def resolve_owners(flags):
"""Resolves sets of owners for every flag in the provided list.
Given a list of flags, for each flag, resolves owners for that flag. Resolving
owners means, for each entry in a flag's owners list:
* Turning owners files references into the transitive set of owners listed in
those files
* Turning bare usernames into @chromium.org email addresses
* Passing any other type of entry through unmodified
"""
owners_db = owners_client.GetCodeOwnersClient(
host="chromium-review.googlesource.com",
project="chromium/src",
branch="main")
new_flags = []
for f in flags:
new_flag = f.copy()
new_owners = set()
for o in f['owners']:
# Assume any filepath is to an OWNERS file.
if '/' in o:
new_owners.update(set(owners_db.ListBestOwners(re.sub('//', '', o))))
elif '@' not in o:
new_owners.add(o + '@chromium.org')
else:
new_owners.add(o)
new_flag['resolved_owners'] = sorted(new_owners)
new_flags.append(new_flag)
return new_flags
def find_unused(flags):
FLAG_FILES = [
'chrome/browser/about_flags.cc',
'ios/chrome/browser/flags/about_flags.mm',
]
flag_files_data = [open(f, 'r', encoding='utf-8').read() for f in FLAG_FILES]
unused_flags = []
for flag in flags:
# Search for the name in quotes.
needle = '"%s"' % flag['name']
if not any([needle in data for data in flag_files_data]):
unused_flags.append(flag)
return unused_flags
def filter_by_owner(flags, owner):
"""Filter by owner flag.
Only support single owner.
TODO(zhagnwenyu): Support filter by multiple owners.
Need exact match and need to include @google.com or @chromium.org in the
argument. This is because the owner with ldap only is extended with
@chromium.org automatically via resolve_owners function.
TODO(zhangwenyu): Support filter by ldap.
>>> f1 = {'name': 'f_1', 'owners': ['b@g.com']}
>>> f1['resolved_owners'] = ['b@g.com']
>>> f2 = {'name': 'f_2', 'owners': ['z']}
>>> f2['resolved_owners'] = ['z@c.org']
>>> filter_by_owner([f1, f2], 'b@g.com')
[{'name': 'f_1', 'owners': ['b@g.com'], 'resolved_owners': ['b@g.com']}]
>>> filter_by_owner([f1, f2], 'z@c.org')
[{'name': 'f_2', 'owners': ['z'], 'resolved_owners': ['z@c.org']}]
>>> filter_by_owner([f1, f2], 'z') # Filter by ldap not supported.
[]
>>> filter_by_owner([f1, f2], 'b@g.co') # Need exact match.
[]
>>> filter_by_owner([f1, f2], 'b@g.com,z@c.org') # Multi owners not supported.
[]
"""
filtered_flags = []
for f in flags:
if any([owner == o for o in f['resolved_owners']]):
filtered_flags.append(f)
return filtered_flags
def print_flags(flags, verbose):
"""Prints the supplied list of flags.
In verbose mode, prints name, expiry, and owner list; in non-verbose mode,
prints just the name. Verbose mode is actually tab-separated values, with
commas used as separators within individual fields - this is the format the
rest of the flags automation consumes most readily.
>>> f1 = {'name': 'foo', 'expiry_milestone': 73, 'owners': ['bar', 'baz']}
>>> f1['resolved_owners'] = ['bar@c.org', 'baz@c.org']
>>> f2 = {'name': 'bar', 'expiry_milestone': 74, 'owners': ['//quxx/OWNERS']}
>>> f2['resolved_owners'] = ['quxx@c.org']
>>> print_flags([f1], False)
foo
>>> print_flags([f1], True) # doctest: +NORMALIZE_WHITESPACE
foo 73 bar,baz bar@c.org,baz@c.org
>>> print_flags([f2], False)
bar
>>> print_flags([f2], True) # doctest: +NORMALIZE_WHITESPACE
bar 74 //quxx/OWNERS quxx@c.org
"""
for f in flags:
if verbose:
print('%s\t%d\t%s\t%s' % (f['name'], f['expiry_milestone'], ','.join(
f['owners']), ','.join(f['resolved_owners'])))
else:
print(f['name'])
def main():
import doctest
doctest.testmod()
parser = argparse.ArgumentParser(description=__doc__)
group = parser.add_mutually_exclusive_group()
group.add_argument('-n', '--never-expires', action='store_true')
group.add_argument('-e', '--expired-by', type=int)
group.add_argument('-u', '--find-unused', action='store_true')
group.add_argument('-o', '--has-owner', type=str)
parser.add_argument('-v', '--verbose', action='store_true')
parser.add_argument('--testonly', action='store_true')
args = parser.parse_args()
if args.testonly:
return
flags = utils.load_metadata()
if args.expired_by:
flags = utils.keep_expired_by(flags, args.expired_by)
if args.never_expires:
flags = keep_never_expires(flags)
if args.find_unused:
flags = find_unused(flags)
flags = resolve_owners(flags)
# Filter by owner after resolving owners completed, so it understands
# owners file.
if args.has_owner:
flags = filter_by_owner(flags, args.has_owner)
print_flags(flags, args.verbose)
if __name__ == '__main__':
main()