Configuration Reference
This document covers all operators and options available in .common-repo.yaml.
- Configuration File
- Core Operators
- Structural Operators
- Merge Operators
- Operation Order
- Complete Example
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
| Operator | Description |
|---|---|
repo | Inherit files from a remote Git repository |
include | Add files from the current repository |
exclude | Remove files from the in-memory filesystem |
rename | Transform file paths using regex patterns |
template | Mark files for variable substitution |
template-vars | Define variables for template substitution |
tools | Validate that required tools are installed |
yaml | Merge YAML configuration fragments |
json | Merge JSON configuration fragments |
toml | Merge TOML configuration fragments |
ini | Merge INI configuration fragments |
markdown | Merge markdown document fragments |
self | Run 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
| Option | Required | Description |
|---|---|---|
url | Yes | Git repository URL |
ref | Yes | Git reference (tag, branch, or commit SHA) |
path | No | Sub-directory to use as root |
with | No | Inline 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 directorysrc/**/*.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
| Syntax | Meaning |
|---|---|
* | Any version |
1.70 | Exactly 1.70 |
>=1.70 | 1.70 or higher |
^1.70 | Compatible with 1.70 (>=1.70.0, <2.0.0) |
~1.70 | Approximately 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
- The orchestrator partitions
self:blocks from the rest of the config - The source pipeline runs first (producing the composite filesystem for consumers)
- Each
self:block runs as an independent pipeline afterward - Output from
self:blocks is written to the working directory but excluded from what consumers inherit
Rules
self:blocks must contain at least one operationself:blocks cannot be nested (noself:insideself:)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 onerepo:to populate its composite filesystem. Without arepo:, the composite is empty and filtering operators likeinclude/exclude/renamehave 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:
| Option | Type | Description |
|---|---|---|
auto-merge | string | Shorthand: sets source=dest to this value and implies defer=true |
defer | bool | When 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:
| Syntax | Example | Description |
|---|---|---|
| Dot notation | foo.bar.baz | Access nested keys |
| Bracket notation | foo["bar"] or foo['bar'] | Access keys with special characters |
| Array indices | items[0] or items[1].name | Access array elements by index |
| Escaped dots | foo\.bar | Literal dot in key name |
| Mixed | servers[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
| Option | Required | Default | Description |
|---|---|---|---|
source | Yes* | - | Source fragment file |
dest | Yes* | - | Destination file |
auto-merge | No | - | Shorthand: sets source=dest, implies defer=true |
defer | No | false | Only apply when repo is used as an upstream |
path | No | root | Path to merge at (see Path Syntax) |
array_mode | No | replace | Array handling: replace, append, or append_unique |
append | No | false | Deprecated: use array_mode: append instead |
*Either source+dest or auto-merge is required
Array Merge Modes
| Mode | Description |
|---|---|
replace | Replace destination array with source array (default) |
append | Append source items to the end of destination array |
append_unique | Append 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
| Option | Required | Default | Description |
|---|---|---|---|
source | Yes* | - | Source fragment file |
dest | Yes* | - | Destination file |
auto-merge | No | - | Shorthand: sets source=dest, implies defer=true |
defer | No | false | Only apply when repo is used as an upstream |
path | No | root | Dot-notation path to merge at |
append | No | false | Append to arrays instead of replace |
position | No | - | 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
| Option | Required | Default | Description |
|---|---|---|---|
source | Yes* | - | Source fragment file |
dest | Yes* | - | Destination file |
auto-merge | No | - | Shorthand: sets source=dest, implies defer=true |
defer | No | false | Only apply when repo is used as an upstream |
path | No | root | Path to merge at (see Path Syntax) |
array_mode | No | replace | Array handling: replace, append, or append_unique |
append | No | false | Deprecated: use array_mode: append instead |
preserve-comments | No | false | Keep 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
| Option | Required | Default | Description |
|---|---|---|---|
source | Yes* | - | Source fragment file |
dest | Yes* | - | Destination file |
auto-merge | No | - | Shorthand: sets source=dest, implies defer=true |
defer | No | false | Only apply when repo is used as an upstream |
section | No | - | INI section to merge into |
append | No | false | Append values instead of replace |
allow-duplicates | No | false | Allow 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
| Option | Required | Default | Description |
|---|---|---|---|
source | Yes* | - | Source fragment file |
dest | Yes* | - | Destination file |
auto-merge | No | - | Shorthand: sets source=dest, implies defer=true |
defer | No | false | Only apply when repo is used as an upstream |
section | No | - | Heading to merge under |
level | No | 2 | Heading level (1-6) |
append | No | false | Append to section |
position | No | - | Where to insert: start or end (only used when append: true) |
create-section | No | false | Create 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:
- Ancestor repos are processed before parent repos
- Parent repos are processed before the local repo
- 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