Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate shell scripts with Shellcheck #418

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
651 changes: 651 additions & 0 deletions build-aux/tap-driver.sh

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
AC_INIT([eos-boot-helper], [0.0])

AC_CONFIG_AUX_DIR([build-aux])
AM_INIT_AUTOMAKE([foreign])
AC_CONFIG_SRCDIR([eos-firstboot])
PKG_PROG_PKG_CONFIG
Expand Down Expand Up @@ -137,4 +138,5 @@ AC_CONFIG_FILES([
tests/Makefile
tmpfiles.d/chromium-system-services.conf
])
AC_REQUIRE_AUX_FILE([tap-driver.sh])
AC_OUTPUT
1 change: 1 addition & 0 deletions debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Build-Depends: automake,
python3-parted <!nocheck>,
python3-pytest <!nocheck>,
python3-systemd <!nocheck>,
shellcheck <!nocheck>,
systemd,
udev,
Standards-Version: 3.9.1
Expand Down
4 changes: 2 additions & 2 deletions eos-enable-zram
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Copyright (C) 2018 Endless Mobile, Inc.
# Licensed under the GPLv2

if [ $# -lt 1 -o "$1" -lt 0 -o "$1" -gt 1 ]; then
if [ $# -lt 1 ] || [ "$1" -lt 0 ] || [ "$1" -gt 1 ]; then
echo "Error: 0 or 1 must be specified" >&2
exit 1
fi
Expand All @@ -14,7 +14,7 @@ if [ "$1" -ne 0 ]; then
if [ ! -e /sys/block/zram0 ]; then
modprobe zram
fi
echo $(($ram_size_kb * 3 / 2))K > /sys/block/zram0/disksize
echo $((ram_size_kb * 3 / 2))K > /sys/block/zram0/disksize
udevadm settle
# For now, disable the swap partition if in use
swapoff -a
Expand Down
2 changes: 1 addition & 1 deletion eos-firstboot
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ root_part=$(findmnt -rvnf -o SOURCE /)

resize2fs "${root_part}"

> /var/lib/eos-firstboot
: > /var/lib/eos-firstboot
exit 0
8 changes: 3 additions & 5 deletions eos-live-boot-overlayfs-setup
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ map_persistent_img_file() {

local pfpath="/run/media/eosboot/endless/persistent.img"
local persistent_loop
persistent_loop=$(losetup -f --show "${pfpath}")
if [ $? != 0 ]; then
if ! persistent_loop=$(losetup -f --show "${pfpath}"); then
echo "${pfpath} not found in ${root_partition}" >&2
umount /run/media/eosboot
return 1
fi

echo "Found persistent.img in ${root_partition}, using it for persistent storage" >&2
echo ${persistent_loop}
echo "${persistent_loop}"
return 0
}

Expand All @@ -43,8 +42,7 @@ find_storage_partition() {
fi

local storage_partition
storage_partition=$(map_persistent_img_file "${root_partition}")
if [ $? == 0 ]; then
if storage_partition=$(map_persistent_img_file "${root_partition}"); then
echo "${storage_partition}"
return 0
fi
Expand Down
5 changes: 4 additions & 1 deletion tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ AM_TESTS_ENVIRONMENT = \
$(NULL)

TESTS = \
check-syntax \
check-syntax.tap \
run-tests \
$(NULL)

TEST_EXTENSIONS = .tap
TAP_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) $(top_srcdir)/build-aux/tap-driver.sh

EXTRA_DIST = \
$(TESTS) \
__init__.py \
Expand Down
59 changes: 0 additions & 59 deletions tests/check-syntax

This file was deleted.

89 changes: 89 additions & 0 deletions tests/check-syntax.tap
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/python3
import os
import subprocess
import glob

ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
IGNORE_SHELLCHECK_ERRORS = [
"eos-firewall-localonly",
"eos-firewall-localonly-nm",
"factory-reset/eos-factory-reset-users",
"nvidia/nvidia-graphics-setup",
]


def find_files(pattern):
"""Find files matching 'pattern' on the first line."""
output = subprocess.check_output(
[
"grep",
"-r",
"--files-with-match",
"--null",
# Only first line
"--max-count",
"1",
# Skip .git and friends
"--exclude-dir=.*",
# Skip temporary files
"--exclude=.*",
"--exclude=*~",
pattern,
ROOT,
]
)
return [x.decode("utf-8") for x in output.split(b"\x00") if x]


def main():
"""Checks shell scripts' syntax, printing results in TAP format and
exiting non-zero if any script has syntax errors."""
checks = []

for bash_script in find_files("^#!/bin/bash"):
checks.append((["bash", "-n", bash_script], False))
checks.append(
(
["shellcheck", bash_script],
any(bash_script.endswith(x) for x in IGNORE_SHELLCHECK_ERRORS),
)
)

for dash_script in find_files("^#!/bin/sh"):
checks.append((["dash", "-n", dash_script], False))
checks.append(
(
["shellcheck", bash_script],
any(dash_script.endswith(x) for x in IGNORE_SHELLCHECK_ERRORS),
)
)

# All files with a shebang, regardless of their extension (if any)
python_scripts = set(find_files("^#!/usr/bin/python"))
# .py files in the tests/ directory, which may or may not have a hashtag yell
python_scripts.update(glob.glob(os.path.join(ROOT, "tests", "*.py")))
checks.append((["flake8"] + sorted(python_scripts), False))

print("1..{}".format(len(checks)))
failed = False
for i, (check, xfail) in enumerate(checks, 1):
label = " ".join(check)
cp = subprocess.run(
check, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
)
for line in cp.stdout.splitlines():
print("#", line)
if cp.returncode == 0:
result = "ok"
else:
result = "not ok"
failed &= not xfail

directive = " # TODO" if xfail else ""
print(f"{result} {i} - {label}{directive}")

exit(failed)


if __name__ == "__main__":
main()