170 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
			
		
		
	
	
			170 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
| #!/usr/bin/env python3
 | |
| # Copyright (C) 2019 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.
 | |
| 
 | |
| from __future__ import print_function
 | |
| import argparse
 | |
| import distutils
 | |
| import errno
 | |
| import grp
 | |
| import os
 | |
| import readline
 | |
| import sys
 | |
| import shutil
 | |
| import subprocess
 | |
| from pipes import quote
 | |
| from subprocess import check_call
 | |
| 
 | |
| try:
 | |
|   from shutil import which as find_executable
 | |
| except AttributeError:
 | |
|   from distutils.spawn import find_executable
 | |
| 
 | |
| REPO_ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
 | |
| sys.path.append(os.path.join(REPO_ROOT, 'infra', 'ci'))
 | |
| from config import JOB_CONFIGS, SANDBOX_IMG
 | |
| 
 | |
| try:
 | |
|   input = raw_input
 | |
| except NameError:
 | |
|   pass
 | |
| 
 | |
| 
 | |
| def user_in_docker_group():
 | |
|   try:
 | |
|     group = grp.getgrnam('docker')
 | |
|   except KeyError:
 | |
|     return False
 | |
|   else:
 | |
|     return group.gr_gid in os.getgroups()
 | |
| 
 | |
| 
 | |
| def decision(question='Would you like to continue', confirm=True, default='n'):
 | |
|   default = default.lower().strip()
 | |
|   yes = default in {'y', 'yes'}
 | |
|   no = default in {'n', 'no'}
 | |
|   default = 'y' if yes else 'n'
 | |
|   prompt = '%s? [%s/%s]: ' % (question, 'Y' if yes else 'y', 'N' if no else 'n')
 | |
|   if not confirm:
 | |
|     print('%sy' % prompt)
 | |
|     return
 | |
|   while True:
 | |
|     choice = input(prompt).lower().strip()
 | |
|     if not choice:
 | |
|       choice = default
 | |
|     if choice in {'y', 'yes'}:
 | |
|       return
 | |
|     elif choice in {'n', 'no'}:
 | |
|       sys.exit(3)
 | |
| 
 | |
| 
 | |
| def main():
 | |
|   parser = argparse.ArgumentParser(
 | |
|       formatter_class=argparse.ArgumentDefaultsHelpFormatter)
 | |
|   parser.add_argument('config', choices=JOB_CONFIGS.keys())
 | |
|   parser.add_argument(
 | |
|       '--runner',
 | |
|       help='The container runner executable to use',
 | |
|       choices=('podman', 'docker'),
 | |
|       default='podman' if find_executable('podman') else 'docker')
 | |
|   parser.add_argument(
 | |
|       '--build',
 | |
|       action='store_true',
 | |
|       help='Will perform a build of sandbox image')
 | |
|   group = parser.add_mutually_exclusive_group()
 | |
|   group.add_argument(
 | |
|       '--confirm',
 | |
|       action='store_true',
 | |
|       default=True,
 | |
|       help='User confirmation of decision prompts')
 | |
|   group.add_argument(
 | |
|       '--no-confirm',
 | |
|       dest='confirm',
 | |
|       action='store_false',
 | |
|       help='Forces confirmation of decision prompts')
 | |
|   args = parser.parse_args()
 | |
| 
 | |
|   # Check that the directory is clean.
 | |
|   git_cmd = ['git', '-C', REPO_ROOT, 'status', '--porcelain']
 | |
|   modified_files = subprocess.check_output(git_cmd).decode()
 | |
|   if modified_files:
 | |
|     print('The current Git repo has modified/untracked files.')
 | |
|     print('The sandboxed VM will fetch the HEAD of your current git repo.')
 | |
|     print('This is probably not the state you want to be in.')
 | |
|     print('I suggest you stop, commit and then re-run this script')
 | |
|     print('Modified files:\n' + modified_files)
 | |
|     decision('Do you know what you are doing', confirm=args.confirm)
 | |
| 
 | |
|   if args.build:
 | |
|     print('')
 | |
|     print('About to build %r locally with %r' % (args.image, args.runner))
 | |
|     decision(confirm=args.confirm)
 | |
|     check_call(('make', '-C', os.path.join(REPO_ROOT, 'infra',
 | |
|                                            'ci'),
 | |
|                 'BUILDER=%s' % args.runner, 'build-sandbox'))
 | |
| 
 | |
|   bundle_path = '/tmp/perfetto-ci.bundle'
 | |
|   check_call(['git', '-C', REPO_ROOT, 'bundle', 'create', bundle_path, 'HEAD' ])
 | |
|   os.chmod(bundle_path, 0o664)
 | |
|   env = {
 | |
|       'PERFETTO_TEST_GIT_REF': bundle_path,
 | |
|   }
 | |
|   env.update(JOB_CONFIGS[args.config])
 | |
| 
 | |
|   workdir = os.path.join(REPO_ROOT, 'out', 'tmp.ci')
 | |
|   cmd = []
 | |
|   if args.runner == 'docker' and not user_in_docker_group():
 | |
|     cmd += ['sudo', '--']
 | |
|   cmd += [
 | |
|       args.runner, 'run', '-it', '--name', 'perfetto_ci', '--cap-add',
 | |
|       'SYS_PTRACE', '--rm', '--volume',
 | |
|       '%s:/ci/ramdisk' % workdir, '--tmpfs', '/tmp:exec',
 | |
|       '--volume=%s:%s:ro' % (bundle_path, bundle_path)
 | |
|   ]
 | |
|   for kv in env.items():
 | |
|     cmd += ['--env', '%s=%s' % kv]
 | |
|   cmd += [SANDBOX_IMG]
 | |
|   cmd += [
 | |
|       'bash', '-c',
 | |
|       'cd /ci/ramdisk; bash /ci/init.sh || sudo -u perfetto -EH bash -i'
 | |
|   ]
 | |
| 
 | |
|   print(
 | |
|       'About to run\n',
 | |
|       ' '.join('\n  ' + c if c.startswith('--') or c == 'bash' else quote(c)
 | |
|                for c in cmd))
 | |
|   print('')
 | |
|   print('The VM workdir /ci/ramdisk will be mounted into: %s' % workdir)
 | |
|   print('The contents of %s will be deleted before starting the VM' % workdir)
 | |
|   decision(confirm=args.confirm)
 | |
| 
 | |
|   try:
 | |
|     shutil.rmtree(workdir)
 | |
|   except EnvironmentError as e:
 | |
|     if e.errno == errno.ENOENT:
 | |
|       pass
 | |
|     elif e.errno == errno.EACCES:
 | |
|       print('')
 | |
|       print('Removing previous volume %r' % workdir)
 | |
|       check_call(('sudo', 'rm', '-r', quote(workdir)))
 | |
|     else:
 | |
|       raise
 | |
| 
 | |
|   os.makedirs(workdir)
 | |
|   os.execvp(cmd[0], cmd)
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|   sys.exit(main())
 |