| name: Revert PR | |
| on: | |
| pull_request_target: | |
| # Safe because we only checkout the trusted 'master' branch from the flutteractionsbot/flutter repository | |
| # to execute reverts of merged pull request commits. It does not execute arbitrary user-controlled code. | |
| types: [labeled] # zizmor: ignore[dangerous-triggers] | |
| permissions: | |
| contents: read | |
| jobs: | |
| revert: | |
| name: Revert PR | |
| runs-on: ubuntu-latest | |
| if: github.event.label.name == 'revert' | |
| steps: | |
| - name: Handle Not Merged PR | |
| if: github.event.pull_request.merged != true | |
| run: | | |
| gh pr comment "$PR_NUMBER" -R flutter/flutter -b "Only merged pull requests can be reverted." | |
| gh pr edit "$PR_NUMBER" -R "$REPO" --remove-label "revert" | |
| # Cancel the workflow and poll the API with a 5-minute safety timeout to prevent infinite runner hanging | |
| gh run cancel ${{ github.run_id }} || true | |
| for i in {1..60}; do | |
| echo "Waiting for cancellation signal... [$i/60]" | |
| sleep 5 | |
| done | |
| echo "Cancellation signal timeout reached. Forcing failure." | |
| exit 1 | |
| env: | |
| GH_TOKEN: ${{ secrets.FLUTTERACTIONSBOT_CP_TOKEN }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| REPO: ${{ github.repository }} | |
| - name: Verify Reason and Fetch PR Details | |
| run: | | |
| # Fetch body, comments, and reviews in a single consolidated API call | |
| gh pr view "$PR_NUMBER" -R "$REPO" --json body,comments,reviews > pr_details.json | |
| # Parse the revert reason case-insensitively and robustly, while allowing the latest "revert" reason to win. | |
| jq -r '.comments // [] | .[] | .body | select(test("^reason for revert:"; "i")) | sub("^reason for revert:\\s*"; ""; "i")' pr_details.json | tail -n 1 > reason.txt | |
| if [ ! -s reason.txt ]; then | |
| gh pr comment "$PR_NUMBER" -R flutter/flutter -b "A reason for requesting a revert of $REPO/$PR_NUMBER could not be found or the reason was not properly formatted. Begin a comment with **'Reason for revert:'** to tell the bot why this issue is being reverted." | |
| gh pr edit "$PR_NUMBER" -R "$REPO" --remove-label "revert" | |
| exit 1 | |
| fi | |
| # Extract reviewer and original body with null safety to avoid subsequent API requests | |
| REVIEWER=$(jq -r '[.reviews // [] | .[] | select(.state == "APPROVED")] | .[0].author.login' pr_details.json) | |
| if [ -z "$REVIEWER" ] || [ "$REVIEWER" = "null" ]; then | |
| echo "None" > reviewer.txt | |
| else | |
| echo "@$REVIEWER" > reviewer.txt | |
| fi | |
| jq -r '.body // ""' pr_details.json > original_body.txt | |
| env: | |
| GH_TOKEN: ${{ secrets.FLUTTERACTIONSBOT_CP_TOKEN }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| REPO: ${{ github.repository }} | |
| - name: Checkout Fork | |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | |
| with: | |
| repository: flutteractionsbot/flutter | |
| path: flutter | |
| ref: master | |
| token: ${{ secrets.FLUTTERACTIONSBOT_CP_TOKEN }} | |
| fetch-depth: 0 | |
| persist-credentials: false | |
| - name: Configure git | |
| run: | | |
| gh auth setup-git | |
| git config --global user.name "flutteractionsbot" | |
| git config --global user.email "<flutter-actions-bot@google.com>" | |
| env: | |
| GH_TOKEN: ${{ secrets.FLUTTERACTIONSBOT_CP_TOKEN }} | |
| - name: Prepare working folder | |
| working-directory: ./flutter | |
| run: | | |
| git remote add upstream https://github.com/flutter/flutter.git | |
| git fetch upstream master | |
| git checkout -b "revert-$PR_NUMBER-$(date +%s)" upstream/master | |
| env: | |
| GH_TOKEN: ${{ secrets.FLUTTERACTIONSBOT_CP_TOKEN }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| - name: Revert Commit | |
| working-directory: ./flutter | |
| run: | | |
| if ! git revert "$MERGE_COMMIT" --no-edit; then | |
| gh pr comment "$PR_NUMBER" -R flutter/flutter -b "Failed to revert commit $MERGE_COMMIT cleanly. Please resolve conflicts manually." | |
| gh pr edit "$PR_NUMBER" -R "$REPO" --remove-label "revert" | |
| exit 1 | |
| fi | |
| env: | |
| GH_TOKEN: ${{ secrets.FLUTTERACTIONSBOT_CP_TOKEN }} | |
| MERGE_COMMIT: ${{ github.event.pull_request.merge_commit_sha }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| REPO: ${{ github.repository }} | |
| - name: Push and Create PR | |
| working-directory: ./flutter | |
| run: | | |
| BRANCH_NAME=$(git branch --show-current) | |
| REVIEWER_NAME=$(cat ../reviewer.txt) | |
| REVIEW_LINE="Reviewed By: $REVIEWER_NAME" | |
| git push origin "$BRANCH_NAME" | |
| PR_LINK="https://github.com/flutter/flutter/pull/$PR_NUMBER" | |
| { | |
| echo "Reverts: [$PR_TITLE]($PR_LINK)" | |
| echo "" | |
| echo "Initiated by: @$INITIATOR" | |
| echo "" | |
| echo "Reason for reverting: $(cat ../reason.txt)" | |
| echo "" | |
| echo "Original PR Author: @$ORIGINAL_AUTHOR" | |
| echo "" | |
| echo "$REVIEW_LINE" | |
| echo "" | |
| echo "The original PR description is provided below:" | |
| echo "" | |
| cat ../original_body.txt | |
| } > ../pr_body.txt | |
| NEW_PR_URL=$(gh pr create \ | |
| --title "Revert: $PR_TITLE" \ | |
| --body-file ../pr_body.txt \ | |
| --repo flutter/flutter \ | |
| --base master \ | |
| --head "flutteractionsbot:$BRANCH_NAME") | |
| gh pr comment "$PR_NUMBER" -R flutter/flutter -b "Successfully created revert PR: $NEW_PR_URL" | |
| gh pr edit "$PR_NUMBER" -R "$REPO" --remove-label "revert" | |
| env: | |
| GH_TOKEN: ${{ secrets.FLUTTERACTIONSBOT_CP_TOKEN }} | |
| INITIATOR: ${{ github.event.sender.login }} | |
| ORIGINAL_AUTHOR: ${{ github.event.pull_request.user.login }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| REPO: ${{ github.repository }} |