diff --git a/.github/workflows/auto-update-base.yaml b/.github/workflows/auto-update-base.yaml new file mode 100644 index 000000000..fb8ebc0cc --- /dev/null +++ b/.github/workflows/auto-update-base.yaml @@ -0,0 +1,112 @@ +name: Auto-Update PR Base + +on: + push: + branches: + - main + +jobs: + auto-update-base: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@v7 + with: + github-token: ${{ secrets.ROBOT_TOKEN }} + script: | + let retriesNeeded = true; + let attemptCount = 0; + const maxAttempts = 10; // Set a limit for the maximum number of attempts + let errorEncountered = false; // Track if any errors occur + + while (retriesNeeded && attemptCount < maxAttempts) { + retriesNeeded = false; // Assume no retries needed, unless 'unknown' state is found. + attemptCount += 1; + + try { + const { data: pulls } = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + state: "open", + }); + + for (const pull of pulls) { + const hasAutoUpdateLabel = pull.labels.some(label => label.name === "auto-update-base"); + + if (!hasAutoUpdateLabel) { + continue; + } + + try { + const { data: pr } = await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pull.number, + }); + + switch (pr.mergeable) { + case null: + console.log(`[INFO] PR #${pull.number}: "${pull.title}" - GitHub is still computing the mergeability. Will retry later.`); + retriesNeeded = true; + break; + case false: + console.log(`[INFO] PR #${pull.number}: "${pull.title}" - Skipping update due to merge conflict.`); + break; + case true: + try { + const { data: baseBranch } = await github.rest.repos.getBranch({ + owner: context.repo.owner, + repo: context.repo.repo, + branch: pr.base.ref, + }); + + if (pr.base.sha === baseBranch.commit.sha) { + console.log(`[INFO] PR #${pull.number}: "${pull.title}" - Base branch is already up-to-date.`); + break; + } + + console.log(`[UPDATE] PR #${pull.number}: "${pull.title}" - Updating base branch...`); + try { + await github.rest.pulls.updateBranch({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pull.number, + }); + console.log(`[SUCCESS] PR #${pull.number}: "${pull.title}" - Base branch updated.`); + } catch (updateError) { + console.error(`[ERROR] PR #${pull.number}: Failed to update base branch - ${updateError.message}`); + errorEncountered = true; + } + + break; + } catch (baseError) { + console.error(`[ERROR] PR #${pull.number}: Failed to get base branch - ${baseError.message}`); + errorEncountered = true; + break; + } + } + } catch (prError) { + console.error(`[ERROR] Failed to get PR #${pull.number} - ${prError.message}`); + errorEncountered = true; + } + } + } catch (error) { + console.error(`[ERROR] Failed to list pull requests - ${error.message}`); + errorEncountered = true; + } + + if (retriesNeeded && attemptCount < maxAttempts) { + console.log(`[INFO] Retrying for PRs with 'unknown' mergeable state (Attempt ${attemptCount}/${maxAttempts})...`); + await new Promise(resolve => setTimeout(resolve, 5000)); + } + } + + if (attemptCount >= maxAttempts) { + console.error(`[ERROR] Maximum attempts (${maxAttempts}) reached. Some PRs may still have 'unknown' mergeable state.`); + errorEncountered = true; + } + + if (errorEncountered) { + core.setFailed(`[ERROR] Some PRs failed to update base branch.`); + } else { + console.log(`[INFO] All PRs with 'auto-update-base' label have been processed.`); + }