Skip to content

Pin upstream-version state file to LF, carving out the JSON CRLF rule - #171

Closed
ptr727 wants to merge 1 commit into
developfrom
upstream-version-state-lf-eol
Closed

Pin upstream-version state file to LF, carving out the JSON CRLF rule#171
ptr727 wants to merge 1 commit into
developfrom
upstream-version-state-lf-eol

Conversation

@ptr727

@ptr727 ptr727 commented Jun 21, 2026

Copy link
Copy Markdown
Owner

Closes #170.

Follow-up found while adopting the multi-key check-upstream-version-task.yml (#168 / #169) in ptr727/ESPHome-NonRoot.

Problem

The task writes the state file with printf '%s\n' on the Ubuntu runner — LF — but .editorconfig (a verbatim downstream carry) defaults .json/.jsonc to CRLF. In a CRLF-default downstream the committed upstream-version.json (LF, to match the task and avoid churn) contradicts the repo's stated rule: open it in an .editorconfig-aware editor and save, and it flips to CRLF, so the next tracker run rewrites it back to LF — a spurious Canonicalize upstream version state file PR.

Fix (issue option 2 — keep the task's LF output deterministic, carve the file out)

  • .gitattributes: upstream-version.json text eol=lf — enforces LF at the git layer, so even an editor's CRLF save is normalized back on commit. The blanket * -text does not do this (it only disables normalization), so this override is the part that actually kills the churn.
  • .editorconfig: an [upstream-version.json] end_of_line = lf stanza (placed right after the JSON CRLF stanza so it overrides, above the .NET-only boundary) so editors save LF in the first place.
  • Docs: the task header and the AGENTS.md wrapper-repo bullet now state the state file is canonical LF and that a non-default state-file name must be added to both carve-outs.

Both governance files are verbatim carries, so default-named downstreams get the fix automatically with no extra adoption step.

Validation

  • git check-attr text eol -- upstream-version.jsontext: set, eol: lf; an unrelated version.json stays text: unset (blanket rule untouched).
  • Both files retain CRLF line endings (edited byte-accurately, not via the LF-converting editor path); YAML parses; markdownlint-cli2 clean on AGENTS.md.

🤖 Generated with Claude Code

check-upstream-version-task writes the state file LF on the runner, but
.editorconfig defaults .json/.jsonc to CRLF, so in a CRLF-default downstream the
committed upstream-version.json contradicts the stated rule and an editor save
flips it to CRLF, churning a spurious canonicalize bump on the next run.

Carve the default upstream-version.json out as canonical LF: a .gitattributes
`text eol=lf` rule normalizes an editor CRLF save back on commit (the blanket
`* -text` does not), and an .editorconfig stanza makes editors save LF. Both are
verbatim carries, so default-named downstreams get the fix automatically;
document the canonical-LF contract and the custom-state-file caveat in the task
header and AGENTS.md.

Closes #170

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 21, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR resolves #170 by eliminating line-ending churn for the bot-maintained upstream-version.json state file produced by check-upstream-version-task.yml. It formalizes upstream-version.json as a canonical LF file (an explicit exception to the repository-wide CRLF rule for *.json) via Git- and editor-level carve-outs, and documents the adoption implications for downstreams that rename the state file.

Changes:

  • Add a .gitattributes override to enforce LF for upstream-version.json at the Git layer.
  • Add an .editorconfig stanza to ensure editors save upstream-version.json as LF (overriding the *.json CRLF default).
  • Document the LF exception and the need to update both carve-outs when using a non-default state-file name.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
AGENTS.md Documents upstream-version.json as canonical LF and calls out the required carve-outs when renaming the state file.
.github/workflows/check-upstream-version-task.yml Adds header documentation explaining why the state file is LF and how the repo enforces that deterministically.
.gitattributes Enforces upstream-version.json as text eol=lf to prevent CRLF commits from editor saves.
.editorconfig Carves out upstream-version.json with end_of_line = lf to align editor behavior with the task output.

@ptr727

ptr727 commented Jun 21, 2026

Copy link
Copy Markdown
Owner Author

Replacing this approach. Option 2 (carving the state file out to LF) defeats the reason JSON defaults to CRLF: a Windows vanilla-editor save produces CRLF, so an LF carve-out would churn on exactly the platform the CRLF rule protects. Reworking as option 1 - the task writes the state file as CRLF, honoring the .editorconfig rule with no special case - in a fresh PR.

@ptr727 ptr727 closed this Jun 21, 2026
@ptr727
ptr727 deleted the upstream-version-state-lf-eol branch June 21, 2026 21:33
ptr727 added a commit that referenced this pull request Jun 21, 2026
Closes #170. Supersedes #171 (which took the wrong approach — see
below).

## Problem

`check-upstream-version-task.yml` wrote the state file with `printf
'%s\n'` on the runner — **LF** — but `.editorconfig` defaults
`.json`/`.jsonc` to **CRLF**. The committed `upstream-version.json`
therefore contradicted the repo's own rule: open it in an
`.editorconfig`-aware (or vanilla Windows) editor and save, and it flips
to CRLF, so the next tracker run rewrites it back to LF — a spurious
`Canonicalize upstream version state file` PR.

## Why not carve the file out to LF (the closed #171 / issue option 2)

The whole point of defaulting JSON to CRLF is that **Windows
vanilla-editor saves don't break things**. Carving the state file out to
LF inverts that — a Windows save produces CRLF and now violates the
carve-out, churning on exactly the platform the CRLF rule protects. So
the fix belongs in the task, not in a special case.

## Fix (issue option 1)

Write the state file with **CRLF**, honoring `.editorconfig`'s rule for
`.json` with no per-file carve-out. `jq` emits LF, so convert line
endings on write:

```sh
printf '%s\n' "$new" | sed 's/$/\r/' > "$STATE_FILE"
```

`.editorconfig` and `.gitattributes` are untouched.

## Validation

- Output is CRLF (`file` → `JSON text data`, `cat -A` → `^M$` line ends)
and **idempotent**: rewriting identical content is byte-for-byte
identical, so no churn / no spurious PR.
- `jq -S '.'` still round-trips the CRLF file, so the old-state read and
union diff are unaffected (comparison is on parsed objects,
EOL-agnostic).
- A Windows vanilla-editor save now produces CRLF that **matches** the
committed file.
- YAML parses.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

2 participants