Skip to content

feat(normalize): add an opt-in vaar normalize command - #65

Open
Nitjsefnie wants to merge 1 commit into
envaar:mainfrom
Nitjsefnie-OSC:feat/normalize-command
Open

Nitjsefnie wants to merge 1 commit into
envaar:mainfrom
Nitjsefnie-OSC:feat/normalize-command

Conversation

@Nitjsefnie

@Nitjsefnie Nitjsefnie commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds vaar normalize — the blunt whole-file formatting pass, as its own opt-in surface.

Why

Since #54 made --fix strictly finding-scoped, a uniform CRLF file produces no line-ending finding, so --fix correctly leaves it alone. This gives that normalization an explicit home instead of folding it back into --fix, so a blunt rewrite is always something you asked for by name.

I went with a subcommand rather than a --fix --normalize flag because it matches how diff is structured — same discoverPaths handling, same --target / --target-dir shape, same report style.

Closes #63

Type

  • Bug fix
  • New rule
  • Parser change/fix
  • Reporter change/fix
  • Documentation
  • CI / Release
  • Build
  • Performance improvement/change
  • Internal refactor
  • Hotfix

None of the listed types fits cleanly — this adds a new CLI subcommand rather than a rule, parser or reporter change. Tell me which you'd file it under and I'll tick it.

Breaking change

  • No
  • Yes (describe required migration)

lint --fix is untouched.

Changes

  • internal/lint/normalize.go (+84) — composes the existing envfile transforms unconditionally: line endings (uniform CRLF and lone CR forced to LF), BOM, trailing whitespace, blank-run collapsing
  • internal/cli/normalize.go (+62) — the subcommand, with --target / --target-dir
  • internal/cli/root.go (+1) — registration
  • internal/lint/normalize_test.go (+131), internal/cli/normalize_test.go (+169) — 11 new tests

Files already normalized are left byte-identical and produce no output, so it is idempotent and safe to run repeatedly.

User-visible changes

A new subcommand. On KEY=value\r\nNEXT=2\r\n, verified end-to-end with the built binary:

$ vaar lint --fix        # unchanged behaviour — no finding, file untouched
$ vaar normalize
normalized .env
$ vaar normalize         # idempotent — prints nothing

Validation

Commands run:

  • gofmt -w <touched Go files>gofmt -l empty
  • make lint
  • go test ./...
  • go run ./cmd/vaar lint ...

Additional commands:

go vet ./...
go build ./...
go mod tidy          # no diff
CI lint / JSON / fix smoke tests — all clean

TestLintCommandTargetFileReportsUnreadablePath and TestLintCommandReturnsInternalErrorWhenDiscoveryFails fail in my environment, but they fail identically on a clean checkout: I'm running as uid 0 and chmod 0 doesn't block root. Unrelated to this change.

Tests

  • Added tests

11 new, covering the blunt-vs-scoped divergence at both the NormalizeData/FixData level and through the CLI (the regression guard for #54's behavior), the full transform pipeline on a BOM+CRLF+trailing-whitespace+blank-run fixture, already-normalized files left byte-identical, idempotence, and the --target / --target-dir / both-flags-rejected / positional-args paths.

They were checked against a revert rather than just observed passing: dropping NormalizeLineEndings from the pipeline produces 5 failures (got "KEY=value\r\nNEXT=2\r\n" want "KEY=value\nNEXT=2\n"), and removing the feature entirely fails them with undefined: lint.NormalizeData.

The existing --fix tests are unmodified and passing.

No docs entry, since diff doesn't have one either — happy to add both if you'd like.

Footer

Generated by Claude Opus 5 (implementation), Claude Opus 4.8 (brief, review)

Summary by CodeRabbit

  • New Features
    • Added a normalize CLI command for whole-file dotenv formatting.
    • Supports targeting a specific file or directory.
    • Normalizes line endings, whitespace, blank lines, and file headers.
    • Reports each file that was rewritten while leaving already-normalized files unchanged.
  • Bug Fixes
    • Added validation for conflicting target options and unsupported positional arguments.

lint --fix is strictly finding-scoped: each rule's fix half runs only when
that rule actually reports a finding. A file with uniform CRLF (or lone CR)
line endings has no line-ending finding, so --fix now leaves its endings
alone by design.

Add vaar normalize as the explicit opt-in surface for the blunt whole-file
pass that behavior removed. It applies StripBOM, NormalizeLineEndings,
TrimTrailingWhitespace, CollapseBlankLines and TrimFinalBlankLines
unconditionally, honours --target and --target-dir, reports each rewritten
path and skips files that are already normalized so repeated runs are
stable. lint --fix semantics are untouched.

Closes envaar#63

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an opt-in normalize CLI subcommand backed by whole-file dotenv normalization. The pipeline standardizes BOMs, line endings, whitespace, blank lines, and final newlines, while target flags control scope and unchanged files are not rewritten.

Changes

Dotenv normalization

Layer / File(s) Summary
Normalization pipeline and file rewriting
internal/lint/normalize.go, internal/lint/normalize_test.go
Adds ordered whole-file byte transforms, conditional file rewriting, rewritten-path reporting, and tests for normalization, unchanged files, and repeated runs.
Normalize command and scope validation
internal/cli/normalize.go, internal/cli/root.go, internal/cli/normalize_test.go
Adds and registers the Cobra command, supports --target and --target-dir, reports normalized files, and tests scope and invalid-usage behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant normalize
  participant lint.Normalize
  participant DotenvFiles
  User->>normalize: run normalize with optional target scope
  normalize->>lint.Normalize: pass lint.Options
  lint.Normalize->>DotenvFiles: discover and read dotenv files
  lint.Normalize->>DotenvFiles: write changed normalized content
  lint.Normalize-->>normalize: return rewritten paths
  normalize-->>User: print normalized paths
Loading

Possibly related PRs

  • envaar/vaar#45: Introduces the decomposed dotenv byte transforms reused by the new normalization pipeline.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR adds a separate normalize command for blunt whole-file rewriting and keeps --fix finding-scoped, satisfying issue #63.
Out of Scope Changes check ✅ Passed The changes stay focused on the normalize command, its lint implementation, and tests, with no obvious unrelated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding an opt-in normalize command.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/cli/normalize.go`:
- Line 39: Update the normalize command’s Cobra argument configuration from
rejecting positional arguments to accepting path arguments, then pass those
arguments into the normalization scope used by the command. Revise
TestNormalizeCommandRejectsPositionalArguments to assert the new accepted-path
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dbdc82d2-2b1a-4b52-9456-e09f996302ad

📥 Commits

Reviewing files that changed from the base of the PR and between e34569f and e328740.

📒 Files selected for processing (5)
  • internal/cli/normalize.go
  • internal/cli/normalize_test.go
  • internal/cli/root.go
  • internal/lint/normalize.go
  • internal/lint/normalize_test.go

Comment thread internal/cli/normalize.go

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

Adds an opt-in vaar normalize CLI subcommand that performs a blunt, whole-file dotenv normalization pass (independent of lint findings), addressing the post-#54 gap where uniform CRLF/lone-CR files are intentionally untouched by lint --fix.

Changes:

  • Introduces a lint.NormalizeData pipeline that always applies the canonical envfile transforms (BOM stripping, line ending normalization to LF, whitespace trimming, blank-line collapsing, final newline normalization).
  • Adds lint.Normalize(opts) to discover target files and rewrite only when bytes change (idempotent; reports rewritten paths).
  • Adds vaar normalize subcommand with --target / --target-dir support and comprehensive unit/CLI tests covering the blunt-vs-scoped behavior and idempotence.

Reviewed changes

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

Show a summary per file
File Description
internal/lint/normalize.go Implements the unconditional normalization pipeline and file-rewrite loop with idempotent behavior and path reporting.
internal/lint/normalize_test.go Tests blunt normalization vs finding-scoped fixes, full pipeline composition, rewrite reporting, and stability across runs.
internal/cli/root.go Registers the new normalize subcommand on the root command.
internal/cli/normalize.go Implements the Cobra subcommand, help text, and --target/--target-dir flag wiring, printing rewritten files to stdout.
internal/cli/normalize_test.go End-to-end CLI tests for rewrite behavior, idempotence, scoping flags, and argument validation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

feat: opt-in blunt formatting normalization, separate from finding-scoped --fix

2 participants