-
Notifications
You must be signed in to change notification settings - Fork 408
324 lines (274 loc) · 13.7 KB
/
Copy pathupstream-sync.yml
File metadata and controls
324 lines (274 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
# =============================================================================
# Upstream Sync Workflow
# =============================================================================
# Automatically syncs this repository with the upstream repository.
# Creates a PR when new changes are detected from upstream.
#
# Local repository: current default branch (main)
# Upstream source: CJackHwang/AIstudioProxyAPI (main)
#
# CONFLICT HANDLING:
# README.md can conflict when this repository keeps custom docs/content.
# The workflow auto-resolves README.md conflicts by keeping local version.
# Other conflicts will fail the workflow and require manual intervention.
# =============================================================================
name: Sync with Upstream
on:
# Run every 6 hours (at 00:00, 06:00, 12:00, 18:00 UTC)
# This ensures timely detection of upstream changes while avoiding excessive API calls
schedule:
- cron: '0 */6 * * *'
# Allow manual trigger from GitHub Actions UI
workflow_dispatch:
inputs:
force_sync:
description: 'Force sync even if no new commits detected'
required: false
default: false
type: boolean
# Ensure only one sync workflow runs at a time
concurrency:
group: upstream-sync
cancel-in-progress: true
jobs:
sync:
name: Sync Repository with Upstream
runs-on: ubuntu-latest
# Required permissions for creating PRs and pushing branches
permissions:
contents: write
pull-requests: write
steps:
# -----------------------------------------------------------------------
# Step 1: Checkout current repository
# -----------------------------------------------------------------------
- name: Checkout Repository
uses: actions/checkout@v4
with:
# Fetch all history to properly compare with upstream
fetch-depth: 0
# Use the default GITHUB_TOKEN
token: ${{ secrets.GITHUB_TOKEN }}
# -----------------------------------------------------------------------
# Step 2: Configure Git identity for commits
# -----------------------------------------------------------------------
- name: Configure Git Identity
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
# -----------------------------------------------------------------------
# Step 3: Add upstream remote and fetch latest changes
# -----------------------------------------------------------------------
- name: Add Upstream Remote
run: |
echo "Adding upstream remote..."
git remote add upstream https://github.com/CJackHwang/AIstudioProxyAPI.git
echo "Fetching upstream changes..."
git fetch upstream main --tags
echo "Upstream remote added and fetched successfully."
# -----------------------------------------------------------------------
# Step 4: Check for new commits from upstream
# -----------------------------------------------------------------------
- name: Check for Upstream Changes
id: check_changes
run: |
echo "Comparing local main with upstream/main..."
# Get commit counts
LOCAL_COMMIT=$(git rev-parse HEAD)
UPSTREAM_COMMIT=$(git rev-parse upstream/main)
echo "Local HEAD: $LOCAL_COMMIT"
echo "Upstream HEAD: $UPSTREAM_COMMIT"
# Count commits ahead and behind
COMMITS_BEHIND=$(git rev-list --count HEAD..upstream/main)
COMMITS_AHEAD=$(git rev-list --count upstream/main..HEAD)
echo "Commits behind upstream: $COMMITS_BEHIND"
echo "Commits ahead of upstream: $COMMITS_AHEAD"
# Determine if sync is needed
if [ "$COMMITS_BEHIND" -gt 0 ]; then
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "commits_behind=$COMMITS_BEHIND" >> $GITHUB_OUTPUT
echo "::notice::Found $COMMITS_BEHIND new commit(s) from upstream"
else
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "commits_behind=0" >> $GITHUB_OUTPUT
echo "::notice::Fork is up to date with upstream"
fi
# Get the date for PR title
echo "sync_date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
# Get recent upstream commit messages for PR body
if [ "$COMMITS_BEHIND" -gt 0 ]; then
echo "Getting recent upstream commits..."
COMMIT_LOG=$(git log --oneline HEAD..upstream/main | head -20)
# Write commit log to file for multi-line output
echo "$COMMIT_LOG" > /tmp/commit_log.txt
# Use delimiter for multi-line output
{
echo 'commit_log<<EOF'
cat /tmp/commit_log.txt
echo 'EOF'
} >> $GITHUB_OUTPUT
fi
# -----------------------------------------------------------------------
# Step 5: Create sync branch with upstream changes
# -----------------------------------------------------------------------
# CONFLICT RESOLUTION STRATEGY:
# 1. Attempt merge with upstream
# 2. If conflicts occur, check if ONLY expected files conflict (README.md)
# 3. Auto-resolve expected conflicts by keeping fork's version (--ours)
# 4. Fail only if unexpected files have conflicts
# -----------------------------------------------------------------------
- name: Create Sync Branch
id: create_branch
if: steps.check_changes.outputs.has_changes == 'true' || github.event.inputs.force_sync == 'true'
run: |
BRANCH_NAME="sync/upstream-${{ steps.check_changes.outputs.sync_date }}"
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
echo "Creating sync branch: $BRANCH_NAME"
# Check if branch already exists remotely
if git ls-remote --heads origin "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
echo "::warning::Sync branch already exists. Deleting and recreating..."
git push origin --delete "$BRANCH_NAME" || true
fi
# Create new branch from current main
git checkout -b "$BRANCH_NAME"
echo "Merging upstream/main into sync branch..."
# =====================================================================
# EXPECTED CONFLICT FILES (Translation Fork)
# These files are expected to conflict because they are intentionally
# different in this English fork vs the Chinese upstream.
# =====================================================================
EXPECTED_CONFLICT_FILES="README.md"
# Attempt merge with upstream
# Using --no-edit to auto-generate merge commit message
if git merge upstream/main --no-edit --allow-unrelated-histories; then
echo "merge_success=true" >> $GITHUB_OUTPUT
echo "auto_resolved=false" >> $GITHUB_OUTPUT
echo "::notice::Merge successful (no conflicts)"
else
echo "::warning::Merge conflicts detected. Checking if auto-resolvable..."
# Get list of conflicting files
CONFLICTING_FILES=$(git diff --name-only --diff-filter=U)
echo "Conflicting files:"
echo "$CONFLICTING_FILES"
# Check if all conflicts are in expected files
UNEXPECTED_CONFLICTS=""
for file in $CONFLICTING_FILES; do
IS_EXPECTED=false
for expected in $EXPECTED_CONFLICT_FILES; do
if [ "$file" = "$expected" ]; then
IS_EXPECTED=true
break
fi
done
if [ "$IS_EXPECTED" = false ]; then
UNEXPECTED_CONFLICTS="$UNEXPECTED_CONFLICTS $file"
fi
done
# Trim whitespace
UNEXPECTED_CONFLICTS=$(echo "$UNEXPECTED_CONFLICTS" | xargs)
if [ -n "$UNEXPECTED_CONFLICTS" ]; then
# Unexpected conflicts found - fail the workflow
echo "merge_success=false" >> $GITHUB_OUTPUT
echo "auto_resolved=false" >> $GITHUB_OUTPUT
echo "::error::Unexpected conflicts in: $UNEXPECTED_CONFLICTS"
echo "::error::Manual intervention required for these files."
# Abort the merge to leave clean state
git merge --abort
exit 1
else
# Only expected conflicts - auto-resolve by keeping fork's version
echo "::notice::All conflicts are in expected files. Auto-resolving..."
for file in $CONFLICTING_FILES; do
echo " Resolving $file by keeping fork's version (--ours)..."
git checkout --ours "$file"
git add "$file"
done
# Complete the merge with resolved conflicts
git commit --no-edit
echo "merge_success=true" >> $GITHUB_OUTPUT
echo "auto_resolved=true" >> $GITHUB_OUTPUT
echo "::notice::Merge successful (auto-resolved expected conflicts)"
fi
fi
# -----------------------------------------------------------------------
# Step 6: Push sync branch to origin
# -----------------------------------------------------------------------
- name: Push Sync Branch
if: steps.create_branch.outputs.merge_success == 'true'
run: |
echo "Pushing sync branch to origin..."
git push origin "${{ steps.create_branch.outputs.branch_name }}"
echo "::notice::Sync branch pushed successfully"
# -----------------------------------------------------------------------
# Step 7: Create Pull Request
# -----------------------------------------------------------------------
- name: Create Pull Request
if: steps.create_branch.outputs.merge_success == 'true'
uses: peter-evans/create-pull-request@v7
id: create_pr
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ steps.create_branch.outputs.branch_name }}
base: main
title: "chore: Sync with upstream [${{ steps.check_changes.outputs.sync_date }}]"
body: |
## Upstream Sync
This PR syncs the fork with the upstream repository.
### Summary
- **Upstream Repository**: [CJackHwang/AIstudioProxyAPI](https://github.com/CJackHwang/AIstudioProxyAPI)
- **Commits Behind**: ${{ steps.check_changes.outputs.commits_behind }}
- **Sync Date**: ${{ steps.check_changes.outputs.sync_date }}
- **Auto-resolved Conflicts**: ${{ steps.create_branch.outputs.auto_resolved == 'true' && '✅ Yes (README.md kept from fork)' || '❌ None' }}
### Recent Upstream Commits
```
${{ steps.check_changes.outputs.commit_log }}
```
### Review Notes
- Please review changes carefully before merging
- Check for any conflicts with fork-specific modifications
- Ensure translation/localization files are preserved
${{ steps.create_branch.outputs.auto_resolved == 'true' && '- ⚠️ **README.md conflict was auto-resolved** - The English README from this fork was preserved' || '' }}
---
*This PR was automatically generated by the [Upstream Sync Workflow](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})*
labels: |
upstream-sync
automated
draft: false
delete-branch: false
# -----------------------------------------------------------------------
# Step 8: Output Results
# -----------------------------------------------------------------------
- name: Output Results
if: always()
run: |
echo "=========================================="
echo "Upstream Sync Workflow Complete"
echo "=========================================="
echo ""
if [ "${{ steps.check_changes.outputs.has_changes }}" == "true" ]; then
echo "Status: Changes detected from upstream"
echo "Commits behind: ${{ steps.check_changes.outputs.commits_behind }}"
if [ "${{ steps.create_branch.outputs.merge_success }}" == "true" ]; then
echo "Merge: Successful"
if [ "${{ steps.create_branch.outputs.auto_resolved }}" == "true" ]; then
echo "Conflicts: Auto-resolved (expected files only)"
echo " - README.md: Kept fork's English version"
else
echo "Conflicts: None"
fi
echo "PR Created: Yes"
echo "Branch: ${{ steps.create_branch.outputs.branch_name }}"
else
echo "Merge: Failed (unexpected conflicts detected)"
echo "PR Created: No"
echo ""
echo "Manual intervention required to resolve conflicts."
echo "Expected conflict files (auto-resolved): README.md"
echo "Unexpected conflicts require manual resolution."
fi
else
echo "Status: Fork is up to date with upstream"
echo "No action required."
fi
echo ""
echo "=========================================="