| #!/usr/bin/env python |
| # Copyright 2018 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """A library to help with building/deploying production cobalt packages.""" |
| |
| import subprocess |
| import tempfile |
| import shutil |
| import os |
| |
| import container_util |
| |
| COBALT_REPO_CLONE_URL = "https://fuchsia.googlesource.com/cobalt" |
| |
| def _cobaltb(*args): |
| cmd = ['./cobaltb.py'] |
| cmd.extend(args) |
| subprocess.check_call(cmd) |
| |
| def _select_git_revision(): |
| tags = subprocess.check_output(['git', 'tag', '-l', |
| '--sort=-version:refname']).strip().split('\n')[:5] |
| tags.append('HEAD') |
| |
| while True: |
| print |
| print |
| print('Which version would you like to build for?') |
| for i, tag in enumerate(tags): |
| print('({}) {}'.format(i, tag)) |
| |
| selection = raw_input('? ') |
| try: |
| selection = int(selection) |
| if selection < len(tags): |
| return tags[selection] |
| except: |
| print("Invalid selection") |
| |
| def _build_and_test_cobalt_locally(git_revision): |
| """ Assumes that the current working directory is a Cobalt repo. |
| Checks out Cobalt at the given |git_revision| and then builds and tests |
| Cobalt. Throws an exception if any step fails. |
| """ |
| subprocess.check_call(['git', 'checkout', git_revision]) |
| _cobaltb('setup') |
| _cobaltb('clean', '--full') |
| _cobaltb('build') |
| _cobaltb('test') |
| |
| |
| def build_and_push_production_docker_images(cloud_project_name, production_dir, |
| git_revision, work_dir=None, skip_build=False): |
| """ Builds and pushes production-ready docker images from a clean git repo. |
| |
| Returns a tag string that may be used to reference the versions of the docker |
| images that were upload in the Google Cloud Docker registry. |
| |
| cloud_project_name {string}: For example "fuchsia-cobalt". The name is used |
| when forming the URI to the image in the registry and also the bigtable |
| project name. |
| production_dir {string}: The directory of the production config files. |
| git_revision {string}: A git revision passed in from the command line, if none |
| is provided, the user will be prompted to select one. |
| latest will be used. |
| work_dir {string} The working directory to use. If not provided a temporary |
| directory will be created and later deleted. |
| skip_build {boolean} Should we skip building Cobalt and assume the correct |
| version of Cobalt has previously been successfully built in |work_dir|? |
| optional, defaults to False. Note that it only makes sense to set this True |
| if |work_dir| has been provided. If you do this it is your responsibility |
| to ensure it really does contain a build of the right version of Cobalt. |
| This is useful primarily for testing this Python script. It is dangerous |
| to use this when really deploying to producttion. |
| """ |
| wd = os.getcwd() |
| |
| try: |
| if work_dir is not None: |
| clean_repo_dir = work_dir |
| else: |
| clean_repo_dir = tempfile.mkdtemp('-cobalt-production-build') |
| |
| if not skip_build: |
| shutil.rmtree(clean_repo_dir, True) |
| subprocess.check_call(['git', 'clone', COBALT_REPO_CLONE_URL, |
| clean_repo_dir]) |
| |
| os.chdir(clean_repo_dir) |
| |
| if not git_revision: |
| git_revision = _select_git_revision() |
| |
| if git_revision is 'HEAD': |
| git_revision = subprocess.check_output(['git', 'rev-parse', |
| 'HEAD']).strip() |
| |
| if not skip_build: |
| _build_and_test_cobalt_locally(git_revision) |
| |
| print "\nInvoking 'cobaltb.py deploy build'..." |
| p = subprocess.Popen(['./cobaltb.py', 'deploy', 'build', |
| '--production_dir=%s' % production_dir], |
| stdin=subprocess.PIPE) |
| p.communicate('yes') |
| if p.wait() != 0: |
| raise Exception("Invocation of 'cobaltb.py deploy build' failed.") |
| print "Invocation of 'cobaltb.py deploy build' succeeded.\n" |
| |
| full_rev = subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip() |
| tags_to_apply = ['latest', full_rev] |
| describe = subprocess.check_output(['git', 'describe']).strip() |
| if describe is not git_revision: |
| tags_to_apply.append(describe) |
| subrev = '' |
| # This will construct a series of tags based on the version. e.g. if the |
| # version is v1.2.3, it would create the tags 'v1', 'v1.2', and 'v1.2.3' |
| for part in git_revision.split('.'): |
| if subrev is not '': |
| subrev += '.' |
| subrev += part |
| tags_to_apply.append(subrev) |
| |
| for tag in tags_to_apply: |
| print "Pushing Shuffler to container registry at %s with tag=%s.\n" % ( |
| cloud_project_name, tag) |
| container_util.push_shuffler_to_container_registry('', cloud_project_name, |
| tag) |
| print ("Pushing Analyzer Service to container registry at " |
| "%s with tag=%s.\n" % (cloud_project_name, tag)) |
| container_util.push_analyzer_service_to_container_registry('', |
| cloud_project_name, tag) |
| print ("Pushing ReportMaster to container registry at " |
| "%s with tag=%s.\n" % (cloud_project_name, tag)) |
| container_util.push_report_master_to_container_registry('', |
| cloud_project_name, tag) |
| |
| return full_rev |
| finally: |
| os.chdir(wd) |
| if work_dir is None: |
| raw_input("Press enter to delete temp directory...") |
| print("Cleaning up") |
| shutil.rmtree(clean_repo_dir) |
| |
| def main(): |
| # Note(rudominer) It may be useful to directly run this script in order to |
| # either debug this script itself or else to debug the use of Docker on your |
| # computer. To do so set the following variables as appropriate for your |
| # computer and your personal devel cluster. You may run one time with |
| # skip_build=False in order to perform the build and after that run with |
| # skip_build=True in order to debug Docker (or debug this script) without |
| # needing to redo the build. |
| cloud_project_name='<your project name>' |
| production_dir='' |
| git_revision=None |
| work_dir='<some directory>' |
| skip_build=True |
| build_and_push_production_docker_images(cloud_project_name, production_dir, |
| git_revision, work_dir, skip_build) |
| |
| if __name__ == '__main__': |
| main() |