Skip to content

Enforce two conventions in lint that nothing was checking - #698

Open
dyve wants to merge 4 commits into
mainfrom
lint-no-trailing-whitespace
Open

dyve wants to merge 4 commits into
mainfrom
lint-no-trailing-whitespace

Conversation

@dyve

@dyve dyve commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Two rules this repository has stated for a long time and never enforced, so both held only while someone remembered. Each is a git grep or a grep in just lint, in the same shape as the existing _no-invisible-characters recipe.

Trailing whitespace

.editorconfig has set trim_trailing_whitespace = true for every file since the beginning, and nothing checked it. ruff only sees Python, and the invisible-character check looks for a specific set of code points that does not include a plain space. Two lines had slipped past: django-bootstrap4's .github/dependabot.yml and django-icons' docs/index.rst.

lint checks, format fixes, matching the existing contract. Both are scoped by git grep -lIP, so -I excludes binaries and the pattern excludes files already correct — on a clean tree the fixer is never invoked.

Markdown hard line breaks are preserved. Two trailing spaces in Markdown are a hard break, not an accident, and an earlier revision of this branch deleted them silently. format now passes --markdown-linebreak-ext=md, so exactly two spaces survive while one or three are still stripped, and the check excludes .md. The two have to agree: a check that failed on something format deliberately preserves would leave you stuck between them.

Checking rather than auto-fixing elsewhere is deliberate. These packages render HTML, and trailing space inside a template is output, so stripping it can change a rendered page. That belongs in a diff a person reads.

On the "is there a standard" question: yes, and this uses it. format runs trailing-whitespace-fixer from pre-commit-hooks through uvx, the same implementation the pre-commit framework runs for this hook, invoked the way this justfile already runs ruff, typos and twine. No .pre-commit-config.yaml, no pre-commit install step for contributors, no second place where linting is configured.

One ## Unreleased heading

Branches that each add the heading instead of appending to the existing one merge without a conflict, because the two sides touch different lines. This repository reached four before anyone looked, and several were mine. The consequence is not cosmetic: cutting a release stamps the first heading and leaves the rest, so those entries ship as pending notes for a version that already went out. That is the failure #694 was written to prevent, arriving through a door the changelog itself left open.

The check also rejects the heading sitting below a released section, and deliberately allows none at all, which is the correct state between a cut and the next change.

This branch's rebase consolidated the four headings into one, so main is already repaired by merging it.

Verified

Clean tree passes and format touches nothing. Trailing whitespace outside Markdown fails lint and is fixed by format. A Markdown hard break survives both. A single stray space in Markdown is still cleaned. Two ## Unreleased headings fail with the line numbers printed, a misplaced heading fails, and no heading passes.

Propagation waits on django-bootstrap4#912 and django-icons#668, since the whitespace check would fail in those two until their stray lines are gone.

🤖 Generated with Claude Code

dyve and others added 3 commits October 11, 2026 12:33
.editorconfig has set trim_trailing_whitespace = true for every file all along,
and nothing enforced it. ruff only sees Python, and the invisible-character
check looks for a specific set of code points that does not include a plain
space. So the rule held only where someone's editor honoured it, and two lines
had slipped past: a trailing line in django-bootstrap4's .github/dependabot.yml
and one in django-icons' docs/index.rst. Finding those by hand is not a
safeguard.

lint checks rather than fixes, deliberately. These packages render HTML, and
trailing space inside a template is output, so stripping it can change what a
page looks like. That belongs in a diff a person reads, not in a check that
quietly rewrites files. just format does the fixing, and the test suites assert
rendered output, so a change that matters will fail there.

format uses trailing-whitespace-fixer from pre-commit-hooks through uvx: the
same implementation the pre-commit standard runs, invoked the way this justfile
already invokes ruff, typos and twine, so it needs no config file and no
developer install step. Verified it leaves binary files untouched, which
matters because django-icons tracks a PNG.

Final newlines are deliberately left alone, even though .editorconfig also asks
for them. Eleven files across the five packages lack one, and they include
Django templates where appending a newline changes rendered output, LICENSE,
and that PNG. That needs case-by-case judgement rather than a blanket fixer.

Verified both directions: lint exits 1 naming the file and telling you to run
just format, and passes again once format has run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The first version piped every tracked file into the fixer, binaries included.
It was harmless, since the fixer detects binary content and leaves it alone,
but handing a PNG to a whitespace fixer and trusting it to decline is the wrong
shape. It also opened several hundred files to change at most a couple.

The file list now comes from the same `git grep -lIP` the check uses, so -I
excludes binaries and the pattern excludes everything that is already correct.
On a clean tree the fixer is not invoked at all.

Verified: with trailing whitespace planted in docs/db.rst the list is exactly
that one file, lint exits 1, format fixes it, lint passes. On a clean tree
format reports nothing changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two trailing spaces in Markdown are a hard line break, not an accident, and the
first version of this change deleted them silently. pre-commit-hooks has
--markdown-linebreak-ext for exactly that and I had not used it.

The fixer now keeps exactly two trailing spaces in .md and still strips one or
three, and the check excludes .md entirely. They have to agree: a check that
failed on something `just format` deliberately preserves would leave you stuck
between the two with no way out.

Verified all four cases: a clean tree is untouched, a Markdown hard break
survives format and passes lint, a single stray space in Markdown is still
cleaned, and trailing whitespace outside Markdown still fails lint.

Rebasing onto main also meant resolving the Unreleased section, which had grown
four separate `## Unreleased` headings. Several of my pull requests prepended a
new one instead of appending to the existing one, and git merged them cleanly
because they sat at different line positions. All ten entries are now under a
single heading. A release cut against the old file would have stamped the first
heading and orphaned the other three.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dyve
dyve force-pushed the lint-no-trailing-whitespace branch from 5509b0d to 33131f8 Compare October 11, 2026 10:33
Branches that each add `## Unreleased` instead of appending to the existing one
merge without a conflict, because the two sides touch different lines. This
repository reached four headings before anyone looked, and several of them were
mine.

The consequence is not cosmetic. Cutting a release stamps the first heading and
leaves the rest untouched, so the entries below ship as unreleased notes for a
version that already went out. That is the failure `just release-check` was
widened to prevent, arriving through a door the changelog itself left open.

Also rejects the heading sitting below a released section, which is the same
mistake with the insertion point moved.

None at all passes, deliberately. That is the correct state between a cut and
the next change, and MAINTAINING.md already says what it means.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dyve dyve changed the title Fail lint on trailing whitespace, and strip it in format Enforce two conventions in lint that nothing was checking Oct 11, 2026

This branch has not been deployed

No deployments
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.

1 participant