Kustomize Build Check
An intelligent CLI tool for automatically discovering and validating Kustomize configurations with smart dependency tracking.
Overview
Kustomize Build Check was created to solve a common problem in GitOps workflows: how do you efficiently validate Kustomize configurations when changes happen?
When managing dozens of Kustomize overlays across multiple environments, you need to know:
- Which overlays are affected by a change to a base?
- Did my patch break the build?
- Are all dependent configurations still valid?
The Problem
Managing dependencies in Kustomize repositories becomes complex as your setup grows. Consider this typical structure:
When you change app1/base/kustomization.yaml, you need to test:
- The base itself
- All overlays that depend on it (dev, staging, production)
- Any overlays that depend on those overlays (multi-level dependencies)
Doing this manually is error-prone and time-consuming. Running kustomize build on every single directory is wasteful and slow.
The Solution
Kustomize Build Check automates this with intelligent dependency detection:
- Auto-discovers all kustomization files in your repository
- Builds a dependency graph to understand relationships
- Detects changes via git diff
- Tests only what's affected - bases and all their dependents
- Reports results with clear success/failure output
The key feature is the recursive dependent testing: when a base changes it traverses the entire dependency tree to find everything that could be impacted.
Architecture
The project follows a dual-repository pattern:
Tool Repository
- Go CLI tool source code
- Multi-platform binary builds (GoReleaser)
- Docker multi-arch images (linux/amd64, linux/arm64)
- Published to GitHub Releases + GHCR
Action Repository
- GitHub Action wrapper
- User-facing documentation
- Usage examples
- References pre-built Docker images from GHCR
This separation provides:
- Clean interface for GitHub Actions users
- Reusable tool for any CI/CD pipeline
- Container-based execution for consistency
- Independent versioning and releases
Features
🧠 Smart Dependency Analysis
Automatically builds a dependency graph and tests everything affected by changes:
# Change a base
manifests/app/base/kustomization.yaml
# Automatically tests:
✓ manifests/app/base
✓ manifests/app/overlays/dev
✓ manifests/app/overlays/staging
✓ manifests/app/overlays/production⚡ Helm Support
Built-in support for helmCharts in kustomization files:
helmCharts:
- name: cert-manager
repo: https://charts.jetstack.io
version: v1.13.0📊 Clear Reporting
Detailed build results with execution time:
::: success Kustomize Build Results:
================================================================================
✅ manifests/app/base - Build successful (0.05s)
✅ manifests/app/overlays/dev - Build successful (0.12s)
================================================================================
Summary: 2 total, 2 successful, 0 failed:::
🐛 Debug Logging
Structured logging with configurable levels:
LOG_LEVEL=DEBUG kustomize-build-checkShows detailed execution including:
- Dependency graph construction
- Impact analysis decisions
- File processing steps
- Build commands and results
Getting Started
As a GitHub Action
The easiest way to use Kustomize Build Check is as a GitHub Action:
name: Validate Kustomize
on:
pull_request:
paths:
- '**/*.yaml'
- '**/*.yml'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: michielvha/kustomize-build-check-action@main
with:
enable-helm: true
fail-on-error: true
base-ref: ${{ github.event.pull_request.base.sha || 'HEAD~1' }}This validates all affected Kustomize configurations in pull requests automatically.
As a CLI Tool
For local development or other CI/CD systems:
# Download from releases
wget https://github.com/michielvha/kustomize-build-check/releases/latest/download/kustomize-build-check_linux_amd64.tar.gz
# Or use the Docker image
docker run ghcr.io/michielvha/kustomize-build-check:latest
# Run locally
LOG_LEVEL=DEBUG kustomize-build-checkUse Cases
GitOps Validation
Validate Kustomize configurations before merging to main:
- name: Validate GitOps manifests
uses: michielvha/kustomize-build-check-action@main
with:
enable-helm: true
root-dir: ./manifestsMonorepo Support
Test multiple independent Kustomize projects:
- name: Validate App1
uses: michielvha/kustomize-build-check-action@main
with:
root-dir: ./apps/app1/manifests
- name: Validate App2
uses: michielvha/kustomize-build-check-action@main
with:
root-dir: ./apps/app2/manifestsPre-commit Validation
Integrate with pre-commit hooks to catch issues early:
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: kustomize-build-check
name: Validate Kustomize
entry: kustomize-build-check
language: system
pass_filenames: falseTechnical Implementation
Dependency Graph
The tool builds a directed graph of kustomization dependencies:
// Discovers all kustomization files
kustomizations := discovery.FindAll(rootDir)
// Builds dependency graph
graph.Build(kustomizations)
// For each changed file, finds all dependents recursively
dependents := graph.GetAllDependents(changedPath)This handles complex scenarios like:
- Multi-level overlays (overlay → overlay → base)
- Shared bases used by multiple overlays
- Component dependencies
- Circular dependency detection
Impact Analysis
When a file changes, the analyzer:
- Checks if it's a kustomization file
- If yes, marks that kustomization as affected
- Recursively finds all dependents
- Tests the entire chain
// If base changes
affected = [base] + getAllDependents(base)
// Results in testing:
// - base
// - all overlays using base
// - all overlays using those overlays
// - ...Path Resolution
A key challenge was handling absolute vs relative paths:
- Git returns relative paths
- Discovery uses absolute paths
- Graph needs consistent paths for matching
Solution: Convert git paths to absolute before graph lookup:
absDir, _ := filepath.Abs(dir)
dependents := graph.GetAllDependents(absDir)Security
The tool runs as a non-root user (UID 1001) in containers, matching GitHub Actions runner UID for proper file permissions.
Git safe.directory is configured at build time:
RUN git config --system --add safe.directory '*'This allows the non-root user to run git commands on mounted volumes without permission errors.
Lessons Learned
Building this tool taught several valuable lessons:
1. Dual-Repository Pattern
Separating the tool from the action provides better separation of concerns and cleaner user experience.
2. Path Handling
Absolute vs relative paths caused a bug. Always normalize paths before comparison.
3. Container Security
Running as non-root is important, but UID must match the GitHub Actions runner (1001) for file operations.
4. Structured Logging
Using log/slog with configurable levels makes debugging issues much easier for users.
5. Recursive Dependencies
Testing only direct dependents isn't enough, you need to traverse the entire tree.
Future Enhancements
Potential improvements being considered:
- JSON output: Machine-readable results for integration with other tools
- Caching: Skip unchanged kustomizations across runs
- Performance metrics: Track build times and identify slow configurations
