blob: 416ae5c7df23a1f43e26eb6f64a84d597d5f1a09 [file] [log] [blame]
# Copyright 2018 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 re
from recipe_engine.post_process import (
DoesNotRunRE,
Filter,
StepCommandContains,
StepCommandRE,
)
DEPS = [
'dart',
'depot_tools/git',
'depot_tools/gsutil',
'recipe_engine/context',
'recipe_engine/file',
'recipe_engine/path',
'recipe_engine/platform',
'recipe_engine/properties',
'recipe_engine/raw_io',
'recipe_engine/step',
'recipe_engine/url',
]
PACKAGE = 'dart-sdk'
RELEASE_BUCKET = 'gs://dart-archive/channels/%s/release/'
CHANNELS = ['dev', 'beta', 'stable']
INSTALLER_NAME = 'install.ps1'
INSTALLER = 'https://chocolatey.org/%s' % INSTALLER_NAME
POWERSHELL = (
'C:\\\\WINDOWS\\\\system32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe')
CHECKSUM = ('https://storage.googleapis.com/dart-archive/'
'channels/%s/release/%s/sdk/dartsdk-windows-%s-release.zip.sha256sum')
SEMANTIC_VERSION_PATTERN = r'^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
PYTHON_VERSION_COMPATIBILITY = "PY3"
def _is_version(version):
"Whether the string is a valid semantic version"
return re.match(SEMANTIC_VERSION_PATTERN, version)
def _is_modern_version(version):
"Whether the version is new enough to synchronize to chocolatey"
match = re.match(SEMANTIC_VERSION_PATTERN, version)
if not match:
return False
major = int(match['major'])
minor = int(match['minor'])
return 3 <= major or (2 == major and 17 <= minor)
def _channel_of_version(version):
for channel in CHANNELS:
if channel in version:
return channel
return 'stable'
def _parse_gsurl_release(line):
return line.split('/')[6]
def _parse_chocolatey_release(line):
return line.split(' ')[1]
def _decode_chocolatey_version(version):
"2.1.4.22-c-013-dev -> 2.1.4-22.13.dev"
majors = version.split('.')
if len(majors) <= 3:
return version
minors = majors[3].split('-')
# 1.8.5.0
if len(minors) == 1:
return version
# 2.0.0.53-dev
if len(minors) == 2:
return '.'.join(majors[0:3]) + '-' + minors[1] + '.' + minors[0] + '.0'
# 2.8.0.20-dev-0
if len(minors) == 3:
return '.'.join(
majors[0:3]) + '-' + minors[1] + '.' + minors[0] + '.' + minors[2]
# 2.17.0.69-c-002-beta
return '.'.join(majors[0:3]) + '-' + minors[0] + '.' + str(int(
minors[2])) + '.' + minors[3]
def _encode_chocolatey_version(version):
"2.1.4-22.13.dev -> 2.1.4.22-c-013-dev"
if '.dev' in version or '.beta' in version:
# todo(athom): remove when chocolatey supports semver 2.0.0
# See https://github.com/chocolatey/choco/issues/1610
(version, build) = version.split('-')
parts = build.split('.')
# pad with zeros, because chocolatey compares pre-release alphabetically
version = "%s.%s-c-%s-%s" % (
(version, parts.pop(0)) + tuple(part.zfill(3) for part in parts))
return version
def RunSteps(api):
choco_installer = api.path['cleanup'].join(INSTALLER_NAME)
api.url.get_file(INSTALLER, choco_installer, 'download chocolatey installer')
choco_home = api.path['cleanup'].join('chocolatey')
bin_root = api.path['cleanup'].join('bin_root')
env = {
'ChocolateyInstall': choco_home,
'ChocolateyBinRoot': bin_root
}
with api.context(env=env):
api.step('install chocolatey', [POWERSHELL, choco_installer])
choco = choco_home.join('choco')
api.step('choco --version', [choco, '--version'])
cache = api.path['cleanup'].join('cache')
api.step('choco set package directory',
[choco, 'config', 'set', 'cacheLocation', cache])
# List the released versions via cloud storage.
released_versions = set()
for channel in CHANNELS:
versions_data = api.gsutil.list(
RELEASE_BUCKET % channel,
name='list %s releases' % channel,
stdout=api.raw_io.output_text(add_output_log=True))
for line in versions_data.stdout.splitlines():
version = _parse_gsurl_release(line)
if _is_version(version) and _is_modern_version(version):
released_versions.add(version)
# List the versions already released on chocolatey.
published_data = api.step(
'list published versions',
[choco, 'list', '--all', '--exact', '--pre', PACKAGE],
stdout=api.raw_io.output_text(add_output_log=True))
published_versions = set()
for line in published_data.stdout.splitlines():
if line.startswith(PACKAGE + ' '):
choco_version = _parse_chocolatey_release(line)
version = _decode_chocolatey_version(choco_version)
if _is_modern_version(version):
published_versions.add(version)
# Stop if chocolatey is up to date.
new_versions = sorted(released_versions - published_versions)
if not new_versions:
api.step.empty('chocolatey is up to date')
return
# Check out the chocolatey package installer template.
api.git.checkout(
url='https://github.com/dart-lang/chocolatey-packages.git',
ref='master')
# Read the installer template.
chocolatey_dir = api.path['start_dir'].join('chocolatey-packages')
package_dir = chocolatey_dir.join(PACKAGE)
installer_path = package_dir.join('chocolateyInstall.ps1')
installer = api.file.read_text('read installer', installer_path)
# Decode the private key for chocolatey uploads.
chocolatey_key = api.dart.get_secret('chocolatey')
# Release the new versions on chocolatey.
for version in new_versions:
channel = _channel_of_version(version)
_publish_version(api, channel, version, choco, package_dir, installer,
installer_path, chocolatey_key)
def _publish_version(api, channel, version, choco, package_dir, installer,
installer_path, chocolatey_key):
checksum = api.url.get_text(
CHECKSUM % (channel, version, 'ia32'),
default_test_data='abc *should-not-see-this')
checksum = checksum.output.split()[0]
checksum64 = api.url.get_text(
CHECKSUM % (channel, version, 'x64'),
default_test_data='def *should-not-see-this')
checksum64 = checksum64.output.split()[0]
installer = installer.replace('$version$', version)
installer = installer.replace('$channel$', channel)
installer = installer.replace('$checksum$', checksum)
installer = installer.replace('$checksum64$', checksum64)
api.file.write_text('write installer %s %s' % (channel, version),
installer_path, installer)
with api.context(cwd=package_dir):
choco_version = _encode_chocolatey_version(version)
api.step('choco pack %s %s' % (channel, version),
[choco, 'pack', 'version=%s' % choco_version])
api.step('verify with choco install %s %s' % (channel, version),
[choco, 'install', PACKAGE, '--pre', '-y', '-dv', '-s', '.'])
choco_push = '$secret = cat %s; %s push -k="$secret" %s.%s.nupkg' % (
chocolatey_key, choco, PACKAGE, choco_version)
api.step('choco push %s %s' % (channel, version),
[POWERSHELL, '-Command', choco_push])
DEV_RELEASES = '\n'.join(
map(lambda v: (RELEASE_BUCKET + '%s/') % ('dev', v), [
'1.11.0-dev.3.0', '2.0.0-dev.0.0', '2.16.0-80.0.dev', '2.17.0-1.0.dev',
'2.17.0-162.0.dev', '2.17.0-18.0.dev', '2.17.0-7.0.dev', '45519',
'latest'
])) + '\n'
BETA_RELEASES = '\n'.join(
map(lambda v: (RELEASE_BUCKET + '%s/') % ('beta', v), [
'2.10.0-110.1.beta', '2.16.0-80.1.beta', '2.16.0', '2.16.1',
'2.17.0-69.1.beta', 'latest'
])) + '\n'
STABLE_RELEASES = '\n'.join(
map(lambda v: (RELEASE_BUCKET + '%s/') % ('stable', v), [
'1.11.0', '2.0.0', '2.15.1', '2.16.0', '2.16.1', '2.17.0', '2.2.0',
'45692', 'latest'
])) + '\n'
CHOCOLATEY_SYNCHRONIZED = """
Chocolatey v0.12.1
dart-sdk 2.17.0 [Approved] Downloads cached for licensed users
dart-sdk 2.17.0.162-c-000-dev [Approved] Downloads cached for licensed users
dart-sdk 2.17.0.91-c-000-dev [Approved] Downloads cached for licensed users
dart-sdk 2.17.0.69-c-002-beta [Approved]
dart-sdk 2.17.0.69-c-001-beta [Approved]
dart-sdk 2.17.0.69-c-000-dev [Approved] Downloads cached for licensed users
dart-sdk 2.17.0.18-c-000-dev [Approved] Downloads cached for licensed users
dart-sdk 2.17.0.7-c-000-dev - Possibly broken
dart-sdk 2.17.0.1-c-000-dev [Approved] Downloads cached for licensed users
dart-sdk 2.16.1 [Approved] Downloads cached for licensed users
dart-sdk 2.8.0.15-dev-0
dart-sdk 2.0.0.53-dev [Approved] Downloads cached for licensed users
dart-sdk 1.8.5.0 [Approved]
dart-sdk 1.6.0 [Approved]
401 packages found.
"""
CHOCOLATEY_UNSYNCHRONIZED = '\n'.join([
line for line in CHOCOLATEY_SYNCHRONIZED.split('\n')
if not ' 2.17.0.18-c-000-dev ' in line and
not ' 2.17.0.69-c-001-beta ' in line and not ' 2.17.0 ' in line
])
def GenTests(api):
yield api.test(
'synchronized',
api.platform('win', 64),
api.step_data(
'gsutil list dev releases',
stdout=api.raw_io.output_text(DEV_RELEASES)),
api.step_data(
'gsutil list beta releases',
stdout=api.raw_io.output_text(BETA_RELEASES)),
api.step_data(
'gsutil list stable releases',
stdout=api.raw_io.output_text(STABLE_RELEASES)),
api.step_data(
'list published versions',
stdout=api.raw_io.output_text(CHOCOLATEY_SYNCHRONIZED)),
api.post_process(DoesNotRunRE, 'choco pack'),
)
yield api.test(
'unsynchronized',
api.platform('win', 64),
api.step_data(
'gsutil list dev releases',
stdout=api.raw_io.output_text(DEV_RELEASES)),
api.step_data(
'gsutil list beta releases',
stdout=api.raw_io.output_text(BETA_RELEASES)),
api.step_data(
'gsutil list stable releases',
stdout=api.raw_io.output_text(STABLE_RELEASES)),
api.step_data(
'list published versions',
stdout=api.raw_io.output_text(CHOCOLATEY_UNSYNCHRONIZED)),
api.post_process(StepCommandContains, 'choco pack dev 2.17.0-18.0.dev',
['version=2.17.0.18-c-000-dev']),
api.post_process(StepCommandRE, 'choco push dev 2.17.0-18.0.dev',
['.*', '.*', r'.*2\.17\.0\.18-c-000-dev\.nupkg']),
api.post_process(StepCommandContains, 'choco pack beta 2.17.0-69.1.beta',
['version=2.17.0.69-c-001-beta']),
api.post_process(StepCommandRE, 'choco push beta 2.17.0-69.1.beta',
['.*', '.*', r'.*2\.17\.0\.69-c-001-beta\.nupkg']),
api.post_process(StepCommandContains, 'choco pack stable 2.17.0',
['version=2.17.0']),
api.post_process(StepCommandRE, 'choco push stable 2.17.0',
['.*', '.*', r'.*2\.17\.0.nupkg']),
)