Implement DEPS resolution logic.

BUG=chromium:500622
TEST=unittest

Change-Id: I3efac4028c301328c22c1320a1c0dc850307160d
Reviewed-on: https://chromium-review.googlesource.com/277545
Reviewed-by: Michael Moss <mmoss@chromium.org>
Trybot-Ready: Daniel Jacques <dnj@chromium.org>
Reviewed-by: Daniel Jacques <dnj@chromium.org>
Tested-by: Daniel Jacques <dnj@chromium.org>
(cherry picked from commit a8994aa0ef05a75627e955f43821e945a99ada1e)
Reviewed-on: https://chromium-review.googlesource.com/277677
Reviewed-by: Matthew Yuan <matthewyuan@chromium.org>
Tested-by: Matthew Yuan <matthewyuan@chromium.org>
diff --git a/lib/gclient.py b/lib/gclient.py
index f295309..528a0a9 100644
--- a/lib/gclient.py
+++ b/lib/gclient.py
@@ -17,6 +17,14 @@
 CHROME_COMMITTER_URL = 'https://chromium.googlesource.com/chromium/src'
 STATUS_URL = 'https://chromium-status.appspot.com/current?format=json'
 
+# Last release for each milestone where a '.DEPS.git' was emitted. After this,
+# a Git-only DEPS is emitted as 'DEPS' and '.DEPS.git' is no longer created.
+_DEPS_GIT_TRANSITION_MAP = {
+    45: (45, 0, 2430, 3),
+    44: (44, 0, 2403, 48),
+    43: (43, 0, 2357, 125),
+}
+
 
 def FindGclientFile(path):
   """Returns the nearest higher-level gclient file from the specified path.
@@ -78,6 +86,25 @@
   return solution
 
 
+def BuildspecUsesDepsGit(rev):
+  """Tests if a given buildspec revision uses .DEPS.git or DEPS.
+
+  Previous, Chromium emitted two dependency files: DEPS and .DEPS.git, the
+  latter being a Git-only construction of DEPS. Recently a switch was thrown,
+  causing .DEPS.git to be emitted exclusively as DEPS.
+
+  To support past buildspec checkouts, this logic tests a given Chromium
+  buildspec revision against the transition thresholds, using .DEPS.git prior
+  to transition and DEPS after.
+  """
+  rev = tuple(int(d) for d in rev.split('.'))
+  milestone = rev[0]
+  threshold = _DEPS_GIT_TRANSITION_MAP.get(milestone)
+  if threshold:
+    return rev <= threshold
+  return all(milestone < k for k in _DEPS_GIT_TRANSITION_MAP.iterkeys())
+
+
 def _GetGclientURLs(internal, rev):
   """Get the URLs and deps_file values to use in gclient file.
 
@@ -88,12 +115,9 @@
   if rev is None or git.IsSHA1(rev):
     # Regular chromium checkout; src may float to origin/master or be pinned.
     url = constants.CHROMIUM_GOB_URL
+
     if rev:
       url += ('@' + rev)
-    # TODO(szager): .DEPS.git will eventually be deprecated in favor of DEPS.
-    # When that happens, this could should continue to work, because gclient
-    # will fall back to DEPS if .DEPS.git doesn't exist.  Eventually, this
-    # code should be cleaned up to stop referring to non-existent .DEPS.git.
     results.append(('src', url, '.DEPS.git'))
     if internal:
       results.append(
@@ -102,7 +126,11 @@
     # Internal buildspec: check out the buildspec repo and set deps_file to
     # the path to the desired release spec.
     url = constants.INTERNAL_GOB_URL + '/chrome/tools/buildspec.git'
-    results.append(('CHROME_DEPS', url, 'releases/%s/.DEPS.git' % rev))
+
+    # Chromium switched to DEPS at version 45.0.2432.3.
+    deps_file = '.DEPS.git' if BuildspecUsesDepsGit(rev) else 'DEPS'
+
+    results.append(('CHROME_DEPS', url, 'releases/%s/%s' % (rev, deps_file)))
   else:
     # External buildspec: use the main chromium src repository, pinned to the
     # release tag, with deps_file set to .DEPS.git (which is created by
diff --git a/lib/gclient_unittest.py b/lib/gclient_unittest.py
index dab8abe..045adf6 100644
--- a/lib/gclient_unittest.py
+++ b/lib/gclient_unittest.py
@@ -95,6 +95,16 @@
   'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git'}]
 """)
 
+  def testChromeSpecWithReleaseTag(self):
+    """Test WriteConfigFile with chrome checkout at a given release tag."""
+    gclient.WriteConfigFile('gclient', self._TEST_CWD, True, '45.0.2431.1')
+    self._AssertGclientConfigSpec("""solutions = [{'custom_deps': {},
+  'custom_vars': {},
+  'deps_file': 'releases/45.0.2431.1/DEPS',
+  'name': 'CHROME_DEPS',
+  'url': 'https://chrome-internal.googlesource.com/chrome/tools/buildspec.git'}]
+""")
+
   def testChromiumSpecWithReleaseTag(self):
     """Test WriteConfigFile with chromium checkout at a given release tag."""
     gclient.WriteConfigFile('gclient', self._TEST_CWD, False, '41.0.2270.0')
@@ -105,7 +115,7 @@
   'url': 'https://chromium.googlesource.com/chromium/src.git@refs/tags/41.0.2270.0'}]
 """)
 
-  def testChromeSpecWithReleaseTag(self):
+  def testChromeSpecWithReleaseTagDepsGit(self):
     """Test WriteConfigFile with chrome checkout at a given release tag."""
     gclient.WriteConfigFile('gclient', self._TEST_CWD, True, '41.0.2270.0')
     self._AssertGclientConfigSpec("""solutions = [{'custom_deps': {},
@@ -115,6 +125,18 @@
   'url': 'https://chrome-internal.googlesource.com/chrome/tools/buildspec.git'}]
 """)
 
+  def testChromeSpecDepsResolution(self):
+    """Test BuildspecUsesDepsGit at release thresholds."""
+    for rev, uses_deps_git in (
+        ('41.0.2270.0', True),
+        ('45.0.2430.3', True),
+        ('45.0.2431.0', False),
+        ('44.0.2403.48', True),
+        ('44.0.2404.0', False),
+        ('43.0.2357.125', True),
+        ('43.0.2357.126', False)):
+      self.assertEqual(gclient.BuildspecUsesDepsGit(rev), uses_deps_git)
+
   def testChromeSpecWithGclientTemplate(self):
     """Test WriteConfigFile with chrome checkout with a gclient template."""
     template_path = self._CreateGclientTemplate("""solutions = [