author Andrew Halberstadt <>
Fri, 19 Feb 2016 16:19:03 -0500
changeset 322616 651ae113dc0d9eef77b0f253c7ee122cf952ddce
parent 150821 d8f26f00c82f12df862edd0a36d7f47b5848362a
permissions -rw-r--r--
Bug 1249733 - Sign talos harness and test extensions, r=jmaher The various harness addons and test related addons used by talos all need to be signed before we can enforce addon signing. For now, signing will be a manual process. See the following guide for more details: MozReview-Commit-ID: CKeOyuN4JJG

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=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 = ''
        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, branch, required=[]):
    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)
        # 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':,
                'pathstyle': 'short',
                'leading_path': '',
        # 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|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'] =
            ret['locale'] =
            ret['product'] =
            ret['version'] =
            ret['contents'] =
            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 ="(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'] =
                ret['locale'] =
                ret['product'] =
                ret['version'] =
                m ="(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'] =
                ret['platform'] =
                ret['locale'] =
                ret['product'] =
                ret['version'] =
            raise ValueError("Unknown filetype for %s" % filepath)

        return ret