def is_rc(release): if not is_beta(release['version']) and not is_esr(release['version']): if isFinalRelease(release["version"]): return True # RC release types will enable beta-channel testing & # shipping. We need this for all "final" releases # and also any releases that include a beta as a partial. # The assumption that "shipping to beta channel" always # implies other RC behaviour is bound to break at some # point, but this works for now. for version in release["partial_updates"]: if is_beta(version): return True return False
def getPossibleNextVersions(version): """Return possibly next versions for a given version. There's a few distinct cases here: * ESRs: The only possible next version is the next minor version. Eg: 17.0.3esr -> 17.0.4esr * Betas: The next beta with the same major version and also the next major version's beta 1. Eg: 18.0b4 -> 18.0b5, 19.0b1 * Other: The next major version's .0 release and the next minor version. Eg: 15.0 -> 15.0.1, 16.0; 17.0.2 -> 17.0.3, 18.0 Versions with 'pre' are deprecated, and explicitly not supported. """ ret = set() # Get the parts we care about from the version. The last group is the 'pre' # tag, which doesn't affect our work. m = re.match(ANY_VERSION_REGEX, version) if not m: return ret base, beta, _, esr = m.groups()[:4] # The next major version is used in a couple of places, so we figure it out # ahead of time. Eg: 17.0 -> 18.0 or 15.0.3 -> 16.0 nextMajorVersion = increment(base.split('.')[0]) + '.0' # Modern ESRs have two possibilities: # 1) Bump the second digit for a planned release and reset the third digit # to 0. # 2) Bump the last digit for an unexpected release # # Prior to ESR 24 we did #2 for all types of releases. if esr: # if version is like N.0esr, add an extra 0 to make it bump properly if version.count('.') < 2: version = version.replace('esr', '.0esr') first, second, _ = version.split('.', 2) if int(first) >= 24: ret.add('%s.%s.0esr' % (first, increment(second))) ret.add(increment(version)) # Betas are similar, except we need the next major version's beta 1, too. elif beta: ret.add(increment(version)) ret.add('%sb1' % nextMajorVersion) # Other releases are a bit more complicated, because we need to handle # going from a x.y -> x.y.z version number. else: ret.add(nextMajorVersion) if isFinalRelease(version): ret.add('%s.1' % version) else: ret.add(increment(version)) return ret
def bump_configs(release, cfgFile, l10nContents, workdir, hg_username, productionBranch, defaultBranch='default'): # Update the production branch first, because that's where we want to read # the templates from update(workdir, productionBranch) cfgDir = path.join(workdir, 'mozilla') templateFile = path.join(cfgDir, '%s.template' % cfgFile) tags = set(getTags(getBaseTag(release['product'], release['version']), release['buildNumber'])) cfgFile = path.join(cfgDir, cfgFile) l10nChangesetsFile = path.join( cfgDir, readReleaseConfig(cfgFile)['l10nRevisionFile'] ) subs = release.copy() if 'partials' in release: subs['partials'] = getPartials(release) # This is true 99% of the time. It's exceedingly rare that we ship a point # release that we first push to the beta channel. If we need to, the # expectation is that this will be ignored by hardcoding True in the # template. if isFinalRelease(release["version"]): subs["betaChannelEnabled"] = True else: subs["betaChannelEnabled"] = False with open(templateFile) as f: template = f.read() releaseConfig = substituteReleaseConfig(template, **subs) # Write out the new configs on the production branch... with open(cfgFile, 'w') as f: f.write(releaseConfig) with open(l10nChangesetsFile, 'w') as f: f.write(l10nContents) prodRev = commit(workdir, 'Update release config for %s' % release['name'], user=hg_username) # We always force tagging, because it makes it easier to retrigger a # release that fails for infrastructure reasons. tag(workdir, tags, rev=prodRev, force=True, user=hg_username) # And then write the same files to the default branch update(workdir, defaultBranch) with open(cfgFile, 'w') as f: f.write(releaseConfig) with open(l10nChangesetsFile, 'w') as f: f.write(l10nContents) commit(workdir, 'Update release config for %s' % release['name'], user=hg_username)
def main(options): log.info('Loading config from %s' % options.config) with open(options.config, 'r') as config_file: config = yaml.load(config_file) if config['release-runner'].get('verbose', False): log_level = logging.DEBUG else: log_level = logging.INFO logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s", level=log_level) # Suppress logging of retry(), see bug 925321 for the details logging.getLogger("util.retry").setLevel(logging.WARN) api_root = config['api']['api_root'] username = config['api']['username'] password = config['api']['password'] rr_config = config['release-runner'] sleeptime = rr_config['sleeptime'] smtp_server = rr_config.get('smtp_server', 'localhost') notify_from = rr_config.get('notify_from') notify_to = rr_config.get('notify_to_announce') if isinstance(notify_to, basestring): notify_to = [x.strip() for x in notify_to.split(',')] rr = ReleaseRunner(api_root=api_root, username=username, password=password) tc_config = { "credentials": { "clientId": config["taskcluster"].get("client_id"), "accessToken": config["taskcluster"].get("access_token"), }, "maxRetries": 12, } queue = taskcluster.Queue(tc_config) while True: try: log.debug('Fetching release requests') rr.get_release_requests([r['pattern'] for r in config['releases']]) if rr.new_releases: new_releases = run_prebuild_sanity_checks( rr, config['releases']) break else: log.debug('Sleeping for %d seconds before polling again' % sleeptime) time.sleep(sleeptime) except: log.error("Caught exception when polling:", exc_info=True) sys.exit(5) rc = 0 for release in new_releases: try: next_version = bump_version(release["version"].replace("esr", "")) project = release["branchShortName"] revision = release["mozillaRevision"] decision_task_id = find_decision_task_id(project, revision) action_task_input = { "build_number": release["buildNumber"], "next_version": next_version, "release_promotion_flavor": "promote_{}".format(release["product"]), "previous_graph_ids": [decision_task_id], } if "partial_updates" in release: action_task_input["partial_updates"] = {} for version, info in release["partial_updates"].items(): action_task_input["partial_updates"][version] = { "buildNumber": info["buildNumber"], "locales": info["locales"] } if release["product"] == "firefox": if "b" in release["version"]: action_task_input["desktop_release_type"] = "beta" elif "esr" in release["version"]: action_task_input["desktop_release_type"] = "esr" else: # RC release types will enable beta-channel testing & # shipping. We need this for all "final" releases # and also any releases that include a beta as a partial. # The assumption than "shipping to beta channel" always # implies other RC behaviour is bound to break at some # point, but this works for now. if isFinalRelease(release["version"]): action_task_input["desktop_release_type"] = "rc" else: for version in release["partial_updates"]: if "b" in version: action_task_input[ "desktop_release_type"] = "rc" break else: action_task_input[ "desktop_release_type"] = "release" elif release["product"] == "devedition": action_task_input["desktop_release_type"] = "devedition" action_task_id, action_task = generate_action_task( project=release["branchShortName"], revision=release["mozillaRevision"], action_task_input=action_task_input, ) submit_action_task(queue=queue, action_task_id=action_task_id, action_task=action_task) rr.mark_as_completed(release) l10n_url = rr.release_l10n_api.getL10nFullUrl(release['name']) email_release_drivers(smtp_server=smtp_server, from_=notify_from, to=notify_to, release=release, task_group_id=action_task_id, l10n_url=l10n_url) except Exception as exception: # We explicitly do not raise an error here because there's no # reason not to start other releases if creating the Task Graph # fails for another one. We _do_ need to set this in order to exit # with the right code, though. rc = 2 rr.mark_as_failed( release, 'Failed to start release promotion. Error(s): %s' % (exception)) log.exception('Failed to start release "%s". Error(s): %s', release['name'], exception) log.debug('Release failed: %s', release) if rc != 0: sys.exit(rc) log.debug('Sleeping for %s seconds before polling again', sleeptime) time.sleep(sleeptime)