Skip to content

feat: pin line endings so a checkout produces the same bytes on every platform - #5

Merged
hendrikebbers merged 4 commits into
mainfrom
feat/002-pinned-line-endings
Sep 10, 2026
Merged

hendrikebbers merged 4 commits into
mainfrom
feat/002-pinned-line-endings

Conversation

@herbie-bot

Copy link
Copy Markdown

Summary

The repository pinned line endings nowhere, so the bytes of every text file depended
on the Git configuration of whoever checked it out. On a default Windows install
(core.autocrlf=true) every text file arrives with CRLF. This affects the parent
directly and not only its children: with packaging=pom and no
flatten-maven-plugin, pom.xml is deployed to Maven Central verbatim, so a CRLF
checkout would publish different .pom bytes.

Spec

  • Spec folder: docs/specs/002-pinned-line-endings/
  • Design: docs/specs/002-pinned-line-endings/design.md
  • Behaviors: docs/specs/002-pinned-line-endings/behaviors.md
  • Steps and measured coverage: docs/specs/002-pinned-line-endings/steps.md

Changes

  • .gitattributes* text=auto eol=lf baseline, so LF is forced in the working
    tree regardless of the developer's core.autocrlf. A bare text=auto would not do
    this: it normalizes only the index and leaves checkout to local configuration.
    *.bat/*.cmd are the single exception, because cmd.exe is fragile with LF-only
    scripts. Explicit binary markers remove the dependency on Git's content heuristic
    for file types added later.
  • .editorconfig — the Open Elements standard, with [*.java] corrected to
    Google Java Format (2 spaces, 100 columns, continuation 4). The shared baseline
    specifies 4 spaces and 120 columns, which contradicts the formatter this parent
    enforces via Spotless — an editor following it would fight spotless:apply on every
    save. A [*.{cmd,bat}] block matches the CRLF pin.
  • pom.xml<lineEndings>UNIX</lineEndings> on Spotless. The plugin defaults to
    GIT_ATTRIBUTES, so in a child repository without its own .gitattributes it
    actively writes CRLF on Windows. This is the only part of the change Maven
    inheritance can deliver to children.
  • README.md — what is pinned and why the published .pom is at stake, a
    copy-paste block for child projects, and an explicit statement of how narrow the
    inherited Spotless setting is.

Verification

No test framework exists here — this is a packaging=pom project with no Java
sources — so the scenarios were verified with Git and Maven commands:

  • No tracked file carries CRLF or mixed endings in the index; git add --renormalize .
    stages nothing, so the change contributes no incidental byte churn.
  • Checking files out with core.autocrlf=true, core.autocrlf=false and
    core.eol=crlf forced all yield LF for text files and CRLF for *.cmd — the
    attribute beats local configuration, which is the behaviour the whole design rests on.
  • Probe files confirm normalization: CRLF-authored .java, .cmd, .md and .toml
    all land as i/lf; a file without line endings stays untouched; an explicitly marked
    .p12 is stored byte-identical to disk.
  • ./mvnw -Pfull-build clean verify and ./mvnw spotless:check pass.

16 of 23 scenarios measured, 2 verified by inspection, 5 not verifiable here
they need a child project or a real Windows machine. Both gaps are recorded in
docs/TODO.md.

Scope boundaries

Cross-OS reproducibility is not claimed. Line endings are one variance source
among several; JDK-dependent Javadoc output, archive entry ordering and locale
defaults are untouched. There is no CI guard, by decision.

Note for the reviewer

This branch and feat/001-reproducible-build-timestamp both add a README section
and will conflict there on the second merge. The conflict is additive — both sections
are kept.

Closes #4

🤖 Generated with Claude Code

hendrikebbers and others added 4 commits September 10, 2026 18:39
Links spec 002 to issue #4 and moves it out of the open backlog.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The repository pinned line endings nowhere, so the bytes of every text file
depended on the Git configuration of whoever checked it out. On a default
Windows install (core.autocrlf=true) all text files arrive with CRLF.

This affects the parent directly, not only its children: with packaging=pom
and no flatten-maven-plugin, pom.xml is deployed to Maven Central verbatim,
so a CRLF checkout would publish different .pom bytes.

- .gitattributes: `* text=auto eol=lf` baseline, so LF is forced in the
  working tree regardless of core.autocrlf. `*.bat`/`*.cmd` are the single
  exception (cmd.exe is fragile with LF-only scripts); explicit binary
  markers remove the dependency on Git's content heuristic for future
  additions.
- .editorconfig: the Open Elements standard, with [*.java] corrected to
  Google Java Format (2 spaces, 100 columns). The shared baseline specifies
  4/120, which would make the editor fight the formatter this parent
  enforces. A [*.{cmd,bat}] block matches the CRLF pin.
- pom.xml: Spotless `<lineEndings>UNIX</lineEndings>`, inherited by every
  child, so spotless:apply cannot write CRLF on a Windows machine.
- README: what is pinned, why the published .pom is at stake, and a
  copy-paste block for child projects, which Maven inheritance cannot
  deliver for them.

Verified: no tracked file carries CRLF in the index, `git add --renormalize .`
stages nothing, and checking files out with core.autocrlf=true or
core.eol=crlf forced still yields LF for text and CRLF for *.cmd.

Cross-OS reproducibility is not claimed; it stays in docs/TODO.md.

Closes #4

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Spec review found the README offered child projects only the .gitattributes
block, although the design corrected .editorconfig's [*.java] precisely so
that children copying it would stop fighting the formatter. The child section
now points at both files and says why.

Also corrects steps.md, which listed a "Related behaviors" entry that does not
exist in behaviors.md, and replaces its coverage table with what implementation
actually measured: 16 of 23 scenarios verified by executed commands, 2 by
inspection, 5 needing a child project or a real Windows machine.

Quality review: clarifies that the keystore entries in .gitattributes say how
Git must treat such a file if it appears, not that one belongs in the
repository. The absent CLAUDE.md is recorded in docs/TODO.md rather than
generated here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@hendrikebbers
hendrikebbers merged commit 74888a2 into main Sep 10, 2026
1 check passed
@hendrikebbers
hendrikebbers deleted the feat/002-pinned-line-endings branch September 10, 2026 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pin line endings so a checkout produces the same bytes on every platform

2 participants