Spaces:
Paused
Paused
| # ============================================================================= | |
| # 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 "==========================================" | |