aboutsummaryrefslogtreecommitdiffstats
path: root/python/tidy
diff options
context:
space:
mode:
Diffstat (limited to 'python/tidy')
-rw-r--r--python/tidy/servo_tidy/licenseck.py8
-rw-r--r--python/tidy/servo_tidy/tidy.py42
-rw-r--r--python/tidy/servo_tidy_tests/shell_tidy.sh7
-rw-r--r--python/tidy/servo_tidy_tests/test_tidy.py6
4 files changed, 60 insertions, 3 deletions
diff --git a/python/tidy/servo_tidy/licenseck.py b/python/tidy/servo_tidy/licenseck.py
index 03113da507d..dce01460d24 100644
--- a/python/tidy/servo_tidy/licenseck.py
+++ b/python/tidy/servo_tidy/licenseck.py
@@ -24,6 +24,14 @@ licenses = [
""",
"""\
+#!/usr/bin/env bash
+
+# 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/.
+""",
+
+"""\
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
diff --git a/python/tidy/servo_tidy/tidy.py b/python/tidy/servo_tidy/tidy.py
index 873af6ca22b..34eea7f882a 100644
--- a/python/tidy/servo_tidy/tidy.py
+++ b/python/tidy/servo_tidy/tidy.py
@@ -26,7 +26,7 @@ MAX_LICENSE_LINESPAN = max(len(license.splitlines()) for license in licenses)
# File patterns to include in the non-WPT tidy check.
FILE_PATTERNS_TO_CHECK = ["*.rs", "*.rc", "*.cpp", "*.c",
- "*.h", "Cargo.lock", "*.py",
+ "*.h", "Cargo.lock", "*.py", "*.sh",
"*.toml", "*.webidl", "*.json"]
# File patterns that are ignored for all tidy and lint checks.
@@ -43,6 +43,7 @@ IGNORED_FILES = [
os.path.join(".", "tests", "wpt", "metadata", "MANIFEST.json"),
os.path.join(".", "tests", "wpt", "metadata-css", "MANIFEST.json"),
os.path.join(".", "components", "script", "dom", "webidls", "ForceTouchEvent.webidl"),
+ os.path.join(".", "support", "android", "openssl.sh"),
# FIXME(pcwalton, #11679): This is a workaround for a tidy error on the quoted string
# `"__TEXT,_info_plist"` inside an attribute.
os.path.join(".", "components", "servo", "platform", "macos", "mod.rs"),
@@ -169,7 +170,11 @@ def check_modeline(file_name, lines):
def check_length(file_name, idx, line):
if file_name.endswith(".lock") or file_name.endswith(".json"):
raise StopIteration
- max_length = 120
+ # Prefer shorter lines when shell scripting.
+ if file_name.endswith(".sh"):
+ max_length = 80
+ else:
+ max_length = 120
if len(line.rstrip('\n')) > max_length:
yield (idx + 1, "Line is longer than %d characters" % max_length)
@@ -306,6 +311,36 @@ def check_toml(file_name, lines):
yield (0, ".toml file should contain a valid license.")
+def check_shell(file_name, lines):
+ if not file_name.endswith(".sh"):
+ raise StopIteration
+
+ shebang = "#!/usr/bin/env bash"
+ required_options = {"set -o errexit", "set -o nounset", "set -o pipefail"}
+
+ if len(lines) == 0:
+ yield (0, 'script is an empty file')
+ else:
+ if lines[0].rstrip() != shebang:
+ yield (1, 'script does not have shebang "{}"'.format(shebang))
+
+ for idx in range(1, len(lines)):
+ stripped = lines[idx].rstrip()
+
+ # Comments or blank lines are ignored. (Trailing whitespace is caught with a separate linter.)
+ if lines[idx].startswith("#") or stripped == "":
+ continue
+ elif stripped in required_options:
+ required_options.remove(stripped)
+ else:
+ # The first non-comment, non-whitespace, non-option line is the first "real" line of the script.
+ # The shebang, options, etc. must come before this.
+ if len(required_options) != 0:
+ formatted = ['"{}"'.format(opt) for opt in required_options]
+ yield (idx + 1, "script is missing options {}".format(", ".join(formatted)))
+ break
+
+
def check_rust(file_name, lines):
if not file_name.endswith(".rs") or \
file_name.endswith(".mako.rs") or \
@@ -694,7 +729,8 @@ def scan(only_changed_files=False, progress=True):
# standard checks
files_to_check = filter_files('.', only_changed_files, progress)
checking_functions = (check_flake8, check_lock, check_webidl_spec, check_json)
- line_checking_functions = (check_license, check_by_line, check_toml, check_rust, check_spec, check_modeline)
+ line_checking_functions = (check_license, check_by_line, check_toml, check_shell,
+ check_rust, check_spec, check_modeline)
errors = collect_errors_for_files(files_to_check, checking_functions, line_checking_functions)
# check dependecy licenses
dep_license_errors = check_dep_license_errors(get_dep_toml_files(only_changed_files), progress)
diff --git a/python/tidy/servo_tidy_tests/shell_tidy.sh b/python/tidy/servo_tidy_tests/shell_tidy.sh
new file mode 100644
index 00000000000..fbf0e784755
--- /dev/null
+++ b/python/tidy/servo_tidy_tests/shell_tidy.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+#
+# Tests tidy for shell scripts.
+
+set -o nounset
+
+echo "hello world"
diff --git a/python/tidy/servo_tidy_tests/test_tidy.py b/python/tidy/servo_tidy_tests/test_tidy.py
index 8db19a5efd7..3ad7e03d667 100644
--- a/python/tidy/servo_tidy_tests/test_tidy.py
+++ b/python/tidy/servo_tidy_tests/test_tidy.py
@@ -48,6 +48,12 @@ class CheckTidiness(unittest.TestCase):
self.assertEqual('incorrect license', errors.next()[2])
self.assertNoMoreErrors(errors)
+ def test_shell(self):
+ errors = tidy.collect_errors_for_files(iterFile('shell_tidy.sh'), [], [tidy.check_shell], print_text=False)
+ self.assertEqual('script does not have shebang "#!/usr/bin/env bash"', errors.next()[2])
+ self.assertEqual('script is missing options "set -o errexit", "set -o pipefail"', errors.next()[2])
+ self.assertNoMoreErrors(errors)
+
def test_rust(self):
errors = tidy.collect_errors_for_files(iterFile('rust_tidy.rs'), [], [tidy.check_rust], print_text=False)
self.assertEqual('use statement spans multiple lines', errors.next()[2])