All-in-one GitHub Action to keep your fork in sync with upstream
Branch sync · PR monitoring · Backporting · Conflict cleanup
Pin to a major version (@v1) for automatic non-breaking updates, or to an exact tag (@v1.1.0) for full reproducibility.
Marketplace: github.com/marketplace/actions/fork-shepherd
Source: github.com/FasterApiWeb/fork-shepherd
Insights / traffic: Repository insights (maintainers)
- Features
- Installation
- Usage
- Configuration
- Example workflows
- How it works
- Permissions
- FAQ
- Contributing
- Releasing
- License
| Feature | What it does |
|---|---|
| Branch sync | Fast-forward or merge upstream into your fork branches; open conflict issues when needed |
| PR monitor | Keep labeled PRs up to date with their base branch; detect upstream reverts |
| Backport | Cherry-pick merged PRs onto release branches via labels |
| Cleanup | Auto-close resolved sync-conflict issues |
Fork Shepherd needs a token that can push branches and manage issues/PRs on your fork.
Fine-grained PAT (recommended for one fork):
- Open Fine-grained tokens
- Generate new token → select your fork repository
- Permissions:
- Contents: Read and write
- Issues: Read and write
- Pull requests: Read and write
- Generate and copy the token
Classic PAT: enable repo (add workflow only if you sync branches that contain workflow files).
If the org uses SAML SSO, authorize the token for that organization.
On your fork: Settings → Secrets and variables → Actions → New repository secret
| Name | Value |
|---|---|
FORK_SYNC_PAT |
The PAT from step 1 |
Create .github/workflows/fork-shepherd.yml on your fork (see Usage).
With monitor_all_prs: "true" you do not need sync-bot for auto-updates.
| Label | Used for |
|---|---|
sync-bot |
Only if monitor_all_prs is false (opt-in monitoring) |
sync-conflict |
Conflict issues (default conflict_label) |
backport-to-<branch> |
e.g. backport-to-release-1.0 |
| Goal | How it works |
|---|---|
| Keep fork branches synced with upstream | schedule (e.g. hourly) — GitHub cannot notify your fork when upstream moves |
| Auto-update a PR into main when main advances | push to main → merges/rebases main into that PR’s head |
| Same for master / develop | push to those branches → only PRs with that base are updated |
| Conflicts | Bot comments with fix steps; no force-push — you resolve manually |
With monitor_all_prs: "true", every open PR is kept current for its own base (main, master, develop, …). No sync-bot label and no “Update branch” button unless there is a conflict.
Create .github/workflows/fork-shepherd.yml on your fork:
name: Fork Shepherd
on:
schedule:
- cron: "0 * * * *" # hourly upstream sync
workflow_dispatch:
push:
branches: [main, master, develop]
pull_request_target:
types: [opened, labeled, closed]
jobs:
shepherd:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.FORK_SYNC_PAT }}
- uses: FasterApiWeb/fork-shepherd@v1
with:
upstream_repo: "original-owner/original-repo"
sync_branches: "main,master,develop"
github_token: ${{ secrets.FORK_SYNC_PAT }}
monitor_all_prs: "true"Replace original-owner/original-repo with the upstream you forked from. Adjust sync_branches / push.branches to match your repo.
name: Sync Fork
on:
schedule:
- cron: "0 */6 * * *"
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.FORK_SYNC_PAT }}
- uses: FasterApiWeb/fork-shepherd@v1
with:
upstream_repo: "original-owner/original-repo"
sync_branches: "main"
github_token: ${{ secrets.FORK_SYNC_PAT }}| Goal | What to do |
|---|---|
| Auto-update all PRs | monitor_all_prs: "true" (recommended) |
| Opt-in per PR only | monitor_all_prs: "false" and add label sync-bot |
| Backport a merged PR | Add label backport-to-release-1.0 (branch must exist) |
| Run manually | Actions → Run workflow |
| Preview without writes | dry_run: "true" |
# Recommended — gets non-breaking updates
- uses: FasterApiWeb/fork-shepherd@v1
# Exact — fully reproducible
- uses: FasterApiWeb/fork-shepherd@v1.1.0More examples: examples/full.yml, examples/basic.yml, examples/pr-only.yml.
| Input | Required | Default | Description |
|---|---|---|---|
upstream_repo |
Yes | — | Upstream repo in owner/repo format |
sync_branches |
Yes | main |
Comma-separated branches to sync |
github_token |
Yes | — | PAT with access to your fork (and read on upstream) |
pr_label |
No | sync-bot |
Label for PR monitoring when monitor_all_prs is false |
monitor_all_prs |
No | false |
Monitor every open PR for its own base (main/master/develop/…) |
backport_prefix |
No | backport-to- |
Label prefix for backport targets |
enable_branch_sync |
No | true |
Toggle branch sync |
enable_pr_monitor |
No | true |
Toggle PR monitoring |
enable_backport |
No | true |
Toggle backport |
enable_cleanup |
No | true |
Toggle cleanup |
dry_run |
No | false |
Log without making changes |
conflict_label |
No | sync-conflict |
Label for conflict issues |
bot_name |
No | sync-bot |
Name shown in bot comments |
merge_strategy |
No | merge |
PR update strategy: merge or rebase |
| Output | Description |
|---|---|
synced_branches |
Comma-separated list of successfully synced branches |
conflict_branches |
Comma-separated list of branches with conflicts |
monitored_prs |
Comma-separated list of monitored PR numbers |
backported_prs |
Comma-separated list of backported PR numbers |
closed_issues |
Comma-separated list of auto-closed issue numbers |
name: Sync Fork
on:
schedule:
- cron: "0 */6 * * *"
workflow_dispatch:
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.FORK_SYNC_PAT }}
- uses: FasterApiWeb/fork-shepherd@v1
with:
upstream_repo: "upstream-owner/upstream-repo"
sync_branches: "main"
github_token: ${{ secrets.FORK_SYNC_PAT }}
enable_pr_monitor: "false"
enable_backport: "false"name: PR Monitor
on:
schedule:
- cron: "0 */4 * * *"
pull_request_target:
types: [labeled]
jobs:
monitor:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.FORK_SYNC_PAT }}
- uses: FasterApiWeb/fork-shepherd@v1
with:
upstream_repo: "upstream-owner/upstream-repo"
github_token: ${{ secrets.FORK_SYNC_PAT }}
enable_branch_sync: "false"
enable_backport: "false"
enable_cleanup: "false"Fetches from upstream and updates each configured branch in your fork. Tries fast-forward first, then a regular merge. On conflict, opens an issue with the sync-conflict label and resolution steps. Branches that exist upstream but not in your fork are created automatically.
Finds open PRs with the configured label and merges (or rebases) the latest base into each PR branch. Comments with success or conflict instructions. Also warns if upstream contains reverts of commits present in the PR.
When a PR is merged with a label like backport-to-release-1.0, cherry-picks the merge commit onto release-1.0 and opens a backport PR. On conflict, comments on the original PR with manual steps.
Closes open sync-conflict issues once the named branch is in sync again (upstream is an ancestor of the fork branch).
| Scope / permission | Why |
|---|---|
repo (classic) or Contents/Issues/PRs write (fine-grained) |
Push branches, open PRs, manage issues |
workflow (classic, optional) |
Only if synced branches include workflow files |
Use the same secret for actions/checkout (token:) and this action (github_token:).
GITHUB_TOKEN cannot reliably push the updates this action needs across fork workflows. A PAT (or fine-grained token) on the fork is required.
Yes. The token needs read access to upstream and read/write on your fork.
The action merges; it never force-pushes your fork. Conflicts open a sync-conflict issue for manual resolution.
Remove the sync-bot label (or your custom pr_label).
See CONTRIBUTING.md. CI runs shellcheck, validates action.yml, and scans with leash-secrets.
Maintainers: see release steps in CONTRIBUTING.md. Summary:
- Actions → Release → Run workflow (pick branch + semver bump)
- Edit the draft release → check Publish this Action to the GitHub Marketplace → Publish release
Drafts are not Marketplace-listed until published in the UI.
See SECURITY.md.
