Configuration Reference

This document covers all operators and options available in .common-repo.yaml.

Configuration File

The .common-repo.yaml file is a list of operations executed in order. Each operation is a YAML map with a single key indicating the operator type.

Operator Quick Reference

OperatorDescription
repoInherit files from a remote Git repository
includeAdd files from the current repository
excludeRemove files from the in-memory filesystem
renameTransform file paths using regex patterns
templateMark files for variable substitution
template-varsDefine variables for template substitution
toolsValidate that required tools are installed
yamlMerge YAML configuration fragments
jsonMerge JSON configuration fragments
tomlMerge TOML configuration fragments
iniMerge INI configuration fragments
markdownMerge markdown document fragments
selfRun operations locally without exposing them to consumers

Example configuration:

# .common-repo.yaml
- repo: { ... }
- include: [ ... ]
- exclude: [ ... ]
- rename: [ ... ]
- template: [ ... ]
- template-vars: { ... }
- tools: [ ... ]
- yaml: { ... }
- json: { ... }
- toml: { ... }
- ini: { ... }
- markdown: { ... }
- self: [ ... ]

Core Operators

repo - Inherit from a Repository

Pull files from a remote Git repository.

- repo:
    url: https://github.com/owner/repo
    ref: v1.0.0

Options

OptionRequiredDescription
urlYesGit repository URL
refYesGit reference (tag, branch, or commit SHA)
pathNoSub-directory to use as root
withNoInline operations to apply

Examples

Basic inheritance:

- repo:
    url: https://github.com/common-repo/rust-cli
    ref: v1.2.0

Inherit a sub-directory:

# Only pull files from the 'templates/rust' directory
- repo:
    url: https://github.com/common-repo/templates
    ref: main
    path: templates/rust

Inline filtering with with:

- repo:
    url: https://github.com/common-repo/configs
    ref: v2.0.0
    with:
      - include: [".github/**", ".pre-commit-config.yaml"]
      - exclude: [".github/CODEOWNERS"]
      - rename:
          - ".github/workflows/ci-template.yml": ".github/workflows/ci.yml"

include - Add Files

Add files from the current repository to the output based on glob patterns.

- include:
    - "**/*"           # All files
    - ".*"             # Hidden files at root
    - ".*/**/*"        # All files in hidden directories

Patterns

  • **/* - All files recursively
  • *.rs - All Rust files in current directory
  • src/**/*.rs - All Rust files under src/
  • .* - Hidden files (dotfiles) at root
  • .*/**/* - All files in hidden directories

exclude - Remove Files

Remove files from the in-memory filesystem based on glob patterns.

- exclude:
    - ".git/**"
    - "target/**"
    - "**/*.bak"
    - "node_modules/**"

rename - Transform Paths

Transform file paths using regex patterns with capture group placeholders.

- rename:
    - "old-name/(.*)": "new-name/$1"
    - "^templates/(.*)": "$1"
    - "(.+)\\.template$": "$1"

Placeholders

  • $1 - First capture group
  • $2 - Second capture group
  • etc.

Examples

Strip a directory prefix:

- rename:
    - "^files/(.*)": "$1"

Result: files/config.yaml becomes config.yaml

Move files to a subdirectory:

- rename:
    - "^(.+\\.md)$": "docs/$1"

Result: README.md becomes docs/README.md

Rename file extensions:

- rename:
    - "(.+)\\.template$": "$1"

Result: config.yaml.template becomes config.yaml

template - Mark Template Files

Mark files for variable substitution. Files matching these glob patterns are scanned for the __COMMON_REPO__ prefix — if found, the file is flagged as a template for processing during composite construction.

- template:
    - "**/*.template"
    - ".github/workflows/*.yml"
    - "Cargo.toml"

Template files use the __COMMON_REPO__VARNAME__ sentinel pattern for variable placeholders:

# In a template file (e.g., Cargo.toml)
name = "__COMMON_REPO__PROJECT_NAME__"
version = "__COMMON_REPO__VERSION__"

Variable names must be valid identifiers ([A-Za-z_][A-Za-z0-9_]*) and must not contain double underscores (__).

template-vars - Define Variables

Define variables for template substitution. Values are literal strings.

- template-vars:
    project_name: my-project
    author: Jane Doe
    rust_version: "1.75"

Variable Cascading

Variables cascade through the inheritance tree. Child repos can override ancestor variables:

# In parent repo
- template-vars:
    log_level: info

# In child repo (overrides parent)
- template-vars:
    log_level: debug

tools - Validate Required Tools

Check that required tools are installed with correct versions.

- tools:
    - rustc: ">=1.70"
    - cargo: "*"
    - pre-commit: "^3.0"
    - node: "~18.0"

Version Constraints

SyntaxMeaning
*Any version
1.70Exactly 1.70
>=1.701.70 or higher
^1.70Compatible with 1.70 (>=1.70.0, <2.0.0)
~1.70Approximately 1.70 (>=1.70.0, <1.71.0)

This operator validates but does not install tools. Warnings are issued for missing or incompatible versions.

Structural Operators

self - Local-Only Operations

Run a sub-pipeline of operations that apply only to the local repository. Files produced by self: blocks are written to disk but never appear in the composite filesystem that downstream consumers see.

This is useful when a repository is both a source (providing shared configuration to consumers) and wants to consume from its own upstream repos.

- self:
    - repo:
        url: https://github.com/org/upstream-tooling
        ref: v1.0.0
    - exclude:
        - ".releaserc.yaml"

How It Works

  1. The orchestrator partitions self: blocks from the rest of the config
  2. The source pipeline runs first (producing the composite filesystem for consumers)
  3. Each self: block runs as an independent pipeline afterward
  4. Output from self: blocks is written to the working directory but excluded from what consumers inherit

Rules

  • self: blocks must contain at least one operation
  • self: blocks cannot be nested (no self: inside self:)
  • self: blocks are stripped when a repo is consumed as an upstream — consumers never see them
  • Any operation valid at the top level can appear inside self: (repo, include, exclude, rename, template, merge operators, etc.)
  • A self: block needs at least one repo: to populate its composite filesystem. Without a repo:, the composite is empty and filtering operators like include/exclude/rename have nothing to operate on.

Example: Source Repo That Consumes Upstream Tooling

# .common-repo.yaml for a shared-config repo

# Self operations — local only, consumers don't see these
- self:
    - repo:
        url: https://github.com/org/ci-tooling
        ref: v2.0.0
    - exclude:
        - ".releaserc.yaml"
        - "commitlint.config.cjs"

# Source API — what consumers get
- include:
    - "src/**"
    - "src/.*"
- template:
    - "src/.github/workflows/release.yaml"
- template-vars:
    GH_APP_ID_VAR: CHRISTMAS_ISLAND_APP_ID
- rename:
    - from: "^src/(.*)$"
      to: "$1"

In this example, the repo pulls CI tooling for its own use via self:, while consumers only see the files exposed by include, template, template-vars, and rename.

For a detailed guide on using self: when authoring upstream repositories, see Authoring Upstream Repositories.

Merge Operators

Merge operators intelligently combine configuration fragments into destination files.

Upstream-Declared Merge (defer/auto-merge)

All merge operators support two additional options for upstream-declared merge behavior:

OptionTypeDescription
auto-mergestringShorthand: sets source=dest to this value and implies defer=true
deferboolWhen true, this operation only runs when repo is used as an upstream

auto-merge is the preferred syntax when the source and destination filenames are the same:

# In upstream repo's .common-repo.yaml
- markdown:
    auto-merge: CLAUDE.md     # source=dest=CLAUDE.md, defer=true
    section: "## Rules"
    append: true

defer: true is used when source and destination paths differ:

- yaml:
    source: config/base.yaml
    dest: config.yaml
    path: settings
    defer: true               # Only applies when repo is inherited

See Upstream-Declared Merge Behavior for detailed usage.

Path Syntax

The path option in merge operators supports multiple notations for navigating nested structures:

SyntaxExampleDescription
Dot notationfoo.bar.bazAccess nested keys
Bracket notationfoo["bar"] or foo['bar']Access keys with special characters
Array indicesitems[0] or items[1].nameAccess array elements by index
Escaped dotsfoo\.barLiteral dot in key name
Mixedservers[0].config["special.key"]Combine notations

Examples:

# Navigate to nested object
- yaml:
    source: labels.yml
    dest: config.yml
    path: metadata.labels

# Access array element
- json:
    source: script.json
    dest: package.json
    path: scripts[0]

# Key with special characters
- toml:
    source: deps.toml
    dest: Cargo.toml
    path: dependencies["my-crate"]

# Escaped dot in key name
- yaml:
    source: fragment.yml
    dest: config.yml
    path: config\.v2.settings

yaml - Merge YAML Files

- yaml:
    source: fragment.yml
    dest: config.yml

Options

OptionRequiredDefaultDescription
sourceYes*-Source fragment file
destYes*-Destination file
auto-mergeNo-Shorthand: sets source=dest, implies defer=true
deferNofalseOnly apply when repo is used as an upstream
pathNorootPath to merge at (see Path Syntax)
array_modeNoreplaceArray handling: replace, append, or append_unique
appendNofalseDeprecated: use array_mode: append instead

*Either source+dest or auto-merge is required

Array Merge Modes

ModeDescription
replaceReplace destination array with source array (default)
appendAppend source items to the end of destination array
append_uniqueAppend only items not already in destination array

Examples

Merge at root:

- yaml:
    source: extra-config.yml
    dest: config.yml

Merge at a specific path:

# Merge labels into metadata.labels
- yaml:
    source: labels.yml
    dest: kubernetes.yml
    path: metadata.labels

Append to a list:

# Add items to an existing list
- yaml:
    source: extra-items.yml
    dest: config.yml
    path: items
    append: true

Add jobs to CI workflow:

# Merge shared CI jobs into existing workflow
- yaml:
    source: ci-jobs.yml
    dest: .github/workflows/ci.yml
    path: jobs
    append: true

json - Merge JSON Files

- json:
    source: fragment.json
    dest: package.json
    path: dependencies

Options

OptionRequiredDefaultDescription
sourceYes*-Source fragment file
destYes*-Destination file
auto-mergeNo-Shorthand: sets source=dest, implies defer=true
deferNofalseOnly apply when repo is used as an upstream
pathNorootDot-notation path to merge at
appendNofalseAppend to arrays instead of replace
positionNo-Where to append: start or end (only used when append: true)

*Either source+dest or auto-merge is required

Examples

Add dependencies to package.json:

- json:
    source: extra-deps.json
    dest: package.json
    path: dependencies

Append scripts:

- json:
    source: scripts.json
    dest: package.json
    path: scripts
    append: true
    position: start

Merge TypeScript compiler options:

- json:
    source: strict-options.json
    dest: tsconfig.json
    path: compilerOptions

toml - Merge TOML Files

- toml:
    source: fragment.toml
    dest: Cargo.toml
    path: dependencies

Options

OptionRequiredDefaultDescription
sourceYes*-Source fragment file
destYes*-Destination file
auto-mergeNo-Shorthand: sets source=dest, implies defer=true
deferNofalseOnly apply when repo is used as an upstream
pathNorootPath to merge at (see Path Syntax)
array_modeNoreplaceArray handling: replace, append, or append_unique
appendNofalseDeprecated: use array_mode: append instead
preserve-commentsNofalseKeep comments in output

*Either source+dest or auto-merge is required

See Array Merge Modes for details on array handling options.

Examples

Add Cargo dependencies:

- toml:
    source: common-deps.toml
    dest: Cargo.toml
    path: dependencies

Add dev-dependencies with comments preserved:

- toml:
    source: test-deps.toml
    dest: Cargo.toml
    path: dev-dependencies
    preserve-comments: true

Merge pyproject.toml settings:

- toml:
    source: lint-settings.toml
    dest: pyproject.toml
    path: tool.ruff

ini - Merge INI Files

- ini:
    source: fragment.ini
    dest: config.ini
    section: database

Options

OptionRequiredDefaultDescription
sourceYes*-Source fragment file
destYes*-Destination file
auto-mergeNo-Shorthand: sets source=dest, implies defer=true
deferNofalseOnly apply when repo is used as an upstream
sectionNo-INI section to merge into
appendNofalseAppend values instead of replace
allow-duplicatesNofalseAllow duplicate keys

*Either source+dest or auto-merge is required

Examples

Merge database settings:

- ini:
    source: db-settings.ini
    dest: config.ini
    section: database

Add editor configuration rules:

# Merge settings into the [*] section for all files
- ini:
    source: editor-rules.ini
    dest: .editorconfig
    section: "*"

Configure git settings:

- ini:
    source: git-aliases.ini
    dest: .gitconfig
    section: alias

markdown - Merge Markdown Files

- markdown:
    source: installation.md
    dest: README.md
    section: "## Installation"

Options

OptionRequiredDefaultDescription
sourceYes*-Source fragment file
destYes*-Destination file
auto-mergeNo-Shorthand: sets source=dest, implies defer=true
deferNofalseOnly apply when repo is used as an upstream
sectionNo-Heading to merge under
levelNo2Heading level (1-6)
appendNofalseAppend to section
positionNo-Where to insert: start or end (only used when append: true)
create-sectionNofalseCreate section if missing

*Either source+dest or auto-merge is required

Examples

Add installation instructions:

- markdown:
    source: install-instructions.md
    dest: README.md
    section: "## Installation"
    append: true
    position: end
    create-section: true

Add contributing section:

# Insert a standard Contributing section in the README
- markdown:
    source: contributing-section.md
    dest: README.md
    section: "## Contributing"
    create-section: true

Merge CLAUDE.md rules (upstream-declared):

# In upstream repo - will only apply when inherited
- markdown:
    auto-merge: CLAUDE.md
    section: "## Rules"
    append: true

Operation Order

Operations execute in the order they appear in the configuration file. For inheritance:

  1. Ancestor repos are processed before parent repos
  2. Parent repos are processed before the local repo
  3. Siblings are processed in declaration order

This means later operations can override earlier ones, and child repos can customize what they inherit from ancestors.

self: blocks execute after the source pipeline completes. Each self: block runs as an independent pipeline invocation with its own composite filesystem. Like the source pipeline, composite (upstream-derived) files take precedence over local files for shared paths. Because each self: block starts with an empty composite, it needs a repo: operator to pull in upstream content — otherwise filtering operators have nothing to work with.

Example Order

# local .common-repo.yaml
- repo: {url: A, ref: v1}  # A is processed first (including A's ancestors)
- repo: {url: B, ref: v2}  # B is processed second (including B's ancestors)
- include: ["local/**"]    # Local operations are processed last

If A inherits from C, and B inherits from D:

Processing order: C -> A -> D -> B -> local

Complete Example

Here’s a complete configuration showing multiple operators:

# .common-repo.yaml

# Inherit base Rust CLI configuration
- repo:
    url: https://github.com/common-repo/rust-cli
    ref: v2.0.0
    with:
      - include: ["**/*"]
      - exclude: [".git/**", "target/**"]

# Inherit pre-commit configuration
- repo:
    url: https://github.com/common-repo/pre-commit-rust
    ref: v1.5.0
    with:
      - include: [".pre-commit-config.yaml"]

# Consume shared tooling for this repo only (not exposed to consumers)
- self:
    - repo:
        url: https://github.com/common-repo/shared-tooling
        ref: v1.0.0

# Include local files
- include:
    - src/**
    - Cargo.toml
    - README.md

# Exclude generated files
- exclude:
    - "**/*.generated.rs"

# Rename template files
- rename:
    - "(.+)\\.template$": "$1"

# Define template variables
- template-vars:
    project_name: my-project
    author: Your Name
    rust_edition: "2021"

# Mark files as templates
- template:
    - Cargo.toml
    - README.md

# Require tools
- tools:
    - rustc: ">=1.70"
    - cargo: "*"
    - pre-commit: ">=3.0"

# Merge additional dependencies into Cargo.toml
- toml:
    source: extra-deps.toml
    dest: Cargo.toml
    path: dependencies

# Add CI workflow
- yaml:
    source: ci-jobs.yml
    dest: .github/workflows/ci.yml
    path: jobs
    append: true