Skip to content
Merged
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
31 changes: 27 additions & 4 deletions bin/y-script-lint
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,13 @@ is_node() { [ "$1" = "node" ] || [ "$1" = "typescript" ]; }

# --- Static checks ---

check_header_pipefail() { grep -qE '^set -e(o pipefail)?$' "$1" 2>/dev/null; }
check_header_pipefail() { grep -qE '^set -e(u?o pipefail)?$' "$1" 2>/dev/null; }
check_header_debug() { grep -qE '^\[ -z "\$DEBUG" \] \|\| set -x' "$1" 2>/dev/null; }

check_help_handler() {
local file="$1" lang="$2"
if is_shell "$lang"; then
grep -qE '(\-h\|--help|"\$1" = "help"|"\$1" = "--help"|\$1 =~ help|^\s*help\))' "$file" 2>/dev/null
grep -qE '(\-h\|--help|"\$1" = "help"|"\$1" = "--help"|\$1 =~ help|^\s*help\)|[|]help\))' "$file" 2>/dev/null
elif is_node "$lang"; then
grep -qE "process\.argv\.includes\(['\"](-h|--help)['\"]" "$file" 2>/dev/null ||
grep -qE "process\.argv\[2\] === ['\"]help['\"]" "$file" 2>/dev/null
Expand All @@ -173,6 +173,28 @@ check_no_eval() {
fi
}

# y-script-lint or-true: Flag || true unless suppressed via ylint disable comment.
# Suppress with: # y-script-lint:disable=or-true # reason
# on the line before, or as an inline comment on the same line.
check_no_or_true() {
local file="$1" lang="$2"
is_shell "$lang" || return 0
local prev=""
while IFS= read -r line; do
# Skip comment-only lines
[[ "$line" =~ ^[[:space:]]*# ]] && { prev="$line"; continue; }
if echo "$line" | grep -qE '\|\|\s*true'; then
# Allow if same line has inline disable comment
echo "$line" | grep -qE '#.*y-script-lint:disable=or-true' && { prev="$line"; continue; }
# Allow if previous line has disable directive
echo "$prev" | grep -qE '#.*y-script-lint:disable=or-true' && { prev="$line"; continue; }
return 1
fi
prev="$line"
done < "$file"
return 0
}

# Detect trivial binary wrapper: header + YBIN + y-bin-download + versioned exec, nothing else
is_bin_wrapper() {
local file="$1"
Expand Down Expand Up @@ -313,7 +335,7 @@ lint_file() {
[ "$lang" = "unknown" ] && { checks_shebang=false; errors+=("unrecognized or missing shebang"); }

if is_shell "$lang"; then
check_header_pipefail "$file" || { checks_header=false; errors+=("missing set -eo pipefail or set -e"); }
check_header_pipefail "$file" || { checks_header=false; errors+=("missing set -eo pipefail (or -euo pipefail) or set -e"); }
check_header_debug "$file" || { checks_debug=false; errors+=("missing DEBUG pattern: [ -z \"\\\$DEBUG\" ] || set -x"); }
else
checks_header=null; checks_debug=null
Expand All @@ -335,6 +357,7 @@ lint_file() {
fi
check_no_npx "$file" "$lang" || { checks_no_npx=false; errors+=("uses npx"); }
check_no_eval "$file" "$lang" || { checks_no_eval=false; errors+=("uses eval"); }
check_no_or_true "$file" "$lang" || { errors+=("y-script-lint or-true: || true without justification (add: # y-script-lint:disable=or-true # reason)"); }

if is_shell "$lang" && [ "$HAS_SHELLCHECK" = "true" ]; then
run_shellcheck "$file" && checks_shellcheck=true || { checks_shellcheck=false; errors+=("shellcheck --severity=error failed"); }
Expand Down Expand Up @@ -389,7 +412,7 @@ lint_file() {
for err in "${errors[@]}"; do
local level="WARN"
case "$err" in
"unrecognized or missing shebang"|"missing set -eo pipefail"*|"uses npx"|"uses eval"|"shellcheck "*) level="FAIL" ;;
"unrecognized or missing shebang"|"missing set -eo pipefail"*|"uses npx"|"uses eval"|"y-script-lint or-true"*|"shellcheck "*) level="FAIL" ;;
esac
echo " $level $err"
done
Expand Down
Loading