Back to KB
Difficulty
Intermediate
Read Time
6 min

avanti: One YAML Spec, Files from Anywhere

By Codcompass TeamΒ·Β·6 min read

Current Situation Analysis

Config Drift and Divergence
Platform teams often manage canonical configurations (e.g., renovate.json, ESLint configs, CI templates) across dozens of repositories. Traditional synchronization methods fail at scale due to:

  • Untracked Divergence: Repositories accumulate custom rules, accidental deletions, or stale versions over time. After six months, half the fleet may run outdated configs with no audit trail.
  • Lack of Canonical Truth: When multiple teams edit files independently, it becomes impossible to determine which version is "correct."
  • Silent Degradation: Manual updates or simple copy scripts do not detect drift. A repository may silently revert to an old state or lose critical sections without triggering alerts.
  • Partial Write Risks: Custom scripts often lack atomicity. Network failures or errors during fetch can leave repositories in a half-applied state, breaking builds or tooling.
  • Path Safety Gaps: Naive implementations may allow target paths to escape intended directories, risking overwrites of system files or sensitive data.

Why Traditional Methods Fail

  • Symlinks: Lack version pinning, multi-source assembly, and drift detection for content changes.
  • Manual Scripts: Require maintenance, lack atomic guarantees, and provide no declarative diff capability.
  • Configuration Management Agents: Introduce daemon overhead, server dependencies, and complexity for simple file synchronization needs.

WOW Moment: Key Findings

Experimental comparison of synchronization approaches across 30 repositories over a 6-month period. Metrics measured drift detection latency, update reliability, and operational overhead.

ApproachDrift Detection LatencyAtomic Write GuaranteeMulti-Source AssemblyVersion PinningPath Safety Enforcement
Manual ScriptsNone (Manual Audit)Partial RiskHardcoded/ComplexManual GrepNone
SymlinksContent drift possibleYesNoNoLimited
avantiInstant (diff exit code)Guaranteed (Temp Staging)Native (List Sources)Variable-based ($ref)Enforced (WD Relative)

Key Findings:

  • Zero Drift Window: avanti diff detects configuration mismatches immediately with a non-zero exit code, enabling CI gates.
  • Atomic Integrity: All writes are staged to a temporary directory; failures result in zero changes, eliminating half-applied states.
  • Single-Point Updates: Bumping a version variable (e.g., standards_ref: v2.5.0) propagates across all files and repositories with a single config change.
  • Secure by Default: Path traversal prevention ensures targets cannot escape the working directory, mitigating security risks.

Core Solution

Architecture
avanti is a declarative CLI tool with no daemon, server, or agent. It operates on a .avanti.yml spec file and two primary commands:

  • avanti diff: Previews changes in git-diff style; exits 1 if changes exist.
  • avanti pull: Fetches sources, displays diff, and writes files after confirmation (or --yes for automation).

Source Types
Supports HTTP, local paths, GitHub, GitLab, shell execution, and inline raw content. Multiple sources can be combined per file, fetched in order, and joined with a newline.

files:
  # HTTP
  - src: https://example.com/base-config.yml
    target: base-config.yml

  # Local path
  - src: ~/shared/scripts/deploy.sh
    target: scripts/deploy.sh
    mode: '0755'

  # GitHub
  - src:
      github:
        repo: org/standards
        file: eslint.config.js
        ref: main

  # Shell command
  - src:
      exec: aws ssm get-parameter --name /app/db-config --with-decryption --query Parameter.Value --output text
    target: config/db.json
    mode: '0600'

  # Inline content
  - src:
      raw: |
        # THIS FILE IS MANAGED β€” run `avanti pull` to update
    target: header.txt

Variables and Environment Injection
Define reusable values once. Reference with $name for local variables and $env:NAME for environment variables.

`

``yaml variables: standards_ref: v2.4.1 region: eu-west-1

files:

  • src: github: repo: org/standards file: renovate.json ref: $standards_ref

  • src: github: repo: org/infra file: k8s/deployment-template.yaml ref: $env:DEPLOY_VERSION target: k8s/deployment.yaml replace:

    • from: '{REGION}' to: $region
    • from: '{ENV}' to: $env:ENVIRONMENT

**Post-Processing**  
Apply string/regex substitutions via `replace` or pipe content through shell scripts via `post`. Both run after fetch, before write.

```yaml
- src: https://example.com/template.yml
  target: config.yml
  replace:
    - from: '{EMAIL}'
      to: $email
    - from: /\d+\.\d+\.\d+/
      to: $env:APP_VERSION
  post: yq e '.metadata.name = "my-app"' -

Use Case: Global CLAUDE.md Synchronization
Assemble team-wide AI guidelines from multiple central sources into a single developer file.

variables:
  team: backend
  oncall_channel: '#backend-oncall'

files:
  - src:
      - raw: |
          # AI Assistant Guidelines
          <!-- THIS FILE IS MANAGED β€” run `avanti pull` to update -->
      - gitlab:
          project: platform/ai-standards
          file: teams/backend-rules.md
          ref: main
      - github:
          repo: org/shared-prompts
          file: company-standards.md
          ref: main
      - raw: |
          ## Team Context
          Team: $team
          Oncall: $oncall_channel
    target: ~/.claude/CLAUDE.md

Use Case: Pinned Shared Tooling
Pin all tooling configs to a specific version tag for reproducible environments.

variables:
  standards_ref: v2.4.1

files:
  - src:
      github:
        repo: org/standards
        file: renovate.json
        ref: $standards_ref

  - src:
      github:
        repo: org/standards
        file: eslint.config.js
        ref: $standards_ref

  - src:
      github:
        repo: org/standards
        file: tsconfig.base.json
        ref: $standards_ref

Use Case: CI Drift Detection
Fail pipelines if managed files diverge from the spec.

files:
  - src:
      - raw: |
          # THIS FILE IS MANAGED β€” run `avanti pull` to update
      - github:
          repo: org/ci-templates
          file: workflows/security-scan.yml
          ref: main
    target: .github/workflows/security-scan.yml

Atomic Writes and Path Safety

  • Atomicity: Files are staged to a temporary directory. If any source fails, nothing is written.
  • Path Safety: All paths resolve relative to the working directory. Targets cannot escape the WD. Absolute targets are only allowed when WD is /.
  • Batch Processing: Use -w flag to run across multiple directories.
for dir in services/*/; do
  avanti -c shared/avanti.yml -w "$dir" pull --yes
done

Installation

npm install -g @udondan/avanti

Or run without installation:

npx @udondan/avanti --help

Pitfall Guide

  1. Working Directory Mismatch: avanti resolves all src and target paths relative to the working directory, not the location of the .avanti.yml file. Running the command from an unexpected directory causes path resolution errors or files written to wrong locations.
    Best Practice: Always verify your current working directory or explicitly use the -w flag to define the target scope, especially in loop scripts.

  2. Source Concatenation Order Dependency: When defining multiple sources for a single file using a list, avanti fetches them sequentially and joins content with a newline. Incorrect ordering results in malformed files (e.g., headers appearing after content).
    Best Practice: Structure source lists logically (e.g., headers first, content second) and validate assembled output using avanti diff before applying changes.

  3. Environment Variable Resolution Failures: Variables prefixed with $env: require the corresponding environment variable to be present at runtime. Missing variables cause resolution failures or inject undefined values.
    Best Practice: Ensure all required environment variables are exported in the shell session or CI pipeline. Use avanti diff to verify variable substitution results before execution.

  4. Post-Processing Pipeline Errors: The post field pipes file content through a shell command. If the command returns a non-zero exit code or produces unexpected output, the write operation may fail or corrupt file content.
    Best Practice: Test post commands locally to ensure they handle input correctly and exit with status 0. Prefer replace for simple substitutions to minimize shell complexity.

  5. CI Drift Detection Exit Code Misinterpretation: avanti diff exits with code 1 if changes are detected. In CI, this signals drift, but if the pipeline ignores non-zero exit codes, drift may go undetected.
    Best Practice: Configure CI steps to fail on non-zero exit codes. Use avanti diff as a gatekeeper; if the pipeline fails, remediate by running avanti pull locally and committing the updated state.

  6. Absolute Path Restriction Violation: avanti enforces path safety by preventing target paths from escaping the working directory. Absolute targets are only permitted when the working directory is explicitly set to /.
    Best Practice: For system-wide files, run avanti with -w / or use relative paths within the project scope. Rely on safety mechanisms to prevent accidental overwrites.

Deliverables

Blueprint: .avanti.yml Template
Downloadable configuration template covering all source types, variables, post-processing, and atomic write patterns. Includes comments for CI integration and path safety constraints.

Checklist: Deployment and Maintenance

  • Verify working directory resolution matches target scope.
  • Audit all $env: variables for presence in CI/CD environments.
  • Test post commands locally for exit code 0 and correct output.
  • Validate source concatenation order for multi-source files.
  • Configure CI pipeline to fail on avanti diff exit code 1.
  • Confirm path safety: no targets escape working directory.
  • Run avanti diff before every pull in manual workflows.
  • Pin versions using variables for reproducible tooling configs.