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