Skip to content

Commit

Permalink
Bug 1360595 - Add a git pre-commit hook for running ESLint. r=mossop
Browse files Browse the repository at this point in the history
MozReview-Commit-ID: 1YJL5Sd4dlb

--HG--
rename : tools/mercurial/eslintvalidate.py => tools/lint/eslint/hook_helper.py
extra : rebase_source : eec3ee2761dd7178de1562229bfda24c0859b4ae
  • Loading branch information
Standard8 committed Apr 28, 2017
1 parent 046debb commit 72059d8
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 60 deletions.
35 changes: 35 additions & 0 deletions tools/git/eslintvalidate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/python
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "lint", "eslint"))
from hook_helper import is_lintable, runESLint


def output(message):
print >> sys.stderr, message


def eslint():
f = os.popen('git diff --cached --name-only --diff-filter=ACM')

files = [file for file in f.read().splitlines() if is_lintable(file)]

if len(files) == 0:
return True

print "Running ESLint..."

return runESLint(output, files)


if __name__ == '__main__':
if not eslint():
output("Note: ESLint failed, but the commit will still happen. "
"Please fix before pushing.")

# We output successfully as that seems to be a better model. See
# https://bugzilla.mozilla.org/show_bug.cgi?id=1230300#c4 for more
# information.
13 changes: 13 additions & 0 deletions tools/git/git-lint-commit-hook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/sh

# Simple wrapper to try and ensure we get the right python version loaded.
which python2.7 > /dev/null

if [ $? -eq 0 ]
then
python2.7 ./tools/git/eslintvalidate.py
else
python ./tools/git/eslintvalidate.py
fi

exit $?
90 changes: 90 additions & 0 deletions tools/lint/eslint/hook_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

# This file provides helper functions for the repository hooks for git/hg.

import os
import re
import json
from subprocess import check_output, CalledProcessError

lintable = re.compile(r'.+\.(?:js|jsm|jsx|xml|html)$')
ignored = 'File ignored because of a matching ignore pattern. Use "--no-ignore" to override.'


def is_lintable(filename):
"""Determine if a file is lintable based on the file extension.
Keyword arguments:
filename -- the file to check.
"""
return lintable.match(filename)


def display(print_func, output_json):
"""Formats an ESLint result into a human readable message.
Keyword arguments:
print_func -- A function to call to print the output.
output_json -- the json ESLint results to format.
"""
results = json.loads(output_json)
for file in results:
path = os.path.relpath(file["filePath"])
for message in file["messages"]:
if message["message"] == ignored:
continue

if "line" in message:
print_func("%s:%d:%d %s\n" % (path, message["line"], message["column"],
message["message"]))
else:
print_func("%s: %s\n" % (path, message["message"]))


def runESLint(print_func, files):
"""Runs ESLint on the files that are passed.
Keyword arguments:
print_func -- A function to call to print the output.
files -- A list of files to be checked.
"""
try:
basepath = get_project_root()

if not basepath:
return

dir = os.path.join(basepath, "node_modules", ".bin")

eslint_path = os.path.join(dir, "eslint")
if os.path.exists(os.path.join(dir, "eslint.cmd")):
eslint_path = os.path.join(dir, "eslint.cmd")
output = check_output([eslint_path,
"--format", "json", "--plugin", "html"] + files,
cwd=basepath)
display(print_func, output)
return True
except CalledProcessError as ex:
display(print_func, ex.output)
return False


def get_project_root():
"""Returns the absolute path to the root of the project, by looking for the
mach executable.
"""
file_found = False
folder = os.getcwd()

while (folder):
if os.path.exists(os.path.join(folder, 'mach')):
file_found = True
break
else:
folder = os.path.dirname(folder)

if file_found:
return os.path.abspath(folder)

return None
1 change: 1 addition & 0 deletions tools/lint/flake8.lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ def lint(files, **lintargs):
'testing/mozbase',
'testing/mochitest',
'testing/talos/',
'tools/git',
'tools/lint',
'tools/mercurial',
'toolkit/components/telemetry',
Expand Down
66 changes: 6 additions & 60 deletions tools/mercurial/eslintvalidate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,9 @@
# GNU General Public License version 2 or any later version.

import os
import re
import json
from subprocess import check_output, CalledProcessError

lintable = re.compile(r'.+\.(?:js|jsm|jsx|xml|html)$')
ignored = 'File ignored because of a matching ignore pattern. Use "--no-ignore" to override.'


def is_lintable(filename):
return lintable.match(filename)


def display(ui, output):
results = json.loads(output)
for file in results:
path = os.path.relpath(file["filePath"])
for message in file["messages"]:
if message["message"] == ignored:
continue

if "line" in message:
ui.warn("%s:%d:%d %s\n" % (path, message["line"], message["column"],
message["message"]))
else:
ui.warn("%s: %s\n" % (path, message["message"]))
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "lint", "eslint"))
from hook_helper import is_lintable, runESLint


def eslinthook(ui, repo, node=None, **opts):
Expand All @@ -40,42 +18,10 @@ def eslinthook(ui, repo, node=None, **opts):
if len(files) == 0:
return

try:
basepath = get_project_root()

if not basepath:
return

dir = os.path.join(basepath, "node_modules", ".bin")

eslint_path = os.path.join(dir, "eslint")
if os.path.exists(os.path.join(dir, "eslint.cmd")):
eslint_path = os.path.join(dir, "eslint.cmd")
output = check_output([eslint_path,
"--format", "json", "--plugin", "html"] + files,
cwd=basepath)
display(ui, output)
except CalledProcessError as ex:
display(ui, ex.output)
ui.warn("ESLint found problems in your changes, please correct them.\n")
if not runESLint(ui.warn, files):
ui.warn("Note: ESLint failed, but the commit will still happen. "
"Please fix before pushing.\n")


def reposetup(ui, repo):
ui.setconfig('hooks', 'commit.eslint', eslinthook)


def get_project_root():
file_found = False
folder = os.getcwd()

while (folder):
if os.path.exists(os.path.join(folder, 'mach')):
file_found = True
break
else:
folder = os.path.dirname(folder)

if file_found:
return os.path.abspath(folder)

return None

0 comments on commit 72059d8

Please sign in to comment.