blob: 5db27d8a9529d0eef61a90d38efe711849fd8d39 [file] [log] [blame]
Anushree Ganjamda9c6242023-10-19 14:33:35 -07001#! /usr/bin/env python3
2
3import sys
4import re
5import argparse
6
7# partially copied from tools/repohooks/rh/hooks.py
8
9TEST_MSG = """Commit message is missing a "Flag:" line. It must match one of the
10following case-sensitive regex:
11
12 %s
13
14The Flag: stanza is regex matched and should describe whether your change is behind a flag or flags.
15
16As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the
17flag in addition to its state (ENABLED|DISABLED|DEVELOPMENT|TEAMFOOD|TRUNKFOOD|NEXTFOOD).
18
19Some examples below:
20
21Flag: NONE
22Flag: NA
23Flag: LEGACY ENABLE_ONE_SEARCH DISABLED
24Flag: ACONFIG com.android.launcher3.enable_twoline_allapps DEVELOPMENT
25Flag: ACONFIG com.android.launcher3.enable_twoline_allapps TRUNKFOOD
26
27Check the git history for more examples. It's a regex matched field.
28"""
29
30def main():
31 """Check the commit message for a 'Flag:' line."""
32 parser = argparse.ArgumentParser(
33 description='Check the commit message for a Flag: line.')
34 parser.add_argument('--msg',
35 metavar='msg',
36 type=str,
37 nargs='?',
38 default='HEAD',
39 help='commit message to process.')
40 parser.add_argument(
41 '--files',
42 metavar='files',
43 nargs='?',
44 default='',
45 help=
46 'PREUPLOAD_FILES in repo upload to determine whether the check should run for the files.')
47 parser.add_argument(
48 '--project',
49 metavar='project',
50 type=str,
51 nargs='?',
52 default='',
53 help=
54 'REPO_PATH in repo upload to determine whether the check should run for this project.')
55
56 # Parse the arguments
57 args = parser.parse_args()
58 desc = args.msg
59 files = args.files
60 project = args.project
61
62 if not should_run_path(project, files):
63 return
64
65 field = 'Flag'
66 none = '(NONE|NA|N\/A)' # NONE|NA|N/A
67
68 typeExpression = '\s*(LEGACY|ACONFIG)' # [type:LEGACY|ACONFIG]
69
70 # legacyFlagName contains only uppercase alphabets with '_' - Ex: ENABLE_ONE_SEARCH
71 # Aconfig Flag name format = "packageName"."flagName"
72 # package name - Contains only lowercase alphabets + digits + '.' - Ex: com.android.launcher3
73 # For now alphabets, digits, "_", "." characters are allowed in flag name and not adding stricter format check.
74 #common_typos_disable
75 flagName = '([a-zA-z0-9_.])+'
76
77 #[state:ENABLED|DISABLED|DEVELOPMENT|TEAM*(TEAMFOOD)|TRUNK*(TRUNK_STAGING, TRUNK_FOOD)|NEXT*(NEXTFOOD)]
78 stateExpression = '\s*(ENABLED|DISABLED|DEVELOPMENT|TEAM[a-zA-z]*|TRUNK[a-zA-z]*|NEXT[a-zA-z]*)'
79 #common_typos_enable
80
81 readableRegexMsg = '\n\tFlag: (NONE|NA)\n\tFlag: LEGACY|ACONFIG FlagName|packageName.flagName ENABLED|DISABLED|DEVELOPMENT|TEAMFOOD|TRUNKFOOD|NEXTFOOD'
82
83 flagRegex = fr'^{field}: .*$'
84 check_flag = re.compile(flagRegex) #Flag:
85
86 # Ignore case for flag name format.
87 flagNameRegex = fr'(?i)^{field}:\s*({none}|{typeExpression}\s*{flagName}\s*{stateExpression})\s*'
88 check_flagName = re.compile(flagNameRegex) #Flag: <flag name format>
89
90 flagError = False
91 foundFlag = []
92 # Check for multiple "Flag:" lines and all lines should match this format
93 for line in desc.splitlines():
94 if check_flag.match(line):
95 if not check_flagName.match(line):
96 flagError = True
97 break
98 foundFlag.append(line)
99
100 # Throw error if
101 # 1. No "Flag:" line is found
102 # 2. "Flag:" doesn't follow right format.
103 if (not foundFlag) or (flagError):
104 error = TEST_MSG % (readableRegexMsg)
105 print(error)
106 sys.exit(1)
107
108 sys.exit(0)
109
110
111def should_run_path(path, files):
112 """Returns a boolean if this check should run with these paths.
113 If you want to check for a particular subdirectory under the path,
114 add a check here, call should_run_files and check for a specific sub dir path in should_run_files.
115 """
116 if not path:
117 return False
118 if path == 'frameworks/base':
119 return should_run_files(files)
120 # Default case, run for all other paths which calls this script.
121 return True
122
123
124def should_run_files(files):
125 """Returns a boolean if this check should run with these files."""
126 if not files:
127 return False
128 if 'packages/SystemUI' in files:
129 return True
130 return False
131
132
133if __name__ == '__main__':
134 main()