diff --git a/.gitattributes b/.gitattributes index 06a4e43d8..a76ea0bd6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,9 @@ # Vendored tree-sitter grammars — machine-generated, hide from stats and diffs internal/cbm/vendored/grammars/**/parser.c linguist-generated=true diff=false internal/cbm/vendored/grammars/**/scanner.c linguist-generated=true diff=false + +# Shell entrypoints must check out LF everywhere: a CRLF shebang line breaks +# them under WSL/MSYS (`bash\r: no such file`). The *.sh rule covers scripts; +# the git hooks are extensionless and need explicit entries. (#1272) +scripts/git-hooks/commit-msg text eol=lf +scripts/hooks/pre-commit text eol=lf diff --git a/scripts/test.sh b/scripts/test.sh index 0ec489a59..0dc575b04 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -260,6 +260,9 @@ bash "$ROOT/tests/test_release_gate_chain_contract.sh" echo "=== Step 0t: test runtime isolation contract (#1691) ===" bash "$ROOT/tests/test_runtime_isolation_contract.sh" +echo "=== Step 0u: shell line-ending contract ===" +bash "$ROOT/tests/test_shell_line_endings.sh" + # Verify compiler supports target arch verify_compiler "$CC" diff --git a/tests/test_shell_line_endings.sh b/tests/test_shell_line_endings.sh new file mode 100644 index 000000000..a6f3fc387 --- /dev/null +++ b/tests/test_shell_line_endings.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Regression guard: shell entrypoints must remain LF in Windows checkouts so +# they can run directly from WSL and MSYS without a `bash\r` shebang failure. +# +# Distilled from PR #1272 by @xumian520, who both hit the breakage under +# core.autocrlf=true and wrote this contract so it stays fixed. The .sh rule +# itself landed via #1314; the extensionless git hooks need their own +# entries, which this guard also covers. + +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +failures=0 +checked=0 +while IFS= read -r -d '' path && + IFS= read -r -d '' attribute && + IFS= read -r -d '' eol; do + checked=$((checked + 1)) + if [[ "$eol" != "lf" ]]; then + echo "FAIL: $path must declare eol=lf (got ${eol:-unset})" >&2 + failures=$((failures + 1)) + fi +done < <( + git ls-files -z '*.sh' 'scripts/git-hooks/*' 'scripts/hooks/*' | + git check-attr -z --stdin eol +) + +if ((checked == 0)); then + echo "FAIL: the line-ending contract matched no files — the glob set is broken" >&2 + exit 1 +fi + +if ((failures > 0)); then + echo "FAIL: $failures shell entrypoint(s) lack an LF checkout contract" >&2 + exit 1 +fi + +echo "Shell line-ending contract passed ($checked files)"