Bug 1246992 - Only accept artifacts from a specific revision when one is provided to mach artifact. r=nalexander
authorChris Manchester <cmanchester@mozilla.com>
Mon, 15 Feb 2016 12:08:40 -0800
changeset 331331 ff750b0019403ef7d5a901707fe533fe3ee5d92a
parent 331330 8e7ae3dc738782436cf0886f0d84768843da849c
child 331332 73eef28fd4e74dd528c20d86a1ccc7b01ab118e1
child 331391 840dacbede7ab96c8ccf3a303d4cd6d7a36a8704
push id10956
push userjolesen@mozilla.com
push dateTue, 16 Feb 2016 19:12:12 +0000
reviewersnalexander
bugs1246992
milestone47.0a1
Bug 1246992 - Only accept artifacts from a specific revision when one is provided to mach artifact. r=nalexander MozReview-Commit-ID: FHa6awAFZj8
python/mozbuild/mozbuild/artifacts.py
--- a/python/mozbuild/mozbuild/artifacts.py
+++ b/python/mozbuild/mozbuild/artifacts.py
@@ -684,27 +684,27 @@ class Artifacts(object):
         if buildconfig.defines.get('XP_WIN', False):
             return 'win64' if target_64bit else 'win32'
         if buildconfig.defines.get('XP_MACOSX', False):
             # We only produce unified builds in automation, so the target_cpu
             # check is not relevant.
             return 'macosx64'
         raise Exception('Cannot determine default job for |mach artifact|!')
 
-    def _find_pushheads(self, parent):
+    def _find_pushheads(self):
         # Return an ordered dict associating revisions that are pushheads with
         # trees they are known to be in (starting with the first tree they're
         # known to be in).
 
         try:
             output = subprocess.check_output([
                 self._hg, 'log',
                 '--template', '{node},{join(trees, ",")}\n',
-                '-r', 'last(pushhead({tree}) and ::{parent}, {num})'.format(
-                    tree=self._tree or '', parent=parent, num=NUM_PUSHHEADS_TO_QUERY_PER_PARENT)
+                '-r', 'last(pushhead({tree}) and ::., {num})'.format(
+                    tree=self._tree or '', num=NUM_PUSHHEADS_TO_QUERY_PER_PARENT)
             ])
         except subprocess.CalledProcessError:
             # We probably don't have the mozext extension installed.
             ret = subprocess.call([self._hg, 'showconfig', 'extensions.mozext'])
             if ret:
                 raise Exception('Could not find pushheads for recent revisions.\n\n'
                                 'You need to enable the "mozext" hg extension: '
                                 'see https://developer.mozilla.org/en-US/docs/Artifact_builds')
@@ -728,16 +728,21 @@ class Artifacts(object):
                                 num=NUM_PUSHHEADS_TO_QUERY_PER_PARENT))
 
         return rev_trees
 
     def find_pushhead_artifacts(self, task_cache, tree_cache, job, pushhead, trees):
         known_trees = set(tree_cache.artifact_trees(pushhead, trees))
         if not known_trees:
             return None
+        if not trees:
+            # Accept artifacts from any tree where they are available.
+            trees = list(known_trees)
+            trees.sort()
+
         # If we ever find a rev that's a pushhead on multiple trees, we want
         # the most recent one.
         for tree in reversed(trees):
             tree = self._tree_replacements.get(tree) or tree
             if tree not in known_trees:
                 continue
             try:
                 urls = task_cache.artifact_urls(tree, job, pushhead)
@@ -796,49 +801,69 @@ class Artifacts(object):
     def install_from_url(self, url, distdir):
         self.log(logging.INFO, 'artifact',
             {'url': url},
             'Installing from {url}')
         with self._artifact_cache as artifact_cache:  # The with block handles persistence.
             filename = artifact_cache.fetch(url)
         return self.install_from_file(filename, distdir)
 
-    def install_from_hg(self, revset, distdir):
-        if not revset:
-            revset = '.'
-        rev_pushheads = self._find_pushheads(revset)
+    def _install_from_pushheads(self, rev_pushheads, distdir):
         urls = None
         # with blocks handle handle persistence.
         with self._task_cache as task_cache, self._tree_cache as tree_cache:
-            while rev_pushheads:
-                rev, trees = rev_pushheads.popitem(last=False)
+            for rev, trees in rev_pushheads.items():
                 self.log(logging.DEBUG, 'artifact',
-                    {'rev': rev},
-                    'Trying to find artifacts for pushhead {rev}.')
+                         {'rev': rev},
+                         'Trying to find artifacts for pushhead {rev}.')
                 urls = self.find_pushhead_artifacts(task_cache, tree_cache,
                                                     self._job, rev, trees)
                 if urls:
                     for url in urls:
                         if self.install_from_url(url, distdir):
                             return 1
                     return 0
         self.log(logging.ERROR, 'artifact',
-                 {'revset': revset},
-                 'No built artifacts for {revset} found.')
+                 {'count': len(rev_pushheads)},
+                 'Tried {count} pushheads, no built artifacts found.')
         return 1
 
+    def install_from_recent(self, distdir):
+        rev_pushheads = self._find_pushheads()
+        return self._install_from_pushheads(rev_pushheads, distdir)
+
+    def install_from_revset(self, revset, distdir):
+        revision = subprocess.check_output([self._hg, 'log', '--template', '{node}\n',
+                                            '-r', revset]).strip()
+        if len(revision.split('\n')) != 1:
+            raise ValueError('hg revision specification must resolve to exactly one commit')
+        rev_pushheads = {revision: None}
+        self.log(logging.INFO, 'artifact',
+                 {'revset': revset,
+                  'revision': revision},
+                 'Will only accept artifacts from a pushhead at {revision} '
+                 '(matched revset "{revset}").')
+        return self._install_from_pushheads(rev_pushheads, distdir)
+
     def install_from(self, source, distdir):
         """Install artifacts from a ``source`` into the given ``distdir``.
         """
         if source and os.path.isfile(source):
             return self.install_from_file(source, distdir)
         elif source and urlparse.urlparse(source).scheme:
             return self.install_from_url(source, distdir)
         else:
-            return self.install_from_hg(source, distdir)
+            if source is None and 'MOZ_ARTIFACT_REVISION' in os.environ:
+                source = os.environ['MOZ_ARTIFACT_REVISION']
+
+            if source:
+                return self.install_from_revset(source, distdir)
+
+            return self.install_from_recent(distdir)
+
 
     def print_last(self):
         self.log(logging.INFO, 'artifact',
             {},
             'Printing last used artifact details.')
         self._pushhead_cache.print_last()
         self._task_cache.print_last()
         self._artifact_cache.print_last()