common-repo
Manage repository configuration files as dependencies. Define inheritance in .common-repo.yaml, pull files from multiple Git repositories, and merge them with version pinning.
Why common-repo?
Modern software repositories require extensive configuration infrastructure: CI/CD pipelines, pre-commit hooks, linters, formatters, and countless dotfiles. Managing these across multiple projects typically means:
- Manual copy-paste - Configuration files copied between projects lead to inconsistency and drift
- No versioning - Unlike source dependencies, configs aren’t semantically versioned or tracked
- Difficult updates - No automated way to propagate standards across repositories
- No inheritance - Can’t easily extend standard configurations
common-repo treats repository configuration as software dependencies.
Configuration files become:
- Semantically versioned - Track exactly which version you’re using
- Automatically updateable - Detect outdated configs and upgrade deterministically
- Composable - Pull from multiple upstream repos and merge intelligently
- Inheritable - Build upon standards that themselves extend other standards
- Self-consuming - Upstream repos can use
self:to pull their own tooling without leaking it to consumers
Quick Start
Install common-repo:
curl -fsSL https://raw.githubusercontent.com/common-repo/common-repo/main/install.sh | sh
Create .common-repo.yaml in your repository:
- repo:
url: https://github.com/your-org/shared-configs
ref: v1.0.0
with:
- include: [".github/**", ".pre-commit-config.yaml"]
Apply configuration:
common-repo ls # List files that would be created
common-repo diff # Preview changes
common-repo apply # Apply configuration
Next Steps
- Getting Started - Installation and first steps
- Configuration Reference - All operators and options
- CLI Reference - Command documentation
- Recipes - Common patterns
Getting Started with common-repo
This guide walks you through installing common-repo and applying your first configuration.
Installation
Quick Install (Recommended)
Install the latest release with a single command:
curl -fsSL https://raw.githubusercontent.com/common-repo/common-repo/main/install.sh | sh
This automatically detects your platform and installs the appropriate binary to ~/.local/bin. The installer also creates a short alias cr so you can use either common-repo or cr to run commands.
Installation options:
# Install a specific version
VERSION=v0.20.0 curl -fsSL https://raw.githubusercontent.com/common-repo/common-repo/main/install.sh | sh
# Install to a custom directory
INSTALL_DIR=/usr/local/bin curl -fsSL https://raw.githubusercontent.com/common-repo/common-repo/main/install.sh | sh
# Also install prek (fast pre-commit hooks)
INSTALL_PREK=1 curl -fsSL https://raw.githubusercontent.com/common-repo/common-repo/main/install.sh | sh
# Install with sudo (for system-wide installation)
curl -fsSL https://raw.githubusercontent.com/common-repo/common-repo/main/install.sh | sudo sh
# Skip creating the 'cr' alias
SKIP_ALIAS=1 curl -fsSL https://raw.githubusercontent.com/common-repo/common-repo/main/install.sh | sh
# Use GitHub token to avoid API rate limits (useful in CI)
GITHUB_TOKEN=ghp_xxx curl -fsSL https://raw.githubusercontent.com/common-repo/common-repo/main/install.sh | sh
From Pre-built Binaries
Download the latest release for your platform from GitHub Releases.
Available platforms:
- Linux x86_64 (glibc):
common-repo-vX.Y.Z-x86_64-unknown-linux-gnu.tar.gz - Linux x86_64 (musl):
common-repo-vX.Y.Z-x86_64-unknown-linux-musl.tar.gz - Linux ARM64:
common-repo-vX.Y.Z-aarch64-unknown-linux-gnu.tar.gz - macOS ARM64 (Apple Silicon):
common-repo-vX.Y.Z-aarch64-apple-darwin.tar.gz - Windows x86_64:
common-repo-vX.Y.Z-x86_64-pc-windows-msvc.zip
From Source
# Clone the repository
git clone https://github.com/common-repo/common-repo.git
cd common-repo
# Build and install
cargo install --path .
Or install directly from GitHub:
cargo install --git https://github.com/common-repo/common-repo
Verify Installation
common-repo --version
# Or use the short alias
cr --version
Your First Configuration
1. Initialize a Configuration File
Navigate to your project directory and run the interactive wizard:
cd your-project
common-repo init
The wizard will:
- Prompt you to enter repository URLs (supports GitHub shorthand like
org/repo) - Auto-detect the latest semver tag for each repository
- Optionally set up pre-commit hooks
- Generate a ready-to-use
.common-repo.yaml
Alternative: Initialize from an existing repo
# Full URL
common-repo init https://github.com/your-org/shared-configs
# GitHub shorthand
common-repo init your-org/shared-configs
2. Define What to Inherit
Edit .common-repo.yaml to inherit from a repository. Here’s a simple example that pulls pre-commit configuration:
# .common-repo.yaml
# Inherit files from a remote repository
- repo:
url: https://github.com/your-org/shared-configs
ref: v1.0.0
with:
- include: ["**/*"]
- exclude: [".git/**", "README.md"]
3. Preview Changes
Before applying, see what files would be created:
# List files that would be created
common-repo ls
# See a diff of changes
common-repo diff
4. Apply the Configuration
# Dry run first (recommended)
common-repo apply --dry-run
# Apply for real
common-repo apply
Understanding the Output
After running common-repo apply, you’ll see:
- Files pulled from inherited repositories
- Any merge operations applied (YAML, JSON, etc.)
- Template variables substituted
The tool caches cloned repositories at ~/.common-repo/cache/ for faster subsequent runs.
Common Workflows
Check for Updates
# See if inherited repos have newer versions
common-repo check --updates
Update to Latest Compatible Versions
# Update refs to latest compatible versions (minor/patch only)
common-repo update
# Include breaking changes (major versions)
common-repo update --latest
View Inheritance Tree
# See how repos inherit from each other
common-repo tree
Example: Inheriting CI/CD Configuration
Here’s a practical example that inherits GitHub Actions workflows:
# .common-repo.yaml
# Pull CI workflows from your org's standard configs
- repo:
url: https://github.com/your-org/ci-templates
ref: v2.1.0
with:
- include: [".github/**"]
# Customize the project name in templates
- template-vars:
project_name: my-awesome-project
rust_version: "1.75"
# Mark workflow files as templates for variable substitution
- template:
- ".github/workflows/*.yml"
Next Steps
- Configuration Reference - All operators and options
- CLI Reference - Complete command documentation
- Recipes - Common patterns and examples
Update Workflow
This guide explains how to check for updates and apply them to your inherited repositories.
Workflow Overview
The update workflow follows a check-review-apply pattern:
- Check for available updates
- Review changes before applying
- Apply with confidence
Checking for Updates
See if any inherited repositories have newer versions available:
common-repo check --updates
This compares your pinned refs against available tags in each repository. Output shows:
- Current ref for each inherited repo
- Available newer versions
- Whether updates are compatible (minor/patch) or breaking (major)
Reviewing Changes
Before updating refs, preview what would change in your files:
# Update refs without applying
common-repo update --dry-run
# See file differences after updating
common-repo diff
The --dry-run flag shows which refs would change without modifying your config file.
Applying Updates
Compatible Updates (Safe)
Update to the latest compatible versions (same major version):
common-repo update
This updates minor and patch versions only. For example, v1.2.0 might update to v1.3.5 but not v2.0.0.
Latest Updates (Breaking)
Include breaking changes when updating:
common-repo update --latest
This updates to the newest available version regardless of major version changes.
Skip Confirmation
For scripting or CI, skip the confirmation prompt:
common-repo update --yes
Selective Updates
Update only specific upstream repos using glob patterns:
# Update repos matching a pattern
common-repo update --filter "github.com/org/*"
# Update multiple patterns (OR logic)
common-repo update --filter "*/*/ci-*" --filter "*/*/linter-*"
# Combine with other flags
common-repo update --filter "github.com/org/*" --latest --dry-run
Patterns match against the repository URL (with scheme stripped) combined with the optional path. For example:
| Config | Match Target |
|---|---|
url: https://github.com/org/repo | github.com/org/repo |
url: https://github.com/org/monorepo, path: configs/eslint | github.com/org/monorepo/configs/eslint |
Pattern examples:
github.com/org/*- repos under a specific org*/*/ci-*- any repo with “ci-” prefixgithub.com/**- all GitHub repos
Complete Example
A typical update session:
# Step 1: Check what's available
common-repo check --updates
# Step 2: Preview ref changes
common-repo update --dry-run
# Step 3: Apply ref updates to config
common-repo update
# Step 4: Preview file changes
common-repo diff
# Step 5: Apply file changes with dry-run first
common-repo apply --dry-run
# Step 6: Apply for real
common-repo apply
Semantic Versioning
common-repo understands semantic versioning when comparing refs:
| Update Type | Example | --compatible | --latest |
|---|---|---|---|
| Patch | v1.2.0 → v1.2.1 | Yes | Yes |
| Minor | v1.2.0 → v1.3.0 | Yes | Yes |
| Major | v1.2.0 → v2.0.0 | No | Yes |
The --compatible flag (default) follows semver: patch and minor updates are safe, major updates may contain breaking changes.
Tips
Pin to specific versions for production stability:
- repo:
url: https://github.com/your-org/configs
ref: v2.1.0 # Pinned version
Use branches for development or always-latest behavior:
- repo:
url: https://github.com/your-org/configs
ref: main # Tracks branch HEAD
Check updates in CI to catch drift:
# Fails if updates are available
common-repo check --updates || echo "Updates available"
File Filtering
This guide explains how to use include and exclude patterns to control which files are inherited from upstream repositories.
Basic Syntax
Use include and exclude in the with clause of a repo operation:
- repo:
url: https://github.com/your-org/configs
ref: v1.0.0
with:
- include: ["pattern1", "pattern2"]
- exclude: ["pattern3"]
Or at the top level for local files:
- include: ["src/**", "Cargo.toml"]
- exclude: ["**/*.bak"]
Pattern Syntax
Patterns use glob syntax:
| Pattern | Matches |
|---|---|
* | Any filename in current directory |
** | Any path (recursive) |
*.rs | All .rs files in current directory |
**/*.rs | All .rs files recursively |
src/** | Everything under src/ |
.* | Hidden files at root |
.*/** | Everything in hidden directories |
Order of Operations
includeruns first, adding files to the outputexcluderuns second, removing files from the output
This means you can include a broad pattern and then exclude specific files:
- repo:
url: https://github.com/your-org/configs
ref: v1.0.0
with:
- include: [".github/**"]
- exclude: [".github/CODEOWNERS"]
Common Patterns
Include Only CI Files
Pull GitHub Actions workflows and nothing else:
- repo:
url: https://github.com/your-org/ci-templates
ref: v1.0.0
with:
- include: [".github/workflows/*.yml"]
Exclude Tests and Examples
Pull everything except test and example files:
- repo:
url: https://github.com/your-org/library
ref: v2.0.0
with:
- include: ["**/*"]
- exclude: ["tests/**", "examples/**", "**/*_test.rs"]
Include Hidden Files
Dotfiles and hidden directories require explicit patterns:
- repo:
url: https://github.com/your-org/dotfiles
ref: v1.0.0
with:
- include:
- ".*" # .gitignore, .editorconfig, etc.
- ".*/**" # .github/*, .vscode/*, etc.
Exclude Generated Files
Skip files that shouldn’t be version-controlled:
- repo:
url: https://github.com/your-org/project
ref: v1.0.0
with:
- include: ["**/*"]
- exclude:
- ".git/**"
- "target/**"
- "node_modules/**"
- "**/*.generated.*"
Multiple File Types
Include specific file types only:
- repo:
url: https://github.com/your-org/configs
ref: v1.0.0
with:
- include:
- "**/*.yml"
- "**/*.yaml"
- "**/*.toml"
- "**/*.json"
Combining with Other Operations
Filtering happens before other operations in the with clause:
- repo:
url: https://github.com/your-org/templates
ref: v1.0.0
with:
# Filter first
- include: ["templates/**"]
- exclude: ["templates/internal/**"]
# Then rename
- rename:
- "^templates/(.*)": "$1"
Viewing Filtered Results
Check which files match your patterns:
# List all files that would be created
common-repo ls
# Filter the listing by pattern
common-repo ls --pattern "*.yml"
# Long format shows sizes
common-repo ls -l
Troubleshooting
Files not appearing? Check that your include pattern matches. Use common-repo ls to see what’s included.
Too many files? Add exclude patterns to filter out unwanted files.
Hidden files missing? Remember to explicitly include .* and .*/** patterns.
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
CLI Reference
Complete reference for all common-repo commands.
Global Options
These options are available for all commands:
| Option | Description |
|---|---|
--color <WHEN> | Colorize output: always, never, auto (default: auto) |
--log-level <LEVEL> | Set log level: error, warn, info, debug, trace (default: info) |
--verbose | Increase output verbosity (can be repeated for more detail) |
--quiet | Suppress output except errors |
-h, --help | Print help information |
-V, --version | Print version |
Verbosity Control
The --verbose and --quiet flags provide convenient shortcuts for controlling output verbosity:
--quiet: Shows only error messages. Useful for scripting.--verbose: Shows debug-level messages. Can be repeated for more detail.--verbose --verbose: Shows trace-level messages (maximum verbosity).
These flags override --log-level when specified. Note that --verbose and --quiet cannot be used together.
Examples:
# Minimal output for scripts
common-repo apply --quiet
# Debug output to troubleshoot issues
common-repo apply --verbose
# Maximum verbosity for detailed debugging
common-repo check --verbose --verbose
Commands
add - Add Repository
Add a repository to an existing .common-repo.yaml configuration file. Automatically detects the latest semver tag.
common-repo add [OPTIONS] <URI>
Arguments
| Argument | Description |
|---|---|
<URI> | Repository URL to add (e.g., https://github.com/org/repo or org/repo GitHub shorthand) |
Options
| Option | Description |
|---|---|
-y, --yes | Non-interactive mode: create minimal config without prompting if none exists |
Examples
# Add a repo to existing config
common-repo add your-org/shared-configs
# Add using full URL
common-repo add https://github.com/your-org/shared-configs
# Create new config with --yes (non-interactive)
common-repo add --yes your-org/shared-configs
Behavior
- If
.common-repo.yamlexists: appends the new repository before theincludesection - If no config exists: prompts for confirmation to create a minimal config (use
--yesto skip prompt) - Automatically fetches and uses the latest semver tag, or falls back to
mainif no tags found - Warns when adding repositories with only 0.x.x versions (unstable API)
apply - Apply Configuration
Apply the .common-repo.yaml configuration to your repository. This runs the full 6-phase pipeline: discover repos, clone, process, merge, and write files.
common-repo apply [OPTIONS]
Options
| Option | Description |
|---|---|
-c, --config <PATH> | Path to config file (default: .common-repo.yaml) |
-o, --output <PATH> | Output directory (default: current directory) |
--cache-root <PATH> | Cache directory (default: ~/.cache/common-repo on Linux, ~/Library/Caches/common-repo on macOS) |
-n, --dry-run | Show what would be done without making changes |
-f, --force | Overwrite existing files without prompting |
--no-cache | Bypass cache and fetch fresh clones |
Use global --verbose or --quiet flags for verbosity control.
Examples
# Apply configuration
common-repo apply
# Preview changes without applying
common-repo apply --dry-run
# Apply with verbose output
common-repo apply --verbose
# Apply to a different directory
common-repo apply --output ./output
# Force fresh clones (ignore cache)
common-repo apply --no-cache
# Use a different config file
common-repo apply --config my-config.yaml
check - Validate and Check Updates
Check configuration validity and optionally check for repository updates.
common-repo check [OPTIONS]
Options
| Option | Description |
|---|---|
-c, --config <FILE> | Path to config file (default: .common-repo.yaml) |
--cache-root <DIR> | Cache directory |
--updates | Check for newer versions of inherited repositories |
Examples
# Validate configuration
common-repo check
# Check for available updates
common-repo check --updates
Output
When checking for updates, you’ll see:
- Current ref for each inherited repo
- Available newer versions (if any)
- Whether updates are compatible (minor/patch) or breaking (major)
completions - Generate Shell Completions
Generate shell completion scripts for tab-completion support.
common-repo completions <SHELL>
Arguments
| Argument | Description |
|---|---|
<SHELL> | Shell to generate completions for: bash, zsh, fish, powershell, elvish |
Examples
# Generate bash completions
common-repo completions bash > ~/.local/share/bash-completion/completions/common-repo
# Generate zsh completions
common-repo completions zsh > ~/.zfunc/_common-repo
# Generate fish completions
common-repo completions fish > ~/.config/fish/completions/common-repo.fish
# Generate PowerShell completions
common-repo completions powershell >> $PROFILE
Installation
Bash
# Option 1: User-level installation
mkdir -p ~/.local/share/bash-completion/completions
common-repo completions bash > ~/.local/share/bash-completion/completions/common-repo
# Option 2: Source directly in .bashrc
echo 'eval "$(common-repo completions bash)"' >> ~/.bashrc
Zsh
# Add to fpath (recommended)
mkdir -p ~/.zfunc
common-repo completions zsh > ~/.zfunc/_common-repo
# Add to .zshrc (before compinit)
echo 'fpath=(~/.zfunc $fpath)' >> ~/.zshrc
echo 'autoload -Uz compinit && compinit' >> ~/.zshrc
Fish
common-repo completions fish > ~/.config/fish/completions/common-repo.fish
PowerShell
# Add to your PowerShell profile
common-repo completions powershell >> $PROFILE
Elvish
common-repo completions elvish >> ~/.elvish/rc.elv
Note on cr alias: If you installed via the shell installer, the cr alias is available. Completions generated for common-repo will work when you type common-repo. For the cr alias, you can create a symlink or alias in your shell configuration, or generate completions separately using cr completions <shell>.
diff - Preview Changes
Show differences between current files and what the configuration would produce.
common-repo diff [OPTIONS]
Options
| Option | Description |
|---|---|
-c, --config <FILE> | Path to config file (default: .common-repo.yaml) |
--cache-root <DIR> | Cache directory |
--working-dir <DIR> | Directory to compare against (default: current) |
--summary | Show only a summary, not individual files |
Examples
# Show full diff
common-repo diff
# Show summary only
common-repo diff --summary
# Compare against a different directory
common-repo diff --working-dir ./other-project
init - Initialize Configuration
Create a new .common-repo.yaml configuration file. By default, launches an interactive wizard that guides you through adding repositories with automatic version detection.
common-repo init [OPTIONS] [URI]
Arguments
| Argument | Description |
|---|---|
[URI] | Repository URL to initialize from (e.g., https://github.com/org/repo or org/repo GitHub shorthand) |
Options
| Option | Description |
|---|---|
-i, --interactive | Interactive setup wizard (default when no URI provided) |
-f, --force | Overwrite existing configuration |
Examples
# Launch interactive wizard (default)
common-repo init
# Initialize from a specific repository
common-repo init https://github.com/your-org/shared-configs
# Use GitHub shorthand
common-repo init your-org/shared-configs
# Overwrite existing config
common-repo init --force
# Explicitly use interactive mode
common-repo init --interactive
Interactive Wizard
When run without arguments, init launches an interactive wizard that:
- Prompts for repository URLs (supports GitHub shorthand like
org/repo) - Auto-detects the latest semver tag for each repository
- Falls back to
mainbranch if no semver tags are found - Optionally sets up pre-commit hooks (detects
prekorpre-commitCLI) - Generates a ready-to-use
.common-repo.yaml
update - Update Repository Refs
Update repository refs in your configuration to newer versions.
common-repo update [OPTIONS]
Options
| Option | Description |
|---|---|
-c, --config <FILE> | Path to config file (default: .common-repo.yaml) |
--cache-root <DIR> | Cache directory |
--compatible | Update to latest compatible versions only (default) |
--latest | Update to latest versions, including breaking changes |
--yes | Don’t ask for confirmation |
--dry-run | Show what would be updated without changing files |
Examples
# Update to latest compatible versions (minor/patch only)
common-repo update
# Preview updates without applying
common-repo update --dry-run
# Update to latest versions including breaking changes
common-repo update --latest
# Update without confirmation
common-repo update --yes
info - Show Configuration Info
Display information about the current configuration.
common-repo info [OPTIONS]
Options
| Option | Description |
|---|---|
-c, --config <FILE> | Path to config file (default: .common-repo.yaml) |
--cache-root <DIR> | Cache directory |
Examples
# Show configuration overview
common-repo info
Output
Shows:
- Inherited repositories and their refs
- Operations defined
- Template variables
- Required tools
ls - List Files
List files that would be created or modified by the configuration.
common-repo ls [OPTIONS]
Options
| Option | Description |
|---|---|
-c, --config <FILE> | Path to config file (default: .common-repo.yaml) |
--cache-root <DIR> | Cache directory |
--working-dir <DIR> | Working directory for local file operations |
-p, --pattern <PATTERN> | Filter by glob pattern (e.g., *.rs, src/**) |
-l, --long | Long format with size and permissions |
-s, --sort <SORT> | Sort by: name, size, path (default: name) |
--count | Show only total file count |
-r, --reverse | Reverse sort order |
Examples
# List all files
common-repo ls
# Long format
common-repo ls -l
# Filter by pattern
common-repo ls --pattern "*.yml"
common-repo ls -p "src/**/*.rs"
# Count files
common-repo ls --count
# Sort by size, largest first
common-repo ls -l --sort size --reverse
validate - Validate Configuration
Validate a .common-repo.yaml configuration file for syntax and semantic errors.
common-repo validate [OPTIONS]
Options
| Option | Description |
|---|---|
-c, --config <FILE> | Path to config file (default: .common-repo.yaml) |
--cache-root <DIR> | Cache directory |
--check-repos | Also verify that referenced repositories are accessible |
--strict | Fail on warnings (not just errors) |
Examples
# Validate syntax
common-repo validate
# Also check that repos are accessible
common-repo validate --check-repos
# Strict mode (fail on warnings)
common-repo validate --strict
cache - Manage Cache
Manage the repository cache.
common-repo cache <SUBCOMMAND> [OPTIONS]
Global Cache Options
| Option | Description |
|---|---|
--cache-root <DIR> | Cache directory (default: ~/.cache/common-repo) |
Subcommands
list - List cached repositories
common-repo cache list [OPTIONS]
| Option | Description |
|---|---|
--detailed | Show detailed info (last modified time, file count) |
--json | Output in JSON format for scripting |
clean - Clean cached repositories
common-repo cache clean [OPTIONS]
At least one filter must be specified:
| Option | Description |
|---|---|
--all | Delete all cached repositories |
--unused | Delete entries older than 30 days |
--older-than <DURATION> | Delete entries older than specified duration |
--dry-run | Show what would be deleted without deleting |
--yes | Skip confirmation prompt |
Duration format: Number followed by unit: s (seconds), m (minutes), h (hours), d (days), w (weeks).
Examples: 30d, 7d, 1h, 2w, 30days, 1week
Examples
# List all cached repos
common-repo cache list
# List with detailed info
common-repo cache list --detailed
# Output as JSON for scripting
common-repo cache list --json
# Preview what would be cleaned (dry run)
common-repo cache clean --unused --dry-run
# Delete entries older than 30 days
common-repo cache clean --unused
# Delete entries older than 7 days
common-repo cache clean --older-than 7d
# Delete all cached repos without prompting
common-repo cache clean --all --yes
JSON Output Schema
When using cache list --json, the output format is:
[
{
"hash": "a1b2c3d4",
"ref": "v1.0.0",
"path": "subdir or null",
"size": 12345,
"file_count": 42,
"last_modified": 1704067200
}
]
tree - Show Inheritance Tree
Display the repository inheritance tree.
common-repo tree [OPTIONS]
Options
| Option | Description |
|---|---|
-c, --config <FILE> | Path to config file (default: .common-repo.yaml) |
--cache-root <DIR> | Cache directory |
--depth <NUM> | Maximum depth to display (omit for full tree) |
Examples
# Show full inheritance tree
common-repo tree
# Show only first two levels
common-repo tree --depth 2
Output
my-project
├── github.com/common-repo/rust-cli@v2.0.0
│ └── github.com/common-repo/base@v1.0.0
└── github.com/common-repo/pre-commit@v1.5.0
Environment Variables
| Variable | Description |
|---|---|
COMMON_REPO_CONFIG | Default config file path |
COMMON_REPO_CACHE | Default cache directory |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error (configuration errors, network failures, I/O errors) |
| 2 | Invalid command-line usage (unknown flags, missing required arguments) |
Special case for diff command:
- Exit code 0: No changes detected (files match configuration)
- Exit code 1: Changes detected (files differ from configuration)
This follows the convention established by diff(1) and git diff.
Scripting examples:
# Check if config is valid
common-repo validate && echo "Config is valid"
# Check if changes are needed
common-repo diff && echo "Up to date" || echo "Changes detected"
# Handle usage errors separately from runtime errors
common-repo apply
case $? in
0) echo "Success" ;;
1) echo "Error during execution" ;;
2) echo "Invalid arguments" ;;
esac
Common Workflows
First-Time Setup
# Initialize config interactively
common-repo init
# Or add repos one at a time
common-repo add your-org/shared-configs
common-repo add your-org/ci-templates
# Preview what would be created
common-repo ls
common-repo diff
# Apply
common-repo apply
Regular Maintenance
# Check for updates
common-repo check --updates
# Review and apply updates
common-repo update --dry-run
common-repo update
Shell Completions
Enable tab-completion for faster command entry:
# Bash
common-repo completions bash > ~/.local/share/bash-completion/completions/common-repo
# Zsh
common-repo completions zsh > ~/.zfunc/_common-repo
# Fish
common-repo completions fish > ~/.config/fish/completions/common-repo.fish
See completions for detailed installation instructions.
Debugging
# Debug output
common-repo apply --verbose
# Maximum verbosity (trace level)
common-repo apply --verbose --verbose
# Validate configuration
common-repo validate --check-repos --strict
# View inheritance
common-repo tree
common-repo info
GitHub Actions
common-repo provides two GitHub Actions:
- setup-common-repo – installs the
common-repobinary - common-repo – checks for upstream updates and creates PRs
Setup Action
Installs the common-repo binary and adds it to PATH.
Use this in CI workflows that need the binary directly.
steps:
- uses: common-repo/setup-common-repo@v1
- run: common-repo validate
Inputs
| Input | Description | Default |
|---|---|---|
version | Version to install (e.g., v0.28.1) | latest |
Outputs
| Output | Description |
|---|---|
version | The installed version of common-repo |
path | Path to the installed binary |
Pin to a specific version
- uses: common-repo/setup-common-repo@v1
with:
version: v0.28.1
The cr alias is also available after setup:
- uses: common-repo/setup-common-repo@v1
- run: cr validate
Sync Action
Checks for upstream updates and creates pull requests with the changes. This is the primary action for keeping inherited configuration files up to date.
Quick Start
Add this workflow to your repository:
# .github/workflows/upstream-sync.yml
name: Sync Upstream Configuration
on:
schedule:
- cron: '0 9 * * 1' # Weekly on Monday at 9am UTC
workflow_dispatch: # Allow manual trigger
permissions:
contents: write
pull-requests: write
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: common-repo/common-repo@v1
Inputs
| Input | Description | Default |
|---|---|---|
token | GitHub token for creating PRs | github.token |
config-path | Path to .common-repo.yaml | .common-repo.yaml |
version | Pin common-repo version (e.g., v0.27.0) | latest |
update-strategy | compatible (minor/patch) or latest (all) | compatible |
force-sync | Run apply even without version updates | false |
pr-title | Pull request title | chore(deps): update common-repo inherited files |
pr-branch | Branch name for the PR | chore/upstream-sync |
commit-message | Commit message | chore(deps): update common-repo files |
dry-run | Check without creating PR | false |
Outputs
| Output | Description |
|---|---|
has-updates | true if version updates were available |
has-changes | true if any file changes were made |
pr-url | URL of created/updated PR |
pr-number | PR number |
files-changed | JSON array of changed files |
Examples
Update to latest versions (including breaking changes)
- uses: common-repo/common-repo@v1
with:
update-strategy: latest
Pin common-repo version for reproducibility
- uses: common-repo/common-repo@v1
with:
version: v0.27.0
Force re-sync (even without version updates)
Useful if someone manually edited a managed file:
- uses: common-repo/common-repo@v1
with:
force-sync: true
Dry-run to check for updates without creating PR
- uses: common-repo/common-repo@v1
id: check
with:
dry-run: true
- run: echo "Updates available: ${{ steps.check.outputs.has-updates }}"
Custom PR settings
- uses: common-repo/common-repo@v1
with:
pr-title: 'chore: sync shared configs'
pr-branch: 'automation/config-sync'
commit-message: 'chore: update shared configuration files'
Requirements
Permissions
The workflow needs these permissions:
permissions:
contents: write # Push to PR branch
pull-requests: write # Create/update PRs
Private inherited repos
If your .common-repo.yaml references private repositories,
you need a Personal Access Token (PAT) with repo scope:
- uses: common-repo/common-repo@v1
with:
token: ${{ secrets.PAT_TOKEN }}
Self-hosted runners
The action requires curl, git, gh (GitHub CLI), and
jq. These are pre-installed on GitHub-hosted runners but
may need to be installed on self-hosted runners.
How It Works
- Installs the
common-repobinary (viasetup-common-repointernally) - Runs
common-repo check --updatesto detect available updates - If updates exist (or
force-sync: true):- Runs
common-repo updateto bump refs in.common-repo.yaml - Runs
common-repo applyto regenerate files
- Runs
- Creates or updates a PR with all changes
The action adds the dependencies label to PRs if that
label exists in your repository.
Recipes and Patterns
Practical examples for common use cases.
Standard Project Setups
Rust CLI Project
A complete Rust CLI project with CI, pre-commit hooks, and conventional commits.
# .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/**", "src/**"]
# Add pre-commit hooks for Rust
- repo:
url: https://github.com/common-repo/pre-commit-rust
ref: v1.0.0
with:
- include: [".pre-commit-config.yaml"]
# Project-specific variables
- template-vars:
project_name: my-cli
author: Your Name
description: A command-line tool
# Mark config files as templates
- template:
- Cargo.toml
- README.md
- .github/workflows/*.yml
# Require essential tools
- tools:
- rustc: ">=1.70"
- cargo: "*"
- pre-commit: ">=3.0"
Python Project with UV
Modern Python project using UV for dependency management.
# .common-repo.yaml
# Base Python project structure
- repo:
url: https://github.com/common-repo/python-uv
ref: v1.0.0
with:
- include: ["**/*"]
- exclude: [".git/**", "__pycache__/**", ".venv/**"]
# Pre-commit hooks for Python
- repo:
url: https://github.com/common-repo/pre-commit-python
ref: v1.5.0
with:
- include: [".pre-commit-config.yaml"]
# Variables
- template-vars:
project_name: my-python-project
python_version: "3.11"
author: Your Name
- template:
- pyproject.toml
- README.md
- tools:
- python: ">=3.11"
- uv: "*"
Node.js/TypeScript Project
TypeScript project with ESLint, Prettier, and GitHub Actions.
# .common-repo.yaml
# TypeScript base configuration
- repo:
url: https://github.com/common-repo/typescript
ref: v3.0.0
with:
- include:
- tsconfig.json
- .eslintrc.json
- .prettierrc
- .github/**
# Add package.json dependencies
- json:
source: dev-deps.json
dest: package.json
path: devDependencies
- template-vars:
project_name: my-project
node_version: "20"
- tools:
- node: ">=20"
- npm: ">=10"
Upstream Repo Patterns
Upstream Repo That Consumes Its Own Tooling
An upstream repo that provides shared configuration to consumers, while also pulling CI and release tooling for its own use. The self: block keeps local consumption separate from the source API.
# .common-repo.yaml in an upstream repo
# Pull tooling for this repo only — consumers never see this
- self:
- repo:
url: https://github.com/org/release-tooling
ref: v3.0.0
- repo:
url: https://github.com/org/ci-base
ref: v1.2.0
- exclude:
- ".releaserc.yaml"
- "commitlint.config.cjs"
# Source API — what consumers inherit
- include:
- "configs/**"
- ".github/**"
- template:
- ".github/workflows/ci.yml"
- template-vars:
GH_APP_ID_VAR: ORG_APP_ID
- rename:
- from: "^configs/(.*)$"
to: "$1"
Without self:, the release tooling and CI base files would leak into every consumer that inherits from this repo. See Authoring Upstream Repositories for more details.
CI/CD Patterns
GitHub Actions Workflow Inheritance
Inherit and customize GitHub Actions workflows.
# .common-repo.yaml
# Base CI workflows
- repo:
url: https://github.com/your-org/ci-templates
ref: v2.0.0
with:
- include: [".github/**"]
# Add project-specific jobs to CI
- yaml:
source: local-ci-jobs.yml
dest: .github/workflows/ci.yml
path: jobs
append: true
# Customize workflow variables
- template-vars:
default_branch: main
node_version: "20"
deploy_environment: production
- template:
- ".github/workflows/*.yml"
local-ci-jobs.yml:
integration-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run integration tests
run: ./scripts/integration-test.sh
Multi-Stage Deployment
Different configurations for different environments.
# .common-repo.yaml
# Base deployment configuration
- repo:
url: https://github.com/your-org/deploy-configs
ref: v1.0.0
path: kubernetes # Only kubernetes configs
# Environment-specific overrides
- yaml:
source: env/production.yml
dest: kubernetes/deployment.yml
path: spec.template.spec
- template-vars:
environment: staging
replicas: "3"
image_tag: latest
- template:
- "kubernetes/*.yml"
Organization Standards
Company-Wide Defaults
Create a base configuration that all projects inherit.
In your org’s base repo (github.com/your-org/base-config):
# .common-repo.yaml (this repo exports these files)
- include:
- .editorconfig
- .gitignore
- .github/CODEOWNERS
- .github/PULL_REQUEST_TEMPLATE.md
- .github/ISSUE_TEMPLATE/**
- LICENSE
- template-vars:
org_name: Your Organization
support_email: support@your-org.com
In each project:
# .common-repo.yaml
# Inherit org-wide defaults
- repo:
url: https://github.com/your-org/base-config
ref: v1.0.0
# Add language-specific configuration
- repo:
url: https://github.com/your-org/rust-standards
ref: v2.0.0
# Project-specific customization
- template-vars:
project_name: my-service
team: platform
Security Configurations
Standardize security tooling across projects.
# .common-repo.yaml
# Security scanning configuration
- repo:
url: https://github.com/your-org/security-configs
ref: v1.2.0
with:
- include:
- .github/workflows/security.yml
- .snyk
- .trivyignore
# Merge security checks into existing CI
- yaml:
source: security-jobs.yml
dest: .github/workflows/ci.yml
path: jobs
append: true
Monorepo Patterns
Shared Configuration Across Packages
Use path filtering to share configs in a monorepo.
# packages/web/.common-repo.yaml
# Inherit from monorepo root configs
- repo:
url: https://github.com/your-org/your-monorepo
ref: main
path: shared/web-configs
with:
- include: ["**/*"]
# Package-specific settings
- template-vars:
package_name: web
port: "3000"
Multiple Config Repositories
Compose from multiple specialized config repos.
# .common-repo.yaml
# Linting standards
- repo:
url: https://github.com/your-org/lint-configs
ref: v2.0.0
with:
- include: [".eslintrc.json", ".prettierrc"]
# Testing configuration
- repo:
url: https://github.com/your-org/test-configs
ref: v1.5.0
with:
- include: ["jest.config.js", "vitest.config.ts"]
# CI/CD templates
- repo:
url: https://github.com/your-org/ci-configs
ref: v3.0.0
with:
- include: [".github/**"]
Configuration Merging
Extending package.json
Add dependencies without overwriting the whole file.
# .common-repo.yaml
# Base project
- repo:
url: https://github.com/your-org/node-base
ref: v1.0.0
# Add shared dev dependencies
- json:
source: shared-dev-deps.json
dest: package.json
path: devDependencies
# Add shared scripts
- json:
source: shared-scripts.json
dest: package.json
path: scripts
append: true
shared-dev-deps.json:
{
"eslint": "^8.0.0",
"prettier": "^3.0.0",
"typescript": "^5.0.0"
}
Merging Cargo.toml Dependencies
Add common Rust dependencies.
# .common-repo.yaml
- repo:
url: https://github.com/your-org/rust-base
ref: v1.0.0
# Add logging dependencies
- toml:
source: logging-deps.toml
dest: Cargo.toml
path: dependencies
# Add dev dependencies
- toml:
source: dev-deps.toml
dest: Cargo.toml
path: dev-dependencies
logging-deps.toml:
tracing = "0.1"
tracing-subscriber = "0.3"
Extending Pre-commit Hooks
Add hooks to an existing pre-commit configuration.
# .common-repo.yaml
# Base pre-commit config
- repo:
url: https://github.com/your-org/pre-commit-base
ref: v1.0.0
with:
- include: [".pre-commit-config.yaml"]
# Add additional hooks
- yaml:
source: extra-hooks.yml
dest: .pre-commit-config.yaml
path: repos
append: true
extra-hooks.yml:
- repo: local
hooks:
- id: custom-check
name: Custom Check
entry: ./scripts/check.sh
language: script
Advanced Patterns
Conditional Configuration via Environment
Use environment variables for conditional behavior.
# .common-repo.yaml
- repo:
url: https://github.com/your-org/base-config
ref: v1.0.0
- template-vars:
# Default to development settings
log_level: debug
enable_metrics: "false"
# CI-specific overrides
ci_mode: "false"
- template:
- config/*.yml
Version Pinning Strategy
Pin specific versions while allowing updates.
# .common-repo.yaml
# Pin to specific major version for stability
- repo:
url: https://github.com/your-org/stable-configs
ref: v2.0.0 # Update manually for major versions
# Use branch for frequently updated configs
- repo:
url: https://github.com/your-org/evolving-configs
ref: main # Always get latest (use with caution)
# Use commit SHA for maximum reproducibility
- repo:
url: https://github.com/your-org/critical-configs
ref: abc123def456 # Exact commit
Template File Patterns
Use template files for customization.
In config repo:
templates/
Cargo.toml.template
README.md.template
In .common-repo.yaml:
- repo:
url: https://github.com/your-org/rust-templates
ref: v1.0.0
with:
- include: ["templates/**"]
- rename:
- "templates/(.+)\\.template$": "$1"
- template-vars:
project_name: my-project
description: A great project
license: MIT
- template:
- Cargo.toml
- README.md
Debugging Tips
Verbose Output
# See detailed processing (debug level)
common-repo apply --verbose
# Maximum verbosity (trace level)
common-repo apply --verbose --verbose
Inspect Before Applying
# List what would be created
common-repo ls -l
# See the diff
common-repo diff
# Dry run
common-repo apply --dry-run
Check Inheritance
# View inheritance tree
common-repo tree
# Get configuration overview
common-repo info
Validate Configuration
# Check syntax
common-repo validate
# Also check repo accessibility
common-repo validate --check-repos
Troubleshooting
This guide covers common issues and their solutions when using common-repo.
Git Authentication Errors
Problem
Git clone error for https://github.com/org/private-repo@main: Authentication failed
hint: Check SSH keys, git credentials, or personal access token
or
Git command failed for https://github.com/org/repo: ls-remote - Permission denied
Causes
- Accessing a private repository without credentials
- Expired or invalid Git credentials
- SSH key not configured for the repository host
Solutions
For HTTPS URLs:
- Ensure you have access to the repository
- Configure Git credential helper:
git config --global credential.helper store - Or use a personal access token in the URL:
- repo: url: https://${GITHUB_TOKEN}@github.com/org/private-repo ref: main
For SSH URLs:
- Ensure your SSH key is added to the Git host
- Test SSH connectivity:
ssh -T git@github.com - Use SSH URL format in configuration:
- repo: url: git@github.com:org/repo.git ref: main
For CI/CD environments:
- GitHub Actions: Use
${{ secrets.GITHUB_TOKEN }}or a deploy key - GitLab CI: Use
CI_JOB_TOKENor deploy tokens
YAML Configuration Syntax Errors
Problem
Configuration parsing error: Missing url field
hint: Add 'url: https://github.com/...' to the repo block
or
YAML parsing error: expected ',' or ']' at line X column Y
Causes
- Incorrect indentation (YAML requires consistent spacing)
- Missing or extra colons, brackets, or quotes
- Tabs instead of spaces
Solutions
-
Validate your YAML before running:
common-repo validate -
Check indentation - YAML uses 2-space indentation by convention:
# Correct - repo: url: https://github.com/org/repo ref: main # Incorrect (inconsistent indentation) - repo: url: https://github.com/org/repo ref: main -
Quote special characters in strings:
# Correct - include: ["*.yml", "**/*.yaml"] # May cause issues - include: [*.yml, **/*.yaml] -
Use a YAML linter in your editor (e.g., YAML extension for VS Code)
Circular Dependency Detected
Problem
Cycle detected in repository dependencies: repo-a -> repo-b -> repo-a
Cause
Two or more repositories reference each other, creating an infinite loop.
Solution
- Review your dependency chain - Draw out which repos inherit from which
- Break the cycle by removing one of the circular references
- Use composition instead of inheritance - Extract shared config into a third repository that both can inherit from without referencing each other
Example fix:
Before: A -> B -> A (cycle)
After: A -> C, B -> C (shared base, no cycle)
Network Connectivity Issues
Problem
Network operation error: https://github.com/org/repo - Connection timeout
or
Git clone error: Could not resolve host
Causes
- No internet connection
- Firewall blocking Git traffic
- GitHub/GitLab outage
- Proxy misconfiguration
Solutions
-
Check connectivity:
ping github.com git ls-remote https://github.com/common-repo/common-repo -
Configure proxy if behind a corporate firewall:
git config --global http.proxy http://proxy.example.com:8080 -
Check service status:
- GitHub: https://www.githubstatus.com/
- GitLab: https://status.gitlab.com/
-
Retry - Transient network issues often resolve themselves
Cache Problems
Problem
Cache operation error: Failed to read cached repository
or stale/corrupted cached data causing unexpected behavior.
Cause
- Corrupted cache files
- Disk space issues
- Interrupted previous operation
Solutions
-
Clear the cache:
common-repo cache clear -
View cache status:
common-repo cache list -
Force fresh clone by clearing cache before apply:
common-repo cache clear && common-repo apply
The cache is stored in your system’s cache directory (typically ~/.cache/common-repo on Linux/macOS).
Merge Conflicts
Problem
Merge conflict warning: source.txt -> dest.txt: File already exists
Cause
A file from an inherited repository conflicts with an existing file in your repository or another inherited repository.
Solutions
-
Use
excludeto skip conflicting files:- repo: url: https://github.com/org/configs ref: main with: - exclude: ["conflicting-file.yml"] -
Use
renameto place the file elsewhere:- repo: url: https://github.com/org/configs ref: main with: - rename: - "ci.yml": ".github/workflows/inherited-ci.yml" -
Check operation order - Files from later operations overwrite earlier ones. Reorder your configuration if needed.
Invalid Glob Patterns
Problem
Glob pattern error: invalid pattern syntax
Cause
Malformed glob pattern in include, exclude, or other operators.
Solutions
-
Use valid glob syntax:
# Correct patterns - include: - "**/*.rs" # All .rs files recursively - "src/**/*" # All files under src/ - "*.md" # .md files in root only - ".*" # Hidden files in root # Invalid patterns - include: - "**[.rs" # Unclosed bracket - "src/***" # Invalid triple asterisk -
Test patterns with
common-repo lsbefore applying:common-repo ls
Git Reference Not Found
Problem
Git clone error for https://github.com/org/repo@v2.0.0: reference not found
hint: Verify the repository URL and ref (branch/tag) are correct
Cause
The specified ref (branch, tag, or commit) does not exist in the repository.
Solutions
-
Verify the ref exists:
git ls-remote https://github.com/org/repo -
Check for typos in branch/tag names
-
Use the correct ref format:
# Tag ref: v1.0.0 # Branch ref: main # Commit SHA (full or abbreviated) ref: abc1234 -
Check for updates - the tag may have been deleted or renamed:
common-repo check --updates
Permission Denied Writing Files
Problem
I/O error: Permission denied
Cause
- Running in a read-only directory
- File is owned by another user
- File is locked by another process
Solutions
-
Check directory permissions:
ls -la . -
Ensure you own the files or have write access
-
Close editors that may have files open
-
On Windows, check if files are marked read-only
Getting Help
If your issue isn’t covered here:
-
Run with verbose output for more details:
common-repo apply --verbose # Or for maximum verbosity (trace level) common-repo apply --verbose --verbose -
Check existing issues: https://github.com/common-repo/common-repo/issues
-
Open a new issue with:
- Your
.common-repo.yaml(sanitized of secrets) - Full error message
- Output of
common-repo --version - Your operating system
- Your
Authoring Upstream Repositories
This guide explains how to create and maintain upstream repositories that other projects can inherit from using common-repo.
What is an Upstream Repository?
An upstream repository contains configuration files, templates, and standards that other repositories (consumers) can inherit—essentially a library of reusable project configurations.
Upstream repository vs consumer repository:
| Aspect | Upstream Repository | Consumer Repository |
|---|---|---|
| Purpose | Provides files for others to inherit | Uses files from upstream repos |
| Audience | Maintainers of shared standards | Individual projects |
| Config file | Optional .common-repo.yaml | Required .common-repo.yaml |
| Versioning | Semantic versioning with Git tags | References upstream repo versions |
| Can also consume | Yes, via self: blocks (local-only) | Yes, directly via repo: |
Common use cases for upstream repositories:
- Organization-wide coding standards (linting, formatting rules)
- CI/CD workflow templates (GitHub Actions, GitLab CI)
- Project scaffolding and boilerplate
- Security policies and configurations
- Documentation templates
Getting Started
Minimal Upstream Repository Structure
A upstream repository can be as simple as a directory with files to share:
my-upstream-repo/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .pre-commit-config.yaml
└── README.md
No special configuration is required. Consumers reference your repository directly:
# Consumer's .common-repo.yaml
- repo:
url: https://github.com/your-org/my-upstream-repo
ref: v1.0.0
Optional: Configuration in Upstream Repos
Upstream repositories can include their own .common-repo.yaml to inherit from other upstreams, creating an inheritance chain:
org-base/ # Base standards
└── rust-base/ # Rust-specific (inherits org-base)
└── my-app/ # Consumer (inherits rust-base)
Using self: for Local Consumption
An upstream repo often needs to consume tooling from its own upstreams — CI config, pre-commit hooks, release automation — without leaking those files to the repos that consume it. The self: operator solves this.
Operations inside a self: block run in an isolated pipeline. Their output is written to the local working directory but never enters the composite filesystem that consumers see. This lets a single .common-repo.yaml define both what the repo provides (its source API) and what it consumes locally.
# .common-repo.yaml for an upstream repo
# Local consumption — pull tooling for this repo's own use.
# Consumers never see these operations.
- self:
- repo:
url: https://github.com/org/ci-tooling
ref: v2.0.0
- exclude:
- ".releaserc.yaml"
# Source API — what consumers inherit
- include:
- "src/**"
- "src/.*"
- rename:
- from: "^src/(.*)$"
to: "$1"
Without self:, this repo would need a separate mechanism to pull its own tooling, or its consumers would inherit the CI tooling files unintentionally.
Key points:
self:blocks are stripped when a consumer inherits from this repo — consumers never see them- Any operator can appear inside
self:(repo, include, exclude, rename, merge operators, etc.) - A
self:block needs at least onerepo:to populate its composite filesystem — without one, filtering operators likeinclude/exclude/renamehave nothing to operate on - Multiple
self:blocks are allowed; each runs as an independent pipeline self:blocks cannot be nested- The source pipeline runs first, then each
self:block runs afterward as an independent pipeline invocation
See the Configuration Reference for the full operator specification.
Upstream-Declared File Filtering
Upstream repositories can define their “public API” by specifying which files are exposed to consumers. This is useful when a repository contains internal files that should not be inherited.
Filtering Operations in Upstream Repos
Upstream repos can use these operations to control which files consumers receive:
| Operation | Purpose |
|---|---|
include | Allowlist of files to expose (all others excluded) |
exclude | Blocklist of files to hide (all others included) |
rename | Transform file paths before consumers see them |
Example: Exposing Only Public Files
An upstream repo with internal test fixtures that should not be inherited:
# Upstream repo: .common-repo.yaml
- include:
patterns:
- "templates/**"
- "configs/**"
- ".github/**"
# Internal test fixtures, scripts, and docs are NOT exposed
Consumers automatically receive only the declared public files.
Example: Hiding Internal Files
An upstream repo that exposes everything except internal directories:
# Upstream repo: .common-repo.yaml
- exclude:
patterns:
- "internal/**"
- "scripts/dev-*.sh"
- ".internal-*"
Example: Renaming Template Files
An upstream repo that provides templates with a different naming convention:
# Upstream repo: .common-repo.yaml
- rename:
- from: "templates/(.*)\\.template"
to: "$1"
This transforms templates/config.yaml.template to config.yaml in consumers.
Operation Order
Operations are applied in this order:
- Upstream filtering (include/exclude/rename from upstream’s config)
- Upstream merge declarations (deferred merge operations)
- Consumer’s with: clause (further filtering/transforms by consumer)
This ensures upstream repos control their exposed files, while consumers can further filter (but not expand) what they receive.
Config File Auto-Exclusion
Upstream repository config files (.common-repo.yaml and .commonrepo.yaml) are automatically excluded and never copied to consumers. This prevents upstream configs from overwriting consumer configs.
Upstream-Declared Merge Behavior
By default, files from upstream repositories overwrite files in consumer repositories. However, upstream authors often know best how their files should integrate. The defer mechanism allows upstream repos to declare merge behavior that automatically applies when consumers inherit from them.
When to Use Upstream-Declared Merges
Use upstream-declared merges when:
- Your upstream provides partial content meant to augment consumer files (e.g., additional CLAUDE.md rules)
- Files need intelligent merging rather than overwriting (e.g., shared dependencies in Cargo.toml)
- You want to reduce boilerplate in consumer configurations
Two Syntax Forms
1. auto-merge - When source and destination have the same filename (most common):
# Upstream repo: .common-repo.yaml
- markdown:
auto-merge: CLAUDE.md
section: "## Inherited Rules"
append: true
This is shorthand for: source=CLAUDE.md, dest=CLAUDE.md, defer=true.
2. defer: true - When source and destination paths differ:
# Upstream repo: .common-repo.yaml
- yaml:
source: config/labels.yaml
dest: kubernetes.yaml
path: metadata.labels
defer: true
Example: Sharing CLAUDE.md Rules
An organization wants all repos to inherit coding guidelines:
# Upstream repo: org-standards/.common-repo.yaml
- markdown:
auto-merge: CLAUDE.md
section: "## Organization Standards"
append: true
create-section: true
Consumer repos automatically get merged CLAUDE.md content:
# Consumer repo: .common-repo.yaml
- repo:
url: https://github.com/org/org-standards
ref: v1.0.0
# No 'with:' clause needed - CLAUDE.md merges automatically
Example: Sharing Dependencies
A base Rust configuration shares common dependencies:
# Upstream repo: rust-base/.common-repo.yaml
- toml:
auto-merge: Cargo.toml
path: dependencies
Consumers inherit the base dependencies merged into their Cargo.toml.
Consumer Override
Consumers can always override upstream-declared behavior using the with: clause:
# Consumer .common-repo.yaml
- repo:
url: https://github.com/org/org-standards
ref: v1.0.0
with:
# Override: copy instead of merge
- include: ["CLAUDE.md"]
# This replaces the upstream-declared merge with a simple copy
When a consumer specifies merge operations for the same destination file, the consumer’s merge operations run after deferred (upstream-declared) merge operations, so consumer parameters take effect last.
Supported Merge Operators
All merge operators support defer and auto-merge:
| Operator | Example Use Case |
|---|---|
markdown | Shared CLAUDE.md rules, README sections |
yaml | Kubernetes labels, GitHub Actions workflow steps |
json | package.json dependencies, tsconfig settings |
toml | Cargo.toml dependencies, pyproject.toml settings |
ini | Git config defaults, editor settings |
Publishing Your First Version
- Commit your configuration files to the repository
- Create a Git tag following semantic versioning:
git tag v1.0.0 git push origin v1.0.0 - Consumers can now reference it:
- repo: url: https://github.com/your-org/my-upstream-repo ref: v1.0.0
File Organization
Repository Root vs Subdirectory
By default, consumers inherit from the repository root. Use the path option to expose a subdirectory:
my-upstream-repo/
├── templates/
│ ├── rust/ # Rust project templates
│ │ ├── .github/
│ │ └── Cargo.toml
│ └── python/ # Python project templates
│ ├── .github/
│ └── pyproject.toml
└── README.md
Consumers select which template to use:
# Consumer's .common-repo.yaml
- repo:
url: https://github.com/your-org/my-upstream-repo
ref: v1.0.0
path: templates/rust # Only inherit from this subdirectory
Organizing Files by Concern
Group related files together to make selective inheritance easier:
upstream-repo/
├── ci/ # CI/CD configurations
│ ├── .github/
│ └── .gitlab-ci.yml
├── quality/ # Code quality tools
│ ├── .pre-commit-config.yaml
│ ├── .editorconfig
│ └── rustfmt.toml
├── security/ # Security configurations
│ ├── .github/dependabot.yml
│ └── SECURITY.md
└── README.md
Consumers can pick specific concerns:
- repo:
url: https://github.com/your-org/upstream-repo
ref: v1.0.0
with:
- include: ["ci/**", "quality/**"]
Dotfiles and Hidden Files
Dotfiles (files starting with .) are included by default. Structure them naturally:
upstream-repo/
├── .github/
│ ├── workflows/
│ │ └── ci.yml
│ └── dependabot.yml
├── .pre-commit-config.yaml
├── .editorconfig
└── .gitignore
Files you typically should not include in upstream repos:
.git/directory (automatically excluded).envfiles with secrets- Local IDE configurations (
.vscode/,.idea/)
Template Variables
Template variables let consumers customize inherited files.
Naming Conventions
Use descriptive, lowercase names with underscores:
# Good variable names
project_name: my-app
rust_version: "1.75"
org_name: acme-corp
enable_coverage: true
# Avoid
PROJECT: my-app # Uppercase
proj: my-app # Abbreviation
projectName: my-app # CamelCase
Required vs Optional Variables
Document which variables consumers must provide. In your template files, use sensible defaults where possible:
# .github/workflows/ci.yml (in upstream repo)
name: CI
env:
RUST_VERSION: ${{ vars.rust_version || '1.75' }} # Optional with default
PROJECT_NAME: ${{ vars.project_name }} # Required
Documenting Variables
Include a README section or separate file listing available variables:
## Template Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `project_name` | Yes | - | Name of the project |
| `rust_version` | No | 1.75 | Rust toolchain version |
| `enable_coverage` | No | false | Enable code coverage |
Variable Overrides
Child repos can override variables defined by their parents:
# Consumer's .common-repo.yaml
- template-vars:
project_name: my-project
ci_timeout: "30"
Versioning and Releases
Semantic Versioning
Follow semantic versioning for upstream repositories:
- MAJOR (
v2.0.0): Breaking changes that require consumer updates - MINOR (
v1.1.0): New files or features, backward compatible - PATCH (
v1.0.1): Bug fixes, documentation updates
When to Bump Versions
| Change Type | Version Bump | Example |
|---|---|---|
| Removed file that consumers depend on | Major | Deleting .pre-commit-config.yaml |
| Renamed template variable | Major | project_name → name |
| Changed file structure consumers reference | Major | Moving ci.yml to new path |
| Added new optional files | Minor | New workflow file |
| Added new template variable with default | Minor | New enable_feature variable |
| Fixed bug in configuration | Patch | Corrected YAML syntax |
| Updated documentation | Patch | Clarified usage instructions |
Git Tagging Guidelines
# Create an annotated tag with message
git tag -a v1.2.0 -m "Add Python support and coverage workflows"
# Push the tag
git push origin v1.2.0
# List existing tags
git tag -l "v*"
Annotated tags (-a) are preferred as they include author and date information.
Changelog Maintenance
Maintain a CHANGELOG.md to document changes:
# Changelog
## [1.2.0] - 2024-01-15
### Added
- Python project template in `templates/python/`
- Coverage workflow for all language templates
### Changed
- Updated Rust version default to 1.75
## [1.1.0] - 2024-01-01
### Added
- Security policy template
Testing Your Upstream Repository
Testing Locally
Test your upstream repo against a local consumer before publishing:
# In consumer repository
cd my-consumer-project
# Reference local upstream repo instead of remote
# Edit .common-repo.yaml temporarily:
# - repo:
# url: /path/to/local/upstream-repo
# ref: HEAD
# Or use a file:// URL
# - repo:
# url: file:///absolute/path/to/upstream-repo
# ref: HEAD
common-repo ls # Verify expected files
common-repo diff # Check for issues
common-repo apply --dry-run
Creating a Test Consumer
Maintain a test consumer repository or directory:
upstream-repo/
├── .github/
├── templates/
├── tests/
│ └── test-consumer/ # Test consumer project
│ ├── .common-repo.yaml
│ └── validate.sh
└── README.md
The test consumer validates that your upstream repo works correctly:
# tests/test-consumer/.common-repo.yaml
- repo:
url: ../.. # Relative path to upstream repo root
ref: HEAD
with:
- include: ["templates/rust/**"]
CI Testing Strategies
Add CI workflows that validate your upstream repository:
# .github/workflows/test.yml
name: Test Upstream Repo
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install common-repo
run: cargo install common-repo
- name: Validate against test consumer
run: |
cd tests/test-consumer
common-repo validate
common-repo apply --dry-run
Composability
Designing for Multiple Upstream Inheritance
Consumers often inherit from multiple upstream repositories. Design yours to work alongside others:
# Consumer inheriting from multiple upstream repos
- repo:
url: https://github.com/org/base-standards
ref: v1.0.0
- repo:
url: https://github.com/org/rust-config
ref: v2.0.0
- repo:
url: https://github.com/org/security-policies
ref: v1.5.0
Avoiding File Conflicts
When multiple upstream repos provide similar files, conflicts can occur. Strategies to avoid this:
Use specific subdirectories:
# Instead of:
upstream-repo/
└── ci.yml
# Use:
upstream-repo/
└── .github/workflows/
└── source-name-ci.yml # Prefixed to avoid conflicts
Document file ownership:
## Files Provided
This upstream repository provides:
- `.github/workflows/lint.yml` - Linting workflow
- `.github/workflows/test.yml` - Test workflow
If you inherit from other upstream repos, ensure they don't provide the same files,
or use `exclude` to skip conflicting files.
Namespace Considerations
Consider prefixing files with your upstream repo’s purpose:
org-security/
└── .github/
└── workflows/
└── security-scan.yml # Clear ownership
org-quality/
└── .github/
└── workflows/
└── quality-lint.yml # No conflict with security-scan.yml
What to Include and Exclude
Good Candidates for Upstream Repos
| File Type | Examples | Why Share |
|---|---|---|
| CI/CD workflows | .github/workflows/, .gitlab-ci.yml | Standardize build/test/deploy |
| Code quality | .pre-commit-config.yaml, rustfmt.toml | Consistent formatting |
| Editor configs | .editorconfig, .vscode/settings.json | Shared development experience |
| Security | SECURITY.md, dependabot.yml | Org-wide security policies |
| Documentation templates | CONTRIBUTING.md, issue templates | Consistent contributor experience |
| Build configurations | Cargo.toml fragments, tsconfig.json | Shared build settings |
What to Avoid
Do not include:
- Secrets or credentials: API keys, tokens, passwords
- Environment-specific paths:
/Users/yourname/... - Large binary files: Images, compiled artifacts
- Generated files:
target/,node_modules/,dist/ - Personal IDE settings: User-specific configurations
- Repository-specific data: Git history, issues, PRs
Handling Sensitive Data
If your templates reference secrets, use placeholders:
# Good: Reference secrets by name
env:
API_KEY: ${{ secrets.API_KEY }}
# Bad: Never include actual secrets
env:
API_KEY: sk-1234567890abcdef
Document required secrets in your README:
## Required Secrets
Consumers must configure these repository secrets:
| Secret | Description |
|--------|-------------|
| `API_KEY` | API key for external service |
| `DEPLOY_TOKEN` | Token for deployment |
Next Steps
- Configuration Reference - All operators for consumers
- Recipes - Common inheritance patterns
- Troubleshooting - Common issues and solutions
Schema Reference
This page documents the configuration schemas for common-repo.
v1 Schema (Legacy)
The original v1 schema from commonrepo. This has been superseded by the v2 schema with operators.
######################
# CommonRepo v1 schema
#
# This is the original schema definition for the v1 of commonrepo. This has been
# superceded by the v2 common-repo.
#
# This schema has the problem where the operations like "include", "rename" and
# "exclude" are applied in a fixed order, and only once, which is inflexible and
# actually more difficult to implement than operators. It also makes it more
# difficult to implement new operators in the form of plugins.
#
# This v1 schema is from the codebase at https://github.com/shakefu/commonrepo.
#########
# Upstreams
#
# Upstream repositories define which of their files should be imported into the
# child repository.
#
# This should be defined in the `.commonrepo.yml` file.
#
# The bare minimum configuration is a single `include: ["**/*"]` entry.
# include opts-in files and folders based on globbing patterns
include:
# Everything
- "**/*"
# Every hidden file
- .*
# Every hidden directory
- .*/**/*
# Everything in the files/ directory
- files/**/*
# Individual file
- .gitignore
# after the full list of included files is built, the exclusion filters are
# applied to remove any unwanted files from the working list
exclude:
# Template related workflows
- .github/workflows/template_*
# Markdown files
- "**/*.md"
# before renames are applied, files that are templates store the vars: context
template:
- "templates/**"
# after the filtered list is created, destination file names are generated by
# passing the working list through the rename transforms, in order
rename:
# Rename a path
- "badname/(.*)": "goodname/$1"
# Strip one directory from the path
- "^files/(.*)": "$1"
# Strip multiple directories from the path
- "some/parent/dir/(.*)": "$1"
# Recompose directories
- "parent/([^/]+)/dir/(.*)": "$1/$2"
# Add a prefix to the path
- "(.*\\.md)": "docs/$1"
# Move templates to repo root
- "templates/(.*)": "$1"
# Install specs use SemVer constraints
install:
# List of maps, where the key name matches the tool filename/path, the version
# describes the desired version.
- golang: 1.17.x
- jq: ^1
# Optional override for path if you don't want to use the default
install-from: ./.commonrepo/install/
# Optional override for preferred install manager order
install-with: [apt-get, brew, platform]
---
###########
# Consumers
#
# Consumer repositories can define which upstream repos they want to consume from, as
# well as apply additional filters to the ones defined in the upstream repos.
# define a list of upstream repositories, which mimic the consumer repos keys
upstream:
- url: https://github.com/shakefu/commonrepo
ref: v1.1.0
overwrite: false # TBD if this should be implemented
include: [.*]
exclude: [.gitignore]
rename: [{".*\\.md": "docs/$1"}]
# Template context for all upstreams...
template-vars:
project: myprojectname
v2 Schema (Current)
The v2 schema uses operators for flexible, composable configuration. All operators are documented in the Configuration Reference.
Available operators: repo, include, exclude, rename, template, template-vars, tools, self, yaml, json, toml, ini, markdown.