info.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. from datetime import datetime
  2. import os
  3. from os import path
  4. import re
  5. import shutil
  6. import sys
  7. from urllib2 import urlopen
  8. from release.paths import makeCandidatesDir
  9. import logging
  10. log = logging.getLogger(__name__)
  11. # If version has two parts with no trailing specifiers like "rc", we
  12. # consider it a "final" release for which we only create a _RELEASE tag.
  13. FINAL_RELEASE_REGEX = "^\d+\.\d+$"
  14. class ConfigError(Exception):
  15. pass
  16. def getBuildID(platform, product, version, buildNumber, nightlyDir='nightly',
  17. server='stage.mozilla.org'):
  18. infoTxt = makeCandidatesDir(product, version, buildNumber, nightlyDir,
  19. protocol='http', server=server) + \
  20. '%s_info.txt' % platform
  21. try:
  22. buildInfo = urlopen(infoTxt).read()
  23. except:
  24. log.error("Failed to retrieve %s" % infoTxt)
  25. raise
  26. for line in buildInfo.splitlines():
  27. key, value = line.rstrip().split('=', 1)
  28. if key == 'buildID':
  29. return value
  30. def findOldBuildIDs(product, version, buildNumber, platforms,
  31. nightlyDir='nightly', server='stage.mozilla.org'):
  32. ids = {}
  33. if buildNumber <= 1:
  34. return ids
  35. for n in range(1, buildNumber):
  36. for platform in platforms:
  37. if platform not in ids:
  38. ids[platform] = []
  39. try:
  40. id = getBuildID(platform, product, version, n, nightlyDir,
  41. server)
  42. ids[platform].append(id)
  43. except Exception, e:
  44. log.error("Hit exception: %s" % e)
  45. return ids
  46. def getReleaseConfigName(product, branch, version=None, staging=False):
  47. # XXX: Horrible hack for bug 842741. Because Thunderbird release
  48. # and esr both build out of esr17 repositories we'll bump the wrong
  49. # config for release without this.
  50. if product == 'thunderbird' and 'esr17' in branch and version and 'esr' not in version:
  51. cfg = 'release-thunderbird-comm-release.py'
  52. else:
  53. cfg = 'release-%s-%s.py' % (product, branch)
  54. if staging:
  55. cfg = 'staging_%s' % cfg
  56. return cfg
  57. def readReleaseConfig(configfile, required=[]):
  58. return readConfig(configfile, keys=['releaseConfig'], required=required)
  59. def readBranchConfig(dir, localconfig, branch, required=[]):
  60. shutil.copy(localconfig, path.join(dir, "localconfig.py"))
  61. oldcwd = os.getcwd()
  62. os.chdir(dir)
  63. sys.path.append(".")
  64. try:
  65. return readConfig("config.py", keys=['BRANCHES', branch],
  66. required=required)
  67. finally:
  68. os.chdir(oldcwd)
  69. sys.path.remove(".")
  70. def readConfig(configfile, keys=[], required=[]):
  71. c = {}
  72. execfile(configfile, c)
  73. for k in keys:
  74. c = c[k]
  75. items = c.keys()
  76. err = False
  77. for key in required:
  78. if key not in items:
  79. err = True
  80. log.error("Required item `%s' missing from %s" % (key, c))
  81. if err:
  82. raise ConfigError("Missing at least one item in config, see above")
  83. return c
  84. def isFinalRelease(version):
  85. return bool(re.match(FINAL_RELEASE_REGEX, version))
  86. def getBaseTag(product, version):
  87. product = product.upper()
  88. version = version.replace('.', '_')
  89. return '%s_%s' % (product, version)
  90. def getTags(baseTag, buildNumber, buildTag=True):
  91. t = ['%s_RELEASE' % baseTag]
  92. if buildTag:
  93. t.append('%s_BUILD%d' % (baseTag, int(buildNumber)))
  94. return t
  95. def getRuntimeTag(tag):
  96. return "%s_RUNTIME" % tag
  97. def getReleaseTag(tag):
  98. return "%s_RELEASE" % tag
  99. def generateRelbranchName(version, prefix='GECKO'):
  100. return '%s%s_%s_RELBRANCH' % (
  101. prefix, version.replace('.', ''),
  102. datetime.utcnow().strftime('%Y%m%d%H'))
  103. def getReleaseName(product, version, buildNumber):
  104. return '%s-%s-build%s' % (product.title(), version, str(buildNumber))
  105. def getRepoMatchingBranch(branch, sourceRepositories):
  106. for sr in sourceRepositories.values():
  107. if branch in sr['path']:
  108. return sr
  109. return None
  110. def fileInfo(filepath, product):
  111. """Extract information about a release file. Returns a dictionary with the
  112. following keys set:
  113. 'product', 'version', 'locale', 'platform', 'contents', 'format',
  114. 'pathstyle'
  115. 'contents' is one of 'complete', 'installer'
  116. 'format' is one of 'mar' or 'exe'
  117. 'pathstyle' is either 'short' or 'long', and refers to if files are all in
  118. one directory, with the locale as part of the filename ('short' paths,
  119. firefox 3.0 style filenames), or if the locale names are part of the
  120. directory structure, but not the file name itself ('long' paths,
  121. firefox 3.5+ style filenames)
  122. """
  123. try:
  124. # Mozilla 1.9.0 style (aka 'short') paths
  125. # e.g. firefox-3.0.12.en-US.win32.complete.mar
  126. filename = os.path.basename(filepath)
  127. m = re.match("^(%s)-([0-9.]+)\.([-a-zA-Z]+)\.(win32)\.(complete|installer)\.(mar|exe)$" % product, filename)
  128. if not m:
  129. raise ValueError("Could not parse: %s" % filename)
  130. return {'product': m.group(1),
  131. 'version': m.group(2),
  132. 'locale': m.group(3),
  133. 'platform': m.group(4),
  134. 'contents': m.group(5),
  135. 'format': m.group(6),
  136. 'pathstyle': 'short',
  137. 'leading_path': '',
  138. }
  139. except:
  140. # Mozilla 1.9.1 and on style (aka 'long') paths
  141. # e.g. update/win32/en-US/firefox-3.5.1.complete.mar
  142. # win32/en-US/Firefox Setup 3.5.1.exe
  143. ret = {'pathstyle': 'long'}
  144. if filepath.endswith('.mar'):
  145. ret['format'] = 'mar'
  146. 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)
  147. if not m:
  148. raise ValueError("Could not parse: %s" % filepath)
  149. ret['platform'] = m.group(1)
  150. ret['locale'] = m.group(2)
  151. ret['product'] = m.group(3)
  152. ret['version'] = m.group(4)
  153. ret['contents'] = m.group(5)
  154. ret['leading_path'] = ''
  155. elif filepath.endswith('.exe'):
  156. ret['format'] = 'exe'
  157. ret['contents'] = 'installer'
  158. # EUballot builds use a different enough style of path than others
  159. # that we can't catch them in the same regexp
  160. if filepath.find('win32-EUballot') != -1:
  161. ret['platform'] = 'win32'
  162. m = re.search("(win32-EUballot/)([-a-zA-Z]+)/((?i)%s) Setup (\d+\.\d+(?:\.\d+)?(?:\w+\d+)?(?:\ \w+\ \d+)?)\.exe" % product, filepath)
  163. if not m:
  164. raise ValueError("Could not parse: %s" % filepath)
  165. ret['leading_path'] = m.group(1)
  166. ret['locale'] = m.group(2)
  167. ret['product'] = m.group(3).lower()
  168. ret['version'] = m.group(4)
  169. else:
  170. 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)
  171. if not m:
  172. raise ValueError("Could not parse: %s" % filepath)
  173. ret['leading_path'] = m.group(1)
  174. ret['platform'] = m.group(2)
  175. ret['locale'] = m.group(3)
  176. ret['product'] = m.group(4).lower()
  177. ret['version'] = m.group(5)
  178. else:
  179. raise ValueError("Unknown filetype for %s" % filepath)
  180. return ret