blob: 45307c2c9cd3161fd11c05e4c4b13c0fabe524ee [file] [log] [blame]
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Module for setup environemtn for local testing & Google App Engine.
DO NOT RUN THIS MODULE DIRECTLY.
Use bin/setup_environment instead.
See [README.md].
"""
from __future__ import print_function
import argparse
import collections
import os
import pipes
import shutil
import subprocess
import sys
_ROOT = os.path.dirname(os.path.realpath(__file__))
_INFRA_LIBS_PATH = os.path.join(_ROOT, 'infra_libs')
_REFRESH_CREDENTIALS_SCRIPT = os.path.join(_ROOT, 'bin', 'refresh_credentials')
_REQUIREMENTS_PATH = os.path.join(_ROOT, "requirements.txt")
_THIRD_PARTY_LIB_PATH = os.path.join(_ROOT, 'lib')
def _install_third_party_lib():
"""Install useful third-party lib.
The libraries are specified in requirement.txt. They're used in both local
development environment and Google App Engine, which means they will be
uploaded to GAE.
"""
if os.path.exists(_THIRD_PARTY_LIB_PATH):
shutil.rmtree(_THIRD_PARTY_LIB_PATH)
os.mkdir(_THIRD_PARTY_LIB_PATH)
print('Installing third party dependencies in %s' %
(_THIRD_PARTY_LIB_PATH, ))
try:
subprocess.check_call([
sys.executable, '-m', 'pip', 'install', '-t', _THIRD_PARTY_LIB_PATH,
'-r', _REQUIREMENTS_PATH
])
except OSError:
print('Please check that `pip` is installed. See README.md')
raise
def create_file(fname):
"""Create an empty file by opening it for writing and closing."""
open(fname, 'w').close()
def fixup_chromite_imports():
"""Fixup chromite __init__.py files.
Chromite runs Python3 now so relies on implicit namespacing, which Python2
doesn't support, so we have to go back and add a few __init__.py files to make
things importable.
"""
def remove_third_party(fname, package):
"Remove 'chromite.third_party.' prefix from given import string."
cmd = [
'sed',
'-i',
's/%s/%s/' % ('chromite.third_party.%s' % package, package),
fname,
]
subprocess.check_call(cmd)
for root, _, files in os.walk('infra_libs/chromite'):
for fname in files:
if fname.endswith('_pb2.py'):
path = os.path.join(root, fname)
remove_third_party(path, 'google.protobuf')
# Add explicit __init__.py files to workaround implicit namespaces in python3
create_file('infra_libs/chromite/__init__.py')
create_file('infra_libs/chromite/third_party/__init__.py')
create_file('infra_libs/chromite/third_party/google/__init__.py')
create_file('infra_libs/chromite/third_party/google/api/__init__.py')
# _GITDEP Specifies a dependency in the form of a particular reference in a git
# project.
#
# local_name: Name of the local directory to fetch into.
# project: URL to the git project to fetch from.
# ref: Git ref to fetch.
# sparse_checkout_paths: A list of paths to checkout. Some of the dependencies
# contain too many files and AppEngine does not allow uploading > 10K files.
# Thus, we checkout only required sub-packages. This list is passed in to
# (and interpreted by) `git sparse-checkout`.
# post_process: An optional callable to evaluate after cloning dependency
_GitDep = collections.namedtuple(
'_GitDep',
'local_name git_project git_ref sparse_checkout_paths post_process')
# List of all git project dependencies.
_GIT_DEPS = [
# Note: When upreving chromite, ensure that protobuf version in
# requirements.txt still matches the protobuf version used by chromite.
_GitDep(
'chromite',
'https://chromium.googlesource.com/chromiumos/chromite',
'1678d4792a11268172ff7ff79535706b6c0f7c03',
[
'/api/gen/**/*.py',
'/api/__init__.py',
'/third_party/infra_libs/*',
'/third_party/six/*',
'/third_party/google/api/*',
'/third_party/google/rpc/*',
'/third_party/__init__.py',
'/__init__.py',
'!*test.py',
],
fixup_chromite_imports,
),
_GitDep('luci', 'https://chromium.googlesource.com/infra/luci/luci-py',
'33a910c714948a5189f9c58101ce9b131f01c1fd', [
'/appengine/components/components/auth/**/*.py',
'/appengine/components/components/datastore_utils/**/*.py',
'/appengine/components/components/prpc/**/*.py',
'/appengine/components/components/*.py',
'!*test.py',
], None),
]
def _install_infra_libs():
"""Install useful libs from Chromium infra and Chromite.
infra_libs consists of prpc client from luci and necessary proto files from
Chromite. Suite scheduler relies on those libs to conduct prpc call to
Buildbucket.
"""
if os.path.exists(_INFRA_LIBS_PATH):
shutil.rmtree(_INFRA_LIBS_PATH)
os.mkdir(_INFRA_LIBS_PATH)
for dep in _GIT_DEPS:
dep_path = os.path.join(_INFRA_LIBS_PATH, dep.local_name)
print('Creating sparse checkout of %s at %s in %s' %
(dep.git_project, dep.git_ref, dep_path))
subprocess.check_call(
['git', 'clone', '--no-checkout', dep.git_project, dep.local_name],
cwd=_INFRA_LIBS_PATH)
subprocess.check_call(['git', 'sparse-checkout', 'init'], cwd=dep_path)
_set_sparse_checkout_paths(dep_path, dep.sparse_checkout_paths)
subprocess.check_call(
['git', 'checkout', dep.git_ref, '-b', 'deploy', '-f'], cwd=dep_path)
if dep.post_process:
dep.post_process()
def _set_sparse_checkout_paths(checkout_dir, sparse_checkout_paths):
"""Set the path patterns to checkout in a git checkout."""
# Pass in path patterns via stdin to avoid shell escaping issues.
proc = subprocess.Popen(['git', 'sparse-checkout', 'set', '--no-cone', '--stdin'],
stdin=subprocess.PIPE,
cwd=checkout_dir)
proc.communicate(input='\n'.join(sparse_checkout_paths))
if proc.poll() is None:
raise Exception('Leaked process when setting sparse checkout paths')
if proc.poll() != 0:
raise Exception('Failed to set sparse checkout paths')
def _load_credentials():
subprocess.check_call([_REFRESH_CREDENTIALS_SCRIPT])
def main():
"""Entry point of the script"""
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
'--load-creds',
action='store_true',
help='Also install credentials needed for releasing suite '
'scheduler. Not needed to modify config/*')
args = parser.parse_args()
_install_third_party_lib()
_install_infra_libs()
if args.load_creds:
_load_credentials()
if __name__ == '__main__':
main()