From 730334e94eb0aef520c91be58a58985576b12230 Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Wed, 11 Feb 2026 14:05:05 -0800 Subject: [PATCH 1/3] Add backmerge release workflow to automate merging changes from release/13.2 to main --- .github/workflows/README.md | 23 ++++ .github/workflows/backmerge-release.yml | 147 ++++++++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 .github/workflows/backmerge-release.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 06975dcd71c..a1f98d7d33c 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -99,3 +99,26 @@ When you comment on a PR (not an issue), the workflow will automatically push ch ### Concurrency The workflow uses concurrency groups based on the issue/PR number to prevent race conditions when multiple commands are issued on the same issue. + +## Backmerge Release Workflow + +The `backmerge-release.yml` workflow automatically creates PRs to merge changes from `release/13.2` back into `main`. + +### Schedule + +Runs daily at 4pm PST (midnight UTC). Can also be triggered manually via `workflow_dispatch`. + +### Behavior + +1. **Change Detection**: Checks if `release/13.2` has commits not in `main` +2. **PR Creation**: If changes exist, creates a PR to merge `release/13.2` → `main` +3. **Auto-merge**: Enables GitHub's auto-merge feature, so the PR merges automatically once approved +4. **Conflict Handling**: If merge conflicts occur, creates an issue instead of a PR + +### Assignees + +PRs and conflict issues are automatically assigned to @joperezr and @radical. + +### Manual Trigger + +To trigger manually, go to Actions → "Backmerge Release to Main" → "Run workflow". diff --git a/.github/workflows/backmerge-release.yml b/.github/workflows/backmerge-release.yml new file mode 100644 index 00000000000..8e74ed1a9f1 --- /dev/null +++ b/.github/workflows/backmerge-release.yml @@ -0,0 +1,147 @@ +name: Backmerge Release to Main + +on: + schedule: + - cron: '0 0 * * *' # 4pm PST = midnight UTC + workflow_dispatch: # Allow manual trigger + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + backmerge: + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'dotnet' }} + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 # Full history needed for merge + + - name: Check for changes to backmerge + id: check + run: | + git fetch origin main release/13.2 + BEHIND_COUNT=$(git rev-list --count origin/main..origin/release/13.2) + echo "behind_count=$BEHIND_COUNT" >> $GITHUB_OUTPUT + if [ "$BEHIND_COUNT" -gt 0 ]; then + echo "changes=true" >> $GITHUB_OUTPUT + echo "Found $BEHIND_COUNT commits in release/13.2 not in main" + else + echo "changes=false" >> $GITHUB_OUTPUT + echo "No changes to backmerge - release/13.2 is up-to-date with main" + fi + + - name: Attempt merge and create branch + if: steps.check.outputs.changes == 'true' + id: merge + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + git checkout origin/main + git checkout -b backmerge/release-13.2-to-main + + # Attempt the merge + if git merge origin/release/13.2 --no-edit; then + echo "merge_success=true" >> $GITHUB_OUTPUT + git push origin backmerge/release-13.2-to-main --force + echo "Merge successful, branch pushed" + else + echo "merge_success=false" >> $GITHUB_OUTPUT + git merge --abort + echo "Merge conflicts detected" + fi + + - name: Create Pull Request + if: steps.check.outputs.changes == 'true' && steps.merge.outputs.merge_success == 'true' + id: create-pr + uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: backmerge/release-13.2-to-main + base: main + title: "[Automated] Backmerge release/13.2 to main" + body: | + ## Automated Backmerge + + This PR merges changes from `release/13.2` back into `main`. + + **Commits to merge:** ${{ steps.check.outputs.behind_count }} + + This PR was created automatically to keep `main` up-to-date with release branch changes. + Once approved, it will auto-merge. + + --- + *This PR was generated by the [backmerge-release](${{ github.server_url }}/${{ github.repository }}/actions/workflows/backmerge-release.yml) workflow.* + assignees: joperezr, radical + labels: area-engineering-systems + + - name: Enable auto-merge + if: steps.create-pr.outputs.pull-request-number + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr merge ${{ steps.create-pr.outputs.pull-request-number }} --auto --merge + + - name: Create issue for merge conflicts + if: steps.check.outputs.changes == 'true' && steps.merge.outputs.merge_success == 'false' + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const workflowRunUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + // Check if there's already an open issue for this + const existingIssues = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + labels: 'backmerge-conflict', + creator: 'github-actions[bot]' + }); + + if (existingIssues.data.length > 0) { + console.log(`Existing backmerge conflict issue found: #${existingIssues.data[0].number}`); + // Add a comment to the existing issue + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: existingIssues.data[0].number, + body: `⚠️ Merge conflicts still exist.\n\n**Workflow run:** ${workflowRunUrl}\n\nPlease resolve the conflicts manually.` + }); + return; + } + + // Create a new issue + await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: '[Backmerge] Merge conflicts between release/13.2 and main', + body: `## Backmerge Conflict + + The automated backmerge from \`release/13.2\` to \`main\` failed due to merge conflicts. + + ### What to do + + 1. Checkout main and attempt the merge locally: + \`\`\`bash + git checkout main + git pull origin main + git merge origin/release/13.2 + \`\`\` + 2. Resolve the conflicts + 3. Push the merge commit or create a PR manually + + ### Details + + **Workflow run:** ${workflowRunUrl} + **Commits to merge:** ${{ steps.check.outputs.behind_count }} + + --- + *This issue was created automatically by the [backmerge-release](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/workflows/backmerge-release.yml) workflow.*`, + assignees: ['joperezr', 'radical'], + labels: ['area-engineering-systems', 'backmerge-conflict'] + }); From bd98f5a27f1301bdc252b422fb708601cc59ff22 Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Wed, 11 Feb 2026 15:05:14 -0800 Subject: [PATCH 2/3] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/README.md | 2 +- .github/workflows/backmerge-release.yml | 101 +++++++++++++++--------- 2 files changed, 63 insertions(+), 40 deletions(-) diff --git a/.github/workflows/README.md b/.github/workflows/README.md index a1f98d7d33c..e802e904706 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -106,7 +106,7 @@ The `backmerge-release.yml` workflow automatically creates PRs to merge changes ### Schedule -Runs daily at 4pm PST (midnight UTC). Can also be triggered manually via `workflow_dispatch`. +Runs daily at 00:00 UTC (4pm PT during standard time, 5pm PT during daylight saving time). Can also be triggered manually via `workflow_dispatch`. ### Behavior diff --git a/.github/workflows/backmerge-release.yml b/.github/workflows/backmerge-release.yml index 8e74ed1a9f1..72465472ba1 100644 --- a/.github/workflows/backmerge-release.yml +++ b/.github/workflows/backmerge-release.yml @@ -2,7 +2,7 @@ name: Backmerge Release to Main on: schedule: - - cron: '0 0 * * *' # 4pm PST = midnight UTC + - cron: '0 0 * * *' # Runs daily at 00:00 UTC (16:00 PST / 17:00 PDT) workflow_dispatch: # Allow manual trigger permissions: @@ -59,27 +59,46 @@ jobs: - name: Create Pull Request if: steps.check.outputs.changes == 'true' && steps.merge.outputs.merge_success == 'true' id: create-pr - uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 - with: - token: ${{ secrets.GITHUB_TOKEN }} - branch: backmerge/release-13.2-to-main - base: main - title: "[Automated] Backmerge release/13.2 to main" - body: | - ## Automated Backmerge + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -e - This PR merges changes from `release/13.2` back into `main`. + PR_BRANCH="backmerge/release-13.2-to-main" + PR_BASE="main" + PR_TITLE="[Automated] Backmerge release/13.2 to main" - **Commits to merge:** ${{ steps.check.outputs.behind_count }} + PR_BODY=$(cat << 'EOF' + ## Automated Backmerge - This PR was created automatically to keep `main` up-to-date with release branch changes. - Once approved, it will auto-merge. + This PR merges changes from `release/13.2` back into `main`. - --- - *This PR was generated by the [backmerge-release](${{ github.server_url }}/${{ github.repository }}/actions/workflows/backmerge-release.yml) workflow.* - assignees: joperezr, radical - labels: area-engineering-systems + **Commits to merge:** ${{ steps.check.outputs.behind_count }} + + This PR was created automatically to keep `main` up-to-date with release branch changes. + Once approved, it will auto-merge. + + --- + *This PR was generated by the [backmerge-release](${{ github.server_url }}/${{ github.repository }}/actions/workflows/backmerge-release.yml) workflow.* + EOF + ) + + # If a PR already exists for this branch/base pair, reuse it; otherwise create a new one. + if gh pr view "$PR_BRANCH" --base "$PR_BASE" > /dev/null 2>&1; then + PR_NUMBER=$(gh pr view "$PR_BRANCH" --base "$PR_BASE" --json number --jq .number) + else + PR_NUMBER=$(gh pr create \ + --base "$PR_BASE" \ + --head "$PR_BRANCH" \ + --title "$PR_TITLE" \ + --body "$PR_BODY" \ + --assignee "joperezr,radical" \ + --label "area-engineering-systems" \ + --json number \ + --jq .number) + fi + echo "pull-request-number=$PR_NUMBER" >> "$GITHUB_OUTPUT" - name: Enable auto-merge if: steps.create-pr.outputs.pull-request-number env: @@ -116,32 +135,36 @@ jobs: } // Create a new issue + const issueBody = [ + '## Backmerge Conflict', + '', + 'The automated backmerge from `release/13.2` to `main` failed due to merge conflicts.', + '', + '### What to do', + '', + '1. Checkout main and attempt the merge locally:', + ' ```bash', + ' git checkout main', + ' git pull origin main', + ' git merge origin/release/13.2', + ' ```', + '2. Resolve the conflicts', + '3. Push the merge commit or create a PR manually', + '', + '### Details', + '', + `**Workflow run:** ${workflowRunUrl}`, + '**Commits to merge:** ${{ steps.check.outputs.behind_count }}', + '', + '---', + `*This issue was created automatically by the [backmerge-release](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/workflows/backmerge-release.yml) workflow.*` + ].join('\n'); + await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: '[Backmerge] Merge conflicts between release/13.2 and main', - body: `## Backmerge Conflict - - The automated backmerge from \`release/13.2\` to \`main\` failed due to merge conflicts. - - ### What to do - - 1. Checkout main and attempt the merge locally: - \`\`\`bash - git checkout main - git pull origin main - git merge origin/release/13.2 - \`\`\` - 2. Resolve the conflicts - 3. Push the merge commit or create a PR manually - - ### Details - - **Workflow run:** ${workflowRunUrl} - **Commits to merge:** ${{ steps.check.outputs.behind_count }} - - --- - *This issue was created automatically by the [backmerge-release](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/workflows/backmerge-release.yml) workflow.*`, + body: issueBody, assignees: ['joperezr', 'radical'], labels: ['area-engineering-systems', 'backmerge-conflict'] }); From 053484aa0f22e1b3d443b2ada12f79ad44eed50d Mon Sep 17 00:00:00 2001 From: Jose Perez Rodriguez Date: Wed, 11 Feb 2026 17:19:59 -0800 Subject: [PATCH 3/3] Apply more fixes and use dotnet's action --- .github/workflows/backmerge-release.yml | 54 +++++++++---------------- .github/workflows/ci.yml | 1 + 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/.github/workflows/backmerge-release.yml b/.github/workflows/backmerge-release.yml index 72465472ba1..05cd2c507d1 100644 --- a/.github/workflows/backmerge-release.yml +++ b/.github/workflows/backmerge-release.yml @@ -13,6 +13,7 @@ permissions: jobs: backmerge: runs-on: ubuntu-latest + timeout-minutes: 15 if: ${{ github.repository_owner == 'dotnet' }} steps: @@ -59,51 +60,32 @@ jobs: - name: Create Pull Request if: steps.check.outputs.changes == 'true' && steps.merge.outputs.merge_success == 'true' id: create-pr - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -e - - PR_BRANCH="backmerge/release-13.2-to-main" - PR_BASE="main" - PR_TITLE="[Automated] Backmerge release/13.2 to main" - - PR_BODY=$(cat << 'EOF' - ## Automated Backmerge - - This PR merges changes from `release/13.2` back into `main`. + uses: dotnet/actions-create-pull-request@e8d799aa1f8b17f324f9513832811b0a62f1e0b1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + head: backmerge/release-13.2-to-main + base: main + title: "[Automated] Backmerge release/13.2 to main" + labels: area-engineering-systems + body: | + ## Automated Backmerge - **Commits to merge:** ${{ steps.check.outputs.behind_count }} + This PR merges changes from `release/13.2` back into `main`. - This PR was created automatically to keep `main` up-to-date with release branch changes. - Once approved, it will auto-merge. + **Commits to merge:** ${{ steps.check.outputs.behind_count }} - --- - *This PR was generated by the [backmerge-release](${{ github.server_url }}/${{ github.repository }}/actions/workflows/backmerge-release.yml) workflow.* - EOF - ) + This PR was created automatically to keep `main` up-to-date with release branch changes. + Once approved, it will auto-merge. - # If a PR already exists for this branch/base pair, reuse it; otherwise create a new one. - if gh pr view "$PR_BRANCH" --base "$PR_BASE" > /dev/null 2>&1; then - PR_NUMBER=$(gh pr view "$PR_BRANCH" --base "$PR_BASE" --json number --jq .number) - else - PR_NUMBER=$(gh pr create \ - --base "$PR_BASE" \ - --head "$PR_BRANCH" \ - --title "$PR_TITLE" \ - --body "$PR_BODY" \ - --assignee "joperezr,radical" \ - --label "area-engineering-systems" \ - --json number \ - --jq .number) - fi + --- + *This PR was generated by the [backmerge-release](${{ github.server_url }}/${{ github.repository }}/actions/workflows/backmerge-release.yml) workflow.* - echo "pull-request-number=$PR_NUMBER" >> "$GITHUB_OUTPUT" - - name: Enable auto-merge + - name: Add assignees and enable auto-merge if: steps.create-pr.outputs.pull-request-number env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | + gh pr edit ${{ steps.create-pr.outputs.pull-request-number }} --add-assignee joperezr,radical gh pr merge ${{ steps.create-pr.outputs.pull-request-number }} --auto --merge - name: Create issue for merge conflicts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a0007e8030f..0901bf2f888 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,6 +43,7 @@ jobs: eng/pipelines/.* eng/test-configuration.json \.github/workflows/apply-test-attributes.yml + \.github/workflows/backmerge-release.yml \.github/workflows/backport.yml \.github/workflows/dogfood-comment.yml \.github/workflows/generate-api-diffs.yml