A command-line tool that compares local files used in Kustomize configMapGenerator with the actual files stored in ConfigMaps deployed to your Kubernetes cluster.
Why cmdiff?
When using Kustomize's configMapGenerator to manage configuration files, it's useful to know what changes you're about to apply before running kustomize build | kubectl apply. While kubectl diff shows the full ConfigMap diff, it doesn't provide a clear view of the actual file changes embedded within the ConfigMap data.
cmdiff solves this by:
- Parsing your
kustomization.yaml to find configMapGenerator entries
- Fetching the corresponding ConfigMap from your Kubernetes cluster
- Extracting and comparing individual files between local and cluster versions
- Displaying beautiful, color-coded diffs with clear file-by-file comparisons
Features
- π¨ Beautiful CLI output using charmbracelet/lipgloss
- π₯οΈ Interactive TUI mode - side-by-side diffs with bubbletea
- π Clear diff visualization - unified diff format or side-by-side view
- π Smart ConfigMap detection - automatically finds the latest ConfigMap or specify exact name
- π Multiple file support - handles ConfigMaps with multiple files
- π― Multiple ConfigMap support - diff specific ConfigMaps by name
- β‘ Fast and lightweight - written in Go
- π Kubeconfig support - works with default kubeconfig or custom path
- πΉ Keyboard navigation - vim-style keybindings (h/j/k/l) in interactive mode
Installation
From source
go install github.com/stibi/cmdiff@latest
Build locally
git clone https://github.com/stibi/cmdiff
cd cmdiff
go build -o cmdiff
Usage
Basic usage
Run cmdiff in a directory containing kustomization.yaml:
cmdiff
This will:
- Parse the local
kustomization.yaml
- Find the first
configMapGenerator entry
- Use the namespace defined in
kustomization.yaml (or "default" if not specified)
- Fetch the latest ConfigMap with matching name from the cluster
- Compare all files and display diffs
Specify a ConfigMap name
If you have multiple configMapGenerators or want to compare against a specific ConfigMap:
cmdiff --configmap my-config
This works two ways:
- If
--configmap matches a configMapGenerator name, it compares against the latest ConfigMap with that prefix
- If
--configmap is an exact ConfigMap name (e.g., my-config-abc123), it uses that specific ConfigMap
Namespace handling
cmdiff automatically uses the namespace from your kustomization.yaml:
# kustomization.yaml
namespace: production
configMapGenerator:
- name: my-config
files:
- config.yaml
When you run cmdiff, it will look for the ConfigMap in the production namespace.
Override with the --namespace flag:
cmdiff --namespace staging
Namespace priority (highest to lowest):
--namespace flag (command-line override)
namespace field in configMapGenerator (per-generator)
namespace field in kustomization.yaml (global)
"default" (fallback)
Custom kustomization path
cmdiff --path ./k8s/overlays/production
Interactive mode with side-by-side diffs
NEW! Use the -i or --interactive flag for an interactive TUI with side-by-side diffs:
cmdiff --interactive
# or
cmdiff -i
Features:
- Side-by-side diff view - See Kubernetes vs Local files in parallel columns
- Navigate between files - Use arrow keys or tab to switch between files
- Scroll through diffs - Arrow keys to scroll up/down
- Color-coded differences - Red (removed) and green (added) highlighting
- File tabs - Visual tabs showing all files with status icons
Keyboard shortcuts:
β/β or l/h or tab/shift+tab - Navigate between files
β/β or k/j - Scroll up/down
q or esc - Quit
pgup/pgdown - Page up/down
home/end - Jump to top/bottom
Custom kubeconfig
cmdiff --kubeconfig ~/.kube/other-cluster-config
All flags combined
cmdiff \
--interactive \
--configmap app-config \
--namespace production \
--path ./k8s/overlays/prod \
--kubeconfig ~/.kube/prod-cluster
Example Output
ConfigMap Diff Results
ConfigMap: app-config-m2k9tc5f8b
β config.yaml [identical]
~ settings.json [modified]
--- k8s/settings.json
+++ local/settings.json
@@ -1,5 +1,5 @@
{
"debug": false,
- "timeout": 30,
+ "timeout": 60,
"features": {
"newUI": true
}
}
+ database.conf [only in local]
βββββββββββββββββββββββββββββββββββββββββββββββ
β 1 identical β’ 1 modified β’ 1 only local β
βββββββββββββββββββββββββββββββββββββββββββββββ
Exit Codes
0 - No differences found
1 - Differences found or error occurred
This makes cmdiff perfect for use in CI/CD pipelines:
if cmdiff; then
echo "No changes to apply"
else
echo "Changes detected, applying..."
kustomize build | kubectl apply -f -
fi
Example kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: app-config
files:
- config.yaml
- settings.json
- database.conf
- name: nginx-config
files:
- nginx.conf
How It Works
- Parse - Reads
kustomization.yaml and extracts configMapGenerator entries
- Fetch - Connects to Kubernetes cluster and retrieves ConfigMap data
- Compare - Diffs each file between local and cluster versions
- Display - Shows beautiful, color-coded results with file-by-file breakdown
Requirements
- Go 1.25+ (for building)
- Access to a Kubernetes cluster with valid kubeconfig
- A
kustomization.yaml file with configMapGenerator entries
Learning Go?
This project is designed with clean, readable Go code to help you learn:
- Package structure - Well-organized internal packages
- Kubernetes client-go - How to interact with K8s API
- YAML parsing - Using
gopkg.in/yaml.v3
- CLI development - Using
cobra for command-line interfaces
- Terminal UI - Using
lipgloss for beautiful output
- Error handling - Proper Go error patterns
- Testing patterns - Clean, testable code structure
Project Structure
cmdiff/
βββ main.go # Entry point
βββ cmd/
β βββ root.go # CLI command definition
βββ internal/
β βββ kustomize/
β β βββ parser.go # Kustomization.yaml parsing
β βββ k8s/
β β βββ client.go # Kubernetes API client
β βββ differ/
β β βββ differ.go # File comparison logic
β βββ ui/
β βββ display.go # Terminal output formatting
βββ go.mod
βββ README.md
Contributing
Contributions are welcome! This is a learning project, so:
- Keep code simple and well-commented
- Follow Go best practices
- Add tests for new features
- Update documentation
License
MIT License - feel free to use and modify!
Acknowledgments