diff --git a/script/lint-ci.sh b/script/lint-ci.sh new file mode 100755 index 0000000..2cc2e9f --- /dev/null +++ b/script/lint-ci.sh @@ -0,0 +1,44 @@ +#!/bin/sh + +# Run all linters +# Depends: git + +# ------------------------------------------ + +# Get the full path to this script while handling spaces and symlinks correctly +scriptPath="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" +cd "$scriptPath/.." || exit 1 + +# Get all files staged for commit +files="$(git --no-pager diff --cached --name-only)" + +green="$(tput setf 2)" +red="$(tput setf 4)" +nc="$(tput sgr0)" + +failures=0 + +linters=" +lint-shell-script.sh +" + +for linter in $linters; do + echo "Running script/$linter" + if "script/$linter" "$files"; then + echo "[${green}PASS${nc}]: script/$linter" + else + echo "[${red}FAIL${nc}]: script/$linter" + failures=$(( failures + 1 )) + fi +done + +echo "Running script/lint-clang-format.sh" +# shellcheck disable=SC2086 +if script/lint-clang-format.sh "$files" && git diff --exit-code $files; then + echo "[${green}PASS${nc}]: script/lint-clang-format.sh" +else + echo "[${red}FAIL${nc}]: script/lint-clang-format.sh" + failures=$(( failures + 1 )) +fi + +exit "$failures" diff --git a/script/lint-clang-format.sh b/script/lint-clang-format.sh new file mode 100755 index 0000000..8969fc9 --- /dev/null +++ b/script/lint-clang-format.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +# Run clang-format across the codebase +# Depends: clang-format, git + +# ------------------------------------------ + +# Get the full path to this script while handling spaces and symlinks correctly +scriptPath="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" +cd "$scriptPath/.." || exit 1 + +formatter=false +if command -v clang-format-11 >/dev/null 2>&1; then + formatter="clang-format-11" +elif command -v clang-format >/dev/null 2>&1; then + formatter="clang-format" + if ! "$formatter" --version | awk '{ if (substr($NF, 1, index($NF, ".") - 1) < 11) exit 1; }'; then + echo "You are using '$("${CLANG_FORMAT}" --version)', which appears to not be clang-format 11 or later." + exit 1 + fi +else + echo "clang-format-11 is not available, but C++ files need linting!" + echo "Either skip this script, or install clang-format-11." + exit 1 +fi + +files="${1:-$(git --no-pager diff --cached --name-only)}" +files="$(echo "$files" | grep -E '\.(cpp|h)$')" + +if [ -z "$files" ]; then + echo "No .cpp or .h files to check." + exit +fi + +# shellcheck disable=SC2086 +"$formatter" --style=file -i $files diff --git a/script/lint-shell-script.sh b/script/lint-shell-script.sh new file mode 100755 index 0000000..dfcf00d --- /dev/null +++ b/script/lint-shell-script.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Run shellcheck across the codebase +# Depends: git, shellcheck + +# ------------------------------------------ + +# Get the full path to this script while handling spaces and symlinks correctly +scriptPath="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" +cd "$scriptPath/.." || exit 1 + +if ! command -v shellcheck > /dev/null 2>&1; then + echo "shellcheck is not available, but shell script files need linting!" + echo "Either skip this script, or install shellcheck." + exit 1 +fi + +files="${1:-$(git --no-pager diff --cached --name-only)}" +files="$(echo "$files" | grep -E '\.sh$')" + +if [ -z "$files" ]; then + echo "No .sh files to check." + exit +fi + +# shellcheck disable=SC2086 +shellcheck $files diff --git a/script/pre-commit.sh b/script/pre-commit.sh new file mode 100755 index 0000000..9095383 --- /dev/null +++ b/script/pre-commit.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +# Manage pre-commit hooks in .git/hooks +# Depends: - + +# ------------------------------------------ + +scriptName="$(basename "$0")" + +b="$(tput bold)" +u="$(tput smul)" +red="$(tput setf 4)" +n="$(tput sgr0)" + +help() { + cat << EOF +${b}NAME${n} + ${scriptName} - manage pre-commit hooks + +${b}SYNOPSIS${n} + ${b}${scriptName}${n} ${u}COMMAND${n} + +${b}COMMANDS${n} + ${b}install${n} + Install all pre-commit hooks. + + ${b}remove${n} + Remove all pre-commit hooks. +EOF +} + +# Exit if no option is provided +[ "$#" -eq 0 ] && help && exit 1 + +if [ ! -d ".git" ]; then + echo "${b}${red}Error:${n} please run this script from the project root" >&2 + exit 1 +fi + +currentDir="$(pwd -P)" + +# Get the path from the project root to the script +subDir="$(dirname -- "$0")" + +# Get the full path to this script while handling spaces and symlinks correctly +scriptPath="$(cd -P -- "$subDir" && pwd -P)" +cd "$scriptPath/.." || exit 1 + +hooks=" +lint-ci.sh +" + +install() { + echo "Installing pre-commit hooks" + + preCommit="$currentDir/.git/hooks/pre-commit" + if ! test -f "$preCommit"; then + touch "$preCommit" + chmod +x "$preCommit" + echo "#!/bin/sh" > "$preCommit" + fi + + for hook in $hooks; do + sed -Ei "/$hook/d" "$preCommit" + sed -Ei "\$ a $subDir/$hook" "$preCommit" + done +} + +remove() { + echo "Removing pre-commit hooks" + + preCommit=".git/hooks/pre-commit" + for hook in $hooks; do + sed -Ei "/$hook/d" "$preCommit" + done +} + +# Command handling +shift $((OPTIND - 1)) +case "$1" in + install | remove) + "$1" + ;; + *) + echo "$scriptName: invalid command '$1'" + echo "Try '$scriptName -h' for more information." + exit 1 + ;; +esac