11#! /usr/bin/env bash
22# ##############################################################################
3- # NAME : check_bash_style.sh
4- # DESCRIPTION : Comprehensive Bash style checker using ShellCheck, shfmt,
5- # and custom regex checks for prohibited patterns.
6- # AUTHOR : Adam Compton
7- # DATE CREATED : 2024-12-16
3+ # NAME : check_bash_style.sh
4+ # DESCRIPTION : Comprehensive Bash style checker using ShellCheck, shfmt,
5+ # and custom regex checks for prohibited patterns.
6+ # AUTHOR : Adam Compton
7+ # DATE CREATED : 2024-12-16
88# ##############################################################################
99# EDIT HISTORY:
1010# DATE | EDITED BY | DESCRIPTION
1111# ------------|----------------|------------------------------------------------
1212# 2024-12-16 | Adam Compton | Initial creation
1313# 2025-01-09 | Adam Compton | Moved to tools/, updated to style guide
14+ # 2026-01-11 | Adam Compton | Excluded .claude/ directory from checks
1415# ##############################################################################
1516
1617set -uo pipefail
@@ -26,23 +27,26 @@ readonly PASS FAIL
2627SHELLCHECK_RC=" .shellcheckrc"
2728STYLE_FAIL=0
2829
30+ # Directory to exclude from all checks
31+ EXCLUDE_DIR=" .claude"
32+
2933# ===============================================================================
3034# Logging Functions
3135# ===============================================================================
3236info () { printf ' [INFO ] %s\n' " $* " ; }
33- warn () { printf ' [WARN ] %s\n' " $* " >&2 ; }
3437error () { printf ' [ERROR] %s\n' " $* " >&2 ; }
3538
3639# ##############################################################################
3740# run_shellcheck
3841# ------------------------------------------------------------------------------
39- # Purpose : Run ShellCheck on all .sh files in current directory
40- # Usage : run_shellcheck
41- # Returns : Sets STYLE_FAIL=1 if ShellCheck reports errors
42+ # Purpose : Run ShellCheck on all .sh files in current directory
43+ # Usage : run_shellcheck
44+ # Returns : Sets STYLE_FAIL=1 if ShellCheck reports errors
4245# ##############################################################################
4346run_shellcheck () {
4447 info " Running ShellCheck..."
45- if ! find . -type f -name " *.sh" -print0 \
48+ # Use -prune to skip the excluded directory entirely
49+ if ! find . -type d -name " ${EXCLUDE_DIR} " -prune -o -type f -name " *.sh" -print0 \
4650 | xargs -0 shellcheck --shell=bash --rcfile=" ${SHELLCHECK_RC} " ; then
4751 error " ShellCheck failed"
4852 STYLE_FAIL=1
@@ -52,13 +56,14 @@ run_shellcheck() {
5256# ##############################################################################
5357# run_shfmt
5458# ------------------------------------------------------------------------------
55- # Purpose : Check formatting of all .sh files using shfmt
56- # Usage : run_shfmt
57- # Returns : Sets STYLE_FAIL=1 if formatting issues found
59+ # Purpose : Check formatting of all .sh files using shfmt
60+ # Usage : run_shfmt
61+ # Returns : Sets STYLE_FAIL=1 if formatting issues found
5862# ##############################################################################
5963run_shfmt () {
6064 info " Checking formatting with shfmt..."
61- if ! find . -type f -name " *.sh" -print0 \
65+ # Use -prune to skip the excluded directory entirely
66+ if ! find . -type d -name " ${EXCLUDE_DIR} " -prune -o -type f -name " *.sh" -print0 \
6267 | xargs -0 shfmt -d -i 4 -ci -bn -kp -sr -ln bash; then
6368 error " shfmt reported formatting issues"
6469 STYLE_FAIL=1
@@ -68,10 +73,10 @@ run_shfmt() {
6873# ##############################################################################
6974# run_custom_checks
7075# ------------------------------------------------------------------------------
71- # Purpose : Run custom regex-based style checks for prohibited patterns
72- # Usage : run_custom_checks
73- # Returns : Sets STYLE_FAIL=1 if prohibited patterns found
74- # Checks : set -e, echo -e, backticks, for f in $(ls)
76+ # Purpose : Run custom regex-based style checks for prohibited patterns
77+ # Usage : run_custom_checks
78+ # Returns : Sets STYLE_FAIL=1 if prohibited patterns found
79+ # Checks : set -e, echo -e, backticks, for f in $(ls)
7580# ##############################################################################
7681run_custom_checks () {
7782 info " Running custom regex style checks..."
@@ -80,28 +85,28 @@ run_custom_checks() {
8085 local awk_filter=" { code=\$ 0; sub(/^[^:]+:[0-9]+:/,\"\" ,code); if (code !~ /^[[:space:]]*#/) print \$ 0 }"
8186
8287 # Ban set -e / errexit
83- if grep -rn --include=" *.sh" -E " set[[:space:]]+-?(e|eu)|set -o errexit" . \
88+ if grep -rn --exclude-dir= " ${EXCLUDE_DIR} " -- include=" *.sh" -E " set[[:space:]]+-?(e|eu)|set -o errexit" . \
8489 | awk " ${awk_filter} " | grep -v " check_bash_style.sh" ; then
8590 error " Found disallowed use of 'set -e'"
8691 STYLE_FAIL=1
8792 fi
8893
8994 # Ban echo -e
90- if grep -rn --include=" *.sh" -E " echo[[:space:]]+-e" . \
95+ if grep -rn --exclude-dir= " ${EXCLUDE_DIR} " -- include=" *.sh" -E " echo[[:space:]]+-e" . \
9196 | awk " ${awk_filter} " | grep -v " check_bash_style.sh" ; then
9297 error " Found disallowed 'echo -e'"
9398 STYLE_FAIL=1
9499 fi
95100
96101 # Ban backticks
97- if grep -rn --include=" *.sh" ' `' . \
102+ if grep -rn --exclude-dir= " ${EXCLUDE_DIR} " -- include=" *.sh" ' `' . \
98103 | awk " ${awk_filter} " | grep -v " check_bash_style.sh" ; then
99104 error " Found disallowed backticks (use \$ (...) instead)"
100105 STYLE_FAIL=1
101106 fi
102107
103108 # Ban for f in $(ls)
104- if grep -rn --include=" *.sh" -E " for[[:space:]]+f[[:space:]]+in[[:space:]]+\$\\ (ls" . \
109+ if grep -rn --exclude-dir= " ${EXCLUDE_DIR} " -- include=" *.sh" -E " for[[:space:]]+f[[:space:]]+in[[:space:]]+\$\\ (ls" . \
105110 | awk " ${awk_filter} " | grep -v " check_bash_style.sh" ; then
106111 error " Found disallowed 'for f in \$ (ls)'"
107112 STYLE_FAIL=1
@@ -111,9 +116,9 @@ run_custom_checks() {
111116# ##############################################################################
112117# main
113118# ------------------------------------------------------------------------------
114- # Purpose : Main entry point - runs all style checks
115- # Usage : main "$@"
116- # Returns : PASS (0) if all checks pass, FAIL (1) otherwise
119+ # Purpose : Main entry point - runs all style checks
120+ # Usage : main "$@"
121+ # Returns : PASS (0) if all checks pass, FAIL (1) otherwise
117122# ##############################################################################
118123main () {
119124 run_shellcheck
0 commit comments