Bug 1433410 - Add codespell support for mach lint r=ahal
authorSylvestre Ledru <sledru@mozilla.com>
Sat, 27 Jan 2018 10:35:31 +0100
changeset 401043 3eefd4e1087c7c151485379f6360a05338f8b98d
parent 401042 c5d8b1ed27228f4bcf45b2746f78dba3795d0868
child 401044 433a6f0d0ac31d9515be1e48401f378825660ef1
push id58734
push usersledru@mozilla.com
push dateSat, 27 Jan 2018 09:43:31 +0000
treeherderautoland@3eefd4e1087c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersahal
bugs1433410
milestone60.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 1433410 - Add codespell support for mach lint r=ahal MozReview-Commit-ID: Ii6QjPMN0Ks
taskcluster/docker/lint/system-setup.sh
tools/lint/codespell.yml
tools/lint/spell/__init__.py
--- a/taskcluster/docker/lint/system-setup.sh
+++ b/taskcluster/docker/lint/system-setup.sh
@@ -5,16 +5,17 @@ export DEBIAN_FRONTEND=noninteractive
 set -ve
 
 test "$(whoami)" == 'root'
 
 mkdir -p /setup
 cd /setup
 
 apt_packages=()
+apt_packages+=('codespell')
 apt_packages+=('curl')
 apt_packages+=('locales')
 apt_packages+=('git')
 apt_packages+=('python')
 apt_packages+=('python-pip')
 apt_packages+=('python3')
 apt_packages+=('python3-pip')
 apt_packages+=('shellcheck')
new file mode 100644
--- /dev/null
+++ b/tools/lint/codespell.yml
@@ -0,0 +1,27 @@
+---
+codespell:
+    description: Check code for common misspellings
+    include: ['.']
+    exclude:
+        - third_party
+    # List of extensions coming from:
+    # tools/lint/{flake8,eslint}.yml
+    # tools/mach_commands.py (clang-format)
+    # + documentation
+    # + localization files
+    extensions:
+        - js
+        - jsm
+        - jxs
+        - xml
+        - html
+        - xhtml
+        - cpp
+        - c
+        - h
+        - configure
+        - py
+        - properties
+        - rst
+    type: external
+    payload: spell:lint
new file mode 100644
--- /dev/null
+++ b/tools/lint/spell/__init__.py
@@ -0,0 +1,117 @@
+# 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/.
+
+from __future__ import absolute_import, print_function
+
+import os
+import signal
+import which
+import re
+
+# Py3/Py2 compatibility.
+try:
+    from json.decoder import JSONDecodeError
+except ImportError:
+    JSONDecodeError = ValueError
+
+from mozlint import result
+from mozprocess import ProcessHandlerMixin
+
+
+CODESPELL_NOT_FOUND = """
+Unable to locate codespell, please ensure it is installed and in
+your PATH or set the CODESPELL environment variable.
+
+https://github.com/lucasdemarchi/codespell or your system's package manager.
+""".strip()
+
+results = []
+
+CODESPELL_FORMAT_REGEX = re.compile(r'(.*):(.*): (.*) ==> (.*)$')
+
+
+class CodespellProcess(ProcessHandlerMixin):
+    def __init__(self, config, *args, **kwargs):
+        self.config = config
+        kwargs['processOutputLine'] = [self.process_line]
+        ProcessHandlerMixin.__init__(self, *args, **kwargs)
+
+    def process_line(self, line):
+        try:
+            match = CODESPELL_FORMAT_REGEX.match(line)
+            abspath, line, typo, correct = match.groups()
+        except AttributeError:
+            print('Unable to match regex against output: {}'.format(line))
+            return
+
+        # Ignore false positive like aParent (which would be fixed to apparent)
+        # See https://github.com/lucasdemarchi/codespell/issues/314
+        m = re.match(r'^[a-z][A-Z][a-z]*', typo)
+        if m:
+            return
+        res = {'path': os.path.relpath(abspath, self.config['root']),
+               'message': typo + " ==> " + correct,
+               'level': "warning",
+               'lineno': line,
+               }
+        results.append(result.from_config(self.config, **res))
+
+    def run(self, *args, **kwargs):
+        orig = signal.signal(signal.SIGINT, signal.SIG_IGN)
+        ProcessHandlerMixin.run(self, *args, **kwargs)
+        signal.signal(signal.SIGINT, orig)
+
+
+def run_process(config, cmd):
+    proc = CodespellProcess(config, cmd)
+    proc.run()
+    try:
+        proc.wait()
+    except KeyboardInterrupt:
+        proc.kill()
+
+
+def get_codespell_binary():
+    """
+    Returns the path of the first codespell binary available
+    if not found returns None
+    """
+    binary = os.environ.get('CODESPELL')
+    if binary:
+        return binary
+
+    try:
+        return which.which('codespell')
+    except which.WhichError:
+        return None
+
+
+def lint(paths, config, fix=None, **lintargs):
+
+    binary = get_codespell_binary()
+
+    if not binary:
+        print(CODESPELL_NOT_FOUND)
+        if 'MOZ_AUTOMATION' in os.environ:
+            return 1
+        return []
+
+    config['root'] = lintargs['root']
+    cmd_args = [binary,
+                '--disable-colors',
+                # Silence some warnings:
+                # 1: disable warnings about wrong encoding
+                # 2: disable warnings about binary file
+                '--quiet-level=3',
+                ]
+
+# Disabled for now because of
+# https://github.com/lucasdemarchi/codespell/issues/314
+#    if fix:
+#        cmd_args.append('--write-changes')
+
+    base_command = cmd_args + paths
+
+    run_process(config, base_command)
+    return results