Skip to content

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:

kustomize 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:

  1. Auto-discovers all kustomization files in your repository
  2. Builds a dependency graph to understand relationships
  3. Detects changes via git diff
  4. Tests only what's affected - bases and all their dependents
  5. 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

kustomize-build-check

  • 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

kustomize-build-check-action

  • 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:

bash
# 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:

yaml
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:

bash
LOG_LEVEL=DEBUG kustomize-build-check

Shows 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:

yaml
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:

bash
# 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-check

Use Cases

GitOps Validation

Validate Kustomize configurations before merging to main:

yaml
- name: Validate GitOps manifests
  uses: michielvha/kustomize-build-check-action@main
  with:
    enable-helm: true
    root-dir: ./manifests

Monorepo Support

Test multiple independent Kustomize projects:

yaml
- 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/manifests

Pre-commit Validation

Integrate with pre-commit hooks to catch issues early:

yaml
# .pre-commit-config.yaml
repos:
  - repo: local
    hooks:
      - id: kustomize-build-check
        name: Validate Kustomize
        entry: kustomize-build-check
        language: system
        pass_filenames: false

Technical Implementation

Dependency Graph

The tool builds a directed graph of kustomization dependencies:

go
// 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:

  1. Checks if it's a kustomization file
  2. If yes, marks that kustomization as affected
  3. Recursively finds all dependents
  4. Tests the entire chain
go
// 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:

go
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:

dockerfile
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

Resources