Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 0 additions & 73 deletions .github/workflows/auto-retarget-main-pr-to-dev.yml

This file was deleted.

171 changes: 171 additions & 0 deletions .github/workflows/auto-sync-upstream-copilot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
name: auto-sync-upstream-copilot

on:
schedule:
# daily at 02:30 UTC (10:30 Asia/Singapore) — offset 30m from auto-sync-upstream
- cron: '30 2 * * *'
workflow_dispatch:

permissions:
contents: write
pull-requests: write
issues: write
actions: write

concurrency:
group: auto-sync-upstream-copilot
cancel-in-progress: false

env:
UPSTREAM_URL: https://github.com/router-for-me/CLIProxyAPI.git
UPSTREAM_BRANCH: main
TARGET_BRANCH: feat/copilot
PR_BRANCH: auto-sync/upstream-main-copilot

jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout target branch
uses: actions/checkout@v4
with:
ref: ${{ env.TARGET_BRANCH }}
fetch-depth: 0

- name: Configure git identity
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

- name: Add upstream remote and fetch
run: |
git remote add upstream "$UPSTREAM_URL"
git fetch upstream "$UPSTREAM_BRANCH"

- name: Check if behind upstream
id: check
run: |
BEHIND=$(git rev-list --count "HEAD..upstream/${UPSTREAM_BRANCH}")
echo "behind=$BEHIND" >> "$GITHUB_OUTPUT"
if [ "$BEHIND" = "0" ]; then
echo "Already up to date with upstream/${UPSTREAM_BRANCH}."
else
echo "$BEHIND new commits from upstream:"
git log --oneline "HEAD..upstream/${UPSTREAM_BRANCH}"
fi

- name: Build merge commit message
if: steps.check.outputs.behind != '0'
run: |
{
echo "Merge upstream/${UPSTREAM_BRANCH} (auto-sync feat/copilot)"
echo
git log --reverse --pretty=format:'- %h %s' "HEAD..upstream/${UPSTREAM_BRANCH}"
echo
} > /tmp/merge-msg.txt
cat /tmp/merge-msg.txt

- name: Try clean merge
if: steps.check.outputs.behind != '0'
id: merge
run: |
set +e
git merge --no-ff -F /tmp/merge-msg.txt "upstream/${UPSTREAM_BRANCH}"
rc=$?
if [ "$rc" = "0" ]; then
echo "clean=true" >> "$GITHUB_OUTPUT"
else
echo "clean=false" >> "$GITHUB_OUTPUT"
git merge --abort || true
fi
exit 0

- name: Set up Go
if: steps.merge.outputs.clean == 'true'
uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true

- name: Verify build
if: steps.merge.outputs.clean == 'true'
id: build
run: |
set +e
go build -o /tmp/cli-proxy-api ./cmd/server
rc=$?
rm -f /tmp/cli-proxy-api
if [ "$rc" = "0" ]; then
echo "ok=true" >> "$GITHUB_OUTPUT"
else
echo "ok=false" >> "$GITHUB_OUTPUT"
fi
exit 0

- name: Push merged changes to target branch
if: steps.merge.outputs.clean == 'true' && steps.build.outputs.ok == 'true'
run: git push origin "${TARGET_BRANCH}"

- name: Trigger downstream image build
if: steps.merge.outputs.clean == 'true' && steps.build.outputs.ok == 'true'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run ghcr-feat-copilot.yml --repo "${{ github.repository }}" --ref "${TARGET_BRANCH}"

- name: Prepare PR branch (build failure case)
if: steps.merge.outputs.clean == 'true' && steps.build.outputs.ok != 'true'
run: |
# HEAD is the (clean) merge commit — push it as the PR branch so reviewers
# see exactly what would land, and pr-test-build.yml runs against it.
git push --force origin "HEAD:refs/heads/${PR_BRANCH}"

- name: Prepare PR branch (conflict case)
if: steps.check.outputs.behind != '0' && steps.merge.outputs.clean != 'true'
run: |
# Reset working tree, then push raw upstream tip as the PR branch so GitHub
# surfaces the conflict against feat/copilot and offers web-based resolution.
git reset --hard "origin/${TARGET_BRANCH}"
git checkout -B "${PR_BRANCH}" "upstream/${UPSTREAM_BRANCH}"
git push --force origin "${PR_BRANCH}"

- name: Open or update sync PR
if: steps.check.outputs.behind != '0' && (steps.merge.outputs.clean != 'true' || steps.build.outputs.ok != 'true')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
run: |
set -e

if [ "${{ steps.merge.outputs.clean }}" != "true" ]; then
REASON='conflicts with `'"${TARGET_BRANCH}"'`'
else
REASON='merge clean but `go build` failed'
fi

{
echo "Auto-sync from \`upstream/${UPSTREAM_BRANCH}\` (router-for-me/CLIProxyAPI) into \`${TARGET_BRANCH}\`."
echo
echo "**Reason for manual review:** ${REASON}"
echo
echo "### New commits"
git log --reverse --pretty=format:'- %h %s' "origin/${TARGET_BRANCH}..upstream/${UPSTREAM_BRANCH}"
} > /tmp/pr-body.md

# Idempotently ensure the label exists.
gh label create auto-sync-copilot --color FBCA04 --description "Automated upstream sync (feat/copilot)" || true

EXISTING=$(gh pr list --head "${PR_BRANCH}" --base "${TARGET_BRANCH}" --state open --json number --jq '.[0].number' || true)

TITLE="auto-sync(copilot): merge upstream/${UPSTREAM_BRANCH} (${REASON})"

if [ -n "$EXISTING" ]; then
gh pr edit "$EXISTING" --title "$TITLE" --body-file /tmp/pr-body.md
echo "Updated existing PR #$EXISTING"
else
gh pr create \
--base "${TARGET_BRANCH}" \
--head "${PR_BRANCH}" \
--title "$TITLE" \
--body-file /tmp/pr-body.md \
--label auto-sync-copilot
fi
Loading