Bug 1433410 - Add codespell support for mach lint r?ahal
MozReview-Commit-ID: Ii6QjPMN0Ks
--- 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