build/release/info.py

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/build/release/info.py	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,218 @@
     1.4 +from datetime import datetime
     1.5 +import os
     1.6 +from os import path
     1.7 +import re
     1.8 +import shutil
     1.9 +import sys
    1.10 +from urllib2 import urlopen
    1.11 +
    1.12 +from release.paths import makeCandidatesDir
    1.13 +
    1.14 +import logging
    1.15 +log = logging.getLogger(__name__)
    1.16 +
    1.17 +# If version has two parts with no trailing specifiers like "rc", we
    1.18 +# consider it a "final" release for which we only create a _RELEASE tag.
    1.19 +FINAL_RELEASE_REGEX = "^\d+\.\d+$"
    1.20 +
    1.21 +
    1.22 +class ConfigError(Exception):
    1.23 +    pass
    1.24 +
    1.25 +
    1.26 +def getBuildID(platform, product, version, buildNumber, nightlyDir='nightly',
    1.27 +               server='stage.mozilla.org'):
    1.28 +    infoTxt = makeCandidatesDir(product, version, buildNumber, nightlyDir,
    1.29 +                                protocol='http', server=server) + \
    1.30 +        '%s_info.txt' % platform
    1.31 +    try:
    1.32 +        buildInfo = urlopen(infoTxt).read()
    1.33 +    except:
    1.34 +        log.error("Failed to retrieve %s" % infoTxt)
    1.35 +        raise
    1.36 +
    1.37 +    for line in buildInfo.splitlines():
    1.38 +        key, value = line.rstrip().split('=', 1)
    1.39 +        if key == 'buildID':
    1.40 +            return value
    1.41 +
    1.42 +
    1.43 +def findOldBuildIDs(product, version, buildNumber, platforms,
    1.44 +                    nightlyDir='nightly', server='stage.mozilla.org'):
    1.45 +    ids = {}
    1.46 +    if buildNumber <= 1:
    1.47 +        return ids
    1.48 +    for n in range(1, buildNumber):
    1.49 +        for platform in platforms:
    1.50 +            if platform not in ids:
    1.51 +                ids[platform] = []
    1.52 +            try:
    1.53 +                id = getBuildID(platform, product, version, n, nightlyDir,
    1.54 +                                server)
    1.55 +                ids[platform].append(id)
    1.56 +            except Exception, e:
    1.57 +                log.error("Hit exception: %s" % e)
    1.58 +    return ids
    1.59 +
    1.60 +
    1.61 +def getReleaseConfigName(product, branch, version=None, staging=False):
    1.62 +    # XXX: Horrible hack for bug 842741. Because Thunderbird release
    1.63 +    # and esr both build out of esr17 repositories we'll bump the wrong
    1.64 +    # config for release without this.
    1.65 +    if product == 'thunderbird' and 'esr17' in branch and version and 'esr' not in version:
    1.66 +        cfg = 'release-thunderbird-comm-release.py'
    1.67 +    else:
    1.68 +        cfg = 'release-%s-%s.py' % (product, branch)
    1.69 +    if staging:
    1.70 +        cfg = 'staging_%s' % cfg
    1.71 +    return cfg
    1.72 +
    1.73 +
    1.74 +def readReleaseConfig(configfile, required=[]):
    1.75 +    return readConfig(configfile, keys=['releaseConfig'], required=required)
    1.76 +
    1.77 +
    1.78 +def readBranchConfig(dir, localconfig, branch, required=[]):
    1.79 +    shutil.copy(localconfig, path.join(dir, "localconfig.py"))
    1.80 +    oldcwd = os.getcwd()
    1.81 +    os.chdir(dir)
    1.82 +    sys.path.append(".")
    1.83 +    try:
    1.84 +        return readConfig("config.py", keys=['BRANCHES', branch],
    1.85 +                          required=required)
    1.86 +    finally:
    1.87 +        os.chdir(oldcwd)
    1.88 +        sys.path.remove(".")
    1.89 +
    1.90 +
    1.91 +def readConfig(configfile, keys=[], required=[]):
    1.92 +    c = {}
    1.93 +    execfile(configfile, c)
    1.94 +    for k in keys:
    1.95 +        c = c[k]
    1.96 +    items = c.keys()
    1.97 +    err = False
    1.98 +    for key in required:
    1.99 +        if key not in items:
   1.100 +            err = True
   1.101 +            log.error("Required item `%s' missing from %s" % (key, c))
   1.102 +    if err:
   1.103 +        raise ConfigError("Missing at least one item in config, see above")
   1.104 +    return c
   1.105 +
   1.106 +
   1.107 +def isFinalRelease(version):
   1.108 +    return bool(re.match(FINAL_RELEASE_REGEX, version))
   1.109 +
   1.110 +
   1.111 +def getBaseTag(product, version):
   1.112 +    product = product.upper()
   1.113 +    version = version.replace('.', '_')
   1.114 +    return '%s_%s' % (product, version)
   1.115 +
   1.116 +
   1.117 +def getTags(baseTag, buildNumber, buildTag=True):
   1.118 +    t = ['%s_RELEASE' % baseTag]
   1.119 +    if buildTag:
   1.120 +        t.append('%s_BUILD%d' % (baseTag, int(buildNumber)))
   1.121 +    return t
   1.122 +
   1.123 +
   1.124 +def getRuntimeTag(tag):
   1.125 +    return "%s_RUNTIME" % tag
   1.126 +
   1.127 +
   1.128 +def getReleaseTag(tag):
   1.129 +    return "%s_RELEASE" % tag
   1.130 +
   1.131 +
   1.132 +def generateRelbranchName(version, prefix='GECKO'):
   1.133 +    return '%s%s_%s_RELBRANCH' % (
   1.134 +        prefix, version.replace('.', ''),
   1.135 +        datetime.now().strftime('%Y%m%d%H'))
   1.136 +
   1.137 +
   1.138 +def getReleaseName(product, version, buildNumber):
   1.139 +    return '%s-%s-build%s' % (product.title(), version, str(buildNumber))
   1.140 +
   1.141 +
   1.142 +def getRepoMatchingBranch(branch, sourceRepositories):
   1.143 +    for sr in sourceRepositories.values():
   1.144 +        if branch in sr['path']:
   1.145 +            return sr
   1.146 +    return None
   1.147 +
   1.148 +
   1.149 +def fileInfo(filepath, product):
   1.150 +    """Extract information about a release file.  Returns a dictionary with the
   1.151 +    following keys set:
   1.152 +    'product', 'version', 'locale', 'platform', 'contents', 'format',
   1.153 +    'pathstyle'
   1.154 +
   1.155 +    'contents' is one of 'complete', 'installer'
   1.156 +    'format' is one of 'mar' or 'exe'
   1.157 +    'pathstyle' is either 'short' or 'long', and refers to if files are all in
   1.158 +        one directory, with the locale as part of the filename ('short' paths,
   1.159 +        firefox 3.0 style filenames), or if the locale names are part of the
   1.160 +        directory structure, but not the file name itself ('long' paths,
   1.161 +        firefox 3.5+ style filenames)
   1.162 +    """
   1.163 +    try:
   1.164 +        # Mozilla 1.9.0 style (aka 'short') paths
   1.165 +        # e.g. firefox-3.0.12.en-US.win32.complete.mar
   1.166 +        filename = os.path.basename(filepath)
   1.167 +        m = re.match("^(%s)-([0-9.]+)\.([-a-zA-Z]+)\.(win32)\.(complete|installer)\.(mar|exe)$" % product, filename)
   1.168 +        if not m:
   1.169 +            raise ValueError("Could not parse: %s" % filename)
   1.170 +        return {'product': m.group(1),
   1.171 +                'version': m.group(2),
   1.172 +                'locale': m.group(3),
   1.173 +                'platform': m.group(4),
   1.174 +                'contents': m.group(5),
   1.175 +                'format': m.group(6),
   1.176 +                'pathstyle': 'short',
   1.177 +                'leading_path': '',
   1.178 +                }
   1.179 +    except:
   1.180 +        # Mozilla 1.9.1 and on style (aka 'long') paths
   1.181 +        # e.g. update/win32/en-US/firefox-3.5.1.complete.mar
   1.182 +        #      win32/en-US/Firefox Setup 3.5.1.exe
   1.183 +        ret = {'pathstyle': 'long'}
   1.184 +        if filepath.endswith('.mar'):
   1.185 +            ret['format'] = 'mar'
   1.186 +            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)
   1.187 +            if not m:
   1.188 +                raise ValueError("Could not parse: %s" % filepath)
   1.189 +            ret['platform'] = m.group(1)
   1.190 +            ret['locale'] = m.group(2)
   1.191 +            ret['product'] = m.group(3)
   1.192 +            ret['version'] = m.group(4)
   1.193 +            ret['contents'] = m.group(5)
   1.194 +            ret['leading_path'] = ''
   1.195 +        elif filepath.endswith('.exe'):
   1.196 +            ret['format'] = 'exe'
   1.197 +            ret['contents'] = 'installer'
   1.198 +            # EUballot builds use a different enough style of path than others
   1.199 +            # that we can't catch them in the same regexp
   1.200 +            if filepath.find('win32-EUballot') != -1:
   1.201 +                ret['platform'] = 'win32'
   1.202 +                m = re.search("(win32-EUballot/)([-a-zA-Z]+)/((?i)%s) Setup (\d+\.\d+(?:\.\d+)?(?:\w+\d+)?(?:\ \w+\ \d+)?)\.exe" % product, filepath)
   1.203 +                if not m:
   1.204 +                    raise ValueError("Could not parse: %s" % filepath)
   1.205 +                ret['leading_path'] = m.group(1)
   1.206 +                ret['locale'] = m.group(2)
   1.207 +                ret['product'] = m.group(3).lower()
   1.208 +                ret['version'] = m.group(4)
   1.209 +            else:
   1.210 +                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)
   1.211 +                if not m:
   1.212 +                    raise ValueError("Could not parse: %s" % filepath)
   1.213 +                ret['leading_path'] = m.group(1)
   1.214 +                ret['platform'] = m.group(2)
   1.215 +                ret['locale'] = m.group(3)
   1.216 +                ret['product'] = m.group(4).lower()
   1.217 +                ret['version'] = m.group(5)
   1.218 +        else:
   1.219 +            raise ValueError("Unknown filetype for %s" % filepath)
   1.220 +
   1.221 +        return ret

mercurial