diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 06975dcd71c..e802e904706 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 00:00 UTC (4pm PT during standard time, 5pm PT during daylight saving time). 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..05cd2c507d1 --- /dev/null +++ b/.github/workflows/backmerge-release.yml @@ -0,0 +1,152 @@ +name: Backmerge Release to Main + +on: + schedule: + - cron: '0 0 * * *' # Runs daily at 00:00 UTC (16:00 PST / 17:00 PDT) + workflow_dispatch: # Allow manual trigger + +permissions: + contents: write + pull-requests: write + issues: write + +jobs: + backmerge: + runs-on: ubuntu-latest + timeout-minutes: 15 + 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: 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 + + 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.* + + - 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 + 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 + 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: issueBody, + assignees: ['joperezr', 'radical'], + labels: ['area-engineering-systems', 'backmerge-conflict'] + }); 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