Skip to content

feat: Add TOML support, new output formats, git integration, GitHub Action, and directory comparison#11

Merged
pfrederiksen merged 3 commits intomainfrom
feat/quick-wins-and-medium
Jan 17, 2026
Merged

feat: Add TOML support, new output formats, git integration, GitHub Action, and directory comparison#11
pfrederiksen merged 3 commits intomainfrom
feat/quick-wins-and-medium

Conversation

@pfrederiksen
Copy link
Copy Markdown
Owner

Summary

This PR implements six high-impact enhancements selected for v0.3.0 release:

Highest Impact Quick Wins ✨

  1. Diff Statistics (--stat): Git-style statistics summary showing changes per path with visual bars
  2. Side-by-Side View: Two-column comparison format familiar from traditional diff tools
  3. Git Diff Driver: Integration with git for automatic semantic diffs on config files

Best Medium Effort Features 🚀

  1. TOML Support: Parser for Rust (Cargo.toml) and Python (pyproject.toml) configuration files
  2. GitHub Action: Published action for easy CI/CD workflow integration
  3. Directory Comparison (--recursive): Compare entire config directories at once

Key Changes

New Output Formats

  • --stat: Shows per-path statistics with visual bars (like git diff --stat)
  • --side-by-side: Two-column old/new value comparison
  • --git-diff: Git-compatible diff format for diff driver integration

TOML Format Support

  • Added FormatTOML to parse package
  • Handles TOML arrays of tables (e.g., [[bin]])
  • Comprehensive test coverage (8 test cases)
  • Works with Cargo.toml, pyproject.toml, and other TOML configs

Directory Comparison

  • New --recursive / -r flag
  • Scans directories for all config files (.yaml, .json, .hcl, .toml, .tf)
  • Reports added/removed files
  • Diffs files that exist in both directories
  • Summary statistics across all files

Git Integration

  • Comprehensive setup guide: docs/GIT_DIFF_DRIVER.md
  • .gitattributes configuration examples
  • Automatic semantic diffs via git diff driver

GitHub Action

  • Action manifest: action.yml
  • Full documentation: docs/GITHUB_ACTION.md
  • Supports all CLI flags as inputs
  • Ready for GitHub Actions marketplace

Documentation

  • ✅ Updated README.md with all new features
  • ✅ Updated CLAUDE.md to document v0.3.0 development
  • ✅ Created git diff driver setup guide
  • ✅ Created GitHub Action usage guide with examples

Testing

  • ✅ Added comprehensive TOML parser tests
  • ✅ All existing tests pass (9 packages)
  • ✅ Tested new output formats manually
  • ✅ Tested directory comparison with sample directories

Files Changed

  • New Files: 6 (action.yml, 2 docs, 3 report formatters)
  • Modified Files: 11 (parse, CLI, docs)
  • Lines Changed: +1439/-27

Examples

TOML Diff

configdiff old.toml new.toml

Directory Comparison

configdiff -r ./config-old ./config-new

Statistics Output

configdiff old.yaml new.yaml -o stat

GitHub Action Usage

- uses: pfrederiksen/configdiff@v0.3.0
  with:
    old-file: config/prod.yaml
    new-file: config/staging.yaml

Next Steps

  • Merge PR
  • Tag release v0.3.0
  • Publish GitHub Action to marketplace
  • Update Homebrew formula (automated via GoReleaser)

🤖 Generated with Claude Code

pfrederiksen and others added 2 commits January 16, 2026 16:51
…ction, and directory comparison

This major feature update adds six highly-requested enhancements:

**New Output Formats:**
- Add --stat flag for git-style statistics summary with visual bars
- Add --side-by-side output format for two-column comparison view
- Add --git-diff output format for git diff driver integration

**TOML Format Support:**
- Add TOML parser for Rust (Cargo.toml) and Python (pyproject.toml) files
- Handle TOML arrays of tables (e.g., [[bin]])
- Add comprehensive test coverage for TOML parsing

**Directory Comparison:**
- Add --recursive/-r flag for comparing entire directories
- Automatically detect and compare all config files (.yaml, .json, .hcl, .toml, .tf)
- Report added/removed files and diff existing files
- Display per-file summaries and overall statistics

**Git Diff Driver Integration:**
- Create comprehensive setup guide at docs/GIT_DIFF_DRIVER.md
- Enable automatic use of configdiff for git diff on config files
- Support for .gitattributes configuration

**GitHub Action:**
- Add action.yml for GitHub Actions marketplace
- Create detailed documentation at docs/GITHUB_ACTION.md
- Support all CLI flags as action inputs
- Enable easy CI/CD integration

**Documentation Updates:**
- Update README.md with all new features and examples
- Update CLAUDE.md to document v0.3.0 development
- Add git diff driver setup guide
- Add GitHub Action usage guide with examples

**Tests:**
- Add comprehensive TOML parser tests (8 test cases)
- All existing tests continue to pass
- Test coverage for array of tables and nested structures

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…hold

Add test coverage for all new features added in this PR:

**Report Format Tests:**
- Add TestGenerateStat with 3 test cases
- Add TestGenerateSideBySide with 4 test cases
- Add TestGenerateGitDiff with 4 test cases
- Coverage for report package increased from 40.0% to 89.6%

**Directory Comparison Tests:**
- Add TestCollectConfigFiles to verify file scanning
- Add TestFileExists with 3 test cases
- Add TestCompareDirectories for full directory diff workflow
- Add TestCompareWithDirectories for error handling
- Coverage for cmd/configdiff increased from 29.4% to 69.1%

**Coverage Results:**
- Total coverage: 84.3% (above 80% threshold)
- All tests passing
- New functions fully tested

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Jan 17, 2026

Code Review

I've reviewed PR #11 and found 2 issues that need to be addressed:


Issue 1: Missing Test Coverage for New Report Functions

Files affected: report/stat.go, report/sidebyside.go, report/gitdiff.go

The three new report format functions lack test coverage, which violates the project's comprehensive testing standards.

Functions without tests:

CLAUDE.md violation: Per CLAUDE.md line 10:

Test-Driven Development: Comprehensive test coverage with unit, integration, and golden tests

Suggested fix: Add comprehensive tests following existing patterns in report/report_test.go:

  1. Unit tests in report/report_test.go with table-driven test cases
  2. Golden file tests in testdata/report/ directory
  3. Integration tests in cmd/configdiff/main_test.go

Issue 2: Exit Code Bug in Directory Comparison

File: cmd/configdiff/compare.go
Location: Line 166

When using --exit-code flag with directory comparison, the program exits immediately upon finding the first file with differences, preventing comparison of remaining files and display of the summary.

The bug: The compareFiles() function calls os.Exit(1) directly at line 116 when --exit-code is set and changes are found. When called from compareDirectories(), this causes premature termination.

Current behavior:

  1. Start comparing directory files
  2. First file with differences triggers os.Exit(1) in compareFiles()
  3. Program terminates immediately
  4. Remaining files never compared
  5. Summary never displayed

Expected behavior: All files should be compared, summary displayed, then exit with code 1 if any changes were found.

Suggested fix: Refactor compareFiles() to return a boolean indicating whether changes were found, instead of calling os.Exit(1) directly. Let compareDirectories() aggregate the results and handle the exit code after all comparisons complete.

**Issue**: When using --exit-code flag with directory comparison, the
program would exit immediately upon finding the first file with
differences, preventing comparison of remaining files and display of
the summary.

**Root Cause**: Both compareFiles() and compareDirectories() were
calling os.Exit(1) directly when --exit-code was set and changes were
found. When compareFiles() was called from compareDirectories(), this
caused premature termination.

**Previous Behavior**:
1. Start comparing directory files
2. First file with differences triggers os.Exit(1) in compareFiles()
3. Program terminates immediately
4. Remaining files never compared
5. Summary never displayed

**Fixed Behavior**:
1. All files are compared
2. Summary is displayed
3. Then exit with code 1 if any changes were found

**Changes**:
- Refactored compareFiles() to return bool indicating if changes found
- Refactored compareDirectories() to return bool indicating if changes found
- Moved exit code handling to compare() function (main entry point)
- Both functions now return results instead of calling os.Exit directly
- The compare() function aggregates results and handles os.Exit

**Tests Added**:
- TestCompareFilesReturnValue: Verifies compareFiles returns correct boolean
- TestDirectoryComparisonDoesNotExitEarly: Ensures all files are compared
  before exit, even with --exit-code flag set

**Verification**:
- All existing tests pass
- New tests verify the fix
- Manual testing confirms all files compared before exit
- Coverage remains above threshold (84.1%)

Fixes issue #2 from PR review comments.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Jan 17, 2026

Code Review - Testing inline comments

This is a test comment to verify comment functionality.

@pfrederiksen pfrederiksen merged commit 3580551 into main Jan 17, 2026
6 checks passed
@pfrederiksen pfrederiksen deleted the feat/quick-wins-and-medium branch January 17, 2026 01:06
@claude
Copy link
Copy Markdown

claude bot commented Jan 17, 2026

Code Review

Found 4 high-priority issues that need to be addressed before merging:


1. 🐛 Bug: Premature exit during directory comparison with --exit-code

Location: cmd/configdiff/compare.go:116

Issue: When using --exit-code flag with directory comparison (-r), the program exits immediately after finding the first file with differences, preventing comparison of remaining files.

The compareFiles() function calls os.Exit(1) when it detects changes with --exit-code enabled:

)
if err != nil {
return false, fmt.Errorf("diff failed: %w", err)
}

Since compareDirectories() calls compareFiles() within its file iteration loop (line 166), the first file with differences causes the entire program to terminate before other files can be compared.

Expected behavior: Compare all files in both directories, then exit with code 1 if any differences were found.

Fix: Modify compareFiles() to return a boolean indicating whether changes were found, instead of calling os.Exit(1). Update compareDirectories() to track this and handle the exit code after all comparisons complete (lines 198-201 already have this logic).


2. 🐛 Bug: Exit code not tracking content changes

Location: cmd/configdiff/compare.go:160-173

Issue: In compareDirectories(), the hasAnyChanges variable is only set to true for added/removed files (lines 180, 187), but never for files that exist in both directories but have content differences.

When compareFiles() is called (line 166), the result indicates whether an error occurred, but not whether the files had differences. This means hasAnyChanges remains false even when file contents differ.

Impact: When running configdiff -r ./dir1 ./dir2 --exit-code, if files have content changes but no files were added/removed, the program will exit with code 0 (success) instead of code 1 (changes detected). This breaks CI/CD workflows that rely on exit codes.

Fix: Modify compareFiles() to return both an error and a boolean indicating changes. Update compareDirectories() to set hasAnyChanges = true when content differences are detected:

hasChanges, err := compareFiles(oldPath, newPath)
if err != nil {
    return err
}
if hasChanges {
    hasAnyChanges = true
}

3. ❌ Missing Feature: GitHub Action outputs not implemented

Location: action.yml:54-57 and cmd/configdiff/

Issue: The GitHub Action declares outputs (has-changes and diff-output) in action.yml:

configdiff/action.yml

Lines 53 to 57 in 72525b6

outputs:
has-changes:
description: 'Whether any changes were detected (true/false)'
diff-output:
description: 'The diff output'

However, the CLI code does not detect the GitHub Actions environment or write to the GITHUB_OUTPUT file. The documentation shows examples using these outputs:

if: steps.diff.outputs.has-changes == 'true'
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '## Configuration Changes\n\n```\n${{ steps.diff.outputs.diff-output }}\n```'

But these outputs will always be empty because they are never populated.

Fix: Add GitHub Actions detection and output writing to the CLI. When GITHUB_OUTPUT environment variable is set, write outputs in this format:

echo "has-changes=true" >> $GITHUB_OUTPUT
echo "diff-output<<EOF" >> $GITHUB_OUTPUT
echo "$diff_text" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

Suggested implementation in cmd/configdiff/compare.go after the comparison completes.


4. 📋 CLAUDE.md Violation: Missing golden tests for new output formats

Location: report/stat.go, report/sidebyside.go, report/gitdiff.go

Issue: CLAUDE.md explicitly requires "Comprehensive test coverage with unit, integration, and golden tests" (line 10) and emphasizes "Comprehensive testing - Table-driven, golden files, fuzz tests" (line 37).

Reference:

- **Test-Driven Development**: Comprehensive test coverage with unit, integration, and golden tests

The existing report format uses golden test files in testdata/report/*.txt (see report/report_test.go:15-211). However, the three new output formats only have substring-matching tests (report/report_test.go:419-650) that verify certain strings appear in output, not the exact formatting.

Missing golden files:

  • testdata/report/stat_*.txt
  • testdata/report/side_by_side_*.txt
  • testdata/report/git_diff_*.txt

Fix: Add golden test files for each new format following the existing pattern in TestGenerate(). This ensures exact output formatting is tested and regressions are caught. Use the -update flag pattern to generate/update golden files.


Summary

  • 2 critical bugs that will cause incorrect behavior with --exit-code and directory comparison
  • 1 incomplete feature (GitHub Action outputs)
  • 1 test coverage gap violating documented standards

Please address these issues before merging. Happy to clarify any of these findings!

pfrederiksen added a commit that referenced this pull request Jan 17, 2026
Add GitHub Actions integration with has-changes and diff-output outputs.
Convert output format tests to use golden files for better maintainability.

Changes:
- Add writeGitHubOutputs() to write GitHub Actions outputs
- Detect GITHUB_OUTPUT env var and write outputs in correct format
- Convert TestGenerateStat to golden file tests (4 test cases)
- Convert TestGenerateSideBySide to golden file tests (5 test cases)
- Convert TestGenerateGitDiff to golden file tests (5 test cases)
- Add comprehensive tests for GitHub Actions output functionality
- Fix variable redeclaration in compareFiles()

Addresses PR review comments from #11:
- Issue 3: GitHub Action outputs now fully implemented
- Issue 4: Golden tests added for all new output formats

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
pfrederiksen added a commit that referenced this pull request Jan 17, 2026
* feat: Implement GitHub Action outputs and golden tests

Add GitHub Actions integration with has-changes and diff-output outputs.
Convert output format tests to use golden files for better maintainability.

Changes:
- Add writeGitHubOutputs() to write GitHub Actions outputs
- Detect GITHUB_OUTPUT env var and write outputs in correct format
- Convert TestGenerateStat to golden file tests (4 test cases)
- Convert TestGenerateSideBySide to golden file tests (5 test cases)
- Convert TestGenerateGitDiff to golden file tests (5 test cases)
- Add comprehensive tests for GitHub Actions output functionality
- Fix variable redeclaration in compareFiles()

Addresses PR review comments from #11:
- Issue 3: GitHub Action outputs now fully implemented
- Issue 4: Golden tests added for all new output formats

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Use randomized delimiter for GitHub Actions heredoc output

Fix heredoc delimiter injection vulnerability by using a cryptographically
random delimiter instead of fixed "EOF" string.

Security issue: A config file containing "EOF" on its own line could
prematurely terminate the heredoc and potentially inject arbitrary
GitHub Actions workflow commands.

Changes:
- Generate random 16-byte delimiter prefixed with "ghadelimiter_"
- Update tests to verify random delimiter format
- Add test case specifically for EOF injection attack
- Add reference to GitHub Actions multiline string documentation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <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.

1 participant