| # Copyright 2016 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import collections |
| import re |
| |
| |
| DEPS = [ |
| 'depot_tools/cipd', |
| 'gae_sdk', |
| 'depot_tools/gsutil', |
| 'recipe_engine/path', |
| 'recipe_engine/python', |
| 'recipe_engine/raw_io', |
| 'recipe_engine/step', |
| 'zip', |
| ] |
| |
| # CIPD builder service account, deployed by Puppet. |
| _CIPD_CREDENTIAL_PATH = ('/creds/service_accounts/' |
| 'service-account-cipd-builder.json') |
| |
| |
| def RunSteps(api): |
| pb = _PackageBuilder(api) |
| |
| api.cipd.set_service_account_credentials(_CIPD_CREDENTIAL_PATH) |
| |
| # Iterate over all of the GAE SDK packages and build any that don't exist. |
| pkg_outdir = api.path.mkdtemp('gae_sdk_package') |
| for plat, arch in api.gae_sdk.all_packages: |
| with api.step.nest('Sync %s/%s' % (plat, arch,)): |
| # Determine the current GAE SDK version. |
| try: |
| version = pb.latest_upstream_version(plat, arch) |
| except pb.VersionParseError as e: |
| api.step.active_result.presentation.step_summary_text = str(e) |
| api.step.active_result.presentation.status = api.step.FAILURE |
| continue |
| |
| version_tag = pb.version_tag(version) |
| pkg_name = api.gae_sdk.package(plat, arch=arch) |
| step = api.cipd.search(pkg_name, '%s:%s' % (version_tag)) |
| if len(step.json.output['result']) > 0: |
| api.python.succeeding_step('Synced', 'Package is up to date.') |
| continue |
| |
| # Create a temporary directory to build the package in. |
| pkg_base = pb.download_and_unpack(plat, arch, version) |
| |
| # Build and register our CIPD package. |
| pkg_path = pkg_outdir.join('gae_sdk_%s_%s.pkg' % (plat, arch)) |
| api.cipd.build( |
| pkg_base, |
| pkg_path, |
| pkg_name, |
| install_mode='copy', |
| ) |
| api.cipd.register( |
| pkg_name, |
| pkg_path, |
| refs=[api.gae_sdk.latest_ref], |
| tags={version_tag[0]: version_tag[1]}, |
| ) |
| |
| |
| class _PackageBuilder(object): |
| # The Google Storage GAE SDK bucket base. All SDK packages are stored in here |
| # under a basename + version ZIP file. |
| _GS_BUCKET_BASE = 'gs://appengine-sdks/featured' |
| |
| class VersionParseError(Exception): |
| pass |
| |
| def __init__(self, api): |
| self._api = api |
| |
| def version_tag(self, version): |
| return ('gae_sdk_version', version) |
| |
| @property |
| def api(self): |
| return self._api |
| |
| def latest_upstream_version(self, plat, arch): |
| _, base, _ = self.api.gae_sdk.package_spec(plat, arch) |
| prefix = '%s/%s' % (self._GS_BUCKET_BASE, base) |
| |
| step_result = self.api.gsutil.list( |
| prefix + '*.zip', |
| name='Get Latest', |
| stdout=self.api.raw_io.output_text()) |
| |
| latest_version = None |
| for line in step_result.stdout.splitlines(): |
| if not line.startswith(prefix): |
| continue |
| |
| # gs://..../prefix-#.#.#.zip |
| vstr = line[len(prefix):].rstrip('.zip') |
| try: |
| version = tuple(int(d) for d in vstr.split('.')) |
| except ValueError: |
| version = () |
| if len(version) == 3 and (not latest_version or latest_version < version): |
| latest_version = version |
| |
| if not latest_version: |
| raise self.VersionParseError('No latest version for prefix: %s' % ( |
| prefix,)) |
| |
| latest = '.'.join(str(d) for d in latest_version) |
| step_result.presentation.step_text += ' %s' % (latest,) |
| return latest |
| |
| def download_and_unpack(self, plat, arch, version): |
| # Get the package base for this OS. |
| _, base, dirname = self.api.gae_sdk.package_spec(plat, arch) |
| name = '%s%s.zip' % (base, version) |
| artifact_url = '%s/%s' % (self._GS_BUCKET_BASE, name) |
| |
| tdir = self.api.path.mkdtemp('gae_sdk') |
| dst = tdir.join(name) # Store the ZIP file here. |
| unzip_dir = tdir.join('unpack') # Unzip contents here. |
| self.api.gsutil.download_url( |
| artifact_url, |
| dst, |
| name='Download %s %s' % (plat, arch,)) |
| self.api.zip.unzip( |
| 'Unzip %s %s' % (plat, arch), |
| dst, |
| unzip_dir, |
| quiet=True) |
| |
| pkg_dir = unzip_dir.join(dirname) |
| self.api.path.mock_add_paths(pkg_dir) |
| assert self.api.path.exists(pkg_dir), ( |
| 'Package directory [%s] does not exist' % (pkg_dir,)) |
| return pkg_dir |
| |
| |
| def GenTests(api): |
| BUCKET_LIST = '\n'.join( |
| (_PackageBuilder._GS_BUCKET_BASE + '/' + s) |
| for s in ( |
| 'go_appengine_sdk_linux_amd64-1.2.3.zip', |
| 'go_appengine_sdk_linux_amd64-10.2.3.zip', |
| 'go_appengine_sdk_linux_amd64-junk.zip', |
| 'go_appengine_sdk_linux_amd64-99.99.zip', |
| 'go_appengine_sdk_linux_386-10.2.3.zip', |
| 'go_appengine_sdk_darwin_amd64-10.2.3.zip', |
| 'google_appengine_10.2.3.zip', |
| 'junk', |
| )) |
| |
| def latest(plat, arch): |
| return api.step_data('Sync %s/%s.gsutil Get Latest' % (plat, arch), |
| api.raw_io.stream_output(BUCKET_LIST, stream='stdout')) |
| |
| def bad_latest(plat, arch): |
| return api.step_data('Sync %s/%s.gsutil Get Latest' % (plat, arch), |
| api.raw_io.stream_output('foobar', stream='stdout')) |
| |
| def cipd_pkg(plat, arch, exists): |
| pkg_name = 'infra/gae_sdk/%s/%s' % (plat, arch) |
| cipd_step = 'cipd search %s gae_sdk_version:10.2.3' % (pkg_name,) |
| instances = (2) if exists else (0) |
| return api.step_data('Sync %s/%s.%s' % (plat, arch, cipd_step), |
| api.cipd.example_search(pkg_name, instances=instances)) |
| |
| packages_test = api.test('packages') |
| bad_version_test = api.test('bad_version_list') |
| for plat, arch, exists in ( |
| ('go', 'linux-amd64', False), |
| ('go', 'linux-386', True), |
| ('go', 'mac-amd64', True), |
| ('python', 'all', False)): |
| packages_test += ( |
| latest(plat, arch) + |
| cipd_pkg(plat, arch, exists)) |
| bad_version_test += bad_latest(plat, arch) |
| |
| yield packages_test |
| yield bad_version_test |