-
Notifications
You must be signed in to change notification settings - Fork 200
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ci: add shellcheck gh action; fix fatal shellcheck errors
This commit made with the assistance of github copilot Signed-off-by: Morgan Rockett <morgan.rockett@tufts.edu>
- Loading branch information
Showing
8 changed files
with
237 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
#!/usr/bin/env bash | ||
|
||
RED="\e[31m" | ||
GREEN="\e[32m" | ||
RST_COLOR="\e[0m" | ||
|
||
if ! command -v shellcheck &>/dev/null; then | ||
echo -e "${RED}[ERROR]${RST_COLOR} shellcheck is not installed." | ||
echo "Run 'sudo ./scripts/install-build-tools.sh' to install shellcheck." | ||
exit 1 | ||
fi | ||
|
||
# Usage: ./scripts/shellcheck.sh [-e|--exclude-code=CODE] [-S|--severity=LEVEL] [-v|--view] | ||
IFS='' read -rd '' usage <<'EOF' | ||
Usage: %s [options] | ||
Options: | ||
-h, --help print this help and exit | ||
-e, --exclude-code exclude specific error code, can be repeated | ||
-S, --severity=LEVEL set severity level (info, warning, error) | ||
-v, --view view shellcheck report | ||
example: ./scripts/shellcheck.sh -e SC1091 -e SC1090 -S warning -v | ||
EOF | ||
|
||
echo; echo "Command line arguments: $0 $*"; echo | ||
|
||
SEVERITY= | ||
EXCLUDE_CODES= | ||
VIEW="False" | ||
|
||
_help= | ||
_err=0 | ||
while [[ $# -gt 0 ]]; do | ||
optarg= | ||
shft_cnt=1 | ||
if [[ "$1" = '--' ]]; then | ||
shift 1 | ||
break | ||
elif [[ "$1" =~ [=] ]]; then | ||
optarg="${1#*=}" | ||
elif [[ "$1" =~ ^-- && $# -gt 1 && ! "$2" =~ ^- ]]; then | ||
optarg="$2" | ||
shft_cnt=2 | ||
elif [[ "$1" =~ ^-[^-] && $# -gt 1 && ! "$2" =~ ^- ]]; then | ||
optarg="$2" | ||
shft_cnt=2 | ||
elif [[ "$1" =~ ^-[^-] ]]; then | ||
optarg="${1/??/}" | ||
fi | ||
|
||
case "$1" in | ||
-S*|--severity*) | ||
# don't let the user enter -S LEVEL more than once | ||
SEV=$(echo "${optarg}" | tr -d '[:space:]') | ||
if [[ -n "$SEVERITY" ]]; then | ||
printf "${RED}[Error]${RST_COLOR} Severity level already set to: %s\n" "${SEVERITY}" | ||
_help=1; _err=1 | ||
# valid if -S has any of 'info', 'warning', 'error' | ||
elif [[ "${optarg}" == "info" || "${optarg}" == "warning" || "${optarg}" == "error" ]]; then | ||
SEVERITY="$SEV" | ||
else | ||
# continue and disregard invalid severity level | ||
printf "${RED}[Error]${RST_COLOR} severity level: %s\n" "${optarg}" | ||
_err=1 | ||
fi | ||
shift "$shft_cnt" | ||
;; | ||
-e*|--exclude-code*) | ||
# strip whitespace from optarg | ||
CODE=$(echo "${optarg}" | tr -d '[:space:]') | ||
# valid if matching format SC1000-SC9999 | ||
if [[ "${optarg}" =~ ^SC[0-9]{4}$ ]]; then | ||
# if empty then populate with just error code, otherwise add pipe before new code for grep later | ||
if [[ -z "${EXCLUDE_CODES}" ]]; then | ||
EXCLUDE_CODES+="${CODE}" | ||
else | ||
EXCLUDE_CODES+="|${CODE}" | ||
fi | ||
else | ||
# continue just don't save invalid error code | ||
printf "${RED}[Error]${RST_COLOR} Invalid error code entered: %s\n" "${optarg}" | ||
_err=1 | ||
fi | ||
shift "$shft_cnt" | ||
;; | ||
-v|--view) | ||
VIEW="True" | ||
shift "$shft_cnt" | ||
;; | ||
-h|--help) | ||
_help=1 | ||
;; | ||
*) | ||
printf "${RED}[Error]${RST_COLOR} Unrecognized option: %s\n" "$1" | ||
_err=1 | ||
shift "$shft_cnt" | ||
;; | ||
esac | ||
|
||
# exit on help message | ||
if [[ $_help -eq 1 ]]; then | ||
printf "%s %s\n" "$usage" "$(basename "$0")" | ||
exit 0 | ||
fi | ||
# continue on invalid arg, let user know but don't exit | ||
if [[ "$_err" -eq 1 ]]; then | ||
printf "${RED}[Error]${RST_COLOR} Invalid argument: %s\n" "${optarg}" | ||
printf "%s %s\n" "$usage" "$(basename "$0")" | ||
fi | ||
done | ||
|
||
# if severity not set, set it to error as default | ||
if [[ -z "$SEVERITY" ]]; then | ||
SEVERITY="error" | ||
fi | ||
|
||
ROOT="$(cd "$(dirname "$0")"/.. && pwd)" | ||
SHELLCHECK_REPORT="${ROOT}/shellcheck-report.txt" | ||
|
||
NUM_CORES=1 | ||
if [[ "$OSTYPE" == "linux-gnu"* ]]; then | ||
NUM_CORES=$(grep -c ^processor /proc/cpuinfo) | ||
elif [[ "$OSTYPE" == "darwin"* ]]; then | ||
NUM_CORES=$(sysctl -n hw.ncpu) | ||
fi | ||
|
||
# run shellcheck in parallel on all tracked shell scripts | ||
# | ||
# checking status of this run will give failure if info/warning/error is found by default | ||
# | ||
# determine status by parsing shellcheck report to see if any messages | ||
# of the severity level or more strict are present to determine failure (true errors) | ||
|
||
# check if git is installed | ||
if command -v git &>/dev/null; then | ||
echo "Using git ls-files to find shell scripts..."; echo | ||
git ls-files '*.sh' | xargs -n 1 -P "$NUM_CORES" shellcheck > "$SHELLCHECK_REPORT" | ||
else | ||
echo "git is not installed. Using find to compile list of shell scripts..."; echo | ||
if [[ -z "$EXCLUDE_CODES" ]]; then | ||
find "$ROOT" -name '*.sh' -print0 | xargs -0 -n 1 -P "$NUM_CORES" shellcheck > "$SHELLCHECK_REPORT" | ||
fi | ||
fi | ||
|
||
# if shell check report exists to determine if shellcheck run was successful | ||
if [[ -z "$SHELLCHECK_REPORT" ]]; then | ||
echo "${RED}[FAIL]${RST_COLOR}Shellcheck report ${SHELLCHECK_REPORT} not found. Exiting..." | ||
exit 1 | ||
else | ||
if [[ ! -s "$SHELLCHECK_REPORT" ]]; then | ||
echo "Shellcheck report is empty: ${SHELLCHECK_REPORT}" | ||
echo "Either there are no info/warning/error messages for all shell scripts" | ||
echo "in the codebase or shellcheck failed to run successfully. Exiting..." | ||
exit 0 | ||
fi | ||
fi | ||
|
||
# view non-empty shellcheck report, includes info, warnings, errors | ||
if [[ "$VIEW" == "True" ]]; then | ||
echo "Shellcheck report: ${SHELLCHECK_REPORT}" | ||
cat "$SHELLCHECK_REPORT" | ||
fi | ||
|
||
# detect if fatal errors are in shellcheck report | ||
echo "Checking for errors in shellcheck report with severity level ${SEVERITY}" | ||
# print exclude codes and replace pipe with ', ' | ||
READABLE_EXCLUDE_CODES=("${EXCLUDE_CODES//|/, }") | ||
echo "Excluding error codes: ${READABLE_EXCLUDE_CODES[*]}"; echo | ||
|
||
# if any messages of severity level or more strict are present, use for grepping report | ||
case "$SEVERITY" in | ||
"info") REGEX_SEVERITY="info|warning|error" ;; | ||
"warning") REGEX_SEVERITY="warning|error" ;; | ||
*) REGEX_SEVERITY="error" ;; | ||
esac | ||
|
||
# just grep report for severity level if no exclude codes, otherwise pipe and exclude codes from matches | ||
SEARCH_CMD() { | ||
if [[ "${#EXCLUDE_CODES[@]}" -eq 0 ]]; then | ||
grep -E "\(${REGEX_SEVERITY}\):" "$SHELLCHECK_REPORT" | ||
else | ||
grep -E "\(${REGEX_SEVERITY}\):" "$SHELLCHECK_REPORT" | grep -v -E "${EXCLUDE_CODES}" | ||
fi | ||
} | ||
|
||
# if grep yielded no output then no violations of severity level or higher found (success) | ||
SEARCH_RESULTS="$(SEARCH_CMD)" | ||
if [[ -z "$SEARCH_RESULTS" ]]; then | ||
echo -e "${GREEN}[PASS]${RST_COLOR} Shellcheck did not detect violations scanning with severity level '${SEVERITY}'" | ||
echo; echo -e "${GREEN}Shellcheck passed.${RST_COLOR} See report: ${SHELLCHECK_REPORT}"; echo | ||
exit 0 | ||
else | ||
COUNT=$(echo "$SEARCH_RESULTS" | wc -l | tr -d '[:space:]') | ||
echo -e "${RED}[FAIL]${RST_COLOR} Shellcheck found ${RED}${COUNT}${RST_COLOR}"\ | ||
"unexcused violations scanning with severity level '${SEVERITY}'"; echo | ||
echo -e "${RED}Shellcheck failed.${RST_COLOR} See report: ${SHELLCHECK_REPORT}"; echo | ||
exit 1 | ||
fi |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters