[infra] Promote beta to stable when stable is ahead of beta.
Don't promote older releases to latest if promoted out of order.
Fixes: b/227744513
Change-Id: I954c8620897e59ed3caa459e0bd22e446e38d02d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/283622
Commit-Queue: Jonas Termansen <sortie@google.com>
Reviewed-by: Alexander Thomas <athom@google.com>
diff --git a/tools/promote.py b/tools/promote.py
index 2f214c8..e784dff 100755
--- a/tools/promote.py
+++ b/tools/promote.py
@@ -6,6 +6,7 @@
# Dart SDK promote tools.
+import json
import optparse
import os
import sys
@@ -14,6 +15,7 @@
import bots.bot_utils as bot_utils
from os.path import join
+from utils import Version
DART_PATH = os.path.abspath(os.path.join(__file__, '..', '..'))
DRY_RUN = False
@@ -93,6 +95,18 @@
_PromoteDartArchiveBuild(options.channel, source, options.revision)
+def GetLatestRelease(channel):
+ release_namer = bot_utils.GCSNamer(channel, bot_utils.ReleaseType.RELEASE)
+ version_object = release_namer.version_filepath('latest')
+ global DRY_RUN
+ was_dry = DRY_RUN
+ DRY_RUN = False
+ (stdout, _, _) = Gsutil(['cat', version_object])
+ DRY_RUN = was_dry
+ version = json.loads(stdout)['version']
+ return Version(version=version)
+
+
def UpdateDocs():
try:
print('Updating docs')
@@ -114,7 +128,6 @@
release_namer = bot_utils.GCSNamer(channel, bot_utils.ReleaseType.RELEASE)
def promote(to_revision):
-
def safety_check_on_gs_path(gs_path, revision, channel):
if not (revision != None and len(channel) > 0 and
('%s' % revision) in gs_path and channel in gs_path):
@@ -182,7 +195,13 @@
Gsutil(no_cache + ['cp', from_loc, to_loc])
promote(revision)
- promote('latest')
+ # Promote to latest unless it's an older version.
+ if GetLatestRelease(channel) <= Version(version=revision):
+ promote('latest')
+ # Promote beta to stable if stable becomes ahead of beta.
+ if channel == 'stable' and \
+ GetLatestRelease('beta') <= Version(version=revision):
+ _PromoteDartArchiveBuild('beta', 'stable', revision)
def Gsutil(cmd, throw_on_error=True):
diff --git a/tools/utils.py b/tools/utils.py
index aab6417..ce932b3 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -9,6 +9,7 @@
import contextlib
import datetime
+from functools import total_ordering
import glob
import imp
import json
@@ -28,6 +29,8 @@
except:
pass
+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-]+)*))?$'
+
# To eliminate clashing with older archived builds on bleeding edge we add
# a base number bigger the largest svn revision (this also gives us an easy
@@ -120,16 +123,88 @@
os.path.join(repo_path, 'tools', 'minidump.py'))
+@total_ordering
class Version(object):
- def __init__(self, channel, major, minor, patch, prerelease,
- prerelease_patch):
+ def __init__(self,
+ channel=None,
+ major=None,
+ minor=None,
+ patch=None,
+ prerelease=None,
+ prerelease_patch=None,
+ version=None):
self.channel = channel
self.major = major
self.minor = minor
self.patch = patch
self.prerelease = prerelease
self.prerelease_patch = prerelease_patch
+ if version:
+ self.set_version(version)
+
+ def set_version(self, version):
+ match = re.match(SEMANTIC_VERSION_PATTERN, version)
+ assert match, '%s must be a valid version' % version
+ self.channel = 'stable'
+ self.major = match['major']
+ self.minor = match['minor']
+ self.patch = match['patch']
+ self.prerelease = '0'
+ self.prerelease_patch = '0'
+ if match['prerelease']:
+ subversions = match['prerelease'].split('.')
+ self.prerelease = subversions[0]
+ self.prerelease_patch = subversions[1]
+ self.channel = subversions[2]
+
+ def __str__(self):
+ result = '%s.%s.%s' % (self.major, self.minor, self.patch)
+ if self.channel != 'stable':
+ result += '-%s.%s.%s' % (self.prerelease, self.prerelease_patch,
+ self.channel)
+ return result
+
+ def __eq__(self, other):
+ return self.channel == other.channel and \
+ self.major == other.major and \
+ self.minor == other.minor and \
+ self.patch == other.patch and \
+ self.prerelease == other.prerelease and \
+ self.prerelease_patch == other.prerelease_patch
+
+ def __lt__(self, other):
+ if int(self.major) < int(other.major):
+ return True
+ if int(self.major) > int(other.major):
+ return False
+ if int(self.minor) < int(other.minor):
+ return True
+ if int(self.minor) > int(other.minor):
+ return False
+ if int(self.patch) < int(other.patch):
+ return True
+ if int(self.patch) > int(other.patch):
+ return False
+ # The stable channel is ahead of the other channels on the same triplet.
+ if self.channel != 'stable' and other.channel == 'stable':
+ return True
+ if self.channel == 'stable' and other.channel != 'stable':
+ return False
+ # The be channel is ahead of the other channels on the same triplet.
+ if self.channel != 'be' and other.channel == 'be':
+ return True
+ if self.channel == 'be' and other.channel != 'be':
+ return False
+ if int(self.prerelease_patch) < int(other.prerelease_patch):
+ return True
+ if int(self.prerelease_patch) > int(other.prerelease_patch):
+ return False
+ if int(self.prerelease) < int(other.prerelease):
+ return True
+ if int(self.prerelease) > int(other.prerelease):
+ return False
+ return False
# Try to guess the host operating system.