Make Firefox nightly download more robust.

The script will now only start complaining if we haven't been able to
download anything for days. The script will always touch the file if
the download is successful, so the modified time of the latest
downloaded time is the benchmark of how fresh our Firefox build is.
There can be many days between FF nightly release, sometimes weeks,
so we only care if we managed to download something and not which date
the build is from.

BUG=360516
R=kjellander@chromium.org

Review URL: https://codereview.chromium.org/883273002

git-svn-id: http://src.chromium.org/svn/trunk/deps/third_party/webrtc/webrtc.DEPS@293895 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
diff --git a/download_firefox_nightly.py b/download_firefox_nightly.py
index 9a1f983..bb3d96b 100755
--- a/download_firefox_nightly.py
+++ b/download_firefox_nightly.py
@@ -5,11 +5,14 @@
 
 """Downloads a Firefox Nightly build for the current platform."""
 
+import datetime
+import glob
 import os
 import shutil
 import sys
 import subprocess
 import tarfile
+import time
 import zipfile
 
 from optparse import OptionParser
@@ -23,6 +26,87 @@
 from mozdownload import scraper
 
 
+def _Touch(a_file):
+  with open(a_file, 'a'):
+    os.utime(a_file, None)
+
+
+def _FindFallbackFirefoxBuild(target_dir):
+  firefox_archives = glob.glob(os.path.join(target_dir, '*tar.bz2'))
+  if not firefox_archives:
+    return None
+
+  firefox_archives.sort(key=os.path.getmtime, reverse=True)
+  newest_build = firefox_archives[0]
+  build_age_seconds = time.time() - os.path.getmtime(newest_build)
+  build_age_days = datetime.timedelta(seconds=build_age_seconds).days
+
+  return newest_build, build_age_days
+
+
+def _MaybeDownload(target_dir, force):
+  try:
+    downloader = scraper.DailyScraper(directory=target_dir, version=None)
+    filename = downloader.build_filename(downloader.binary)
+    firefox_archive = os.path.join(target_dir, filename)
+
+    if os.path.exists(firefox_archive) and not force:
+      # Touch the file anyway since we were 'successful', so we can accurately
+      # compute the age of the most recent download attempt and act accordingly
+      # when a download fails later.
+      _Touch(firefox_archive)
+      print 'Skipping download as %s is already downloaded.' % firefox_archive
+      return None
+
+    downloader.download()
+    print 'Downloaded %s' % firefox_archive
+    return firefox_archive
+  except scraper.NotFoundException as exception:
+    print 'Failed to download firefox: %s.' % exception
+    fallback_build, age_days = _FindFallbackFirefoxBuild(target_dir)
+
+    if not fallback_build:
+      raise Exception('We failed to download Firefox and we have no builds to '
+                      'fall back on; failing...')
+    if age_days > 3:
+      raise Exception('We have failed to download firefox builds for more '
+                      'than 3 days now: failing so someone looks at it. The '
+                      'most recent build we have is %d days old.' % age_days)
+
+    print 'Using %s instead; it is %d days old.' % (fallback_build, age_days)
+    return fallback_build
+
+
+def _ExtractArchive(firefox_archive, target_dir):
+  # TODO(phoglund): implement on win/mac. See http://crbug.com/454303.
+  if sys.platform == 'darwin':
+    print "Disabled on mac..."
+    return 0
+    volume = '/Volumes/Nightly'
+    firefox_executable = '%s/FirefoxNightly.app' % target_dir
+
+    # Unmount any previous downloads.
+    subprocess.call(['hdiutil', 'detach', volume])
+
+    subprocess.check_call(['hdiutil', 'attach', firefox_archive])
+    shutil.copytree('%s/FirefoxNightly.app' % volume, firefox_executable)
+    subprocess.check_call(['hdiutil', 'detach', volume])
+  elif sys.platform == 'linux2':
+    tar_archive = tarfile.open(firefox_archive, 'r:bz2')
+    tar_archive.extractall(path=target_dir)
+  elif sys.platform == 'win32':
+    print "Disabled on win..."
+    return 0
+    zip_archive = zipfile.ZipFile(firefox_archive)
+    zip_archive.extractall(path=target_dir)
+  else:
+    print >> sys.stderr, 'Unsupported platform: %s' % sys.platform
+    return 1
+
+  print 'Extracted %s' % firefox_archive
+  return 0
+
+
 def main():
   usage = 'usage: %prog -t <target_dir>'
   parser = OptionParser(usage)
@@ -40,45 +124,9 @@
   if not os.path.isdir(target_dir):
     os.mkdir(target_dir)
 
-  downloader = scraper.DailyScraper(directory=target_dir, version=None)
-  firefox_archive = os.path.join(target_dir,
-                                 downloader.build_filename(downloader.binary))
-
-  if os.path.exists(firefox_archive) and not options.force:
-    print 'Skipping download as %s is already downloaded.' % firefox_archive
-    return 0
-
-  downloader.download()
-  print 'Downloaded %s' % firefox_archive
-
-  # Extract the archive.
-  # TODO(phoglund): implement on win/mac
-  if sys.platform == 'darwin':
-    print "Temporarily disabled on mac..."
-    return 0
-    volume = '/Volumes/Nightly'
-    firefox_executable = '%s/FirefoxNightly.app' % target_dir
-
-    # Unmount any previous downloads.
-    subprocess.call(['hdiutil', 'detach', volume])
-
-    subprocess.check_call(['hdiutil', 'attach', firefox_archive])
-    shutil.copytree('%s/FirefoxNightly.app' % volume, firefox_executable)
-    subprocess.check_call(['hdiutil', 'detach', volume])
-  elif sys.platform == 'linux2':
-    tar_archive = tarfile.open(firefox_archive, 'r:bz2')
-    tar_archive.extractall(path=target_dir)
-  elif sys.platform == 'win32':
-    print "Temporarily disabled on win..."
-    return 0
-    zip_archive = zipfile.ZipFile(firefox_archive)
-    zip_archive.extractall(path=target_dir)
-  else:
-    print >> sys.stderr, 'Unsupported platform: %s' % sys.platform
-    return 1
-  
-  print 'Extracted %s' % firefox_archive
-  return 0
+  firefox_archive = _MaybeDownload(target_dir, options.force)
+  if firefox_archive:
+    return _ExtractArchive(firefox_archive, target_dir)
 
 if __name__ == '__main__':
   sys.exit(main())