check-exec-bits.sh 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. #!/usr/bin/env bash
  2. # Check git index modes, not filesystem permissions (Git Bash can fake those).
  3. # Root tests/*.sh and scripts/*.sh are excluded because tracked 100644 files
  4. # there are legitimate today; this gate covers skill-bundled scripts only.
  5. set -u
  6. usage() {
  7. cat <<'EOF'
  8. Usage: tests/check-exec-bits.sh [--help]
  9. Print tracked skill scripts whose git mode is not 100755, one per line.
  10. EXAMPLES
  11. bash tests/check-exec-bits.sh
  12. bash tests/check-exec-bits.sh --help
  13. Exit codes: 0 clean, 1 findings, 2 usage error.
  14. EOF
  15. }
  16. case "${1-}" in
  17. "") ;;
  18. -h|--help) usage; exit 0 ;;
  19. *) echo "check-exec-bits: unknown argument: $1" >&2; usage >&2; exit 2 ;;
  20. esac
  21. [ "$#" -le 1 ] || { echo "check-exec-bits: too many arguments" >&2; usage >&2; exit 2; }
  22. ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
  23. cd "$ROOT" || exit 2
  24. findings=0
  25. candidates=0
  26. while read -r mode object stage path; do
  27. case "$path" in
  28. skills/*/scripts/*)
  29. # A "script" is: known script extension, OR extensionless with a
  30. # shebang (e.g. introspect/scripts/cc-session). Data/doc files and
  31. # .gitkeep placeholders are not scripts.
  32. base="${path##*/}"
  33. is_script=0
  34. case "$base" in
  35. *.sh|*.py|*.mjs|*.js) is_script=1 ;;
  36. *.*|.*) ;; # other extensions / dotfiles: not gated
  37. *) head -c 2 -- "$path" 2>/dev/null | grep -q '^#!' && is_script=1 ;;
  38. esac
  39. [ "$is_script" -eq 1 ] || continue
  40. candidates=$((candidates + 1))
  41. if [ "$mode" != "100755" ]; then
  42. echo "$path"
  43. findings=$((findings + 1))
  44. fi
  45. ;;
  46. esac
  47. done < <(git ls-files -s -- skills)
  48. # Empty candidate list means the scan itself is broken (not in a repo, path
  49. # typo, git missing) — a gate that checked nothing must not report clean.
  50. if [ "$candidates" -eq 0 ]; then
  51. echo "check-exec-bits: self-check failed — zero candidate scripts found (broken scan?)" >&2
  52. exit 2
  53. fi
  54. if [ "$findings" -eq 0 ]; then
  55. echo "check-exec-bits: clean ($candidates scripts checked)" >&2
  56. exit 0
  57. fi
  58. echo "check-exec-bits: $findings file(s) are not tracked as 100755" >&2
  59. exit 1