1*b3adc34aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*b3adc34aSAndroid Build Coastguard Worker# Copyright 2019, The Android Open Source Project 3*b3adc34aSAndroid Build Coastguard Worker# 4*b3adc34aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*b3adc34aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*b3adc34aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*b3adc34aSAndroid Build Coastguard Worker# 8*b3adc34aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*b3adc34aSAndroid Build Coastguard Worker# 10*b3adc34aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*b3adc34aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*b3adc34aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*b3adc34aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*b3adc34aSAndroid Build Coastguard Worker# limitations under the License. 15*b3adc34aSAndroid Build Coastguard Worker 16*b3adc34aSAndroid Build Coastguard Worker 17*b3adc34aSAndroid Build Coastguard Workerimport argparse 18*b3adc34aSAndroid Build Coastguard Workerimport os 19*b3adc34aSAndroid Build Coastguard Workerimport shlex 20*b3adc34aSAndroid Build Coastguard Workerimport subprocess 21*b3adc34aSAndroid Build Coastguard Workerimport sys 22*b3adc34aSAndroid Build Coastguard Worker 23*b3adc34aSAndroid Build Coastguard Worker 24*b3adc34aSAndroid Build Coastguard WorkerPREBUILTS_DIR = os.path.dirname(__file__) 25*b3adc34aSAndroid Build Coastguard WorkerPROJECTS = {'acloud', 'aidegen', 'atest'} 26*b3adc34aSAndroid Build Coastguard WorkerARCHS = {'linux-x86', 'darwin-x86'} 27*b3adc34aSAndroid Build Coastguard WorkerSMOKE_TEST = 'smoke_tests' 28*b3adc34aSAndroid Build Coastguard WorkerEXIT_TEST_PASS = 0 29*b3adc34aSAndroid Build Coastguard WorkerEXIT_TEST_FAIL = 1 30*b3adc34aSAndroid Build Coastguard WorkerEXIT_INVALID_BINS = 2 31*b3adc34aSAndroid Build Coastguard Worker 32*b3adc34aSAndroid Build Coastguard Worker 33*b3adc34aSAndroid Build Coastguard Workerdef _get_prebuilt_bins(): 34*b3adc34aSAndroid Build Coastguard Worker """Get asuite prebuilt binaries. 35*b3adc34aSAndroid Build Coastguard Worker 36*b3adc34aSAndroid Build Coastguard Worker Returns: 37*b3adc34aSAndroid Build Coastguard Worker A set of prebuilt binaries. 38*b3adc34aSAndroid Build Coastguard Worker """ 39*b3adc34aSAndroid Build Coastguard Worker bins = {os.path.join(prj, arch, prj) for prj in PROJECTS for arch in ARCHS} 40*b3adc34aSAndroid Build Coastguard Worker bins.add('atest/linux-x86/atest-py3') 41*b3adc34aSAndroid Build Coastguard Worker return bins 42*b3adc34aSAndroid Build Coastguard Worker 43*b3adc34aSAndroid Build Coastguard Worker 44*b3adc34aSAndroid Build Coastguard Workerdef _get_prebuilt_dirs(): 45*b3adc34aSAndroid Build Coastguard Worker """Get asuite prebuilt directories. 46*b3adc34aSAndroid Build Coastguard Worker 47*b3adc34aSAndroid Build Coastguard Worker Returns: 48*b3adc34aSAndroid Build Coastguard Worker A set of prebuilt paths of binaries. 49*b3adc34aSAndroid Build Coastguard Worker """ 50*b3adc34aSAndroid Build Coastguard Worker return {os.path.dirname(bin) for bin in _get_prebuilt_bins()} 51*b3adc34aSAndroid Build Coastguard Worker 52*b3adc34aSAndroid Build Coastguard Worker 53*b3adc34aSAndroid Build Coastguard Workerdef _get_smoke_tests_bins(): 54*b3adc34aSAndroid Build Coastguard Worker """Get asuite smoke test scripts. 55*b3adc34aSAndroid Build Coastguard Worker 56*b3adc34aSAndroid Build Coastguard Worker Returns: 57*b3adc34aSAndroid Build Coastguard Worker A dict of project and smoke test script paths. 58*b3adc34aSAndroid Build Coastguard Worker """ 59*b3adc34aSAndroid Build Coastguard Worker return {prj: os.path.join(prj, SMOKE_TEST) for prj in PROJECTS} 60*b3adc34aSAndroid Build Coastguard Worker 61*b3adc34aSAndroid Build Coastguard Worker 62*b3adc34aSAndroid Build Coastguard Workerdef _is_executable(bin_path): 63*b3adc34aSAndroid Build Coastguard Worker """Check if the given file is executable. 64*b3adc34aSAndroid Build Coastguard Worker 65*b3adc34aSAndroid Build Coastguard Worker Args: 66*b3adc34aSAndroid Build Coastguard Worker bin_path: a string of a file path. 67*b3adc34aSAndroid Build Coastguard Worker 68*b3adc34aSAndroid Build Coastguard Worker Returns: 69*b3adc34aSAndroid Build Coastguard Worker True if it is executable, false otherwise. 70*b3adc34aSAndroid Build Coastguard Worker """ 71*b3adc34aSAndroid Build Coastguard Worker return os.access(bin_path, os.X_OK) 72*b3adc34aSAndroid Build Coastguard Worker 73*b3adc34aSAndroid Build Coastguard Worker 74*b3adc34aSAndroid Build Coastguard Workerdef check_uploaded_bins(preupload_files): 75*b3adc34aSAndroid Build Coastguard Worker """This method validates the uploaded files. 76*b3adc34aSAndroid Build Coastguard Worker 77*b3adc34aSAndroid Build Coastguard Worker If the uploaded file is in prebuilt_bins, ensure: 78*b3adc34aSAndroid Build Coastguard Worker - it is executable. 79*b3adc34aSAndroid Build Coastguard Worker - only one at a time. 80*b3adc34aSAndroid Build Coastguard Worker If the uploaded file is a smoke_test script, ensure: 81*b3adc34aSAndroid Build Coastguard Worker - it is executable. 82*b3adc34aSAndroid Build Coastguard Worker If the uploaded file is placed in prebuilt_dirs, ensure: 83*b3adc34aSAndroid Build Coastguard Worker - it is not executable. 84*b3adc34aSAndroid Build Coastguard Worker (It is to ensure PATH is not contaminated. 85*b3adc34aSAndroid Build Coastguard Worker e.g. atest/linux-x86/atest-dev will override $OUT/bin/atest-dev, or 86*b3adc34aSAndroid Build Coastguard Worker atest/linux-x86/rm does fraud/harmful things.) 87*b3adc34aSAndroid Build Coastguard Worker 88*b3adc34aSAndroid Build Coastguard Worker Args: 89*b3adc34aSAndroid Build Coastguard Worker preupload_files: A list of preuploaded files. 90*b3adc34aSAndroid Build Coastguard Worker 91*b3adc34aSAndroid Build Coastguard Worker Returns: 92*b3adc34aSAndroid Build Coastguard Worker True is the above criteria are all fulfilled, otherwise None. 93*b3adc34aSAndroid Build Coastguard Worker """ 94*b3adc34aSAndroid Build Coastguard Worker prebuilt_bins = _get_prebuilt_bins() 95*b3adc34aSAndroid Build Coastguard Worker prebuilt_dirs = _get_prebuilt_dirs() 96*b3adc34aSAndroid Build Coastguard Worker smoke_tests_bins = _get_smoke_tests_bins().values() 97*b3adc34aSAndroid Build Coastguard Worker # Store valid executables. 98*b3adc34aSAndroid Build Coastguard Worker target_bins = set() 99*b3adc34aSAndroid Build Coastguard Worker # Unexpected executable files which may cause issues(they are in $PATH). 100*b3adc34aSAndroid Build Coastguard Worker illegal_bins = set() 101*b3adc34aSAndroid Build Coastguard Worker # Store prebuilts or smoke test script that are inexecutable. 102*b3adc34aSAndroid Build Coastguard Worker insufficient_perm_bins = set() 103*b3adc34aSAndroid Build Coastguard Worker for f in preupload_files: 104*b3adc34aSAndroid Build Coastguard Worker # Ensure target_bins are executable. 105*b3adc34aSAndroid Build Coastguard Worker if f in prebuilt_bins: 106*b3adc34aSAndroid Build Coastguard Worker if _is_executable(f): 107*b3adc34aSAndroid Build Coastguard Worker target_bins.add(f) 108*b3adc34aSAndroid Build Coastguard Worker else: 109*b3adc34aSAndroid Build Coastguard Worker insufficient_perm_bins.add(f) 110*b3adc34aSAndroid Build Coastguard Worker # Ensure smoke_tests scripts are executable. 111*b3adc34aSAndroid Build Coastguard Worker elif f in smoke_tests_bins and not _is_executable(f): 112*b3adc34aSAndroid Build Coastguard Worker insufficient_perm_bins.add(f) 113*b3adc34aSAndroid Build Coastguard Worker # Avoid fraud commands in $PATH. e.g. atest/linux-x86/rm. 114*b3adc34aSAndroid Build Coastguard Worker # must not be executable. 115*b3adc34aSAndroid Build Coastguard Worker elif os.path.dirname(f) in prebuilt_dirs and _is_executable(f): 116*b3adc34aSAndroid Build Coastguard Worker illegal_bins.add(f) 117*b3adc34aSAndroid Build Coastguard Worker if len(target_bins) > 1: 118*b3adc34aSAndroid Build Coastguard Worker print('\nYou\'re uploading multiple binaries: %s' 119*b3adc34aSAndroid Build Coastguard Worker % ' '.join(target_bins)) 120*b3adc34aSAndroid Build Coastguard Worker print('\nPlease upload one prebuilt at a time.') 121*b3adc34aSAndroid Build Coastguard Worker return False 122*b3adc34aSAndroid Build Coastguard Worker if insufficient_perm_bins: 123*b3adc34aSAndroid Build Coastguard Worker print('\nInsufficient permission found: %s' 124*b3adc34aSAndroid Build Coastguard Worker % ' '.join(insufficient_perm_bins)) 125*b3adc34aSAndroid Build Coastguard Worker print('\nPlease run:\n\tchmod 0755 %s\nand try again.' 126*b3adc34aSAndroid Build Coastguard Worker % ' '.join(insufficient_perm_bins)) 127*b3adc34aSAndroid Build Coastguard Worker return False 128*b3adc34aSAndroid Build Coastguard Worker if illegal_bins: 129*b3adc34aSAndroid Build Coastguard Worker illegal_dirs = {os.path.dirname(bin) for bin in illegal_bins} 130*b3adc34aSAndroid Build Coastguard Worker print('\nIt is forbidden to upload executable file: %s' 131*b3adc34aSAndroid Build Coastguard Worker % '\n - %s\n' % '\n - '.join(illegal_bins)) 132*b3adc34aSAndroid Build Coastguard Worker print('Because they are in the project paths: %s' 133*b3adc34aSAndroid Build Coastguard Worker % '\n - %s\n' % '\n - '.join(illegal_dirs)) 134*b3adc34aSAndroid Build Coastguard Worker print('Please remove the binaries or make the files non-executable.') 135*b3adc34aSAndroid Build Coastguard Worker return False 136*b3adc34aSAndroid Build Coastguard Worker return True 137*b3adc34aSAndroid Build Coastguard Worker 138*b3adc34aSAndroid Build Coastguard Worker 139*b3adc34aSAndroid Build Coastguard Workerdef run_smoke_tests_pass(files_to_check): 140*b3adc34aSAndroid Build Coastguard Worker """Run smoke tests. 141*b3adc34aSAndroid Build Coastguard Worker 142*b3adc34aSAndroid Build Coastguard Worker Args: 143*b3adc34aSAndroid Build Coastguard Worker files_to_check: A list of preuploaded files to check. 144*b3adc34aSAndroid Build Coastguard Worker 145*b3adc34aSAndroid Build Coastguard Worker Returns: 146*b3adc34aSAndroid Build Coastguard Worker True when test passed or no need to test. 147*b3adc34aSAndroid Build Coastguard Worker False when test failed. 148*b3adc34aSAndroid Build Coastguard Worker """ 149*b3adc34aSAndroid Build Coastguard Worker for target in files_to_check: 150*b3adc34aSAndroid Build Coastguard Worker if target in _get_prebuilt_bins(): 151*b3adc34aSAndroid Build Coastguard Worker project = target.split(os.path.sep)[0] 152*b3adc34aSAndroid Build Coastguard Worker test_file = _get_smoke_tests_bins().get(project) 153*b3adc34aSAndroid Build Coastguard Worker if os.path.exists(test_file): 154*b3adc34aSAndroid Build Coastguard Worker try: 155*b3adc34aSAndroid Build Coastguard Worker print(f'Running test binary {test_file} which may take' 156*b3adc34aSAndroid Build Coastguard Worker ' several minutes...') 157*b3adc34aSAndroid Build Coastguard Worker subprocess.check_output(test_file, encoding='utf-8', 158*b3adc34aSAndroid Build Coastguard Worker stderr=subprocess.STDOUT) 159*b3adc34aSAndroid Build Coastguard Worker except subprocess.CalledProcessError as error: 160*b3adc34aSAndroid Build Coastguard Worker print('Smoke tests failed at:\n\n%s' % error.output) 161*b3adc34aSAndroid Build Coastguard Worker return False 162*b3adc34aSAndroid Build Coastguard Worker except OSError as oserror: 163*b3adc34aSAndroid Build Coastguard Worker print('%s: Missing the header of the script.' % oserror) 164*b3adc34aSAndroid Build Coastguard Worker print('Please define shebang like:\n') 165*b3adc34aSAndroid Build Coastguard Worker print('#!/usr/bin/env bash\nor') 166*b3adc34aSAndroid Build Coastguard Worker print('#!/usr/bin/env python3\n') 167*b3adc34aSAndroid Build Coastguard Worker return False 168*b3adc34aSAndroid Build Coastguard Worker return True 169*b3adc34aSAndroid Build Coastguard Worker 170*b3adc34aSAndroid Build Coastguard Worker 171*b3adc34aSAndroid Build Coastguard Workerif __name__ == '__main__': 172*b3adc34aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 173*b3adc34aSAndroid Build Coastguard Worker parser.add_argument('--skip-smoke-test', '-s', action="store_true", 174*b3adc34aSAndroid Build Coastguard Worker help='Disable smoke testing.') 175*b3adc34aSAndroid Build Coastguard Worker parser.add_argument('preupload_files', nargs='*', help='Files to be uploaded.') 176*b3adc34aSAndroid Build Coastguard Worker args = parser.parse_args() 177*b3adc34aSAndroid Build Coastguard Worker files_to_check = args.preupload_files 178*b3adc34aSAndroid Build Coastguard Worker # Pre-process files_to_check(run directly by users.) 179*b3adc34aSAndroid Build Coastguard Worker if not files_to_check: 180*b3adc34aSAndroid Build Coastguard Worker # Only consider added(A), renamed(R) and modified(M) files. 181*b3adc34aSAndroid Build Coastguard Worker cmd = "git status --short | egrep ^[ARM] | awk '{print $NF}'" 182*b3adc34aSAndroid Build Coastguard Worker preupload_files = subprocess.check_output(cmd, shell=True, 183*b3adc34aSAndroid Build Coastguard Worker encoding='utf-8').splitlines() 184*b3adc34aSAndroid Build Coastguard Worker if preupload_files: 185*b3adc34aSAndroid Build Coastguard Worker print('validating: %s' % preupload_files) 186*b3adc34aSAndroid Build Coastguard Worker files_to_check = preupload_files 187*b3adc34aSAndroid Build Coastguard Worker # Validating uploaded files and run smoke test script(run by repohook). 188*b3adc34aSAndroid Build Coastguard Worker if not check_uploaded_bins(files_to_check): 189*b3adc34aSAndroid Build Coastguard Worker sys.exit(EXIT_INVALID_BINS) 190*b3adc34aSAndroid Build Coastguard Worker if not args.skip_smoke_test and not run_smoke_tests_pass(files_to_check): 191*b3adc34aSAndroid Build Coastguard Worker sys.exit(EXIT_TEST_FAIL) 192*b3adc34aSAndroid Build Coastguard Worker sys.exit(EXIT_TEST_PASS) 193