Bug 1392795 - [yamllint] Group paths to lint by their closest config and run each config group separately, r=dustin
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Wed, 23 Aug 2017 11:28:28 -0400
changeset 376980 f384a3acd01687f4f8cc55333298aebb02c33532
parent 376979 1f832bd881f8928bb5779fab3111387d56062b47
child 376981 72adcbfccfbe508a4c51c47551b6b6e0721d1818
push id94212
push userphilringnalda@gmail.com
push dateSun, 27 Aug 2017 01:21:35 +0000
treeherdermozilla-inbound@c691c8de2367 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdustin
bugs1392795
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1392795 - [yamllint] Group paths to lint by their closest config and run each config group separately, r=dustin This makes configuration files for yamllint work a bit better. It's still not perfect, but it's an improvement on the current situation. MozReview-Commit-ID: IKxgQm1a7bP
.yamllint
taskcluster/.yamllint
tools/lint/yamllint_/__init__.py
new file mode 100644
--- /dev/null
+++ b/.yamllint
@@ -0,0 +1,5 @@
+---
+ignore: |
+  *node_modules*
+
+extends: default
--- a/taskcluster/.yamllint
+++ b/taskcluster/.yamllint
@@ -1,9 +1,11 @@
 ---
+ignore: |
+  *node_modules*
 
 extends: default
 
 rules:
   document-end:
      present: false
   # Checks currently failing
   brackets: disable
--- a/tools/lint/yamllint_/__init__.py
+++ b/tools/lint/yamllint_/__init__.py
@@ -1,16 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import re
 import os
 import signal
 import subprocess
+from collections import defaultdict
 
 import which
 from mozprocess import ProcessHandlerMixin
 
 from mozlint import result
 
 
 here = os.path.abspath(os.path.dirname(__file__))
@@ -109,23 +110,45 @@ def run_process(config, cmd):
     except KeyboardInterrupt:
         proc.kill()
 
 
 def gen_yamllint_args(cmdargs, paths=None, conf_file=None):
     args = cmdargs[:]
     if isinstance(paths, basestring):
         paths = [paths]
-    if conf_file:
+    if conf_file and conf_file != 'default':
         return args + ['-c', conf_file] + paths
     return args + paths
 
 
+def ancestors(path):
+    while path:
+        yield path
+        (path, child) = os.path.split(path)
+        if child == "":
+            break
+
+
+def get_relevant_configs(name, path, root):
+    """Returns a list of configuration files that exist in `path`'s ancestors,
+    sorted from closest->furthest.
+    """
+    configs = []
+    for path in ancestors(path):
+        if path == root:
+            break
+
+        config = os.path.join(path, name)
+        if os.path.isfile(config):
+            configs.append(config)
+    return configs
+
+
 def lint(files, config, **lintargs):
-
     if not reinstall_yamllint():
         print(YAMLLINT_INSTALL_ERROR)
         return 1
 
     binary = get_yamllint_binary()
 
     cmdargs = [
         binary,
@@ -133,22 +156,17 @@ def lint(files, config, **lintargs):
     ]
 
     config = config.copy()
     config['root'] = lintargs['root']
 
     # Run any paths with a .yamllint file in the directory separately so
     # it gets picked up. This means only .yamllint files that live in
     # directories that are explicitly included will be considered.
-    no_config = []
+    paths_by_config = defaultdict(list)
     for f in files:
-        yamllint_config = os.path.join(f, '.yamllint')
-        if not os.path.isfile(yamllint_config):
-            no_config.append(f)
-            continue
-        run_process(config,
-                    gen_yamllint_args(cmdargs, conf_file=yamllint_config, paths=f))
+        conf_files = get_relevant_configs('.yamllint', f, config['root'])
+        paths_by_config[conf_files[0] if conf_files else 'default'].append(f)
 
-    if no_config:
-        run_process(config,
-                    gen_yamllint_args(cmdargs, paths=no_config))
+    for conf_file, paths in paths_by_config.items():
+        run_process(config, gen_yamllint_args(cmdargs, conf_file=conf_file, paths=paths))
 
     return results