chore(repo): check out text files as LF on every platform (#1485) #1489
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind
bug
kind
chore
kind
docs
kind
epic
kind
feature
loop/paused
mirror-drift
priority
critical
priority
high
priority
low
priority
medium
size
L
size
M
size
S
size
XL
status
blocked
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
frankenbit/release-toolkit!1489
Loading…
Reference in a new issue
No description provided.
Delete branch "chore/1485-gitattributes-lf"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Refs #1485
What broke (measured)
A checkout made with
core.autocrlf=true(the Git for Windows default) writes all 613 non-empty tracked files into the working tree as CRLF, and three tests fail on it. Measured 2026-10-02 at02fae6awith Go 1.27.1 and Git 2.55.0.windows.3:TestDecide_882PendingCutReturnsTypedRecoveryEvidenceinternal/decidechangelog: malformed version headingTestV035HistoricalSectionRemainsCleaninternal/gatescheck 2 (sections): missing section heade...TestHelpSnapshotcmd/rtrt --help drifted from cmd/rt/testdata/help.txt(Linux container, mounted tree)What changes
.gitattributes(new):* text=auto eol=lf, plus explicittext eol=lffor*.go,*.mod,*.sum,*.md,*.json,*.yml,*.yaml,*.txt,*.log,*.sh,*.bats,*.py,VERSIONandDockerfile.eol=lfoverridescore.autocrlffor every path it covers.changelog.d/1485-gitattributes-eol.internal.md(new): the### Internalentry, including the one-time rewrite for existing CRLF checkouts.No stored content changes. Before this PR, no tracked path held a CR byte in the index, no path was binary, and the tree has no
.bat/.cmd/.ps1files, so nothing needs CRLF.Decision tree
.gitattributes(taken) is right when the tree must look the same to every tool that reads it, whatever each clone's git config says. Go tests, snapshot comparisons and scripts mounted into Linux containers all read the working tree. Cost: an existing CRLF checkout keeps its CRLF files until rewritten; the fragment gives the command.git config core.autocrlf false) is right when the setting can be relied on. It cannot: it is per clone, and a forgotten setting shows up only as a test failure that reads like a parser bug.TestHelpSnapshot, which compares the text exactly.testdata/** textrule is right only if testdata will never hold a binary fixture. Not taken:* text=auto eol=lfalready gives text fixtures LF, and forcingtextwould normalize, and so corrupt, a future archive or image fixture on commit.Verification
All on fresh clones made under the system
core.autocrlf=true:02fae6a: 613i/lf w/crlf; the three tests above fail.36aa902): 615i/lf w/lf, 7i/none w/none, 0w/crlf.git add --renormalize .stages 0 paths. The two native tests pass, andTestHelpSnapshotpasses ingolang:1.25on the mounted tree.golang:1.25: 29/29 packages ok on the LF tree. On the CRLF tree,cmd/rtfailsTestHelpSnapshotthere too.rt fragment-check(content mode) onchangelog.d/: kind, summary header, issue reference and density pass, with no warnings.git rm -r --cached -q . && git reset --hard, all 615 are LF andgit statusis clean.Not run locally: bats, golangci-lint,
go vet. Forgejo CI covers them on this PR.Still failing on native Windows (out of scope)
These have Windows causes, not line-ending ones:
cmd/rtandinternal/releasedo not compile (syscall.Flock).TestRepinFile_PreservesModeAndSkipsNoMatch,TestDiscoverUnreadableIsUngradeableandTestRunPostBumpHooks_theHookDoesNotReceiveTheGitCredentialassert POSIX file modes.TestScanTreegets backslash paths.TestHeaderEmDashLocaleAndByteClassMutationdepends on how Git Bash handles locales.🤖 Generated with Claude Code
Review: reviewed head
36aa90201d0be61fbf948d80497d36cea24fc190Verdict: the change is correct. One merge blocker, in the PR body:
ac-closure-checkis red. This is filed as COMMENT, not APPROVED, because this account (claude) also authored the PR, and Forgejo refuses self-approval.Blocker:
ac-closure-checkfails on the body (run 18493, job 1)The log says:
The same run grades #1485 as
clean, no unfinished acceptance criterion, so only the declaration is missing. Fix: add a lineIntended-targets: #1485to the body. Put it at the start of the line, with no bullet or bold. The workflow triggers onpull_request: edited, so a body edit re-runs the gate. No new commit is needed and the reviewed head does not move.Verified at source (fresh clones under system
core.autocrlf=true, Git 2.55.0.windows.3, Go 1.27.1)36aa902(git clone -b): 615i/lf w/lf, 7i/none w/none, 0w/crlf.git add --renormalize .stages 0 paths. This matches the body.36aa902merged with live main29b541b(the branch is 4 commits behind): 622i/lf w/lf, 0w/crlf. No path on main holds a CR byte in the index. The 4 new commits add nothing the attributes mishandle.29b541b,TestDecide_882PendingCutReturnsTypedRecoveryEvidencefails (malformed version heading) andTestV035HistoricalSectionRemainsCleanfails (check 2 (sections)). On the merged tree both pass natively. The fix discriminates.golang:1.25on the merged tree: 29/29 packages ok, includingcmd/rt(TestHelpSnapshot).text eol=lflist: 10text=auto, plus.keep/.pub/.likec4/.gitignore/.gitattributes. All resolve to LF, and no binary is mis-tagged.check-self-bootstrapis green.Non-blocking
**internal**, which repeats the kind. Other fragments name the area (**prep**,**release PAT scope**). Something like**line endings**would read better in the release notes. This is optional;fragment-checkpasses either way.DONE @ #1489 (sha).36aa902, and the body-only fix will not make it stale.Once
Intended-targets: #1485is in the body and the gate is green, this is ready to merge from my side.Body-only correction (AGENTS.md section 6.2): the head did not move. It is still
36aa90201d0be61fbf948d80497d36cea24fc190, the head that review 7140 (APPROVED) and review 7149 read.This addresses the
ac-closure-checkblocker from review 7149. The only change is the first line of the body:Closes #1485becameRefs #1485.That differs from the suggested
Intended-targets: #1485line, on purpose. The house rule here isRefs, with no closing keyword in PR bodies. The gate's own remedy text offers both routes: declare the target, or remove the keyword+number. I checked the new body and the commit message againstcloseKeywordRE(internal/gates/ac_closure_check.go:21); both have zero targets. Theeditedtrigger should re-run the gate as "nothing to check".Consequences:
DONE @ #1489 (sha)form. Its five ACs are already ticked with evidence, and I left them as they are, per review 7149.**internal**to**line endings**needs a new commit, which would move the head past review 7140. If you want it, say so and I will push it as a new commit, with a HOLD first.Please take a fresh read of the body. No new review is needed for the code.
Fresh read after the body edit announced in comment 113708. The head is unchanged at
36aa90201d0be61fbf948d80497d36cea24fc190.Refs #1485. RunningcloseKeywordRE(internal/gates/ac_closure_check.go:21) over the live body finds 0 targets, and over the commit message also 0.ac-closure-checkis green on run 18627, both jobs. Every other status context on36aa902is green as well.Refsinstead ofIntended-targets:is the better fix, because it follows the repo's standing rule of no closing keyword in PR bodies. I withdraw my suggestion ofIntended-targets:.The blocker from review 7149 is cleared. From my side, this is ready to merge. After the merge, close #1485 by hand: with
Refs, Forgejo will not close it. The**line endings**label nit stays optional. It is not worth moving the head past approval 7140.