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