from datetime import datetime import os from os import path import re import shutil import sys from urllib2 import urlopen from release.paths import makeCandidatesDir import logging log = logging.getLogger(__name__) # If version has two parts with no trailing specifiers like "rc", we # consider it a "final" release for which we only create a _RELEASE tag. FINAL_RELEASE_REGEX = "^\d+\.\d+$" class ConfigError(Exception): pass def getBuildID(platform, product, version, buildNumber, nightlyDir='nightly', server='stage.mozilla.org'): infoTxt = makeCandidatesDir(product, version, buildNumber, nightlyDir, protocol='http', server=server) + \ '%s_info.txt' % platform try: buildInfo = urlopen(infoTxt).read() except: log.error("Failed to retrieve %s" % infoTxt) raise for line in buildInfo.splitlines(): key, value = line.rstrip().split('=', 1) if key == 'buildID': return value def findOldBuildIDs(product, version, buildNumber, platforms, nightlyDir='nightly', server='stage.mozilla.org'): ids = {} if buildNumber <= 1: return ids for n in range(1, buildNumber): for platform in platforms: if platform not in ids: ids[platform] = [] try: id = getBuildID(platform, product, version, n, nightlyDir, server) ids[platform].append(id) except Exception, e: log.error("Hit exception: %s" % e) return ids def getReleaseConfigName(product, branch, version=None, staging=False): # XXX: Horrible hack for bug 842741. Because Thunderbird release # and esr both build out of esr17 repositories we'll bump the wrong # config for release without this. if product == 'thunderbird' and 'esr17' in branch and version and 'esr' not in version: cfg = 'release-thunderbird-comm-release.py' else: cfg = 'release-%s-%s.py' % (product, branch) if staging: cfg = 'staging_%s' % cfg return cfg def readReleaseConfig(configfile, required=[]): return readConfig(configfile, keys=['releaseConfig'], required=required) def readBranchConfig(dir, localconfig, branch, required=[]): shutil.copy(localconfig, path.join(dir, "localconfig.py")) oldcwd = os.getcwd() os.chdir(dir) sys.path.append(".") try: return readConfig("config.py", keys=['BRANCHES', branch], required=required) finally: os.chdir(oldcwd) sys.path.remove(".") def readConfig(configfile, keys=[], required=[]): c = {} execfile(configfile, c) for k in keys: c = c[k] items = c.keys() err = False for key in required: if key not in items: err = True log.error("Required item `%s' missing from %s" % (key, c)) if err: raise ConfigError("Missing at least one item in config, see above") return c def isFinalRelease(version): return bool(re.match(FINAL_RELEASE_REGEX, version)) def getBaseTag(product, version): product = product.upper() version = version.replace('.', '_') return '%s_%s' % (product, version) def getTags(baseTag, buildNumber, buildTag=True): t = ['%s_RELEASE' % baseTag] if buildTag: t.append('%s_BUILD%d' % (baseTag, int(buildNumber))) return t def getRuntimeTag(tag): return "%s_RUNTIME" % tag def getReleaseTag(tag): return "%s_RELEASE" % tag def generateRelbranchName(version, prefix='GECKO'): return '%s%s_%s_RELBRANCH' % ( prefix, version.replace('.', ''), datetime.now().strftime('%Y%m%d%H')) def getReleaseName(product, version, buildNumber): return '%s-%s-build%s' % (product.title(), version, str(buildNumber)) def getRepoMatchingBranch(branch, sourceRepositories): for sr in sourceRepositories.values(): if branch in sr['path']: return sr return None def fileInfo(filepath, product): """Extract information about a release file. Returns a dictionary with the following keys set: 'product', 'version', 'locale', 'platform', 'contents', 'format', 'pathstyle' 'contents' is one of 'complete', 'installer' 'format' is one of 'mar' or 'exe' 'pathstyle' is either 'short' or 'long', and refers to if files are all in one directory, with the locale as part of the filename ('short' paths, firefox 3.0 style filenames), or if the locale names are part of the directory structure, but not the file name itself ('long' paths, firefox 3.5+ style filenames) """ try: # Mozilla 1.9.0 style (aka 'short') paths # e.g. firefox-3.0.12.en-US.win32.complete.mar filename = os.path.basename(filepath) m = re.match("^(%s)-([0-9.]+)\.([-a-zA-Z]+)\.(win32)\.(complete|installer)\.(mar|exe)$" % product, filename) if not m: raise ValueError("Could not parse: %s" % filename) return {'product': m.group(1), 'version': m.group(2), 'locale': m.group(3), 'platform': m.group(4), 'contents': m.group(5), 'format': m.group(6), 'pathstyle': 'short', 'leading_path': '', } except: # Mozilla 1.9.1 and on style (aka 'long') paths # e.g. update/win32/en-US/firefox-3.5.1.complete.mar # win32/en-US/Firefox Setup 3.5.1.exe ret = {'pathstyle': 'long'} if filepath.endswith('.mar'): ret['format'] = 'mar' m = re.search("update/(win32|linux-i686|linux-x86_64|mac|mac64)/([-a-zA-Z]+)/(%s)-(\d+\.\d+(?:\.\d+)?(?:\w+(?:\d+)?)?)\.(complete)\.mar" % product, filepath) if not m: raise ValueError("Could not parse: %s" % filepath) ret['platform'] = m.group(1) ret['locale'] = m.group(2) ret['product'] = m.group(3) ret['version'] = m.group(4) ret['contents'] = m.group(5) ret['leading_path'] = '' elif filepath.endswith('.exe'): ret['format'] = 'exe' ret['contents'] = 'installer' # EUballot builds use a different enough style of path than others # that we can't catch them in the same regexp if filepath.find('win32-EUballot') != -1: ret['platform'] = 'win32' m = re.search("(win32-EUballot/)([-a-zA-Z]+)/((?i)%s) Setup (\d+\.\d+(?:\.\d+)?(?:\w+\d+)?(?:\ \w+\ \d+)?)\.exe" % product, filepath) if not m: raise ValueError("Could not parse: %s" % filepath) ret['leading_path'] = m.group(1) ret['locale'] = m.group(2) ret['product'] = m.group(3).lower() ret['version'] = m.group(4) else: m = re.search("(partner-repacks/[-a-zA-Z0-9_]+/|)(win32|mac|linux-i686)/([-a-zA-Z]+)/((?i)%s) Setup (\d+\.\d+(?:\.\d+)?(?:\w+(?:\d+)?)?(?:\ \w+\ \d+)?)\.exe" % product, filepath) if not m: raise ValueError("Could not parse: %s" % filepath) ret['leading_path'] = m.group(1) ret['platform'] = m.group(2) ret['locale'] = m.group(3) ret['product'] = m.group(4).lower() ret['version'] = m.group(5) else: raise ValueError("Unknown filetype for %s" % filepath) return ret