Skip to content

Repository files navigation

ghfanout

GitHub Pages Coverage PyPI version Python versions License: MIT

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 clone of target repositories — everything goes through the GitHub Git Data API, so it works with nothing more than pip 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 reportsbuild and deploy end with a summary of what happened everywhere, and --json emits a full report for CI and chat integrations
  • Designed for microservice fleets that cannot be consolidated into a monorepo

Installation

pip install ghfanout        # or: uv tool install ghfanout

Requires 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

Quick start

# 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 diff

How 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

Documentation

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

About

Configuration-as-Code for many GitHub repositories: manage shared config files in one place and fan them out as pull requests, using a Kustomize-like base/overlay structure

Resources

Contributing

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages