Skip to content

Sync Upstream

Sync Upstream #859

Workflow file for this run

name: Sync Upstream
on:
schedule:
- cron: '0 * * * *' # every hour at :00 (UTC)
workflow_dispatch: {}
permissions:
actions: write
contents: write
concurrency:
group: sync-upstream
cancel-in-progress: true
jobs:
sync:
if: ${{ github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/overlay' }}
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.MIRROR_REPOS_WRITE_PAT }} # use PAT for gh API calls against the mirror (will trigger workflows)
UPSTREAM_REPO: '${{ vars.UPSTREAM_REPO }}'
UPSTREAM_REMOTE: "upstream"
UPSTREAM_BASELINE_TAG: "upstream-baseline"
MAIN_BRANCH: "main"
OVERLAY_BRANCH: "overlay"
CANCEL_UPSTREAM_WORKFLOWS: 'false'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ env.GH_TOKEN }}
- name: Identify the default branch and its latest commit from the upstream repository
id: up
shell: bash -euo pipefail {0}
run: |
echo "::group::Identify default branch and write to output"
def=$(gh api "repos/${UPSTREAM_REPO}" --jq .default_branch)
echo "def=${def}" >> "$GITHUB_OUTPUT"
echo "> Default branch is ${def}"
echo "::endgroup::"
echo "::group::Identify latest commit and write to output"
sha=$(gh api "repos/${UPSTREAM_REPO}/commits/${def}" --jq .sha)
echo "sha=${sha}" >> "$GITHUB_OUTPUT"
echo "> Latest commit on ${def} is ${sha}"
echo "::endgroup::"
- name: Refresh upstream and fetch latest changes
shell: bash -euo pipefail {0}
run: |
echo "> Remove current upstream remote"
git remote remove ${UPSTREAM_REMOTE} 2>/dev/null || true
echo "> Add upstream remote"
# upstream is assumed public — add unauthenticated upstream remote
git remote add ${UPSTREAM_REMOTE} "https://github.com/${UPSTREAM_REPO}.git"
echo "::group::Fetch upstream changes"
git remote get-url ${UPSTREAM_REMOTE}
git remote -v
git ls-remote ${UPSTREAM_REMOTE}
git fetch ${UPSTREAM_REMOTE} --prune --tags
echo "::endgroup::"
- name: Update main only when upstream or overlay changed
shell: bash -euo pipefail {0}
run: |
def="${{ steps.up.outputs.def }}"
upstream_sha="${{ steps.up.outputs.sha }}"
echo "> Upstream default branch (${def}) latest commit: ${upstream_sha}"
# Fetch refs we will compare (origin/main and overlay) into refs/remotes/origin/*
git fetch origin ${MAIN_BRANCH}:refs/remotes/origin/${MAIN_BRANCH} || true
git fetch origin ${OVERLAY_BRANCH}:refs/remotes/origin/${OVERLAY_BRANCH} || true
# compute .github tree object for origin/main and overlay branch
origin_main_commit=$(git rev-parse --verify refs/remotes/origin/${MAIN_BRANCH} 2>/dev/null || true)
origin_main_tree=""
if [ -n "$origin_main_commit" ]; then
origin_main_tree=$(git rev-parse "${origin_main_commit}:.github" 2>/dev/null || true)
fi
overlay_commit=$(git rev-parse --verify refs/remotes/origin/${OVERLAY_BRANCH} 2>/dev/null || true)
overlay_tree=""
if [ -n "$overlay_commit" ]; then
overlay_tree=$(git rev-parse "${overlay_commit}:.github" 2>/dev/null || true) # resolve tree object ID for .github
fi
# read current baseline tag (may not exist on first run)
baseline_sha=$(git rev-parse --verify ${UPSTREAM_BASELINE_TAG} 2>/dev/null || true)
# SKIP when:
# - baseline tag already equals upstream tip (upstream hasn't moved), AND
# - origin/main's .github equals overlay's .github (overlay hasn't changed), AND
# - origin/main already contains that baseline commit (regardless of extra overlay commit)
if [ -n "$baseline_sha" ] && \
[ "$baseline_sha" = "$upstream_sha" ] && \
[ -n "$origin_main_tree" ] && [ "$origin_main_tree" = "$overlay_tree" ] && \
git merge-base --is-ancestor "$baseline_sha" "refs/remotes/origin/${MAIN_BRANCH}"; then
echo "> Up-to-date: upstream baseline unchanged and .github matches overlay; main already contains baseline. Skipping."
exit 0
fi
echo "> Proceeding to update main (upstream and/or overlay changed)"
# update local main to upstream and apply overlay .github
git update-ref refs/heads/${MAIN_BRANCH} "${upstream_sha}"
git checkout -f ${MAIN_BRANCH}
git config user.name "${GITHUB_ACTOR}"
git config user.email "${GITHUB_ACTOR}@users.noreply.github.com"
# apply overlay .github (creates a small commit only if changed)
git fetch origin ${OVERLAY_BRANCH}:refs/remotes/origin/${OVERLAY_BRANCH} || true
git restore --source refs/remotes/origin/${OVERLAY_BRANCH} -- .github || true
if [ -n "$(git status --porcelain)" ]; then
git add -A
git commit -m "Apply overlay (.github from ${OVERLAY_BRANCH})"
fi
echo "> Push updated main branch"
git push origin ${MAIN_BRANCH} --force-with-lease
- name: Update baseline tag to the latest upstream commit
shell: bash -euo pipefail {0}
run: |
echo "> Move/update lightweight tag to the upstream SHA that main represents"
git tag -f ${UPSTREAM_BASELINE_TAG} "${{ steps.up.outputs.sha }}"
git push -f origin refs/tags/${UPSTREAM_BASELINE_TAG}
- name: Wait for main workflows to start
run: sleep 15
- name: Cancel unexpected workflows on main
if: env.CANCEL_UPSTREAM_WORKFLOWS == 'true'
env:
GH_TOKEN: ${{ github.token }}
run: |
branch="${{ env.MAIN_BRANCH }}"
echo "Checking branch: $branch"
gh run list --repo "$GITHUB_REPOSITORY" --branch "$branch" --json databaseId,status,workflowDatabaseId | \
jq -r '.[] | select(.status == "in_progress" or .status == "queued") | "\(.databaseId) \(.workflowDatabaseId)"' | \
while read -r run_id workflow_id; do
path=$(gh api "/repos/$GITHUB_REPOSITORY/actions/workflows/$workflow_id" --jq '.path')
case "$path" in
".github/workflows/loci-analysis.yml" | \
".github/workflows/sync-upstream.yml" | \
".github/workflows/sync-upstream-prs.yml")
# Allowed workflows - do nothing
;;
*)
echo "> Canceling run $run_id (file: $path) on branch $branch"
gh run cancel "$run_id" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 || true
;;
esac
done