diff mbox series

[5/5] utils/checkpackagelib/lib_sysv: run shellcheck

Message ID 20211226184919.2753591-6-ricardo.martincoski@gmail.com
State Accepted
Headers show
Series check-package for SysV init scripts (including shellcheck) | expand

Commit Message

Ricardo Martincoski Dec. 26, 2021, 6:49 p.m. UTC
For simplicity, when shellcheck returns one or more warnings, count it
as a single check-package warning.
The developer can get the full output either by running shellcheck
directly or by calling check-package with enough -v.

Examples:

|$ ./utils/docker-run utils/check-package --include Shellcheck package/polkit/S50polkit
|package/polkit/S50polkit:0: run 'shellcheck' and fix the warnings
|51 lines processed
|1 warnings generated

|$ ./utils/docker-run utils/check-package --include Shellcheck -vvvvvvvvvvv package/polkit/S50polkit
|package/polkit/S50polkit:0: run 'shellcheck' and fix the warnings
|In package/polkit/S50polkit line 43:
|< tab  >start|stop|restart|reload)
|                           ^----^ SC2221: This pattern always overrides a later one on line 45.
|In package/polkit/S50polkit line 45:
|< tab  >reload)
|        ^----^ SC2222: This pattern never matches because of a previous pattern on line 43.
|For more information:
|  https://www.shellcheck.net/wiki/SC2221 -- This pattern always overrides a l...
|  https://www.shellcheck.net/wiki/SC2222 -- This pattern never matches becaus...
|51 lines processed
|1 warnings generated

NOTICE: shellcheck results depends on the version of the tool.
This is why the examples above run inside the docker image.

Signed-off-by: Ricardo Martincoski <ricardo.martincoski@gmail.com>
---
Please read the cover letter before applying.
---
 utils/checkpackagelib/lib_sysv.py  |  1 +
 utils/checkpackagelib/test_tool.py | 46 ++++++++++++++++++++++++++++++
 utils/checkpackagelib/tool.py      | 15 ++++++++++
 3 files changed, 62 insertions(+)
diff mbox series

Patch

diff --git a/utils/checkpackagelib/lib_sysv.py b/utils/checkpackagelib/lib_sysv.py
index 77e6283b25..786510badf 100644
--- a/utils/checkpackagelib/lib_sysv.py
+++ b/utils/checkpackagelib/lib_sysv.py
@@ -7,6 +7,7 @@  from checkpackagelib.lib import EmptyLastLine          # noqa: F401
 from checkpackagelib.lib import NewlineAtEof           # noqa: F401
 from checkpackagelib.lib import TrailingSpace          # noqa: F401
 from checkpackagelib.tool import NotExecutable as NotExecutable_base
+from checkpackagelib.tool import Shellcheck            # noqa: F401
 
 
 class Indent(_CheckFunction):
diff --git a/utils/checkpackagelib/test_tool.py b/utils/checkpackagelib/test_tool.py
index aab7cb51c9..c5c1d48dfe 100644
--- a/utils/checkpackagelib/test_tool.py
+++ b/utils/checkpackagelib/test_tool.py
@@ -64,3 +64,49 @@  def test_NotExecutable_hint(testname, hint, filename, permissions, string, expec
             return hint
     warnings = check_file(NotExecutable, filename, string, permissions)
     assert warnings == expected
+
+
+Shellcheck = [
+    ('missing shebang',
+     'empty.sh',
+     '',
+     ["dir/empty.sh:0: run 'shellcheck' and fix the warnings",
+      "In dir/empty.sh line 1:",
+      "^-- SC2148: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.",
+      "For more information:",
+      "  https://www.shellcheck.net/wiki/SC2148 -- Tips depend on target shell and y..."]),
+    ('sh shebang',
+     'sh-shebang.sh',
+     '#!/bin/sh',
+     []),
+    ('bash shebang',
+     'bash-shebang.sh',
+     '#!/bin/bash',
+     []),
+    ('2 warnings',
+     'unused.sh',
+     'unused=""',
+     ["dir/unused.sh:0: run 'shellcheck' and fix the warnings",
+      "In dir/unused.sh line 1:",
+      'unused=""',
+      "^-- SC2148: Tips depend on target shell and yours is unknown. Add a shebang or a 'shell' directive.",
+      "^----^ SC2034: unused appears unused. Verify use (or export if used externally).",
+      "For more information:",
+      "  https://www.shellcheck.net/wiki/SC2148 -- Tips depend on target shell and y...",
+      "  https://www.shellcheck.net/wiki/SC2034 -- unused appears unused. Verify use..."]),
+    ('tab',
+     'tab.sh',
+     '\t#!/bin/sh',
+     ["dir/tab.sh:0: run 'shellcheck' and fix the warnings",
+      "In dir/tab.sh line 1:",
+      '\t#!/bin/sh',
+      "^-- SC1114: Remove leading spaces before the shebang.",
+      "For more information:",
+      "  https://www.shellcheck.net/wiki/SC1114 -- Remove leading spaces before the ..."]),
+    ]
+
+
+@pytest.mark.parametrize('testname,filename,string,expected', Shellcheck)
+def test_Shellcheck(testname, filename, string, expected):
+    warnings = check_file(m.Shellcheck, filename, string)
+    assert warnings == expected
diff --git a/utils/checkpackagelib/tool.py b/utils/checkpackagelib/tool.py
index e931272554..47f9634486 100644
--- a/utils/checkpackagelib/tool.py
+++ b/utils/checkpackagelib/tool.py
@@ -1,4 +1,5 @@ 
 import os
+import subprocess
 from checkpackagelib.base import _Tool
 
 
@@ -6,3 +7,17 @@  class NotExecutable(_Tool):
     def run(self):
         if os.access(self.filename, os.X_OK):
             return ["{}:0: This file does not need to be executable{}".format(self.filename, self.hint())]
+
+
+class Shellcheck(_Tool):
+    def run(self):
+        cmd = ['shellcheck', self.filename]
+        try:
+            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            stdout = p.communicate()[0]
+            processed_output = [str(line.decode().rstrip()) for line in stdout.splitlines() if line]
+            if p.returncode == 0:
+                return
+            return ["{}:0: run 'shellcheck' and fix the warnings".format(self.filename)] + processed_output
+        except FileNotFoundError:
+            return ["{}:0: failed to call 'shellcheck'".format(self.filename)]