Skip to content

auto-sync-upstream-copilot #24

auto-sync-upstream-copilot

auto-sync-upstream-copilot #24

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