blob: 1257e46acbdd26ed877a11ea27fcc55ae73dd038 [file] [log] [blame]
# 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.
# ⚠️ WARNING. This is a *RELEASE* recipe, and it is always used from HEAD.
#
# Most recipes are branched, so that a change, for example, to engine_v2, only
# impacts the cooresponding `master` or `main` branch of the serviced
# respository (until that branch is eventually promoted to beta or stable).
#
# A release recipes is always used from HEAD, meaning any breaking changes are
# made immediately, even for older release candidates. Changes must be made in
# a backwards compatible way until older releases are archived or cherrypicks
# are required to the older release candidates.
#
# Example outcome: https://github.com/flutter/flutter/issues/168673
import re
from recipe_engine import post_process
DEPS = [
'flutter/flutter_deps',
'flutter/kms',
'flutter/repo_util',
'fuchsia/git',
'recipe_engine/context',
'recipe_engine/file',
'recipe_engine/path',
'recipe_engine/platform',
'recipe_engine/properties',
'recipe_engine/raw_io',
'recipe_engine/runtime',
'recipe_engine/step',
]
stableTagRegex = r'^(\d+)\.(\d+)\.(\d+)$'
betaTagRegex = r'^(\d+)\.(\d+)\.(\d+)-(\d+)\.(\d+)\.pre$'
def isValidTag(tag, release_channel):
stable = re.search(stableTagRegex, tag)
beta = re.search(betaTagRegex, tag)
if release_channel == 'stable':
return stable
return beta
"""
This recipe executes the tag and publishing stages of a flutter release.
To trigger this recipe, tool proxy must be invoked with multi-party approval.
Tool proxy information can be found at go/tool-proxy.
Because of this configuration, the recipe is triggered manually during the
release process.
It is expected that a valid release git branch, tag, and release_channel are passed
to the recipe.
The recipe will tag and push to github unless triggered
from an experimental run.
"""
def RunSteps(api):
with api.step.nest('validate platform and properties'):
if not api.platform.is_linux:
raise api.step.StepFailure('Must be run on a Linux platform')
git_branch = api.properties.get('git_branch')
if not git_branch:
raise api.step.StepFailure('Missing property "git_branch"')
tag = api.properties.get('tag')
if not tag:
raise api.step.StepFailure('Missing property "tag"')
release_channel = api.properties.get('release_channel')
if not release_channel:
raise api.step.StepFailure('Missing property "release_channel"')
if release_channel not in ('stable', 'beta'):
raise api.step.StepFailure(
'Unexpected "release_channel" value: {release_channel}'.format(
release_channel=release_channel,
)
)
if not isValidTag(tag, release_channel):
raise api.step.StepFailure(
'Cannot use tag {tag} on channel {release_channel}'.format(
tag=tag,
release_channel=release_channel,
)
)
force = False if api.runtime.is_experimental else api.properties.get(
'force', False
)
checkout = api.path.start_dir / 'flutter'
with api.step.nest('checkout flutter release branch'):
rel_hash = api.repo_util.checkout(
'flutter',
checkout,
url='https://github.com/flutter/flutter',
ref="refs/heads/%s" % git_branch,
)
# LINT: The `bin/internal/engine.version` file:
# 1. Must exist
# 2. Should match the SHA emitted by `bin/internal/last_engine_commit.sh
with api.step.nest('validate engine.version'):
engine_version = checkout / 'bin' / 'internal' / 'engine.version'
if not api.path.exists(engine_version):
raise api.step.StepFailure(
'Missing expected file: {engine_version}'.format(
engine_version=engine_version,
)
)
# TODO(matanlurey): Require last_engine_commit.sh and fail if missing.
# This provides backwards compatibility for older release candidates.
# https://github.com/flutter/flutter/issues/168674
compute_last_engine_commit = []
last_engine_script = checkout / 'bin' / 'internal' / 'last_engine_commit.sh'
if api.path.exists(last_engine_script):
compute_last_engine_commit = ['bash', last_engine_script]
else:
compute_last_engine_commit = [
'git', '-C', checkout, 'log', '-1', '--pretty=format:%H', '--',
'DEPS', 'engine'
]
last_commit_step = api.step(
'compute last engine commit',
cmd=compute_last_engine_commit,
stdout=api.raw_io.output_text(),
)
last_commit_sha = last_commit_step.stdout.strip()
engine_version_step = api.step(
'read engine.version file',
cmd=[
'cat',
engine_version,
],
stdout=api.raw_io.output_text()
)
engine_version_content = engine_version_step.stdout.strip()
if last_commit_sha != engine_version_content:
raise api.step.StepFailure(
'Contents of {engine_version}: "{engine_version_content}" do not match expected last engine commit "{last_commit_sha}"'
.format(
engine_version=engine_version,
engine_version_content=engine_version_content,
last_commit_sha=last_commit_sha,
)
)
env, env_prefixes = api.repo_util.flutter_environment(checkout)
api.flutter_deps.required_deps(
env,
env_prefixes,
api.properties.get('dependencies', []),
)
resource_name = api.resource('push_release.sh')
api.step(
'Set execute permission',
['chmod', '755', resource_name],
infra_step=True,
)
with api.context(env=env, env_prefixes=env_prefixes, cwd=checkout):
token_decrypted = api.path.cleanup_dir / 'token.txt'
api.kms.get_secret(
# TODO(fujino): restore this to 'flutter-release-github-token.encrypted'
# once https://github.com/flutter/flutter/issues/162544 is resolved
'pub-roller-github-token.encrypted',
token_decrypted
)
env['FORCE_FLAG'] = '--force' if force else ''
env['TOKEN_PATH'] = token_decrypted
env['TAG'] = tag
env['REL_HASH'] = rel_hash
env['RELEASE_CHANNEL'] = release_channel
env['GIT_BRANCH'] = git_branch
# TODO(fujino) restore this to 'fluttergithubbot' once
# https://github.com/flutter/flutter/issues/162544 is resolved
env['GITHUB_USER'] = 'flutter-pub-roller-bot'
env['REPO'] = 'flutter'
# Run script within a new context to use the new env variables.
# Tag and push flutter/flutter first, then use hash found in
# bin/internal/engine.version to tag and push engine next
with api.context(env=env, env_prefixes=env_prefixes):
api.step('Tag and push release on flutter/flutter', [resource_name])
def GenTests(api):
checkout_path = api.path.start_dir / 'flutter'
# TEST: Must be a linux platform
yield api.test(
'unsupported_platform',
api.platform('mac', 64),
api.properties(
git_branch='flutter-1.2.3-candidate.0',
tag='3.32.0-0.3',
release_channel='gamma',
),
status="FAILURE",
)
# TEST: Cannot use an unexpected channel name
yield api.test(
'unexpected_release_channel',
api.platform('linux', 64),
api.properties(
git_branch='flutter-1.2.3-candidate.0',
tag='3.32.0-0.3',
release_channel='gamma',
),
status="FAILURE",
)
# TEST: Cannot have a missing git_branch
yield api.test(
'missing_git_branch',
api.platform('linux', 64),
api.properties(
tag='3.32.0-0.3',
release_channel='stable',
),
status="FAILURE",
)
# TEST: Cannot have a missing tag
yield api.test(
'missing_tag',
api.platform('linux', 64),
api.properties(
git_branch='flutter-1.2.3-candidate.0',
release_channel='stable',
),
status="FAILURE",
)
# TEST: Cannot have a missing tag
yield api.test(
'missing_channel',
api.platform('linux', 64),
api.properties(
git_branch='flutter-1.2.3-candidate.0',
tag='3.32.0-0.3',
),
status="FAILURE",
)
# TEST: Cannot use a .pre tag on the stable channel
yield api.test(
'unexpected_pre_tag_on_stable_channel',
api.platform('linux', 64),
api.properties(
git_branch='flutter-1.2.3-candidate.0',
tag='3.32.0-0.3',
release_channel='stable',
force=False,
),
status="FAILURE",
)
# TEST: Must be using a .pre tag on the beta channel
yield api.test(
'unexpected_non_pre_tag_on_beta_channel',
api.platform('linux', 64),
api.properties(
git_branch='flutter-1.2.3-candidate.0',
tag='3.29.3',
release_channel='beta',
force=False,
),
status="FAILURE",
)
# TEST: Must have an engine.version file.
yield api.test(
'missing_engine.version',
api.platform('linux', 64),
api.properties(
git_branch='flutter-1.2.3-candidate.0',
tag='3.32.0-0.3.pre',
release_channel='beta',
force=False,
),
status='FAILURE',
)
# TODO(matanlurey): https://github.com/flutter/flutter/issues/168674.
# TEST: Must have bin/internal/last_engine_commit.sh
# yield api.test(
# 'missing_last_engine_commit.sh',
# api.platform('linux', 64),
# api.properties(
# git_branch='flutter-1.2.3-candidate.0',
# tag='3.32.0-0.3.pre',
# release_channel='beta',
# force=False,
# ),
# api.path.exists(checkout_path / 'bin' / 'internal' / 'engine.version',),
# status='FAILURE',
# )
# TEST: The commit in engine.version is of out date compared to the source tree.
yield api.test(
'out_of_date_engine.version',
api.platform('linux', 64),
api.properties(
git_branch='flutter-1.2.3-candidate.0',
tag='3.32.0-0.3.pre',
release_channel='beta',
force=False,
),
api.repo_util.flutter_environment_data(checkout_dir=checkout_path),
api.path.exists(
checkout_path / 'bin' / 'internal' / 'engine.version',
checkout_path / 'bin' / 'internal' / 'last_engine_commit.sh',
),
api.step_data(
'validate engine.version.compute last engine commit',
stdout=api.raw_io.output_text('\tdef456\t'),
),
api.step_data(
'validate engine.version.read engine.version file',
stdout=api.raw_io.output_text('\tabc123\t'),
),
status='FAILURE',
)
# TEST: "Successful" run, using fallback logic for last_engine_commit.sh.
yield api.test(
'success_fallback_last_engine_commit.sh',
api.platform('linux', 64),
api.properties(
git_branch='flutter-1.2.3-candidate.0',
tag='3.32.0-0.3.pre',
release_channel='beta',
force=False,
),
api.repo_util.flutter_environment_data(checkout_dir=checkout_path),
api.path.exists(checkout_path / 'bin' / 'internal' / 'engine.version',),
api.step_data(
'validate engine.version.compute last engine commit',
stdout=api.raw_io.output_text('\tabc123\t'),
),
api.step_data(
'validate engine.version.read engine.version file',
stdout=api.raw_io.output_text('\tabc123\t'),
),
)
# TEST: "Successful" run.
yield api.test(
'success',
api.platform('linux', 64),
api.properties(
git_branch='flutter-1.2.3-candidate.0',
tag='3.32.0-0.3.pre',
release_channel='beta',
force=False,
),
api.repo_util.flutter_environment_data(checkout_dir=checkout_path),
api.path.exists(
checkout_path / 'bin' / 'internal' / 'engine.version',
checkout_path / 'bin' / 'internal' / 'last_engine_commit.sh',
),
api.step_data(
'validate engine.version.compute last engine commit',
stdout=api.raw_io.output_text('\tabc123\t'),
),
api.step_data(
'validate engine.version.read engine.version file',
stdout=api.raw_io.output_text('\tabc123\t'),
),
)