pw_presubmit: Add shellcheck

Change-Id: I6b3cf6fead26d44a5f53f3524c51365b799b9a9c
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/97460
Reviewed-by: Wyatt Hepler <hepler@google.com>
Commit-Queue: Tim Laurence <timlaurence@google.com>
diff --git a/docs/faq.rst b/docs/faq.rst
index 1042356..28d0822 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -90,6 +90,16 @@
   the mailing list. We know this part of Pigweed is incomplete and will help
   those who are interested in giving Pigweed a try.
 
+Why doesn't Pigweed allow shell scripting?
+------------------------------------------
+Pigweed supports multiple platforms. The native shells on these differ and
+additionally "compatible" shells often have sububle differences in behavior.
+Pigweed uses Python instead shell wherever practical and changes to Pigweed that
+include shell scripting will likely be rejected. Users of Pigweed may use shell
+scripts in their own code and we have included support for
+`Shellcheck <https://www.shellcheck.net/>`_ during presubmit checks that is
+automatically enabled if ``shellcheck`` found in the path.
+
 What development hosts are supported?
 -------------------------------------
 We support the following platforms:
diff --git a/pw_presubmit/py/BUILD.gn b/pw_presubmit/py/BUILD.gn
index 235e506..e0e75ae 100644
--- a/pw_presubmit/py/BUILD.gn
+++ b/pw_presubmit/py/BUILD.gn
@@ -34,6 +34,7 @@
     "pw_presubmit/pigweed_presubmit.py",
     "pw_presubmit/presubmit.py",
     "pw_presubmit/python_checks.py",
+    "pw_presubmit/shell_checks.py",
     "pw_presubmit/tools.py",
   ]
   tests = [
diff --git a/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py b/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
index 54a5137..d4cbc64 100755
--- a/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
+++ b/pw_presubmit/py/pw_presubmit/pigweed_presubmit.py
@@ -21,6 +21,7 @@
 import os
 from pathlib import Path
 import re
+import shutil
 import subprocess
 import sys
 from typing import Sequence, IO, Tuple, Optional, Callable, List
@@ -50,6 +51,7 @@
     PresubmitFailure,
     Programs,
     python_checks,
+    shell_checks,
 )
 from pw_presubmit.install_hook import install_git_hook
 
@@ -815,6 +817,7 @@
     cpp_checks.pragma_once,
     build.bazel_lint,
     source_is_in_build_files,
+    shell_checks.shellcheck if shutil.which('shellcheck') else (),
 )
 
 LINTFORMAT = (
diff --git a/pw_presubmit/py/pw_presubmit/shell_checks.py b/pw_presubmit/py/pw_presubmit/shell_checks.py
new file mode 100644
index 0000000..af50031
--- /dev/null
+++ b/pw_presubmit/py/pw_presubmit/shell_checks.py
@@ -0,0 +1,35 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Shell related checks."""
+
+import logging
+from pw_presubmit import (Check, PresubmitContext, filter_paths, tools,
+                          PresubmitFailure)
+
+_LOG = logging.getLogger(__name__)
+
+_SHELL_EXTENSIONS = ('.sh', '.bash')
+
+
+@filter_paths(endswith=_SHELL_EXTENSIONS)
+@Check
+def shellcheck(ctx: PresubmitContext) -> None:
+    """Run shell script static analiyzer on presubmit."""
+
+    _LOG.warning("The Pigweed project discourages use of shellscripts. "
+                 "https://pigweed.dev/docs/faq.html")
+
+    result = tools.log_run(['shellcheck', *ctx.paths])
+    if result.returncode != 0:
+        raise PresubmitFailure('Shellcheck identifed issues.')