123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- #!/usr/bin/env python3
- # Copyright 2019 The Chromium Authors. All rights reserved.
- # 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
- ROOT_PATH = os.path.join(os.path.dirname(__file__), '..', '..')
- PYJSON5_PATH = os.path.join(ROOT_PATH, 'third_party', 'pyjson5', 'src')
- DEPOT_TOOLS_PATH = os.path.join(ROOT_PATH, 'third_party', 'depot_tools')
- sys.path.append(PYJSON5_PATH)
- sys.path.append(DEPOT_TOOLS_PATH)
- import json5
- import owners
- def load_metadata():
- flags_path = os.path.join(ROOT_PATH, 'chrome', 'browser',
- 'flag-metadata.json')
- return json5.load(open(flags_path))
- def keep_expired_by(flags, mstone):
- """Filter flags to contain only flags that expire by mstone.
- Only flags that either never expire or have an expiration milestone <= mstone
- are in the returned list.
- >>> keep_expired_by([{'expiry_milestone': 3}], 2)
- []
- >>> keep_expired_by([{'expiry_milestone': 3}], 3)
- [{'expiry_milestone': 3}]
- >>> keep_expired_by([{'expiry_milestone': -1}], 3)
- []
- """
- return [f for f in flags if -1 != f['expiry_milestone'] <= mstone]
- 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.Database(ROOT_PATH, open, os.path)
- new_flags = []
- for f in flags:
- new_flag = f.copy()
- new_owners = []
- for o in f['owners']:
- if o.startswith('//') or '/' in o:
- new_owners += owners_db.owners_rooted_at_file(re.sub('//', '', o))
- elif '@' not in o:
- new_owners.append(o + '@chromium.org')
- else:
- new_owners.append(o)
- new_flag['resolved_owners'] = 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 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')
- parser.add_argument('-v', '--verbose', action='store_true')
- parser.add_argument('--testonly', action='store_true')
- args = parser.parse_args()
- if args.testonly:
- return
- flags = load_metadata()
- if args.expired_by:
- flags = 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)
- print_flags(flags, args.verbose)
- if __name__ == '__main__':
- main()
|