xref: /aosp_15_r20/prebuilts/asuite/release_checker.py (revision b3adc34a6be591bf5fd4943dfdce52f3982696f0)
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