blob: 12cf66144daa7f440728ef94dbbc12b727b19be9 [file] [log] [blame]
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):
def getBuildID(platform, product, version, buildNumber, nightlyDir='nightly',
infoTxt = makeCandidatesDir(product, version, buildNumber, nightlyDir,
protocol='http', server=server) + \
'%s_info.txt' % platform
buildInfo = urlopen(infoTxt).read()
log.error("Failed to retrieve %s" % infoTxt)
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=''):
ids = {}
if buildNumber <= 1:
return ids
for n in range(1, buildNumber):
for platform in platforms:
if platform not in ids:
ids[platform] = []
id = getBuildID(platform, product, version, n, nightlyDir,
except Exception, e:
log.error("Hit exception: %s" % e)
return ids
def getReleaseConfigName(product, branch, version, staging=False):
if product in ("firefox", "fennec") and branch == "mozilla-release" and "b" in version:
cfg = "" % product
cfg = '' % (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=None, branch="mozilla-central", required=[]):
if localconfig:
shutil.copy(localconfig, path.join(dir, ""))
oldcwd = os.getcwd()
return readConfig("", keys=['BRANCHES', branch],
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('.', ''),'%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',
'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)
# TODO: delete me?
# 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(r"^(%s)-([0-9.]+)\.([-a-zA-Z]+)\.(win32)\.(partial|complete|installer)\.(mar|exe)$" % product, filename)
if not m:
raise ValueError("Could not parse: %s" % filename)
format_ =
ret = {'product':,
'format': format_,
'pathstyle': 'short',
'leading_path': '',
if format_ == 'mar':
ret['previousVersion'] = None
return ret
# 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 ="update/(win32|win64|linux-i686|linux-x86_64|mac|mac64)/([-a-zA-Z]+)/(%s)-(?:(\d+\.\d+(?:\.\d+)?(?:\w+(?:\d+)?)?)-)?(\d+\.\d+(?:\.\d+)?(?:\w+(?:\d+)?)?)\.(complete|partial)\.mar" % product, filepath)
if not m:
raise ValueError("Could not parse: %s" % filepath)
ret['platform'] =
ret['locale'] =
ret['product'] =
ret['previousVersion'] =
ret['version'] =
ret['contents'] =
ret['leading_path'] = ''
elif filepath.endswith('.exe'):
ret['format'] = 'exe'
ret['contents'] = 'installer'
m ="(partner-repacks/[-a-zA-Z0-9_]+/|)(win32|win64|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'] =
ret['platform'] =
ret['locale'] =
ret['product'] =
ret['version'] =
raise ValueError("Unknown filetype for %s" % filepath)
return ret
def getProductDetails(product, appVersion):
return '' % (product, appVersion)