Skip to content

Commit 33e7d61

Browse files
committed
Create sync-branches-from-upstream.yml
1 parent 04b1edb commit 33e7d61

1 file changed

Lines changed: 119 additions & 0 deletions

File tree

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# This workflow syncs every branch that belongs to the current project
2+
# from the upstream repository. It can be triggered manually or runs automatically
3+
# every 5 minutes.
4+
#
5+
# Step 1: The GitHub API is queried to determine whether the current repository
6+
# is a fork. If it is the upstream (not a fork), the job exits immediately.
7+
# Step 2: Each branch present in the fork is synced from the upstream parent.
8+
# Branches that exist only in the fork (not in upstream) are skipped.
9+
# Step 3: The schedule runs every 5 minutes.
10+
11+
name: Sync Branches from Upstream
12+
13+
on:
14+
workflow_dispatch:
15+
schedule:
16+
- cron: '*/5 * * * *'
17+
18+
concurrency:
19+
group: sync-branches-from-upstream
20+
cancel-in-progress: true
21+
22+
jobs:
23+
sync:
24+
name: Sync branches from upstream
25+
runs-on: ubuntu-latest
26+
permissions:
27+
contents: write
28+
steps:
29+
- name: Check if this repository is a fork
30+
id: fork_check
31+
env:
32+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33+
run: |
34+
repo_info=$(gh api "repos/${{ github.repository }}")
35+
is_fork=$(echo "$repo_info" | jq -r '.fork')
36+
if [ "$is_fork" = "true" ]; then
37+
upstream_repo=$(echo "$repo_info" | jq -r '.parent.full_name // empty')
38+
if [ -z "$upstream_repo" ]; then
39+
echo "::error::Repository is marked as a fork but parent information is missing."
40+
exit 1
41+
fi
42+
echo "This repository is a fork of '${upstream_repo}'."
43+
echo "is_fork=true" >> "$GITHUB_OUTPUT"
44+
echo "upstream_repo=${upstream_repo}" >> "$GITHUB_OUTPUT"
45+
else
46+
echo "This repository is not a fork (it is the upstream). Nothing to sync."
47+
echo "is_fork=false" >> "$GITHUB_OUTPUT"
48+
fi
49+
50+
- name: Checkout Repository
51+
if: steps.fork_check.outputs.is_fork == 'true'
52+
uses: actions/checkout@v5
53+
with:
54+
fetch-depth: 0
55+
token: ${{ secrets.GITHUB_TOKEN }}
56+
57+
- name: Sync branches from upstream
58+
if: steps.fork_check.outputs.is_fork == 'true'
59+
env:
60+
GH_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
61+
run: |
62+
git config user.name "github-actions[bot]"
63+
git config user.email "github-actions[bot]@users.noreply.github.com"
64+
65+
upstream="${{ steps.fork_check.outputs.upstream_repo }}"
66+
echo "Upstream repository: $upstream"
67+
git remote add upstream "https://github.com/${upstream}.git"
68+
69+
echo "Fetching all refs from upstream..."
70+
git fetch upstream
71+
72+
echo "Fetching all refs from origin..."
73+
git fetch origin
74+
75+
# Collect all branches that exist in this project (origin), excluding HEAD pointer.
76+
origin_branches=$(git branch -r | grep '^ origin/' | grep -v '^ origin/HEAD' | sed 's|^ origin/||')
77+
78+
if [ -z "$origin_branches" ]; then
79+
echo "No branches found in the repository. Nothing to sync."
80+
exit 0
81+
fi
82+
83+
synced=0
84+
skipped=0
85+
failed=0
86+
87+
for branch in $origin_branches; do
88+
echo "---"
89+
# Only sync branches that also exist in upstream.
90+
if ! git ls-remote --exit-code upstream "refs/heads/${branch}" > /dev/null 2>&1; then
91+
echo "Branch '${branch}' does not exist in upstream – skipping."
92+
skipped=$((skipped + 1))
93+
continue
94+
fi
95+
96+
# If the origin and upstream branches point to the same commit, skip.
97+
origin_sha=$(git rev-parse "origin/${branch}")
98+
upstream_sha=$(git rev-parse "upstream/${branch}")
99+
if [ "$origin_sha" = "$upstream_sha" ]; then
100+
echo "Branch '${branch}' is already up to date."
101+
skipped=$((skipped + 1))
102+
continue
103+
fi
104+
105+
echo "Syncing branch '${branch}' from upstream..."
106+
if git push origin "upstream/${branch}:refs/heads/${branch}" --force-with-lease; then
107+
echo "Branch '${branch}' synced successfully."
108+
synced=$((synced + 1))
109+
else
110+
echo "::warning::Failed to push branch '${branch}'."
111+
failed=$((failed + 1))
112+
fi
113+
done
114+
115+
echo "==="
116+
echo "Sync complete: synced=${synced}, skipped=${skipped}, failed=${failed}"
117+
if [ "$failed" -gt 0 ]; then
118+
exit 1
119+
fi

0 commit comments

Comments
 (0)