Skip to content

Obmondo/gfetch

Repository files navigation

gfetch

GitHub Tests GitHub Docker Go Report Card Ask DeepWiki

A CLI tool that selectively mirrors remote Git repositories to local paths based on YAML configuration.

Features

  • Selective sync — choose exactly which branches and tags to mirror using exact names, wildcards (*), or regex patterns
  • Pruning — detect and remove local branches/tags that no longer match any configured pattern
  • Stale pruning — optionally remove inactive branches that have no new commits in a specified period (e.g., last 6 months); prune_stale only takes effect when prune is also enabled — stale branches are skipped before branch sync when both are set
  • Daemon mode — run as a foreground polling service with per-repo poll intervals
  • Live config reload — daemon re-reads its config on SIGHUP or POST /reload and applies adds, removes, and edits without a restart (Prometheus-style: explicit trigger, no filesystem watcher)
  • Partial-validate tolerance — one invalid repo (missing fields, bad regex, unreachable HTTPS URL) is logged and dropped instead of blocking every other repo
  • SSH and HTTPS auth — private repos via SSH key, public repos via anonymous HTTPS
  • Working tree checkout — optionally keep a working tree checked out on a specific branch or tag
  • OpenVox mode — create per-branch/tag directories with sanitized names, ideal for Puppet environments
  • Production alias (OpenVox) — optional production_alias: true creates/updates a production symlink to the upstream default-branch directory when upstream does not have a real production branch
  • Lightweight clones — repos are initialized empty and only configured refs are fetched

Quick Start

# Install
go install github.com/obmondo/gfetch/cmd/gfetch@latest

# Create a config file
cat <<'EOF' > config.yaml
repos:
  my-repo:
    url: https://github.com/example/repo.git
    local_path: /var/repos/my-repo
    poll_interval: 5m
    branches:
      - main
EOF

# Run a one-shot sync
gfetch sync

Or run with Docker:

docker run -v /path/to/config.yaml:/home/gfetch/config.yaml \
           -v /var/repos:/var/repos \
           ghcr.io/obmondo/gfetch daemon

Installation

From source

git clone https://github.com/obmondo/gfetch.git
cd gfetch
go build -o gfetch ./cmd/gfetch

With go install

go install github.com/obmondo/gfetch/cmd/gfetch@latest

Releases

Pre-built binaries for Linux and macOS (amd64/arm64) are available via GitHub Releases. Each release includes a checksums.txt for verification.

Docker

A pre-built image is published to the GitHub Container Registry on every tagged release.

# Pull the latest image
docker pull ghcr.io/obmondo/gfetch

# Run the daemon with config and repo storage mounted
docker run -v /path/to/config.yaml:/home/gfetch/config.yaml \
           -v /var/repos:/var/repos \
           ghcr.io/obmondo/gfetch daemon

To build locally:

docker build -t gfetch .

# With version info
docker build --build-arg VERSION=1.0.0 \
             --build-arg COMMIT=$(git rev-parse --short HEAD) \
             --build-arg DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ) \
             -t gfetch .

Configuration

gfetch reads a YAML config file (default: config.yaml in the current directory). Use a defaults: key to share settings across multiple repositories.

defaults:
  ssh_key_path: /home/gfetch/.ssh/id_rsa
  poll_interval: 10m
  prune: true                   # remove branches/tags no longer matching any pattern
  prune_stale: true             # remove branches with no commits in 6 months (requires prune: true)
  stale_age: 180d               # supports d (days)
  production_alias: false       # OpenVox-only: if true and upstream has no production branch,
                                 # create/update production -> <default-branch-dir> symlink

# TODO: Add integration tests for real SSH Git repositories.
# TODO: Implement concurrent syncing for OpenVox mode to improve performance and reliability.
# TODO: Add configurable concurrency limit per repository.

repos:
  my-service:
    url: git@github.com:obmondo/my-service.git
    branches:
      - main
      - /^release-.*/        # regex pattern
    tags:
      - "*"                  # wildcard matches all tags

  internal-tool:
    url: git@github.com:org/tool.git
    prune_stale: false          # override default for this repo
    branches:
      - main

See docs/configuration.md for the full configuration reference, including all fields, pattern syntax, auth methods, and validation rules.

Important (OpenVox environments): set production_alias: true to keep a stable production symlink pointing to the upstream default branch directory. gfetch skips alias creation if upstream already has a production branch.

Usage

Global Flags

Flag Default Description
--config, -c config.yaml Path to config file
--log-level info Log level: debug, info, warn, error

gfetch sync

One-shot sync of all repos (or a specific repo).

gfetch sync                    # sync all repos
gfetch sync --repo my-service  # sync a specific repo
gfetch sync --prune            # sync and remove obsolete branches/tags
gfetch sync --prune-stale      # sync and remove branches with no commits in 6 months
gfetch sync --stale-age 30d    # custom threshold for stale pruning
gfetch sync --prune --dry-run  # show what would be pruned without deleting
gfetch sync --prune --prune-stale  # also skips stale branches before branch sync
Flag Default Description
--repo (empty) Sync only the named repo
--prune false Delete local branches/tags that no longer match any pattern
--prune-stale false Delete local branches with no commits in the last 6 months; with --prune, stale branches are skipped before branch sync
--stale-age 180d Custom age threshold for stale pruning (e.g., 30d)
--dry-run false Show what would be pruned without actually deleting

gfetch daemon

Run as a foreground polling daemon. Each repo syncs immediately on start, then polls at its configured poll_interval. Shuts down gracefully on SIGINT or SIGTERM.

gfetch daemon
gfetch daemon --config /etc/gfetch/config.yaml --log-level debug

Pruning is controlled via prune and prune_stale config fields per-repo. Set prune: true in config to enable obsolete-ref pruning; prune_stale: true (also requires prune: true) to additionally prune inactive branches.

The daemon reloads --config (file or directory) explicitly on SIGHUP or POST /reload, following Prometheus's model — there is no filesystem watcher. Edits, adds, and removes are applied without a restart; in-flight syncs finish against the config snapshot they started with, and the next scheduled fire uses the new config. Reload failures (load, validate, apply) are logged and counted, and the previous config keeps running.

The daemon exposes the following HTTP endpoints on the listen address (default :8080):

Method Path Description
GET /health Liveness/readiness probe — returns 200 with {"status":"ok"}.
GET /metrics Prometheus metrics.
POST /reload Re-read the config from disk, validate it, and replace every running job. Returns {"repos": [...]} listing the repos now managed.
POST /sync Trigger a manual sync of every configured repo.
POST /sync/{repo} Trigger a manual sync of a single repo.

To pick up a config change before syncing, call POST /reload (or send SIGHUP) first, then POST /sync.

gfetch validate-config

Validate the config file and exit.

gfetch validate-config
gfetch validate-config -c /path/to/config.yaml

gfetch cat

Print the fully resolved configuration as YAML. Loads the config, applies defaults, validates, and outputs the result to stdout.

gfetch cat
gfetch cat -c /path/to/config.yaml

gfetch version

Print version information.

$ gfetch version
gfetch dev (commit: none, built: unknown)

Version, commit, and build date are injected at build time via ldflags when using GoReleaser or a manual build with -ldflags.

Documentation

License

TBD

About

Git sync daemon that polls remote repos and keeps local mirrors up to date. Supports SSH and HTTPS, branch/tag pattern matching, and OpenVox mode — where each matching ref gets its own checkout directory.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages