Skip to content

Commit 8503d61

Browse files
pfrederiksenclaude
andauthored
Add major enhancements: HCL support, colored output, shell completion, Docker, and config files (#10)
* feat: Add major enhancements - HCL support, colored output, shell completion, Docker, and config files This commit adds several significant enhancements to configdiff: ## New Features ### HCL Format Support - Add full HCL parsing support using hashicorp/hcl/v2 - Support for Terraform .tf and .tfvars files - Recursive conversion of cty.Value types to normalized tree - Test coverage with simple and complex HCL examples ### Colored Output - Add color-coded report output using fatih/color - Green for additions, red for removals, yellow for modifications - Respects NO_COLOR environment variable - Disable with --no-color flag ### Shell Completion - Add completion command for bash, zsh, fish, and powershell - Includes installation instructions for each shell - Uses Cobra's built-in completion generation ### Docker Support - Add Dockerfile based on Alpine Linux - Configure GoReleaser for multi-arch Docker builds (amd64, arm64) - Publish to ghcr.io/pfrederiksen/configdiff - Docker manifests for platform-agnostic pulls ### Configuration File Support - Add .configdiffrc and .configdiff.yaml support - Configure default options: ignore paths, array keys, output format, etc. - Multiple location support: current dir and home dir - CLI flags override config file settings - Merge behavior for arrays and maps ## Documentation - Update README with all new features - Add HCL examples and use cases - Document configuration file format and locations - Add Docker installation and usage instructions - Add shell completion setup instructions - Update project status to reflect completed features ## Tests - Add comprehensive tests for config package - Add HCL parsing tests - Update report tests for color support - All tests passing with >80% coverage Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: Address PR review issues - lint errors, tests, and coverage This commit fixes all issues identified in the PR review: ## Lint Fixes - Fix errcheck violations in completion.go by using RunE and checking errors - All Gen*Completion calls now properly return and check errors ## Bug Fixes - Fix global state pollution in report.go color.NoColor - Save original value and restore via defer - Prevents color settings from persisting across function calls - Critical for library usage and test reliability - Fix ApplyConfigDefaults to handle empty OutputFormat - Check for both "" and "report" as default values - Config values now properly apply when CLI uses defaults ## Test Coverage Improvements - Add comprehensive TestParseHCL with 13 test cases - Boolean, number, string primitives - Objects, nested objects - Lists of strings and objects - Multiple top-level attributes - Invalid HCL and empty HCL - All cases verify paths are set - Add comprehensive TestCLIOptions_ApplyConfigDefaults with 8 test cases - Empty config no-op - Config defaults when CLI empty - CLI precedence over config - Ignore paths deduplication and merging - Array keys merging - Boolean, string, and numeric defaults - All merge scenarios covered - Add integration tests using testdata files - TestParseHCL_Integration uses testdata/hcl/*.hcl files - Verifies real-world HCL parsing - Addresses unused testdata concern ## Coverage Results - Total coverage: 84.5% (above 80% threshold) - parse package: 82.2% - internal/cli: 91.8% - internal/config: 100.0% - All packages above or near 80% All tests passing, all lint checks passing, coverage threshold met. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 3521697 commit 8503d61

23 files changed

+1403
-29
lines changed

.goreleaser.yml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,42 @@ brews:
113113
system "#{bin}/configdiff version"
114114
install: |
115115
bin.install "configdiff"
116+
117+
dockers:
118+
- image_templates:
119+
- "ghcr.io/pfrederiksen/configdiff:{{ .Version }}-amd64"
120+
- "ghcr.io/pfrederiksen/configdiff:latest-amd64"
121+
use: buildx
122+
build_flag_templates:
123+
- "--platform=linux/amd64"
124+
- "--label=org.opencontainers.image.created={{.Date}}"
125+
- "--label=org.opencontainers.image.title={{.ProjectName}}"
126+
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
127+
- "--label=org.opencontainers.image.version={{.Version}}"
128+
- "--label=org.opencontainers.image.source=https://github.com/pfrederiksen/configdiff"
129+
dockerfile: Dockerfile
130+
131+
- image_templates:
132+
- "ghcr.io/pfrederiksen/configdiff:{{ .Version }}-arm64"
133+
- "ghcr.io/pfrederiksen/configdiff:latest-arm64"
134+
use: buildx
135+
build_flag_templates:
136+
- "--platform=linux/arm64"
137+
- "--label=org.opencontainers.image.created={{.Date}}"
138+
- "--label=org.opencontainers.image.title={{.ProjectName}}"
139+
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
140+
- "--label=org.opencontainers.image.version={{.Version}}"
141+
- "--label=org.opencontainers.image.source=https://github.com/pfrederiksen/configdiff"
142+
goarch: arm64
143+
dockerfile: Dockerfile
144+
145+
docker_manifests:
146+
- name_template: "ghcr.io/pfrederiksen/configdiff:{{ .Version }}"
147+
image_templates:
148+
- "ghcr.io/pfrederiksen/configdiff:{{ .Version }}-amd64"
149+
- "ghcr.io/pfrederiksen/configdiff:{{ .Version }}-arm64"
150+
151+
- name_template: "ghcr.io/pfrederiksen/configdiff:latest"
152+
image_templates:
153+
- "ghcr.io/pfrederiksen/configdiff:latest-amd64"
154+
- "ghcr.io/pfrederiksen/configdiff:latest-arm64"

Dockerfile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM alpine:latest
2+
3+
RUN apk add --no-cache ca-certificates
4+
5+
COPY configdiff /usr/bin/configdiff
6+
7+
ENTRYPOINT ["/usr/bin/configdiff"]

README.md

Lines changed: 175 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ Semantic, human-grade diffs for YAML/JSON/HCL configuration files.
1111
- Ignore specific paths or treat arrays as sets
1212
- Handle type coercions (e.g., `"1"` vs `1`, `"true"` vs `true`)
1313
- Generate both machine-readable patches and human-friendly reports
14+
- Colorized output for better readability
15+
- Configuration file support for project defaults
1416

15-
Perfect for GitOps reviews, CI checks, configuration drift detection, and any scenario where you need to understand what actually changed in your config files.
17+
Perfect for GitOps reviews, CI checks, configuration drift detection, Terraform/HCL comparisons, and any scenario where you need to understand what actually changed in your config files.
1618

1719
## Installation
1820

@@ -22,10 +24,35 @@ Perfect for GitOps reviews, CI checks, configuration drift detection, and any sc
2224
# Homebrew (macOS/Linux)
2325
brew install pfrederiksen/tap/configdiff
2426

27+
# Docker
28+
docker pull ghcr.io/pfrederiksen/configdiff:latest
29+
docker run --rm -v $(pwd):/work ghcr.io/pfrederiksen/configdiff:latest old.yaml new.yaml
30+
2531
# Or download binaries from GitHub releases
2632
# https://github.com/pfrederiksen/configdiff/releases
2733
```
2834

35+
### Shell Completion
36+
37+
Enable shell completion for a better CLI experience:
38+
39+
```bash
40+
# Bash
41+
source <(configdiff completion bash)
42+
# Or install permanently:
43+
configdiff completion bash > /etc/bash_completion.d/configdiff # Linux
44+
configdiff completion bash > $(brew --prefix)/etc/bash_completion.d/configdiff # macOS
45+
46+
# Zsh
47+
configdiff completion zsh > "${fpath[1]}/_configdiff"
48+
49+
# Fish
50+
configdiff completion fish > ~/.config/fish/completions/configdiff.fish
51+
52+
# PowerShell
53+
configdiff completion powershell | Out-String | Invoke-Expression
54+
```
55+
2956
### Go Library
3057

3158
```bash
@@ -113,7 +140,7 @@ env: production
113140

114141
```
115142
Format Options:
116-
-f, --format string Input format (yaml, json, auto) (default "auto")
143+
-f, --format string Input format (yaml, json, hcl, auto) (default "auto")
117144
--old-format string Old file format override
118145
--new-format string New file format override
119146
@@ -134,15 +161,55 @@ Output Options:
134161
Other:
135162
-h, --help Help for configdiff
136163
-v, --version Version information
164+
completion [shell] Generate shell completion scripts
137165
```
138166

139167
### Output Formats
140168

141-
- **report** (default): Detailed human-friendly report with values
169+
- **report** (default): Detailed human-friendly report with values, colorized for better readability
142170
- **compact**: Summary with paths only
143171
- **json**: JSON-serialized changes array
144172
- **patch**: JSON Patch (RFC 6902) format
145173

174+
**Color Output**: The report format includes color-coded output by default:
175+
- Green for additions
176+
- Red for removals
177+
- Yellow for modifications
178+
- Cyan for moves
179+
180+
Disable with `--no-color` or `NO_COLOR=1` environment variable.
181+
182+
### Configuration File
183+
184+
Create a `.configdiffrc` or `.configdiff.yaml` file in your project or home directory to set default options:
185+
186+
```yaml
187+
# .configdiffrc
188+
ignore_paths:
189+
- /metadata/generation
190+
- /metadata/creationTimestamp
191+
- /status/*
192+
193+
array_keys:
194+
/spec/containers: name
195+
/spec/volumes: name
196+
197+
numeric_strings: false
198+
bool_strings: false
199+
stable_order: true
200+
output_format: report
201+
max_value_length: 100
202+
no_color: false
203+
```
204+
205+
**Configuration file locations** (checked in order):
206+
1. `./.configdiffrc` (current directory)
207+
2. `./.configdiff.yaml` (current directory)
208+
3. `~/.configdiffrc` (home directory)
209+
4. `~/.configdiff.yaml` (home directory)
210+
211+
CLI flags always override configuration file settings. For arrays and maps (like `ignore_paths` and `array_keys`), CLI flags are merged with config file values.
212+
146213
### Exit Codes
147214

148215
- `0`: Success (no differences, or differences displayed)
@@ -320,7 +387,7 @@ result, _ := configdiff.DiffBytes(jsonConfig, "json", yamlConfig, "yaml", opts)
320387

321388
### Cross-Format Comparison
322389

323-
Compare YAML and JSON representations:
390+
Compare YAML, JSON, and HCL representations:
324391

325392
```go
326393
yamlConfig := []byte(`
@@ -340,6 +407,78 @@ result, _ := configdiff.DiffBytes(yamlConfig, "yaml", jsonConfig, "json", config
340407
// No differences - semantically identical
341408
```
342409

410+
### HCL/Terraform Configuration
411+
412+
Compare Terraform/HCL configuration files:
413+
414+
```bash
415+
# Compare Terraform configs
416+
configdiff old.tf new.tf --format hcl
417+
418+
# Compare Terraform variable files
419+
configdiff terraform.tfvars.old terraform.tfvars.new --format hcl
420+
421+
# Mix formats (YAML to HCL)
422+
configdiff config.yaml config.hcl --old-format yaml --new-format hcl
423+
```
424+
425+
Example HCL comparison:
426+
427+
```go
428+
oldHCL := []byte(`
429+
region = "us-east-1"
430+
instance_type = "t3.micro"
431+
432+
config = {
433+
enabled = true
434+
replicas = 2
435+
}
436+
437+
servers = [
438+
{
439+
name = "web1"
440+
ip = "10.0.1.1"
441+
},
442+
{
443+
name = "web2"
444+
ip = "10.0.1.2"
445+
}
446+
]
447+
`)
448+
449+
newHCL := []byte(`
450+
region = "us-west-2"
451+
instance_type = "t3.small"
452+
453+
config = {
454+
enabled = true
455+
replicas = 3
456+
}
457+
458+
servers = [
459+
{
460+
name = "web1"
461+
ip = "10.0.1.1"
462+
},
463+
{
464+
name = "web2"
465+
ip = "10.0.1.2"
466+
},
467+
{
468+
name = "web3"
469+
ip = "10.0.1.3"
470+
}
471+
]
472+
`)
473+
474+
result, _ := configdiff.DiffBytes(oldHCL, "hcl", newHCL, "hcl", configdiff.Options{
475+
ArraySetKeys: map[string]string{
476+
"/servers": "name",
477+
},
478+
})
479+
// Detects region change, instance_type change, replicas change, and new server
480+
```
481+
343482
### Kubernetes Deployment Diff
344483

345484
Real-world example comparing Kubernetes deployments:
@@ -502,6 +641,7 @@ type Options struct {
502641
Compact bool // If true, only show paths
503642
ShowValues bool // If true, include old/new values
504643
MaxValueLength int // Truncate values longer than this (0 = no limit)
644+
NoColor bool // If true, disable colored output
505645
}
506646
```
507647

@@ -540,6 +680,25 @@ configdiff desired-state.json actual.json \
540680
--ignore /metadata/id
541681
```
542682

683+
### Terraform Configuration Management
684+
685+
```bash
686+
# Compare Terraform configurations
687+
configdiff main.tf.backup main.tf --format hcl
688+
689+
# Compare tfvars files
690+
configdiff staging.tfvars production.tfvars --format hcl \
691+
--array-key /security_groups=name
692+
693+
# Review Terraform state changes
694+
terraform show -json > current-state.json
695+
git show HEAD:terraform/state.json > previous-state.json
696+
configdiff previous-state.json current-state.json \
697+
--ignore /version \
698+
--ignore /terraform_version \
699+
--ignore /serial
700+
```
701+
543702
### Configuration Management
544703

545704
```bash
@@ -620,19 +779,24 @@ go test ./report -update
620779

621780
- [x] Repository setup with CI/CD
622781
- [x] Tree package with normalized representation
623-
- [x] YAML/JSON parsing with format detection
782+
- [x] YAML/JSON/HCL parsing with format detection
624783
- [x] Semantic diff engine with customizable rules
625784
- [x] JSON Patch-like operations
626-
- [x] Human-friendly report generation
627-
- [x] Comprehensive test coverage (84.8%)
785+
- [x] Human-friendly report generation with color output
786+
- [x] Full-featured CLI tool with shell completion
787+
- [x] Docker container support
788+
- [x] Configuration file support (.configdiffrc)
789+
- [x] Homebrew tap for easy installation
790+
- [x] Comprehensive test coverage (>80%)
628791
- [x] Full API documentation and examples
629792

630793
### Future Enhancements
631794

632-
- HCL format support (experimental)
633-
- Additional coercion rules
634-
- Performance optimizations for very large configs
635-
- CLI tool for command-line usage
795+
- Additional coercion rules (e.g., unit conversions, date formats)
796+
- Performance optimizations for very large configs (>100MB)
797+
- TOML format support
798+
- Interactive diff mode
799+
- Diff statistics and analytics
636800

637801
## Contributing
638802

cmd/configdiff/compare.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ func compare(oldFile, newFile string) error {
2929
ExitCode: exitCode,
3030
}
3131

32+
// Apply config file defaults (CLI flags take precedence)
33+
if cfg != nil {
34+
cliOpts.ApplyConfigDefaults(cfg)
35+
}
36+
3237
// Validate options
3338
if err := cliOpts.Validate(); err != nil {
3439
return err

0 commit comments

Comments
 (0)