Skip to content

Sync with Upstream

Sync with Upstream #96

Workflow file for this run

# =============================================================================
# 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 "=========================================="