| #! /usr/bin/env python3 |
| |
| import sys |
| import re |
| import argparse |
| |
| # partially copied from tools/repohooks/rh/hooks.py |
| |
| TEST_MSG = """Commit message is missing a "Flag:" line. It must match one of the |
| following case-sensitive regex: |
| |
| %s |
| |
| The Flag: stanza is regex matched and should describe whether your change is behind a flag or flags. |
| |
| As 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 |
| flag in addition to its state (ENABLED|DISABLED|DEVELOPMENT|STAGING|TEAMFOOD|TRUNKFOOD|NEXTFOOD). |
| |
| Some examples below: |
| |
| Flag: NONE |
| Flag: NA |
| Flag: LEGACY ENABLE_ONE_SEARCH DISABLED |
| Flag: ACONFIG com.android.launcher3.enable_twoline_allapps DEVELOPMENT |
| Flag: ACONFIG com.android.launcher3.enable_twoline_allapps TRUNKFOOD |
| |
| Check the git history for more examples. It's a regex matched field. |
| """ |
| |
| def main(): |
| """Check the commit message for a 'Flag:' line.""" |
| parser = argparse.ArgumentParser( |
| description='Check the commit message for a Flag: line.') |
| parser.add_argument('--msg', |
| metavar='msg', |
| type=str, |
| nargs='?', |
| default='HEAD', |
| help='commit message to process.') |
| parser.add_argument( |
| '--files', |
| metavar='files', |
| nargs='?', |
| default='', |
| help= |
| 'PREUPLOAD_FILES in repo upload to determine whether the check should run for the files.') |
| parser.add_argument( |
| '--project', |
| metavar='project', |
| type=str, |
| nargs='?', |
| default='', |
| help= |
| 'REPO_PATH in repo upload to determine whether the check should run for this project.') |
| |
| # Parse the arguments |
| args = parser.parse_args() |
| desc = args.msg |
| files = args.files |
| project = args.project |
| |
| if not should_run_path(project, files): |
| return |
| |
| field = 'Flag' |
| none = '(NONE|NA|N\/A)' # NONE|NA|N/A |
| |
| typeExpression = '\s*(LEGACY|ACONFIG)' # [type:LEGACY|ACONFIG] |
| |
| # legacyFlagName contains only uppercase alphabets with '_' - Ex: ENABLE_ONE_SEARCH |
| # Aconfig Flag name format = "packageName"."flagName" |
| # package name - Contains only lowercase alphabets + digits + '.' - Ex: com.android.launcher3 |
| # For now alphabets, digits, "_", "." characters are allowed in flag name and not adding stricter format check. |
| #common_typos_disable |
| flagName = '([a-zA-z0-9_.])+' |
| |
| #[state:ENABLED|DISABLED|DEVELOPMENT|TEAM*(TEAMFOOD)|STAGING|TRUNK*(TRUNK_STAGING, TRUNK_FOOD)|NEXT*(NEXTFOOD)] |
| stateExpression = '\s*(ENABLED|DISABLED|DEVELOPMENT|TEAM[a-zA-z]*|STAGING|TRUNK[a-zA-z]*|NEXT[a-zA-z]*)' |
| #common_typos_enable |
| |
| readableRegexMsg = '\n\tFlag: (NONE|NA)\n\tFlag: LEGACY|ACONFIG FlagName|packageName.flagName ENABLED|DISABLED|DEVELOPMENT|TEAMFOOD|STAGING|TRUNKFOOD|NEXTFOOD' |
| |
| flagRegex = fr'^{field}: .*$' |
| check_flag = re.compile(flagRegex) #Flag: |
| |
| # Ignore case for flag name format. |
| flagNameRegex = fr'(?i)^{field}:\s*({none}|{typeExpression}\s*{flagName}\s*{stateExpression})\s*' |
| check_flagName = re.compile(flagNameRegex) #Flag: <flag name format> |
| |
| flagError = False |
| foundFlag = [] |
| # Check for multiple "Flag:" lines and all lines should match this format |
| for line in desc.splitlines(): |
| if check_flag.match(line): |
| if not check_flagName.match(line): |
| flagError = True |
| break |
| foundFlag.append(line) |
| |
| # Throw error if |
| # 1. No "Flag:" line is found |
| # 2. "Flag:" doesn't follow right format. |
| if (not foundFlag) or (flagError): |
| error = TEST_MSG % (readableRegexMsg) |
| print(error) |
| sys.exit(1) |
| |
| sys.exit(0) |
| |
| |
| def should_run_path(path, files): |
| """Returns a boolean if this check should run with these paths. |
| If you want to check for a particular subdirectory under the path, |
| add a check here, call should_run_files and check for a specific sub dir path in should_run_files. |
| """ |
| if not path: |
| return False |
| if path == 'frameworks/base': |
| return should_run_files(files) |
| # Default case, run for all other paths which calls this script. |
| return True |
| |
| |
| def should_run_files(files): |
| """Returns a boolean if this check should run with these files.""" |
| if not files: |
| return False |
| if 'packages/SystemUI' in files: |
| return True |
| return False |
| |
| |
| if __name__ == '__main__': |
| main() |