blob: 9220addf116733dc7eca769d5dbb88a46e890d4d [file] [log] [blame]
# Copyright 2019 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.
"""Recipe for rolling CIPD prebuilts into Fuchsia."""
import os
import re
from recipe_engine.config import List
from recipe_engine.recipe_api import Property
DEPS = [
'fuchsia/auto_roller',
'fuchsia/build',
'fuchsia/buildbucket_util',
'fuchsia/jiri',
'fuchsia/upload_debug_symbols',
'recipe_engine/archive',
'recipe_engine/buildbucket',
'recipe_engine/cipd',
'recipe_engine/context',
'recipe_engine/file',
'recipe_engine/json',
'recipe_engine/path',
'recipe_engine/properties',
'recipe_engine/raw_io',
'recipe_engine/step',
]
PROPERTIES = {
'project':
Property(kind=str, help='Jiri remote manifest project', default=None),
'manifest':
Property(kind=str, help='Jiri manifest to use'),
'remote':
Property(kind=str, help='Remote manifest repository'),
'import_in':
Property(
kind=str, help='Path to the manifest to edit relative to $project'),
'packages':
Property(
kind=List(str),
help='The list of CIPD packages to update in $import_in'),
'debug_symbol_attribute':
Property(
kind=str,
default='debug-symbols',
help='Jiri attribute to match debug symbol packages'),
'debug_symbol_gcs_buckets':
Property(
kind=List(str),
default=(),
help='GCS buckets to upload debug symbols upon successful roll'),
'lockfiles':
Property(
kind=List(str),
default=(),
help='The list of lockfiles to update in "${manifest}=${lockfile}" format'
),
'dry_run':
Property(
kind=bool,
default=False,
help='Whether to dry-run the auto-roller (CQ+1 and abandon the change)'
),
'tag':
Property(
kind=str,
default='version',
help='A CIPD tag common to all $packages where a common version can be extracted'
),
'ref':
Property(
kind=str,
default='latest',
help='A common CIPD ref to resolve when rolling a set of packages'),
'owners':
Property(
kind=List(str),
default=(),
help=('The owners responsible for watching this roller '
'(example: "username@google.com").')),
}
COMMIT_MESSAGE = """[roll] Roll {roller} CIPD packages:
{packages}
From: {old_version}
To: {version}
Cq-Cl-Tag: roller-builder:{builder}
Cq-Cl-Tag: roller-bid:{build_id}
CQ-Do-Not-Cancel-Tryjobs: true"""
CIPD_URL = 'https://chrome-infra-packages.appspot.com/p/{package}/+/{version}'
def get_tags_from_cipd(api, cipd_pkg, tag, ref):
cipd_tags = api.cipd.describe(cipd_pkg, ref).tags
return set(
[cipd_tag.tag for cipd_tag in cipd_tags if cipd_tag.tag.startswith(tag)])
def fetch_and_upload_debug_symbols(api, project, import_in, remote, project_dir,
packages, debug_symbol_attribute,
debug_symbol_gcs_buckets):
"""
Fetch debug symbol archives, unpack them, and upload debug symbols.
Args:
project (str): Jiri remote manifest project.
import_in (str): Path to the edited manifest relative to $project
containing debug symbol packages.
remote (str): Remote manifest repository.
project_dir (Path): Project root path of $import_in.
packages (seq(str)): The list of CIPD packages updated in $import_in.
debug_symbol_attribute (str): Jiri attribute to match debug symbol packages.
debug_symbol_gcs_buckets (seq(str)): GCS buckets to upload debug symbols to.
"""
with api.context(infra_steps=True):
api.jiri.init(
use_lock_file=True,
attributes=(debug_symbol_attribute,),
)
# Fetch debug symbol packages using locally edited manifest.
api.jiri.import_manifest(
manifest=import_in,
remote=remote,
name=project,
)
api.jiri.fetch_packages(local_manifest=True)
with api.step.nest('build'):
gn_results = api.build.gen(
checkout_root=api.path['start_dir'],
fuchsia_build_dir=api.path['start_dir'].join('out', 'default'),
target='x64',
build_type='debug',
product='products/bringup.gni',
# //bundles:infratools is necessary to build upload_debug_symbols.
packages=['//bundles:infratools'],
)
upload_debug_symbols_target = os.path.relpath(
str(gn_results.tool('upload_debug_symbols')),
str(gn_results.fuchsia_build_dir),
)
api.build.ninja(
checkout_root=api.path['start_dir'],
gn_results=gn_results,
build_zircon=False,
targets=[upload_debug_symbols_target],
)
build_id_dirs = []
for package in packages:
# Find archives for each debug symbol package.
with api.context(cwd=project_dir):
package_def = api.jiri.read_manifest_element(
manifest=import_in,
element_type='package',
element_name=package,
)
# Skip non debug symbol packages.
if debug_symbol_attribute not in package_def.get('attributes', ''):
continue
package_path = api.path['start_dir'].join(package_def['path'])
archives = api.file.glob_paths(
name='find archives for %s' % package,
source=package_path,
pattern='**/*.tar.bz2',
test_data=(package_path.join('symbols.tar.bz2'),),
)
# Unpack archives into .build-id dirs.
for archive in archives:
# Extract API requires a unique, non-existent directory.
archive_basename = os.path.basename(api.path.abspath(archive))
output_dir = api.path['start_dir'].join(package, archive_basename)
api.archive.extract(
step_name='extract %s' % archive,
archive_file=archive,
output=output_dir,
)
build_id_dirs.append(output_dir)
for debug_symbol_gcs_bucket in debug_symbol_gcs_buckets:
api.upload_debug_symbols(
step_name='upload debug symbols',
upload_debug_symbols_path=gn_results.tool('upload_debug_symbols'),
bucket=debug_symbol_gcs_bucket,
build_id_dirs=build_id_dirs,
)
def _get_platform_specific_packages(package, output):
platform_regex = '(?<=' + package.replace('${platform}',
r'\${platform=).*(?=})')
pattern = re.compile(platform_regex)
match = pattern.search(output)
if match:
platforms = match.group(0).split(',')
return [package.replace('${platform}', platform) for platform in platforms]
return [package]
def _append_urls(packages, old_version, new_version):
package_line = '{package} old:{old} new:{new}'
packages_with_urls = []
for package in packages:
if '${platform}' in package:
packages_with_urls.append(package)
else:
packages_with_urls.append(
package_line.format(
old=CIPD_URL.format(package=package, version=old_version),
new=CIPD_URL.format(package=package, version=new_version),
package=package))
return packages_with_urls
def RunSteps(api, project, manifest, remote, import_in, packages,
debug_symbol_attribute, debug_symbol_gcs_buckets, lockfiles,
dry_run, tag, ref, owners):
with api.context(infra_steps=True):
if owners:
owners_step = api.step('owners', None)
owners_step.presentation.step_summary_text = ', '.join(owners)
api.jiri.init(use_lock_file=True)
api.jiri.import_manifest(manifest, remote, project)
api.jiri.update(run_hooks=False)
api.jiri.run_hooks()
project_dir = api.path['start_dir'].join(*project.split('/'))
# Populate the set of tags.
tags = get_tags_from_cipd(api, packages[0], tag, ref)
# Get the intersection of tags.
if (len(packages) > 1):
for cipd_pkg in packages[1:]:
tags = tags.intersection(get_tags_from_cipd(api, cipd_pkg, tag, ref))
# Check if the intersection of tag exists.
if not tags:
api.step('unable to find common <tag> to roll', None)
return
# All the instances of tag point to the same 'instance_id'
# Take the first one.
version = list(tags)[0]
with api.context(cwd=project_dir):
changes = api.jiri.edit_manifest(
import_in, packages=[(package, version) for package in packages])
if not changes['packages']:
api.step('manifest up-to-date; nothing to roll', None)
return
old_version = changes['packages'][0]['old_version']
exact_packages = set()
# Test data for jiri.resolve
package_test_data = [
('@Subdir prebuilt/tools/buildbucket\n' +
package[:package.index('${platform}')] +
'${platform=linux-amd64,mac-amd64} git_revision:aa2dae..')
for package in packages
if '${platform}' in package
]
test_data = '\n'.join(package_test_data)
# Update the lockfiles.
for lock_entry in lockfiles:
fields = lock_entry.split('=')
manifest = fields[0]
lock = fields[1]
resolve_output = api.jiri.resolve(
local_manifest=True,
output=lock,
manifests=[manifest],
step_test_data=test_data).stdout
for p in packages:
if '${platform}' in p:
platform_pkgs = _get_platform_specific_packages(p, resolve_output)
exact_packages = exact_packages.union(platform_pkgs)
else:
exact_packages.add(p)
exact_packages = sorted(exact_packages)
packages_with_urls = _append_urls(exact_packages, old_version, version)
roller_name = api.buildbucket.builder_name.rstrip('-roller')
message = COMMIT_MESSAGE.format(
roller=roller_name,
packages='\n'.join(packages_with_urls),
old_version=old_version,
version=version,
builder=api.buildbucket.builder_name,
build_id=api.buildbucket_util.id,
)
# Land the changes.
rolled = api.auto_roller.attempt_roll(
gerrit_project=project,
repo_dir=project_dir,
commit_message=message,
dry_run=dry_run,
)
# If roll succeeded, upload any debug symbols that were rolled.
# TODO(fxb/37432): Upload debug symbols with artifactory.
if rolled and debug_symbol_gcs_buckets:
with api.step.nest('fetch and upload debug symbols'):
fetch_and_upload_debug_symbols(
api=api,
project=project,
import_in=import_in,
remote=remote,
project_dir=project_dir,
packages=packages,
debug_symbol_attribute=debug_symbol_attribute,
debug_symbol_gcs_buckets=debug_symbol_gcs_buckets,
)
def GenTests(api):
default_packages = [
'chromium/fuchsia/webrunner-arm64', 'chromium/fuchsia/webrunner-amd64',
'chromium/fuchsia/fidl'
]
debug_symbol_packages = [
'chromium/fuchsia/webrunner-debug-symbols-arm64',
'chromium/fuchsia/webrunner-debug-symbols-amd64'
]
platform_packages = [
'fuchsia/tools/bb/${platform}', 'fuchsia/tools/luci/gsutil/${platform}'
]
default_lockfiles = [
'integration/flower=integration/jiri.lock',
]
default_properties = api.properties(
project='integration',
manifest='minimal',
remote='https://fuchsia.googlesource.com',
import_in='chromium/chromium',
packages=default_packages,
lockfiles=default_lockfiles,
owners=['nobody@google.com', 'noreply@google.com'])
debug_symbols_properties = api.properties(
project='integration',
manifest='minimal',
remote='https://fuchsia.googlesource.com',
import_in='chromium/chromium',
packages=default_packages + debug_symbol_packages,
debug_symbol_gcs_buckets=('foo-bucket', 'bar-bucket'),
lockfiles=default_lockfiles,
)
platform_pkg_properties = api.properties(
project='integration',
manifest='minimal',
remote='https://fuchsia.googlesource.com',
import_in='fuchsia/prebuilts',
packages=platform_packages,
lockfiles=default_lockfiles,
tag='git_revision')
yield (api.test('default') + default_properties + api.step_data(
'cipd describe chromium/fuchsia/fidl',
api.cipd.example_describe(
package_name='chromium/fuchsia/fidl',
version='GDGGW7Xs89z2apGaYf1mDvbQuHfYIoPexfedNzvKodUC',
test_data_tags=[
'version:70.0.3538.30',
'version:70.0.3538.29',
])) + api.step_data(
'cipd describe chromium/fuchsia/webrunner-amd64',
api.cipd.example_describe(
package_name='chromium/fuchsia/webrunner-amd64',
version='r2S5xldLzzfJa2VzOYgoC6TsIWePSDLBI5FRywd_gHAC',
test_data_tags=[
'version:70.0.3538.30',
])) +
api.step_data(
'cipd describe chromium/fuchsia/webrunner-arm64',
api.cipd.example_describe(
package_name='chromium/fuchsia/webrunner-arm64',
version='mKI1nni0SW4F1cQAuYnkYU_RtDv47noSKO9vGHJVjzYC',
test_data_tags=[
'version:70.0.3538.30',
])) + api.auto_roller.dry_run_step_data() +
api.buildbucket.build(
api.buildbucket.ci_build_message(builder='chromium-roller')))
yield (api.test('no latest version match') + default_properties +
api.step_data(
'cipd describe chromium/fuchsia/fidl',
api.cipd.example_describe(
package_name='chromium/fuchsia/fidl',
version='GDGGW7Xs89z2apGaYf1mDvbQuHfYIoPexfedNzvKodUC',
test_data_tags=[
'version:70.0.3538.30',
])) + api.step_data(
'cipd describe chromium/fuchsia/webrunner-amd64',
api.cipd.example_describe(
package_name='chromium/fuchsia/webrunner-amd64',
version='r2S5xldLzzfJa2VzOYgoC6TsIWePSDLBI5FRywd_gHAC',
test_data_tags=[
'version:70.0.3538.30',
])) +
api.step_data(
'cipd describe chromium/fuchsia/webrunner-arm64',
api.cipd.example_describe(
package_name='chromium/fuchsia/webrunner-arm64',
version='mKI1nni0SW4F1cQAuYnkYU_RtDv47noSKO9vGHJVjzYC',
test_data_tags=[
'version:70.0.3538.29',
])))
yield (api.test('noop') + default_properties + api.step_data(
'cipd describe chromium/fuchsia/fidl',
api.cipd.example_describe(
package_name='chromium/fuchsia/fidl',
version='GDGGW7Xs89z2apGaYf1mDvbQuHfYIoPexfedNzvKodUC',
test_data_tags=[
'version:70.0.3538.30',
])) + api.step_data(
'cipd describe chromium/fuchsia/webrunner-amd64',
api.cipd.example_describe(
package_name='chromium/fuchsia/webrunner-amd64',
version='r2S5xldLzzfJa2VzOYgoC6TsIWePSDLBI5FRywd_gHAC',
test_data_tags=[
'version:70.0.3538.30',
])) +
api.step_data(
'cipd describe chromium/fuchsia/webrunner-arm64',
api.cipd.example_describe(
package_name='chromium/fuchsia/webrunner-arm64',
version='mKI1nni0SW4F1cQAuYnkYU_RtDv47noSKO9vGHJVjzYC',
test_data_tags=[
'version:70.0.3538.30',
])) +
api.step_data('jiri edit', api.json.output({'packages': []})))
yield (api.test('default_with_platform') + platform_pkg_properties +
api.step_data(
'cipd describe fuchsia/tools/bb/${platform}',
api.cipd.example_describe(
package_name='fuchsia/tools/bb/${platform}',
version='IX9BXIY7ZejVNabi6zF5lyq2KIW_ABFvBf1u3Cjr2dIC',
test_data_tags=[
'git_revision:aa2dae2ba04148bbe29726c64cfd9d40b6fc2616',
])) +
api.step_data(
'cipd describe fuchsia/tools/luci/gsutil/${platform}',
api.cipd.example_describe(
package_name='fuchsia/tools/luci/gsutil/${platform}',
version='IX9BXIY7ZejVNabi6zF5lyq2KIW_ABFvBf1u3Cjr2dIC',
test_data_tags=[
'git_revision:aa2dae2ba04148bbe29726c64cfd9d40b6fc2616',
])) + api.auto_roller.dry_run_step_data() +
api.buildbucket.build(
api.buildbucket.ci_build_message(builder='tools-roller')))
yield (api.test('default_platform_not_resolved') + platform_pkg_properties +
api.step_data(
'cipd describe fuchsia/tools/bb/${platform}',
api.cipd.example_describe(
package_name='fuchsia/tools/bb/${platform}',
version='IX9BXIY7ZejVNabi6zF5lyq2KIW_ABFvBf1u3Cjr2dIC',
test_data_tags=[
'git_revision:aa2dae2ba04148bbe29726c64cfd9d40b6fc2616',
])) +
api.step_data(
'cipd describe fuchsia/tools/luci/gsutil/${platform}',
api.cipd.example_describe(
package_name='fuchsia/tools/luci/gsutil/${platform}',
version='IX9BXIY7ZejVNabi6zF5lyq2KIW_ABFvBf1u3Cjr2dIC',
test_data_tags=[
'git_revision:aa2dae2ba04148bbe29726c64cfd9d40b6fc2616',
])) +
api.step_data('jiri resolve', api.raw_io.stream_output('')) +
api.auto_roller.dry_run_step_data() + api.buildbucket.build(
api.buildbucket.ci_build_message(builder='tools-roller')))
yield (api.test('with_debug_symbols') + debug_symbols_properties +
api.step_data(
'cipd describe chromium/fuchsia/fidl',
api.cipd.example_describe(
package_name='chromium/fuchsia/fidl',
version='GDGGW7Xs89z2apGaYf1mDvbQuHfYIoPexfedNzvKodUC',
test_data_tags=[
'version:70.0.3538.30',
'version:70.0.3538.29',
])) + api.step_data(
'cipd describe chromium/fuchsia/webrunner-amd64',
api.cipd.example_describe(
package_name='chromium/fuchsia/webrunner-amd64',
version='r2S5xldLzzfJa2VzOYgoC6TsIWePSDLBI5FRywd_gHAC',
test_data_tags=[
'version:70.0.3538.30',
])) +
api.step_data(
'cipd describe chromium/fuchsia/webrunner-arm64',
api.cipd.example_describe(
package_name='chromium/fuchsia/webrunner-arm64',
version='mKI1nni0SW4F1cQAuYnkYU_RtDv47noSKO9vGHJVjzYC',
test_data_tags=[
'version:70.0.3538.30',
])) +
api.step_data(
'cipd describe chromium/fuchsia/webrunner-debug-symbols-amd64',
api.cipd.example_describe(
package_name='chromium/fuchsia/webrunner-debug-symbols-amd64',
version='sI1KHrov4_lmsDNnkSJ0MHnKgEtWSl8XRCNRhpAhkacC',
test_data_tags=[
'version:70.0.3538.30',
])) +
api.step_data(
'cipd describe chromium/fuchsia/webrunner-debug-symbols-arm64',
api.cipd.example_describe(
package_name='chromium/fuchsia/webrunner-debug-symbols-arm64',
version='0soOR1_LGKIyPtlcA2mtD9dHQoKd0K1kkusVwcXLNLAC',
test_data_tags=[
'version:70.0.3538.30',
])) + api.jiri.read_manifest_element(
api,
'chromium/chromium',
'package',
'chromium/fuchsia/webrunner-debug-symbols-amd64',
test_output={
'path': 'prebuilt/build_ids/amd64/webrunner',
'attributes': 'debug-symbols,debug-symbols-amd64',
},
nesting='fetch and upload debug symbols') +
api.jiri.read_manifest_element(
api,
'chromium/chromium',
'package',
'chromium/fuchsia/webrunner-debug-symbols-arm64',
test_output={
'path': 'prebuilt/build_ids/arm64/webrunner',
'attributes': 'debug-symbols,debug-symbols-amd64',
},
nesting='fetch and upload debug symbols') +
api.jiri.read_manifest_element(
api,
'chromium/chromium',
'package',
'chromium/fuchsia/webrunner-arm64',
test_output={
'path': 'chromium/fuchsia/arm64/webrunner',
},
nesting='fetch and upload debug symbols') +
api.jiri.read_manifest_element(
api,
'chromium/chromium',
'package',
'chromium/fuchsia/webrunner-amd64',
test_output={
'path': 'chromium/fuchsia/amd64/webrunner',
},
nesting='fetch and upload debug symbols') +
api.jiri.read_manifest_element(
api,
'chromium/chromium',
'package',
'chromium/fuchsia/fidl',
test_output={
'path': 'chromium/fuchsia/fidl',
},
nesting='fetch and upload debug symbols') +
api.auto_roller.success_step_data() + api.buildbucket.build(
api.buildbucket.ci_build_message(builder='chromium-roller')))