From 0cc35581ca39be8937bc2f9bdd7b21f980efdadd Mon Sep 17 00:00:00 2001 From: James Fredley Date: Sat, 2 May 2026 09:38:09 -0400 Subject: [PATCH] fix: enforce LF line endings for shell scripts, gradlew, KEYS 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. Assisted-by: claude-code:claude-opus-4-7 --- .gitattributes | 53 ++++++++++++++++++++++++++++++++++++++++++++++ etc/bin/Dockerfile | 9 ++++++++ 2 files changed, 62 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..4928cb0dfca --- /dev/null +++ b/.gitattributes @@ -0,0 +1,53 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Git auto-detects text vs binary; text files are stored as LF in the index. +* text=auto + +# Files that MUST be LF on every platform. +# Shell scripts, the Gradle wrapper launcher, the published KEYS file, and the +# Dockerfile all run on Linux during release verification. CRLF in any of them +# breaks the verification flow (kernel rejects scripts with bash\r shebangs and +# verify-keys.sh fails the byte-for-byte SVN comparison). +*.sh text eol=lf +gradlew text eol=lf +KEYS text eol=lf +Dockerfile text eol=lf +*.properties text eol=lf + +# Files that MUST be CRLF (Windows scripts). +*.bat text eol=crlf +*.cmd text eol=crlf +gradlew.bat text eol=crlf + +# Common binary types - never normalize. +*.jar binary +*.zip binary +*.gz binary +*.tgz binary +*.7z binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.pdf binary +*.p12 binary +*.gpg binary +*.keystore binary +*.class binary +*.so binary +*.dylib binary +*.dll binary diff --git a/etc/bin/Dockerfile b/etc/bin/Dockerfile index 085843dbd13..675049eb75e 100644 --- a/etc/bin/Dockerfile +++ b/etc/bin/Dockerfile @@ -38,6 +38,15 @@ ADD --chown=groovy gradlew /home/groovy/scripts ADD --chown=groovy KEYS /home/groovy/scripts ADD --chown=groovy gradle/wrapper/gradle-wrapper.jar /home/groovy/scripts/gradle/wrapper ADD --chown=groovy gradle/wrapper/gradle-wrapper.properties /home/groovy/scripts/gradle/wrapper + +# Defensive line-ending normalization. The repository's .gitattributes pins +# shell scripts, gradlew, KEYS, and the Dockerfile to LF on every platform, but +# committers with a pre-existing local checkout under core.autocrlf=true may +# still feed CRLF into the build context. Linux refuses scripts with bash\r +# shebangs and verify-keys.sh would fail the byte-for-byte SVN comparison. +# Strip CRs from any text file that must be LF on Linux. +RUN find /home/groovy/scripts -type f \( -name '*.sh' -o -name 'gradlew' -o -name 'KEYS' -o -name '*.properties' \) -exec sed -i 's/\r$//' {} \; + ENV PATH="/home/groovy/scripts:/home/groovy/scripts/etc/bin:$PATH" ENV CI=true ENV LANG=C.UTF-8