Skip to content

fix: enforce LF line endings for shell scripts, gradlew, KEYS#15622

Open
jamesfredley wants to merge 1 commit into7.0.xfrom
fix/eol-normalization-7.0.x
Open

fix: enforce LF line endings for shell scripts, gradlew, KEYS#15622
jamesfredley wants to merge 1 commit into7.0.xfrom
fix/eol-normalization-7.0.x

Conversation

@jamesfredley
Copy link
Copy Markdown
Contributor

Summary

7.0.x companion to #15620. Pin the line endings of shell scripts, gradlew, KEYS, and the Dockerfile to LF on every platform, and add a defensive sed strip step in etc/bin/Dockerfile, so the release verification container is reproducible from a Windows committer's checkout.

Why

There is no .gitattributes in the repo today. With Git for Windows' default core.autocrlf=true, every Windows checkout converts these files from LF to CRLF in the working tree. The Dockerfile's ADD then preserves the CRLF into the image, which produces three independent failures during release verification:

  1. The Linux kernel refuses to execute scripts with bash\r in the shebang, so gradlew and every verify-*.sh script fail with cannot execute: required file not found / /usr/bin/env: 'bash\r': No such file or directory.
  2. verify-keys.sh shasum-512s the in-tree KEYS file and compares against the canonical copy at https://dist.apache.org/repos/dist/release/grails/KEYS. The two byte-streams differ by exactly one \r per line (52 bytes total) and the comparison fails even though the content is byte-identical after normalization.
  3. By extension, verify.sh aborts at its first step (verify-keys.sh) on any Windows committer's machine. The container documented in RELEASE.md is unusable as-is.

CI on ubuntu-latest and any Linux/macOS verifier are unaffected. The bug is only visible to a Windows committer because git's checkout-time conversion is the layer that introduces the corruption.

The line-ending bug exists identically on 7.0.x: etc/bin/Dockerfile and the verify-*.sh scripts are byte-for-byte the same as on 8.0.x (the only Dockerfile delta between branches is the JDK version - 17.0.18 here vs 21.0.7 on 8.0.x), and 7.0.x has the same identical missing-.gitattributes situation.

Why fix this at the .gitattributes layer

.gitattributes is the only mechanism in the repo that operates at git's working-tree write path. Other code-style mechanisms either don't run at the right time or don't cover the right files:

Tool Audits *.sh / gradlew / KEYS? Runs at checkout?
.editorconfig (existing - groovy/java only) No No (editor-time)
Checkstyle / codeStyle task No (Java/Groovy only) No (build-time)
CodeNarc No (Groovy only; LineEnding rule not enabled) No (build-time)
.gitattributes (this PR) Yes Yes

Changes

.gitattributes (new)

  • * text=auto - sane default; git auto-detects text vs binary, stores text as LF in the index
  • *.sh, gradlew, KEYS, Dockerfile, *.properties -> text eol=lf (must be LF on Linux verifier and CI)
  • *.bat, *.cmd, gradlew.bat -> text eol=crlf (must be CRLF on Windows)
  • Common binary types (*.jar, *.zip, *.png, *.pdf, *.gpg, *.keystore, ...) marked binary so git never normalizes them

etc/bin/Dockerfile

Add a RUN sed -i 's/\r$//' step after the ADD lines, scoped to *.sh, gradlew, KEYS, and *.properties. This is belt-and-braces: even if a committer's working tree still has CRLF (because their local checkout predates this PR and they have not re-checked-out / renormalized), the image they build will still be correct.

Index impact

Verified ahead of time on the 7.0.x worktree:

$ git ls-files --eol gradlew etc/bin/verify.sh KEYS gradlew.bat etc/bin/Dockerfile i/lf w/lf attr/text eol=lf KEYS i/lf w/lf attr/text eol=lf etc/bin/Dockerfile i/lf w/lf attr/text eol=lf etc/bin/verify.sh i/lf w/lf attr/text eol=lf gradlew i/crlf w/crlf attr/text eol=crlf gradlew.bat

Every affected file is already LF in the index. This PR therefore does not require git add --renormalize . and does not produce any blob churn - it only changes what git checkout writes to disk on the next checkout, and pins the contract going forward.

Related

With both PRs merged, the container verification flow is reproducible on every platform for both the 7.0.x maintenance line and the 8.0.x development line.

The release verification flow runs in a Linux container built from
etc/bin/Dockerfile, but the repository had no .gitattributes. With
Git for Windows' default core.autocrlf=true, every Windows checkout
converts gradlew, etc/bin/*.sh, the Dockerfile, and the KEYS file
from LF to CRLF in the working tree. The Dockerfile's ADD then
preserves those CRLFs into the image, with three failure modes:

1. The Linux kernel refuses to execute scripts with `bash\r` in the
   shebang, so gradlew and every verify-*.sh script fails with
   `cannot execute: required file not found`.
2. verify-keys.sh hashes the in-tree KEYS file and compares against
   https://dist.apache.org/repos/dist/release/grails/KEYS, which is
   stored as LF. The hashes mismatch even though the content is
   byte-identical after line-ending normalization.
3. By extension, verify.sh aborts at its first step on any Windows
   committer's machine.

Fix at the right layer (git's working-tree write path):

- Add .gitattributes pinning *.sh, gradlew, KEYS, Dockerfile, and
  *.properties to eol=lf, while keeping *.bat / *.cmd / gradlew.bat
  as eol=crlf and marking common binary types as binary so git
  never tries to normalize them. Default `* text=auto` lets git
  auto-detect everything else.
- Add a defensive sed-strip step in etc/bin/Dockerfile after the
  ADD lines, so even if a committer feeds a CRLF-corrupted build
  context (existing local checkout under autocrlf=true), the image
  still ends up with LF scripts.

Existing index content is unchanged: every affected file is already
stored as LF in the index. This commit only changes what `git
checkout` writes to the working tree on Windows and adds belt-and-
braces normalization at image-build time.

CI on ubuntu-latest is unaffected. Linux/macOS verifiers are
unaffected. Windows verifiers can now run the container build flow
documented in RELEASE.md without manual workarounds.

This is the 7.0.x companion to #15620 (against
8.0.x). The line-ending bug exists identically on 7.0.x because the
Dockerfile and verify-*.sh scripts are the same byte-for-byte.

Assisted-by: claude-code:claude-opus-4-7
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@testlens-app
Copy link
Copy Markdown

testlens-app Bot commented May 2, 2026

✅ All tests passed ✅

🏷️ Commit: 86410d0
▶️ Tests: 33563 executed
⚪️ Checks: 37/37 completed


Learn more about TestLens at testlens.app.

Copy link
Copy Markdown
Contributor

@jdaugherty jdaugherty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dockerfile change appears to be windows specific, but we want our environments to function the same so I'm approving.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants