| #!/usr/bin/env python3 | 
 | # Copyright (C) 2024 The Android Open Source Project | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #      http://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an "AS IS" BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 |  | 
 | import argparse | 
 | import os | 
 | import sys | 
 | import subprocess | 
 |  | 
 | UI_DIR = os.path.abspath(os.path.dirname(__file__)) | 
 | ROOT_DIR = os.path.dirname(UI_DIR) | 
 | PRETTIER_PATH = os.path.join(ROOT_DIR, 'ui/node_modules/.bin/prettier') | 
 | ESLINT_PATH = os.path.join(ROOT_DIR, 'ui/node_modules/.bin/eslint') | 
 |  | 
 |  | 
 | def main(): | 
 |   parser = argparse.ArgumentParser() | 
 |   parser.add_argument('--check-only', action='store_true') | 
 |   parser.add_argument('--no-prettier', action='store_true') | 
 |   parser.add_argument('--no-eslint', action='store_true') | 
 |   parser.add_argument( | 
 |       '--all', | 
 |       action='store_true', | 
 |       help='Prettify all .ts sources, not just the changed ones') | 
 |   parser.add_argument('filelist', nargs='*') | 
 |   args = parser.parse_args() | 
 |  | 
 |   # We want to execute all the commands relative to UI_DIR, because eslint looks | 
 |   # for eslintrc in cwd. However, the user might pass paths that are relative to | 
 |   # the current cwd, which might be != UI_DIR. | 
 |   # So before running chdir relativize all the passed paths to UI_DIR | 
 |   filelist = [ | 
 |       os.path.relpath(os.path.abspath(x), UI_DIR) for x in args.filelist | 
 |   ] | 
 |   os.chdir(UI_DIR) | 
 |  | 
 |   # Need to add 'node' to the search path. | 
 |   os.environ['PATH'] += os.pathsep + os.path.join(ROOT_DIR, 'tools') | 
 |  | 
 |   if not os.path.exists(PRETTIER_PATH): | 
 |     print('Cannot find %s' % PRETTIER_PATH) | 
 |     print('Run tools/install-build-deps --ui') | 
 |     return 1 | 
 |  | 
 |   with open('.prettierignore', 'r') as f: | 
 |     ignorelist = set(f.read().strip().split('\n')) | 
 |  | 
 |   all_files = set() | 
 |   for root, _dirs, files in os.walk('src'): | 
 |     if root in ignorelist: | 
 |       continue | 
 |     for file in files: | 
 |       file_path = os.path.join(root, file) | 
 |       if os.path.splitext(file)[1].lower() in ['.ts', '.js', '.scss']: | 
 |         all_files.add(file_path) | 
 |  | 
 |   files_to_check = [] | 
 |   git_cmd = [] | 
 |   if args.all: | 
 |     files_to_check = list(all_files) | 
 |   elif filelist: | 
 |     files_to_check = filelist | 
 |   else: | 
 |     upstream_branch = get_upstream_branch() | 
 |     git_cmd = ['git', 'diff', '--name-only', upstream_branch] | 
 |     git_output = subprocess.check_output(git_cmd, text=True).strip() | 
 |     changed_files = set(git_output.split('\n') if git_output else []) | 
 |     changed_files = [os.path.relpath(x, 'ui') for x in changed_files] | 
 |     files_to_check = all_files.intersection(changed_files) | 
 |  | 
 |   prettier_args = ['--log-level=warn'] | 
 |   eslint_args = [] | 
 |   if args.check_only: | 
 |     prettier_args += ['--check'] | 
 |   else: | 
 |     eslint_args += ['--fix'] | 
 |     prettier_args += ['--write'] | 
 |  | 
 |   if len(files_to_check) == 0: | 
 |     if not args.check_only: | 
 |       # Be quiet when invoked by git cl presubmit. | 
 |       print('No changed files detected by `%s`' % ' '.join(git_cmd)) | 
 |       print('Pass --all to prettify all ts/js/scss files in the repo') | 
 |     return 0 | 
 |  | 
 |   # Run prettier first | 
 |   if not args.no_prettier: | 
 |     print('Running prettier on %d files' % len(files_to_check)) | 
 |     call_or_die([PRETTIER_PATH] + prettier_args + list(files_to_check)) | 
 |  | 
 |   # Then run eslint (but not on .scss) | 
 |   if not args.no_eslint: | 
 |     ts_js_only = lambda f: f.endswith('.ts') or f.endswith('.js') | 
 |     files_to_check = list(filter(ts_js_only, files_to_check)) | 
 |     if len(files_to_check) > 0: | 
 |       print('Running eslint on %d files' % len(files_to_check)) | 
 |       call_or_die([ESLINT_PATH] + eslint_args + files_to_check) | 
 |  | 
 |  | 
 | # Like subprocess.check_call, but in case of errors dies without printing a | 
 | # useless stacktrace that just muddies the stdout. | 
 | def call_or_die(cmd): | 
 |   try: | 
 |     subprocess.check_call(cmd) | 
 |   except subprocess.CalledProcessError as ex: | 
 |     print('`%s` returned %d' % (' '.join(cmd)[:128], ex.returncode)) | 
 |     sys.exit(ex.returncode) | 
 |  | 
 |  | 
 | def get_upstream_branch(): | 
 |   try: | 
 |     cmd = ['git', 'rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}'] | 
 |     res = subprocess.check_output(cmd, text=True, stderr=subprocess.DEVNULL) | 
 |     return res.strip() | 
 |   except subprocess.CalledProcessError: | 
 |     return 'origin/main' | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(main()) |