Skip to main content

gitfloe

gitfloe

License: MIT Python 3.12 GitHub stars merge-upstream sync CI passing

Keep your forks in sync with their upstreams by calling GitHub's merge-upstream API — the exact same thing the web "Sync fork" button and gh repo sync do. No git, no cloning. When a fork has a conflict, it hands off to a pluggable handler (default: email). After each run — and periodically — it also emails an overall digest of what happened.

What it does

One repo ("the manager") holds a list of your forks and, on a schedule, for each one:

  1. calls POST /repos/{owner}/{repo}/merge-upstream with {"branch": "<branch>"},
  2. reads the result:
    • 200 → merged (synced),
    • 204 → already up to date,
    • 409conflict → dispatch a handler (default: email),
    • 403 / 404 / other → error → dispatch a handler,
  3. records each result to state/events.jsonl and emails a digest.

It only ever writes to your forks (via the API), never to upstream.

Layout

assets/                          # logo (logo.svg, logo-light.svg, logo-lockup.svg, favicon.ico)
examples/                        # templates: repos.example.yaml, .env.example
gitfloe/
  cli.py                         # python -m gitfloe.cli
  core.py                        # calls merge-upstream; classifies result; dispatches
  config.py                      # load repos.yaml
  plugins.py                     # handler discovery + dispatch
  smtp.py                        # shared SMTP sender (env-driven)
  digest.py                      # run + interval digest
  handlers/
    email.py                     # default: email on conflict/error
    ai.py                        # example LLM handler (shows the hook)
tests/                           # pytest tests
.github/workflows/
  sync.yml                       # scheduled sync + per-run digest + state commit
  digest.yml                     # periodic aggregate digest
  ci.yml                         # CI + pytest
state/                           # committed back so history persists across runs

Adding your forks

Copy the template examples/repos.example.yaml to repos.yaml, then edit it:

default:
  branch: main
  handler: email
digest:
  on_run: true              # email a summary after every sync run
  on_interval: true         # + weekly digest via digest.yml
repos:
  - name: example-fork
    fork: you/example-fork
    upstream: someorg/example
    branch: main

upstream is for reference/labels (the API derives it from the fork's GitHub parent); branch is the branch merge-upstream merges into.

The handler hook

A handler is a Python module exposing HANDLER and handle(event):

# handlers/mine.py
HANDLER = "my-name"
def handle(event: dict) -> dict:
    # repo, fork, upstream, branch, kind, api_status, error, summary
    return {"handled": True, "summary": "did something"}

The event kind is synced / up_to_date / conflict / error (plus preview during dry-run). Return {"handled": False} to leave it for another handler / log only. External handlers: drop .py in a folder and point the run at it via GITFLOE_HANDLER_PATH (colon-separated); external ones override by name.

Running locally

uv sync                                                      # install into .venv (PyYAML only)
uv run gitfloe --config repos.yaml --dry-run                 # config/validation check (read-only)
uv run gitfloe --config repos.yaml                           # actually sync (needs GITFLOE_TOKEN)
uv run gitfloe --config repos.yaml --digest-interval         # send periodic digest only

Installing from PyPI

uv tool install gitfloe       # or: pip install gitfloe
gitfloe --config repos.yaml --dry-run

You can also trigger a single fork manually with the official CLI:

gh repo sync luoyueyuguang/vllm --branch main

Where the secrets live (the key point)

No secret is ever committed. Credentials come from the environment — GitHub Actions Secrets in CI, a local .env for development.

Secret Used by Purpose
GITFLOE_TOKEN core PAT/fine-grained token on your forks with Contents: write (the API needs it to push the merge).
GITFLOE_SMTP_HOST/PORT/USER/PASS smtp SMTP for the email handler + digest.
GITFLOE_MAIL_TO smtp Where attention + digest emails go.
GITFLOE_AI_ENDPOINT ai handler LLM endpoint (OpenAI-compatible).
GITFLOE_AI_KEY ai handler LLM bearer token.
GITFLOE_AI_MODEL ai handler Model id (default gpt-4o-mini).

LLM token — same rule as the sync token: put GITFLOE_AI_KEY (+ END/MODEL) in Secrets, injected by the workflow's env:. Local runs read them from .env. If unset, the ai handler degrades to a no-op.

Tip: for Gmail use an App Password + smtp.gmail.com:587.

State persistence

state/ is tracked so the digest can aggregate across workflow runs; both workflows commit it back. Peek at the event log: tail -f state/events.jsonl.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gitfloe-0.1.0.tar.gz (29.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gitfloe-0.1.0-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

File details

Details for the file gitfloe-0.1.0.tar.gz.

File metadata

  • Download URL: gitfloe-0.1.0.tar.gz
  • Upload date:
  • Size: 29.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.25 {"installer":{"name":"uv","version":"0.9.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for gitfloe-0.1.0.tar.gz
Algorithm Hash digest
SHA256 b0c0ec0c98bb3726f56b86fc082aaab972b2f6b21dd9f5b4c794ed97a401f6ea
MD5 0660d44b2a74a9f1a4cee053d58147a2
BLAKE2b-256 622ca72594d2cd4a808b9f0c4ac29e5003410acf6e2fb8c09921a19c5768385e

See more details on using hashes here.

File details

Details for the file gitfloe-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: gitfloe-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.25 {"installer":{"name":"uv","version":"0.9.25","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for gitfloe-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5bda394e5bb3370039860f644755e2e11f37216f5abca6fe286fe70dce98c119
MD5 17244583b322b60109b4e9a5b21ab16c
BLAKE2b-256 6c98686d9f2fc1bb0e5b140b1c7b30cd787c8503f4e57493c698f3649dbf594e

See more details on using hashes here.

Release history Release notifications | RSS feed

0.1.1

2 files

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page