From ad07a75490fc2df6a73a9b59b9ea770f0e84f22c Mon Sep 17 00:00:00 2001 From: mptyl Date: Tue, 4 Aug 2026 14:33:45 +0200 Subject: [PATCH] build: enforce portable line endings --- .editorconfig | 9 ++++++ .gitattributes | 12 ++++++++ docker/core.Dockerfile | 4 ++- scripts/test-verify-line-endings.sh | 41 +++++++++++++++++++++++++++ scripts/verify-line-endings.sh | 43 +++++++++++++++++++++++++++++ 5 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100755 scripts/test-verify-line-endings.sh create mode 100755 scripts/verify-line-endings.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..d4bf3f22 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true + +[*.ps1] +end_of_line = crlf diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..9cbe4cac --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +* text=auto +*.sh text eol=lf +Dockerfile* text eol=lf +*.Dockerfile text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.json text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +*.py text eol=lf +*.md text eol=lf +*.ps1 text eol=crlf diff --git a/docker/core.Dockerfile b/docker/core.Dockerfile index ba6d6afc..dbaaa63b 100644 --- a/docker/core.Dockerfile +++ b/docker/core.Dockerfile @@ -70,8 +70,10 @@ ENV PATH="/opt/venv/bin:/usr/local/bin:$PATH" \ PI_BIN=pi \ HOME=/home/thoth +COPY scripts/verify-line-endings.sh /usr/local/bin/verify-line-endings COPY docker/core-entrypoint.sh docker/session-migrate.sh docker/ensure-pi-trust.mjs /app/docker/ -RUN chmod +x /app/docker/core-entrypoint.sh /app/docker/session-migrate.sh +RUN /usr/local/bin/verify-line-endings /app/docker \ + && chmod +x /app/docker/core-entrypoint.sh /app/docker/session-migrate.sh WORKDIR /app/backend USER thoth diff --git a/scripts/test-verify-line-endings.sh b/scripts/test-verify-line-endings.sh new file mode 100755 index 00000000..b4eab323 --- /dev/null +++ b/scripts/test-verify-line-endings.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +fixture_root="$(mktemp -d)" +trap 'rm -rf "$fixture_root"' EXIT + +printf '#!/bin/sh\nexit 0\n' > "$fixture_root/ok.sh" +printf '#!/bin/sh\r\nexit 0\r\n' > "$fixture_root/bad.sh" +printf 'services:\r\n app:\r\n image: example\r\n' > "$fixture_root/compose.yaml" +printf 'FROM scratch\r\n' > "$fixture_root/Dockerfile" + +set +e +output="$("$repo_root/scripts/verify-line-endings.sh" "$fixture_root" 2>&1)" +status=$? +set -e + +if [[ $status -eq 0 ]]; then + echo "expected CRLF rejection" >&2 + exit 1 +fi + +for expected_path in bad.sh compose.yaml Dockerfile; do + if ! grep -Fqx "$expected_path" <<< "$output"; then + echo "missing CRLF path: $expected_path" >&2 + exit 1 + fi +done + +if grep -Fq 'ok.sh' <<< "$output"; then + echo "reported LF-only path: ok.sh" >&2 + exit 1 +fi + +printf '#!/bin/sh\nexit 0\n' > "$fixture_root/bad.sh" +printf 'services:\n app:\n image: example\n' > "$fixture_root/compose.yaml" +printf 'FROM scratch\n' > "$fixture_root/Dockerfile" + +"$repo_root/scripts/verify-line-endings.sh" "$fixture_root" + +echo "line-ending verifier tests passed" diff --git a/scripts/verify-line-endings.sh b/scripts/verify-line-endings.sh new file mode 100755 index 00000000..d83984fc --- /dev/null +++ b/scripts/verify-line-endings.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -gt 1 ]]; then + echo "usage: $0 [root]" >&2 + exit 2 +fi + +if [[ $# -eq 1 ]]; then + root="$1" + fixture_mode=true +else + root="$(git rev-parse --show-toplevel)" + fixture_mode=false +fi + +if [[ ! -d "$root" ]]; then + echo "not a directory: $root" >&2 + exit 2 +fi + +root="$(cd "$root" && pwd)" +offenders=() + +while IFS= read -r -d '' file; do + if LC_ALL=C grep -Il $'\r' "$file" >/dev/null; then + offenders+=("${file#"$root"/}") + fi +done < <( + if [[ "$fixture_mode" == true ]]; then + find "$root" -type f -print0 + else + git -C "$root" ls-files -z | while IFS= read -r -d '' path; do + printf '%s\0' "$root/$path" + done + fi +) + +if (( ${#offenders[@]} )); then + printf '%s\n' "CRLF line endings detected:" >&2 + printf '%s\n' "${offenders[@]}" >&2 + exit 1 +fi