From 81239e61323a1410ae8b9c024c0d6daab45b2029 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 7 Oct 2026 19:52:31 +0200 Subject: [PATCH] ci(release): sync main into every release/v* branch Replace the next-branch sync with per-release merges and conflict pull requests. Document the automatic sync schedule in RELEASE.md. --- .github/workflows/sync-main-to-next.yml | 65 ----------------- .github/workflows/sync-main-to-release.yml | 81 ++++++++++++++++++++++ RELEASE.md | 2 +- 3 files changed, 82 insertions(+), 66 deletions(-) delete mode 100644 .github/workflows/sync-main-to-next.yml create mode 100644 .github/workflows/sync-main-to-release.yml diff --git a/.github/workflows/sync-main-to-next.yml b/.github/workflows/sync-main-to-next.yml deleted file mode 100644 index cc53b0ca88..0000000000 --- a/.github/workflows/sync-main-to-next.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: Sync main to next - -on: - schedule: - - cron: '*/10 * * * *' - workflow_dispatch: - -permissions: - contents: write - pull-requests: write - -concurrency: - group: sync-main-to-next - cancel-in-progress: false - -jobs: - sync: - name: Merge main into next - runs-on: ubuntu-latest - steps: - - name: Checkout next - uses: actions/checkout@v5 - with: - ref: next - fetch-depth: 0 - - - name: Merge main into next - env: - GH_TOKEN: ${{ github.token }} - run: | - git config user.name 'github-actions[bot]' - git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git fetch origin main next - - if git merge --no-edit origin/main; then - git push origin HEAD:next - exit 0 - fi - - conflicts=$(git diff --name-only --diff-filter=U) - git merge --abort - - if [ -z "$conflicts" ]; then - echo 'The merge failed without conflicts, so no pull request was created.' - exit 1 - fi - - sync_branch='automation/sync-main-to-next' - existing_pr=$(gh pr list --base next --head "$sync_branch" --state open --json url --jq '.[0].url') - if [ -n "$existing_pr" ]; then - # The conflict was already reported when this pull request was opened. - # Stay green until it is merged instead of failing every scheduled run. - echo "::notice title=main to next sync blocked::main still conflicts with next; resolve the open pull request: $existing_pr" - exit 0 - fi - - git push --force origin origin/main:"refs/heads/$sync_branch" - gh pr create \ - --base next \ - --head "$sync_branch" \ - --title 'chore: merge main into next' \ - --body 'This pull request was created automatically because main could not be merged into next without conflicts. Resolve conflicts on this temporary branch; never update main with next.' - - echo '::error title=main to next sync blocked::main could not be merged into next without conflicts; a pull request was opened to resolve them.' - exit 1 diff --git a/.github/workflows/sync-main-to-release.yml b/.github/workflows/sync-main-to-release.yml new file mode 100644 index 0000000000..6ea7ea0768 --- /dev/null +++ b/.github/workflows/sync-main-to-release.yml @@ -0,0 +1,81 @@ +name: Sync main to release branches + +on: + schedule: + - cron: '*/10 * * * *' + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +concurrency: + group: sync-main-to-release + cancel-in-progress: false + +jobs: + sync: + name: Merge main into release branches + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Merge main into every release/v* branch + env: + GH_TOKEN: ${{ github.token }} + run: | + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git fetch origin main '+refs/heads/release/v*:refs/remotes/origin/release/v*' + + branches=$(git for-each-ref --format='%(refname:lstrip=3)' 'refs/remotes/origin/release/v*') + if [ -z "$branches" ]; then + echo 'No release/v* branch exists, so there is nothing to sync.' + exit 0 + fi + + failed=0 + for branch in $branches; do + echo "::group::Sync main into $branch" + git checkout --quiet -B "$branch" "origin/$branch" + + if git merge --no-edit origin/main; then + git push origin "HEAD:refs/heads/$branch" + echo '::endgroup::' + continue + fi + + conflicts=$(git diff --name-only --diff-filter=U) + git merge --abort + echo '::endgroup::' + + if [ -z "$conflicts" ]; then + echo "::error title=main to $branch sync failed::The merge failed without conflicts, so no pull request was created." + failed=1 + continue + fi + + sync_branch="automation/sync-main-to-${branch//\//-}" + existing_pr=$(gh pr list --base "$branch" --head "$sync_branch" --state open --json url --jq '.[0].url') + if [ -n "$existing_pr" ]; then + # The conflict was already reported when this pull request was opened. + # Stay green until it is merged instead of failing every scheduled run. + echo "::notice title=main to $branch sync blocked::main still conflicts with $branch; resolve the open pull request: $existing_pr" + continue + fi + + git push --force origin origin/main:"refs/heads/$sync_branch" + gh pr create \ + --base "$branch" \ + --head "$sync_branch" \ + --title "chore: merge main into $branch" \ + --body "This pull request was created automatically because main could not be merged into $branch without conflicts. Resolve conflicts on this temporary branch; never update main with $branch." + + echo "::error title=main to $branch sync blocked::main could not be merged into $branch without conflicts; a pull request was opened to resolve them." + failed=1 + done + + exit "$failed" diff --git a/RELEASE.md b/RELEASE.md index 63cf879cad..15cc64527d 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -17,7 +17,7 @@ Release workflows never edit or commit versions. Stable versions come from `conf - Fixes, security updates, and small improvements target `main`. - New features and larger changes target the current `release/v4.x` branch. -- Merge `main` into `release/v4.x` regularly so every production fix is included in the next release. +- **Sync main to release branches** merges `main` into every `release/v*` branch every 10 minutes, so every production fix is included in the next release. If a merge conflicts, it opens a `chore: merge main into release/vX.Y` pull request to resolve. - Do not merge `release/v4.x` into `main` until an RC is approved for a stable release. ## Feature and RC flow