Configuration-as-Code for many GitHub repositories. Manage shared configuration files (.gitignore, pom.xml, pyproject.toml, package.json, …) in one place — a single source of truth — and fan them out as pull requests (or direct pushes). Edit a file once, run one command, and every affected repository gets a PR.
- No
git cloneof target repositories — everything goes through the GitHub Git Data API, so it works with nothing more thanpip install - Kustomize-like base + overlay composition, with Jinja2 templating for per-repository values and per-repository remapping of distribution paths
- Fleet-wide summaries and machine-readable reports —
buildanddeployend with a summary of what happened everywhere, and--jsonemits a full report for CI and chat integrations - Designed for microservice fleets that cannot be consolidated into a monorepo
pip install ghfanout # or: uv tool install ghfanoutRequires Python 3.11 or later (tested on 3.11–3.14).
To upgrade to the latest release:
pip install --upgrade ghfanout # or: uv tool upgrade ghfanout# 1. Generate a config repository scaffold (with a working sample)
ghfanout init ./my-config --org myorg
cd my-config
# 2. Compose base + overlay locally and inspect the result (no git operations, no network)
ghfanout build example-service # output: dist/example-service/example-service -> dist/example-service (2 files: 1 rendered)
Summary
1 built example-service
# 3. Authenticate, preview the diff against the real repository, then deploy
export GHFANOUT_TOKEN=ghp_xxxx
ghfanout deploy example-service --dry-run
ghfanout deploy example-service # opens a PR if there is a diffHow it fits together — base/ holds the content, overlays/ selects who gets it:
ghfanout.yaml # destination host / org
.ghfanoutignore # optional: files under base/ to exclude (.gitignore syntax)
base/
common/ # distributed to every repository
.gitignore
java-service/ # a profile: distributed only to repos that opt in
pom.xml.jinja # .jinja = Jinja2 template, rendered per repository
python-service/
pyproject.toml
node-service/
package.json
overlays/
user-service/ # directory name = target repository name
manifest.yaml # which profiles / branches this repo receives
api-gateway/
manifest.yaml
Full documentation: https://seiya-koji.github.io/ghfanout/
- Getting Started — install → init → build → deploy, end to end
- Configuration — config repository layout,
ghfanout.yaml,manifest.yaml, per-branch overrides - Templates — embedding per-repository values with Jinja2
- Authentication — PAT and GitHub App
- CLI Reference — commands, options, and behavior details
- Development — local setup, tests, lint, and commit conventions