From 8170c42aedcb48896234b0e33d82e962c02f267b Mon Sep 17 00:00:00 2001 From: Ronnie Dutta <61982285+MetRonnie@users.noreply.github.com> Date: Tue, 24 Oct 2023 13:56:51 +0100 Subject: [PATCH] Add branch sync reusable workflow --- .github/workflows/branch-sync.yml | 119 ++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 .github/workflows/branch-sync.yml diff --git a/.github/workflows/branch-sync.yml b/.github/workflows/branch-sync.yml new file mode 100644 index 0000000..2d76bd2 --- /dev/null +++ b/.github/workflows/branch-sync.yml @@ -0,0 +1,119 @@ +# This is a reusable workflow, rather than an action + +name: Sync PR + +on: + workflow_call: + inputs: + head_branch: + type: string + required: false + +jobs: + sync: + runs-on: ubuntu-latest + timeout-minutes: 5 + env: + BASE_BRANCH: master + HEAD_BRANCH: ${{ inputs.head_branch || github.ref_name }} + STATUS_JSON: https://raw.githubusercontent.com/cylc/cylc-admin/master/docs/status/branches.json + FORCE_COLOR: 2 + steps: + - name: Check branch name + shell: python + run: | + import os + import json + import sys + from urllib.request import urlopen + + if os.environ['GITHUB_EVENT_NAME'] == 'schedule': + # Get branch from status page + meta = json.loads( + urlopen(os.environ['STATUS_JSON']).read() + )['meta_releases'] + version = min(meta) + branch = meta[version][os.environ['GITHUB_REPOSITORY']] + else: + branch = os.environ['HEAD_BRANCH'].strip() + + if branch.endswith('-sync'): + sys.exit("::error::Do not run this workflow for already-created sync branches") + + with open(os.environ['GITHUB_ENV'], 'a') as F: + print(f'HEAD_BRANCH={branch}', file=F) + print(f'SYNC_BRANCH={branch}-sync', file=F) + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: master + + - name: Configure git + uses: cylc/release-actions/configure-git@v1 + + - name: Checkout sync branch if it exists + continue-on-error: true + run: | + git switch -c "$SYNC_BRANCH" "origin/${SYNC_BRANCH}" + echo "BASE_BRANCH=$SYNC_BRANCH" >> "$GITHUB_ENV" + + - name: Attempt fast-forward + id: ff + run: | + if git merge "origin/${HEAD_BRANCH}" --ff-only; then + if [[ "$(git rev-parse HEAD)" == "$(git rev-parse "origin/${BASE_BRANCH}")" ]]; then + echo "::notice::$BASE_BRANCH is up to date with $HEAD_BRANCH" + exit 0 + fi + git push origin "$BASE_BRANCH" + elif [[ "$BASE_BRANCH" == "$SYNC_BRANCH" ]]; then + echo "::notice::Cannot fast-forward $BASE_BRANCH to $HEAD_BRANCH; merge existing PR first" + else + echo "continue=true" >> "$GITHUB_OUTPUT" + fi + + - name: Attempt merge into master + id: merge + if: steps.ff.outputs.continue + run: | + git switch master + if git merge "origin/${HEAD_BRANCH}"; then + if git diff HEAD^ --exit-code --stat; then + echo "::notice::No diff between master and $HEAD_BRANCH" + exit 0 + fi + else + git merge --abort + fi + echo "continue=true" >> $GITHUB_OUTPUT + + - name: Push sync branch + id: push + if: steps.merge.outputs.continue + run: | + git switch -c "$SYNC_BRANCH" "origin/${HEAD_BRANCH}" + git push origin "$SYNC_BRANCH" + echo "continue=true" >> $GITHUB_OUTPUT + + - name: Open PR + if: steps.push.outputs.continue + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BODY: | + Please do a **normal merge**, not squash merge. + Please fix conflicts if necessary. + + --- + + Triggered by `${{ github.event_name }}` + run: | + url="$( + gh pr create --head "$SYNC_BRANCH" \ + --title "🤖 Merge ${SYNC_BRANCH} into master" \ + --body "$BODY" + )" + echo "::notice::PR created at ${url}" + + gh pr edit "$SYNC_BRANCH" --add-label "sync" || true