class Nightly(object): name = None # abstract base class _monthlinks = {} lastdest = None tempdir = None @staticmethod def _getBuildRegex(bits): if mozinfo.os == "win": if bits == 64: # XXX this should actually throw an error to be consumed by the caller print "No builds available for 64 bit Windows (try specifying --bits=32)" sys.exit() return ".*win32.zip" elif mozinfo.os == "linux": if bits == 64: return ".*linux-x86_64.tar.bz2" else: return ".*linux-i686.tar.bz2" elif mozinfo.os == "mac": return ".*mac.*\.dmg" def __init__(self, repo_name=None, bits=mozinfo.bits, persist=None): self.buildRegex = self._getBuildRegex(bits) self.persist = persist self.repo_name = repo_name ### cleanup functions def remove_tempdir(self): if self.tempdir: rmtree(self.tempdir) self.tempdir = None def remove_lastdest(self): if self.lastdest: os.remove(self.lastdest) self.lastdest = None def cleanup(self): self.remove_tempdir() if not self.persist: self.remove_lastdest() __del__ = cleanup ### installation functions def get_destination(self, url, date): repo_name = self.repo_name or self.getRepoName(date) dest = os.path.basename(url) if self.persist is not None: date_str = date.strftime("%Y-%m-%d") dest = os.path.join(self.persist, "%s--%s--%s"%(date_str, repo_name, dest)) return dest def download(self, date=datetime.date.today(), dest=None): url = self.getBuildUrl(date) if url: if not dest: dest = self.get_destination(url, date) if not self.persist: self.remove_lastdest() self.dest = self.lastdest = dest download_url(url, dest) return True else: return False def install(self): if not self.name: raise NotImplementedError("Can't invoke abstract base class") self.remove_tempdir() self.tempdir = tempfile.mkdtemp() self.binary = mozinstall.get_binary(mozinstall.install(src=self.dest, dest=self.tempdir), self.name) return True def getBuildUrl(self, datestamp): if self.appName == 'fennec': repo = 'mobile' else: repo = 'firefox' url = "http://ftp.mozilla.org/pub/mozilla.org/" + repo + "/nightly/" year = str(datestamp.year) month = "%02d" % datestamp.month day = "%02d" % datestamp.day repo_name = self.repo_name or self.getRepoName(datestamp) url += year + "/" + month + "/" linkRegex = '^' + year + '-' + month + '-' + day + '-' + '[\d-]+' + repo_name + '/$' cachekey = year + '-' + month if cachekey in self._monthlinks: monthlinks = self._monthlinks[cachekey] else: monthlinks = urlLinks(url) self._monthlinks[cachekey] = monthlinks # first parse monthly list to get correct directory for dirlink in monthlinks: dirhref = dirlink.get("href") if re.match(linkRegex, dirhref): # now parse the page for the correct build url for link in urlLinks(url + dirhref): href = link.get("href") if re.match(self.buildRegex, href): return url + dirhref + href ### functions for invoking nightly def getAppInfo(self): parser = ConfigParser() ini_file = os.path.join(os.path.dirname(self.binary), "application.ini") parser.read(ini_file) try: changeset = parser.get('App', 'SourceStamp') repo = parser.get('App', 'SourceRepository') return (repo, changeset) except: return None def start(self, profile, addons, cmdargs): if profile: profile = self.profileClass(profile=profile, addons=addons) elif len(addons): profile = self.profileClass(addons=addons) else: profile = self.profileClass() self.runner = Runner(binary=self.binary, cmdargs=cmdargs, profile=profile) self.runner.start() return True def stop(self): self.runner.stop() def wait(self): self.runner.wait()
class MozRunnerLauncher(Launcher): tempdir = None runner = None app_name = 'undefined' binary = None def _install(self, dest): self.tempdir = tempfile.mkdtemp() try: self.binary = mozinstall.get_binary( mozinstall.install(src=dest, dest=self.tempdir), self.app_name ) except: rmtree(self.tempdir) raise def _start(self, profile=None, addons=(), cmdargs=(), preferences=None): profile = self._create_profile(profile=profile, addons=addons, preferences=preferences) LOG.info("Launching %s" % self.binary) self.runner = Runner(binary=self.binary, cmdargs=cmdargs, profile=profile) def _on_exit(): # if we are stopping the process do not log anything. if not self._stopping: # mozprocess (behind mozrunner) fire 'onFinish' # a bit early - let's ensure the process is finished. # we have to call wait() directly on the subprocess # instance of the ProcessHandler, else on windows # None is returned... # TODO: search that bug and fix that in mozprocess or # mozrunner. (likely mozproces) try: exitcode = self.runner.process_handler.proc.wait() except Exception: print LOG.error( "Error while waiting process, consider filing a bug.", exc_info=True ) return if exitcode != 0: # first print a blank line, to be sure we don't # write on an already printed line without EOL. print LOG.warning('Process exited with code %s' % exitcode) self.runner.process_args = { 'processOutputLine': [get_default_logger("process").info], 'onFinish': _on_exit, } self.runner.start() def _wait(self): return self.runner.wait() def _stop(self): self.runner.stop() # release the runner since it holds a profile reference del self.runner def cleanup(self): try: Launcher.cleanup(self) finally: # always remove tempdir if self.tempdir is not None: rmtree(self.tempdir) def get_app_info(self): return mozversion.get_version(binary=self.binary)
class MozRunnerLauncher(Launcher): tempdir = None runner = None app_name = 'undefined' binary = None def _install(self, dest): self.tempdir = safe_mkdtemp() try: self.binary = mozinstall.get_binary( mozinstall.install(src=dest, dest=self.tempdir), self.app_name) except Exception: rmtree(self.tempdir) raise def _disableUpdateByPolicy(self): updatePolicy = {'policies': {'DisableAppUpdate': True}} installdir = os.path.dirname(self.binary) if mozinfo.os == 'mac': # macOS has the following filestructure: # binary at: # PackageName.app/Contents/MacOS/firefox # we need policies.json in: # PackageName.app/Contents/Resources/distribution installdir = os.path.normpath( os.path.join(installdir, '..', 'Resources')) os.mkdir(os.path.join(installdir, 'distribution')) policyFile = os.path.join(installdir, 'distribution', 'policies.json') with open(policyFile, 'w') as fp: json.dump(updatePolicy, fp, indent=2) def _start(self, profile=None, addons=(), cmdargs=(), preferences=None, adb_profile_dir=None): profile = self._create_profile(profile=profile, addons=addons, preferences=preferences) LOG.info("Launching %s" % self.binary) self.runner = Runner(binary=self.binary, cmdargs=cmdargs, profile=profile) def _on_exit(): # if we are stopping the process do not log anything. if not self._stopping: # mozprocess (behind mozrunner) fire 'onFinish' # a bit early - let's ensure the process is finished. # we have to call wait() directly on the subprocess # instance of the ProcessHandler, else on windows # None is returned... # TODO: search that bug and fix that in mozprocess or # mozrunner. (likely mozproces) try: exitcode = self.runner.process_handler.proc.wait() except Exception: print() LOG.error( "Error while waiting process, consider filing a bug.", exc_info=True) return if exitcode != 0: # first print a blank line, to be sure we don't # write on an already printed line without EOL. print() LOG.warning('Process exited with code %s' % exitcode) self.runner.process_args = { 'processOutputLine': [get_default_logger("process").info], 'onFinish': _on_exit, } self.runner.start() def _wait(self): return self.runner.wait() def _stop(self): if mozinfo.os == "win" and self.app_name == 'firefox': # for some reason, stopping the runner may hang on windows. For # example restart the browser in safe mode, it will hang for a # couple of minutes. As a workaround, we call that in a thread and # wait a bit for the completion. If the stop() can't complete we # forgot about that thread. thread = Thread(target=self.runner.stop) thread.daemon = True thread.start() thread.join(0.7) else: self.runner.stop() # release the runner since it holds a profile reference del self.runner def cleanup(self): try: Launcher.cleanup(self) finally: # always remove tempdir if self.tempdir is not None: rmtree(self.tempdir) def get_app_info(self): return safe_get_version(binary=self.binary)
class MozRunnerLauncher(Launcher): tempdir = None runner = None app_name = 'undefined' binary = None def _install(self, dest): self.tempdir = tempfile.mkdtemp() try: self.binary = mozinstall.get_binary( mozinstall.install(src=dest, dest=self.tempdir), self.app_name ) except Exception: rmtree(self.tempdir) raise def _start(self, profile=None, addons=(), cmdargs=(), preferences=None): profile = self._create_profile(profile=profile, addons=addons, preferences=preferences) LOG.info("Launching %s" % self.binary) self.runner = Runner(binary=self.binary, cmdargs=cmdargs, profile=profile) def _on_exit(): # if we are stopping the process do not log anything. if not self._stopping: # mozprocess (behind mozrunner) fire 'onFinish' # a bit early - let's ensure the process is finished. # we have to call wait() directly on the subprocess # instance of the ProcessHandler, else on windows # None is returned... # TODO: search that bug and fix that in mozprocess or # mozrunner. (likely mozproces) try: exitcode = self.runner.process_handler.proc.wait() except Exception: print LOG.error( "Error while waiting process, consider filing a bug.", exc_info=True ) return if exitcode != 0: # first print a blank line, to be sure we don't # write on an already printed line without EOL. print LOG.warning('Process exited with code %s' % exitcode) self.runner.process_args = { 'processOutputLine': [get_default_logger("process").info], 'onFinish': _on_exit, } self.runner.start() def _wait(self): return self.runner.wait() def _stop(self): if mozinfo.os == "win" and self.app_name == 'firefox': # for some reason, stopping the runner may hang on windows. For # example restart the browser in safe mode, it will hang for a # couple of minutes. As a workaround, we call that in a thread and # wait a bit for the completion. If the stop() can't complete we # forgot about that thread. thread = Thread(target=self.runner.stop) thread.daemon = True thread.start() thread.join(0.7) else: self.runner.stop() # release the runner since it holds a profile reference del self.runner def cleanup(self): try: Launcher.cleanup(self) finally: # always remove tempdir if self.tempdir is not None: rmtree(self.tempdir) def get_app_info(self): return safe_get_version(binary=self.binary)
class Nightly(object): def __init__(self, repo_name=None): platform=get_platform() if platform['name'] == "Windows": if platform['bits'] == '64': print "No nightly builds available for 64 bit Windows" sys.exit() self.buildRegex = ".*win32.zip" self.processName = self.name + ".exe" self.binary = "moznightlyapp/" + self.name + "/" + self.name + ".exe" elif platform['name'] == "Linux": self.processName = self.name + "-bin" self.binary = "moznightlyapp/" + self.name + "/" + self.name if platform['bits'] == '64': self.buildRegex = ".*linux-x86_64.tar.bz2" else: self.buildRegex = ".*linux-i686.tar.bz2" elif platform['name'] == "Mac": self.buildRegex = ".*mac.*\.dmg" self.processName = self.name + "-bin" self.binary = "moznightlyapp/Mozilla.app/Contents/MacOS/" + self.name + "-bin" self.repo_name = repo_name self._monthlinks = {} self.lastdest = None def cleanup(self): rmtree('moznightlyapp') if self.lastdest: os.remove(self.lastdest) __del__ = cleanup def download(self, date=datetime.date.today(), dest=None): url = self.getBuildUrl(date) if url: if not dest: dest = os.path.basename(url) print "Downloading nightly from %s" % date if self.lastdest: os.remove(self.lastdest) download_url(url, dest) self.dest = self.lastdest = dest return True else: return False def install(self): rmtree("moznightlyapp") subprocess._cleanup = lambda : None # mikeal's fix for subprocess threading bug MozInstaller(src=self.dest, dest="moznightlyapp", dest_app="Mozilla.app") return True @staticmethod def urlLinks(url): res = [] # do not return a generator but an array, so we can store it for later use h = httplib2.Http(); resp, content = h.request(url, "GET") if resp.status != 200: return res soup = BeautifulSoup(content) for link in soup.findAll('a'): res.append(link) return res def getBuildUrl(self, date): url = "http://ftp.mozilla.org/pub/mozilla.org/" + self.appName + "/nightly/" year = str(date.year) month = "%02d" % date.month day = "%02d" % date.day repo_name = self.repo_name or self.getRepoName(date) url += year + "/" + month + "/" linkRegex = '^' + year + '-' + month + '-' + day + '-' + '[\d-]+' + repo_name + '/$' cachekey = year + '-' + month if cachekey in self._monthlinks: monthlinks = self._monthlinks[cachekey] else: monthlinks = self.urlLinks(url) self._monthlinks[cachekey] = monthlinks # first parse monthly list to get correct directory for dirlink in monthlinks: dirhref = dirlink.get("href") if re.match(linkRegex, dirhref): # now parse the page for the correct build url for link in self.urlLinks(url + dirhref): href = link.get("href") if re.match(self.buildRegex, href): return url + dirhref + href return False def getAppInfo(self): parser = ConfigParser() ini_file = os.path.join(os.path.dirname(self.binary), "application.ini") parser.read(ini_file) try: changeset = parser.get('App', 'SourceStamp') repo = parser.get('App', 'SourceRepository') return (repo, changeset) except: return ("", "") def start(self, profile, addons, cmdargs): if profile: profile = self.profileClass(profile=profile, addons=addons) elif len(addons): profile = self.profileClass(addons=addons) else: profile = self.profileClass() self.runner = Runner(binary=self.binary, cmdargs=cmdargs, profile=profile) self.runner.names = [self.processName] self.runner.start() return True def stop(self): self.runner.stop() def wait(self): self.runner.wait()
class Nightly(object): name = None # abstract base class def __init__(self, repo_name=None, bits=mozinfo.bits, persist=None): if mozinfo.os == "win": if bits == 64: # XXX this should actually throw an error to be consumed by the caller print "No nightly builds available for 64 bit Windows" sys.exit() self.buildRegex = ".*win32.zip" elif mozinfo.os == "linux": if bits == 64: self.buildRegex = ".*linux-x86_64.tar.bz2" else: self.buildRegex = ".*linux-i686.tar.bz2" elif mozinfo.os == "mac": self.buildRegex = ".*mac.*\.dmg" self.persist = persist self.repo_name = repo_name self._monthlinks = {} self.lastdest = None self.tempdir = None ### cleanup functions def remove_tempdir(self): if self.tempdir: rmtree(self.tempdir) self.tempdir = None def remove_lastdest(self): if self.lastdest: os.remove(self.lastdest) self.lastdest = None def cleanup(self): self.remove_tempdir() if not self.persist: self.remove_lastdest() __del__ = cleanup ### installation functions def get_destination(self, url, date): repo_name = self.repo_name or self.getRepoName(date) dest = os.path.basename(url) if self.persist is not None: date_str = date.strftime("%Y-%m-%d") dest = os.path.join(self.persist, "%s--%s--%s"%(date_str, repo_name, dest)) return dest def download(self, date=datetime.date.today(), dest=None): url = self.getBuildUrl(date) if url: if not dest: dest = self.get_destination(url, date) if not self.persist: self.remove_lastdest() self.dest = self.lastdest = dest download_url(url, dest) return True else: return False def install(self): if not self.name: raise NotImplementedError("Can't invoke abstract base class") self.remove_tempdir() self.tempdir = tempfile.mkdtemp() self.binary = mozinstall.get_binary(mozinstall.install(src=self.dest, dest=self.tempdir), self.name) return True def getBuildUrl(self, date): url = "http://ftp.mozilla.org/pub/mozilla.org/" + self.appName + "/nightly/" year = str(date.year) month = "%02d" % date.month day = "%02d" % date.day repo_name = self.repo_name or self.getRepoName(date) url += year + "/" + month + "/" linkRegex = '^' + year + '-' + month + '-' + day + '-' + '[\d-]+' + repo_name + '/$' cachekey = year + '-' + month if cachekey in self._monthlinks: monthlinks = self._monthlinks[cachekey] else: monthlinks = urlLinks(url) self._monthlinks[cachekey] = monthlinks # first parse monthly list to get correct directory for dirlink in monthlinks: dirhref = dirlink.get("href") if re.match(linkRegex, dirhref): # now parse the page for the correct build url for link in urlLinks(url + dirhref): href = link.get("href") if re.match(self.buildRegex, href): return url + dirhref + href return False ### functions for invoking nightly def getAppInfo(self): parser = ConfigParser() ini_file = os.path.join(os.path.dirname(self.binary), "application.ini") parser.read(ini_file) try: changeset = parser.get('App', 'SourceStamp') repo = parser.get('App', 'SourceRepository') return (repo, changeset) except: return ("", "") def start(self, profile, addons, cmdargs): if profile: profile = self.profileClass(profile=profile, addons=addons) elif len(addons): profile = self.profileClass(addons=addons) else: profile = self.profileClass() self.runner = Runner(binary=self.binary, cmdargs=cmdargs, profile=profile) self.runner.start() return True def stop(self): self.runner.stop() def wait(self): self.runner.wait()
class Nightly(object): def __init__(self, repo_name=None): platform = get_platform() if platform['name'] == "Windows": if platform['bits'] == '64': print "No nightly builds available for 64 bit Windows" sys.exit() self.buildRegex = ".*win32.zip" self.processName = self.name + ".exe" self.binary = "moznightlyapp/" + self.name + "/" + self.name + ".exe" elif platform['name'] == "Linux": self.processName = self.name + "-bin" self.binary = "moznightlyapp/" + self.name + "/" + self.name if platform['bits'] == '64': self.buildRegex = ".*linux-x86_64.tar.bz2" else: self.buildRegex = ".*linux-i686.tar.bz2" elif platform['name'] == "Mac": self.buildRegex = ".*mac.*\.dmg" self.processName = self.name + "-bin" self.binary = "moznightlyapp/Mozilla.app/Contents/MacOS/" + self.name + "-bin" self.repo_name = repo_name self._monthlinks = {} self.lastdest = None def cleanup(self): rmtree('moznightlyapp') if self.lastdest: os.remove(self.lastdest) __del__ = cleanup def download(self, date=datetime.date.today(), dest=None): url = self.getBuildUrl(date) if url: if not dest: dest = os.path.basename(url) print "Downloading nightly from %s" % date if self.lastdest: os.remove(self.lastdest) download_url(url, dest) self.dest = self.lastdest = dest return True else: return False def install(self): rmtree("moznightlyapp") subprocess._cleanup = lambda: None # mikeal's fix for subprocess threading bug MozInstaller(src=self.dest, dest="moznightlyapp", dest_app="Mozilla.app") return True @staticmethod def urlLinks(url): res = [ ] # do not return a generator but an array, so we can store it for later use h = httplib2.Http() resp, content = h.request(url, "GET") if resp.status != 200: return res soup = BeautifulSoup(content) for link in soup.findAll('a'): res.append(link) return res def getBuildUrl(self, date): url = "http://ftp.mozilla.org/pub/mozilla.org/" + self.appName + "/nightly/" year = str(date.year) month = "%02d" % date.month day = "%02d" % date.day repo_name = self.repo_name or self.getRepoName(date) url += year + "/" + month + "/" linkRegex = '^' + year + '-' + month + '-' + day + '-' + '[\d-]+' + repo_name + '/$' cachekey = year + '-' + month if cachekey in self._monthlinks: monthlinks = self._monthlinks[cachekey] else: monthlinks = self.urlLinks(url) self._monthlinks[cachekey] = monthlinks # first parse monthly list to get correct directory for dirlink in monthlinks: dirhref = dirlink.get("href") if re.match(linkRegex, dirhref): # now parse the page for the correct build url for link in self.urlLinks(url + dirhref): href = link.get("href") if re.match(self.buildRegex, href): return url + dirhref + href return False def getAppInfo(self): parser = ConfigParser() ini_file = os.path.join(os.path.dirname(self.binary), "application.ini") parser.read(ini_file) try: changeset = parser.get('App', 'SourceStamp') repo = parser.get('App', 'SourceRepository') return (repo, changeset) except: return ("", "") def start(self, profile, addons, cmdargs): if profile: profile = self.profileClass(profile=profile, addons=addons) elif len(addons): profile = self.profileClass(addons=addons) else: profile = self.profileClass() self.runner = Runner(binary=self.binary, cmdargs=cmdargs, profile=profile) self.runner.names = [self.processName] self.runner.start() return True def stop(self): self.runner.stop() def wait(self): self.runner.wait()
class MozRunnerLauncher(Launcher): tempdir = None runner = None app_name = 'undefined' binary = None def _install(self, dest): self.tempdir = safe_mkdtemp() try: self.binary = mozinstall.get_binary( mozinstall.install(src=dest, dest=self.tempdir), self.app_name ) except Exception: rmtree(self.tempdir) raise def _disableUpdateByPolicy(self): updatePolicy = { 'policies': { 'DisableAppUpdate': True } } installdir = os.path.dirname(self.binary) if mozinfo.os == 'mac': # macOS has the following filestructure: # binary at: # PackageName.app/Contents/MacOS/firefox # we need policies.json in: # PackageName.app/Contents/Resources/distribution installdir = os.path.normpath( os.path.join(installdir, '..', 'Resources') ) os.mkdir(os.path.join(installdir, 'distribution')) policyFile = os.path.join( installdir, 'distribution', 'policies.json' ) with open(policyFile, 'w') as fp: json.dump(updatePolicy, fp, indent=2) def _start(self, profile=None, addons=(), cmdargs=(), preferences=None, adb_profile_dir=None): profile = self._create_profile(profile=profile, addons=addons, preferences=preferences) LOG.info("Launching %s" % self.binary) self.runner = Runner(binary=self.binary, cmdargs=cmdargs, profile=profile) def _on_exit(): # if we are stopping the process do not log anything. if not self._stopping: # mozprocess (behind mozrunner) fire 'onFinish' # a bit early - let's ensure the process is finished. # we have to call wait() directly on the subprocess # instance of the ProcessHandler, else on windows # None is returned... # TODO: search that bug and fix that in mozprocess or # mozrunner. (likely mozproces) try: exitcode = self.runner.process_handler.proc.wait() except Exception: print LOG.error( "Error while waiting process, consider filing a bug.", exc_info=True ) return if exitcode != 0: # first print a blank line, to be sure we don't # write on an already printed line without EOL. print LOG.warning('Process exited with code %s' % exitcode) self.runner.process_args = { 'processOutputLine': [get_default_logger("process").info], 'onFinish': _on_exit, } self.runner.start() def _wait(self): return self.runner.wait() def _stop(self): if mozinfo.os == "win" and self.app_name == 'firefox': # for some reason, stopping the runner may hang on windows. For # example restart the browser in safe mode, it will hang for a # couple of minutes. As a workaround, we call that in a thread and # wait a bit for the completion. If the stop() can't complete we # forgot about that thread. thread = Thread(target=self.runner.stop) thread.daemon = True thread.start() thread.join(0.7) else: self.runner.stop() # release the runner since it holds a profile reference del self.runner def cleanup(self): try: Launcher.cleanup(self) finally: # always remove tempdir if self.tempdir is not None: rmtree(self.tempdir) def get_app_info(self): return safe_get_version(binary=self.binary)
class Nightly(object): name = None # abstract base class _monthlinks = {} lastdest = None tempdir = None app_name = None profile_class = Profile build_base_repo_name = "firefox" @staticmethod def _get_os_regex_suffix(bits, with_ext=True): if mozinfo.os == "win": if bits == 64: suffix, ext = ".*win64-x86_64", ".zip" else: suffix, ext = ".*win32", ".zip" elif mozinfo.os == "linux": if bits == 64: suffix, ext = ".*linux-x86_64", ".tar.bz2" else: suffix, ext = ".*linux-i686", ".tar.bz2" elif mozinfo.os == "mac": suffix, ext = r".*mac.*", "\.dmg" if with_ext: return '%s%s' % (suffix, ext) else: return suffix @staticmethod def _get_build_regex(name, bits, with_ext=True): name_prefix = name if ".*%s" % name is not None else '' suffix = Nightly._get_os_regex_suffix(bits, with_ext) return "%s%s" % (name_prefix, suffix) def __init__(self, inbound_branch=None, bits=mozinfo.bits, persist=None): self.inbound_branch = inbound_branch self.bits = bits self.persist = persist self.build_regex = self._get_build_regex(self.name, bits) + "$" self.build_info_regex = \ self._get_build_regex(self.name, bits, with_ext=False) + "\.txt$" def get_inbound_branch(self, date): raise NotImplementedError # cleanup functions def remove_tempdir(self): if self.tempdir: rmtree(self.tempdir) self.tempdir = None def remove_lastdest(self): if self.lastdest: os.remove(self.lastdest) self.lastdest = None def cleanup(self): self.remove_tempdir() if not self.persist: self.remove_lastdest() __del__ = cleanup # installation functions def get_destination(self, url, date): inbound_branch = self.get_inbound_branch(date) dest = os.path.basename(url) if self.persist is not None: if hasattr(date, "strftime"): date_str = date.strftime("%Y-%m-%d") else: date_str = date # Might be just a number with inbound dest = os.path.join(self.persist, "%s--%s--%s" % (date_str, inbound_branch, dest)) return dest def download(self, date=datetime.date.today(), dest=None): url = self.get_build_url(date) if url: if not dest: dest = self.get_destination(url, date) if not self.persist: self.remove_lastdest() if os.path.exists(dest): print "Using local file: %s" % dest else: download_url(url, dest) self.dest = self.lastdest = dest return True else: return False def install(self): if not self.name: raise NotImplementedError("Can't invoke abstract base class") self.remove_tempdir() self.tempdir = tempfile.mkdtemp() self.binary = mozinstall.get_binary( mozinstall.install(src=self.dest, dest=self.tempdir), self.name) return True def get_build_info(self, date): url = self._get_build_url(date, self.build_info_regex, 'builds info') if url is not None: print "Getting %s" % url response = requests.get(url) if response.status_code == 200: for line in response.text.splitlines(): if '/rev/' in line: # returns [repository, changeset] return line.split('/rev/') def get_build_url(self, datestamp): return self._get_build_url(datestamp, self.build_regex, 'builds') def _get_build_url(self, datestamp, regex, what): url = "http://ftp.mozilla.org/pub/mozilla.org/" + \ self.build_base_repo_name + "/nightly/" year = str(datestamp.year) month = "%02d" % datestamp.month day = "%02d" % datestamp.day inbound_branch = self.get_inbound_branch(datestamp) url += year + "/" + month + "/" link_regex = '^' + year + '-' + month + '-' + day + '-' \ + r'[\d-]+' + inbound_branch + '/$' cachekey = year + '-' + month if cachekey in self._monthlinks: monthlinks = self._monthlinks[cachekey] else: monthlinks = url_links(url) self._monthlinks[cachekey] = monthlinks # first parse monthly list to get correct directory matches = [] for dirlink in monthlinks: if re.match(link_regex, dirlink): # now parse the page for the correct build url for link in url_links(url + dirlink, regex=regex): matches.append(url + dirlink + link) if not matches: print "Tried to get %s from %s that match '%s' but didn't find any." % \ (what, url, self.build_regex) return None else: return sorted(matches)[-1] # the most recent build url # functions for invoking nightly def get_app_info(self): return mozversion.get_version(binary=self.binary) def start(self, profile, addons, cmdargs): if profile: profile = self.profile_class(profile=profile, addons=addons) elif len(addons): profile = self.profile_class(addons=addons) else: profile = self.profile_class() self.runner = Runner(binary=self.binary, cmdargs=cmdargs, profile=profile) self.runner.start() return True def stop(self): self.runner.stop() def wait(self): self.runner.wait()
class Nightly(object): name = None # abstract base class _monthlinks = {} lastdest = None tempdir = None app_name = None profile_class = Profile build_base_repo_name = "firefox" @staticmethod def _get_os_regex_suffix(bits, with_ext=True): if mozinfo.os == "win": if bits == 64: raise errors.Win64NoAvailableBuildError() suffix, ext = ".*win32", ".zip" elif mozinfo.os == "linux": if bits == 64: suffix, ext = ".*linux-x86_64", ".tar.bz2" else: suffix, ext = ".*linux-i686", ".tar.bz2" elif mozinfo.os == "mac": suffix, ext = r".*mac.*", "\.dmg" if with_ext: return '%s%s' % (suffix, ext) else: return suffix @staticmethod def _get_build_regex(name, bits, with_ext=True): name_prefix = name if ".*%s" % name is not None else '' suffix = Nightly._get_os_regex_suffix(bits, with_ext) return "%s%s" % (name_prefix, suffix) def __init__(self, inbound_branch=None, bits=mozinfo.bits, persist=None): self.inbound_branch = inbound_branch self.bits = bits self.persist = persist self.build_regex = self._get_build_regex(self.name, bits) + "$" self.build_info_regex = \ self._get_build_regex(self.name, bits, with_ext=False) + "\.txt$" def get_inbound_branch(self, date): raise NotImplementedError # cleanup functions def remove_tempdir(self): if self.tempdir: rmtree(self.tempdir) self.tempdir = None def remove_lastdest(self): if self.lastdest: os.remove(self.lastdest) self.lastdest = None def cleanup(self): self.remove_tempdir() if not self.persist: self.remove_lastdest() __del__ = cleanup # installation functions def get_destination(self, url, date): inbound_branch = self.get_inbound_branch(date) dest = os.path.basename(url) if self.persist is not None: if hasattr(date, "strftime"): date_str = date.strftime("%Y-%m-%d") else: date_str = date # Might be just a number with inbound dest = os.path.join( self.persist, "%s--%s--%s" % (date_str, inbound_branch, dest)) return dest def download(self, date=datetime.date.today(), dest=None): url = self.get_build_url(date) if url: if not dest: dest = self.get_destination(url, date) if not self.persist: self.remove_lastdest() download_url(url, dest) self.dest = self.lastdest = dest return True else: return False def install(self): if not self.name: raise NotImplementedError("Can't invoke abstract base class") self.remove_tempdir() self.tempdir = tempfile.mkdtemp() self.binary = mozinstall.get_binary( mozinstall.install(src=self.dest, dest=self.tempdir), self.name) return True def get_build_info(self, date): url = self._get_build_url(date, self.build_info_regex, 'builds info') if url is not None: print "Getting %s" % url response = requests.get(url) if response.status_code == 200: for line in response.text.splitlines(): if '/rev/' in line: # returns [repository, changeset] return line.split('/rev/') def get_build_url(self, datestamp): return self._get_build_url(datestamp, self.build_regex, 'builds') def _get_build_url(self, datestamp, regex, what): url = "http://ftp.mozilla.org/pub/mozilla.org/" + \ self.build_base_repo_name + "/nightly/" year = str(datestamp.year) month = "%02d" % datestamp.month day = "%02d" % datestamp.day inbound_branch = self.get_inbound_branch(datestamp) url += year + "/" + month + "/" link_regex = '^' + year + '-' + month + '-' + day + '-' \ + r'[\d-]+' + inbound_branch + '/$' cachekey = year + '-' + month if cachekey in self._monthlinks: monthlinks = self._monthlinks[cachekey] else: monthlinks = url_links(url) self._monthlinks[cachekey] = monthlinks # first parse monthly list to get correct directory matches = [] for dirlink in monthlinks: if re.match(link_regex, dirlink): # now parse the page for the correct build url for link in url_links(url + dirlink, regex=regex): matches.append(url + dirlink + link) if not matches: print "Tried to get %s from %s that match '%s' but didn't find any." % \ (what, url, self.build_regex) return None else: return sorted(matches)[-1] # the most recent build url # functions for invoking nightly def get_app_info(self): return mozversion.get_version(binary=self.binary) def start(self, profile, addons, cmdargs): if profile: profile = self.profile_class(profile=profile, addons=addons) elif len(addons): profile = self.profile_class(addons=addons) else: profile = self.profile_class() self.runner = Runner(binary=self.binary, cmdargs=cmdargs, profile=profile) self.runner.start() return True def stop(self): self.runner.stop() def wait(self): self.runner.wait()