Skip to content

Architecture Design

Understand how Cultivator works under the hood.

Overview

Cultivator is a CLI-first tool that orchestrates Terragrunt stack discovery, filtering, and execution. It runs as a command in CI/CD systems (GitHub Actions, GitLab CI) or locally, and delegates state management entirely to Terraform/OpenTofu backends.

Unlike PR-based automation, Cultivator is job-triggered: you call it explicitly in your CI workflow, providing flags to control scope and behavior.

Core Components

1. Config Loader

  • Loads built-in defaults, optional config file (--config), environment variables, and CLI flags
  • Merges configuration with CLI flags and environment variables
  • Validates required configuration fields (root path exists, parallelism is positive)

2. Stack Discovery

  • Walks the root directory recursively
  • Finds all terragrunt.hcl files
  • Builds list of available stacks

3. Scope Filter

  • Filters stacks by --env (environment)
  • Filters stacks by --include / --exclude (path patterns)
  • Filters stacks by --tags (recommended # cultivator:tags = a,b; legacy cultivator_tags = [...] supported)

4. Git Integration (Magic Mode)

  • Queries Git for changed files using git diff
  • Maps file changes to specific Terragrunt modules
  • Filters execution scope to only affected modules when --changed-only is active

5. Dependency Graph (DAG)

  • Parses terragrunt.hcl files to extract dependency blocks
  • Builds a Directed Acyclic Graph (DAG) representing module relationships
  • Performs topological sorting to determine the correct execution order
  • Detects and prevents circular dependencies

6. Executor

  • Runs Terragrunt commands (plan, apply, destroy)
  • Manages parallel execution via worker pool
  • Captures stdout and stderr in a single chronologically-ordered stream using cmd.CombinedOutput()
  • Captures output on per-stack, per-command basis

7. Output Formatter

  • Formats and displays results in human-readable text
  • Reports exit codes and errors per module

8. Logging Boundary

  • CLI owns user-facing logs and summary output.
  • Discovery, runner, and git use injected logger instances for debug-level diagnostics only.

Data Flow

CLI Invocation (plan/apply/destroy)
    |
Config Loader -> Parse defaults + optional --config file + env vars + flags
    |
Stack Discovery + Scope Filter -> Find all terragrunt.hcl files, apply --env, --include, --exclude, --tags
    |
Git Integration -> Filter by changed files (if --changed-only)
    |
Dependency Graph -> Build execution order (DAG)
    |
Executor (parallel worker pool) -> Run Terragrunt commands
    |
Output Formatter -> Display results
    |
Exit Code (0 = success, 1 = failure, 2 = usage error)

Key Design Principles

1. Stateless Operation

  • Cultivator does not manage state, backends, or locks
  • All state is owned by Terraform/OpenTofu
  • Concurrency is controlled via a bounded channel semaphore and dependency signaling between goroutines; there are no filesystem locks

2. Explicit Control

  • No automatic triggers or webhooks
  • User (or CI job) explicitly calls cultivator plan/apply/destroy
  • Flags control what runs and how

3. Filter-First

  • Start with all stacks, then filter by environment/path/tags
  • Filters are composable (combine multiple --include, --exclude, --tags)
  • Results show exactly which stacks will be affected

4. Dependency-Aware

  • Respects Terragrunt dependency blocks
  • Runs stacks in correct order (topological sort)
  • Prevents applying stack A before its dependency B

5. Parallel by Default

  • Configurable worker pool (default: number of CPUs)
  • Runs independent stacks concurrently
  • Respects dependency graph for safe parallelism

6. Human-Readable Output

  • Structured text output with a clear per-module section header
  • Compatible with GitHub Actions, GitLab CI, and local debugging

Configuration Reference

When using a config file, pass it explicitly with --config:

root: live                    # Root directory to scan for stacks
parallelism: 4               # Worker pool size (default: number of CPUs)
non_interactive: false       # Equivalent to -input=false
plan:
  destroy: false             # Defaults for 'plan' subcommand
apply:
  auto_approve: true         # Defaults for 'apply' subcommand
destroy:
  auto_approve: true         # Defaults for 'destroy' subcommand

CLI flags and environment variables override the config file. Flags take highest precedence.

See Configuration for full reference.

Security Considerations

Secrets Redaction

  • Cultivator does not implement secret redaction in its own output layer
  • Use your CI platform's built-in secret masking (GitHub Actions masked secrets, GitLab CI variable masking)
  • Mark sensitive Terraform outputs with sensitive = true so Terragrunt/OpenTofu suppress them in plan output

Access Control

  • Cultivator respects IAM permissions of the CI runner
  • If the CI job lacks permissions to modify a stack, Terraform will error
  • No additional RBAC layer in Cultivator itself

State Backend Protection

  • All state is stored in the backend (S3, Terraform Cloud, etc.)
  • Cultivator does not access or modify state directly
  • Backend authentication is handled by Terragrunt/Terraform