name: "Check cherry-picks"

on:
  pull_request:
    paths:
      - .github/workflows/check-cherry-picks.yml
  pull_request_target:
    branches:
      - 'release-**'
      - 'staging-**'
      - '!staging-next'

concurrency:
  group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.head_ref || github.run_id }}
  cancel-in-progress: true

permissions:
  pull-requests: write

jobs:
  check:
    name: cherry-pick-check
    runs-on: ubuntu-24.04-arm
    steps:
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          fetch-depth: 0
          filter: tree:0
          path: trusted

      - name: Check cherry-picks
        id: check
        continue-on-error: true
        env:
          BASE_SHA: ${{ github.event.pull_request.base.sha }}
          HEAD_SHA: ${{ github.event.pull_request.head.sha }}
        run: |
          ./trusted/ci/check-cherry-picks.sh "$BASE_SHA" "$HEAD_SHA" checked-cherry-picks.md

      - name: Prepare review
        if: steps.check.outcome == 'failure'
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          script: |
            const { readFile, writeFile } = require('node:fs/promises')

            const job_url = (await github.rest.actions.listJobsForWorkflowRun({
              owner: context.repo.owner,
              repo: context.repo.repo,
              run_id: context.runId
            })).data.jobs[0].html_url + '?pr=' + context.payload.pull_request.number

            const header = await readFile('trusted/ci/check-cherry-picks.md')
            const body = await readFile('checked-cherry-picks.md')
            const footer =
              `\n_Hint: The full diffs are also available in the [runner logs](${job_url}) with slightly better highlighting._`

            const review = header + body + footer
            await writeFile('review.md', review)
            core.summary.addRaw(review)
            core.summary.write()

      - name: Request changes
        if: ${{ github.event_name == 'pull_request_target' && steps.check.outcome == 'failure' }}
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          script: |
            const { readFile } = require('node:fs/promises')
            const body = await readFile('review.md', 'utf-8')

            const pendingReview = (await github.paginate(github.rest.pulls.listReviews, {
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.payload.pull_request.number
            })).find(review =>
              review.user.login == 'github-actions[bot]' && (
                // If a review is still pending, we can just update this instead
                // of posting a new one.
                review.state == 'CHANGES_REQUESTED' ||
                // No need to post a new review, if an older one with the exact
                // same content had already been dismissed.
                review.body == body
              )
            )

            // Either of those two requests could fail for very long comments. This can only happen
            // with multiple commits all hitting the truncation limit for the diff. If you ever hit
            // this case, consider just splitting up those commits into multiple PRs.
            if (pendingReview) {
              await github.rest.pulls.updateReview({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: context.payload.pull_request.number,
                review_id: pendingReview.id,
                body
              })
            } else {
              await github.rest.pulls.createReview({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: context.payload.pull_request.number,
                event: 'REQUEST_CHANGES',
                body
              })
            }

      - name: Dismiss old reviews
        if: ${{ github.event_name == 'pull_request_target' && steps.check.outcome == 'success' }}
        uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
        with:
          script: |
            await Promise.all(
              (await github.paginate(github.rest.pulls.listReviews, {
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: context.payload.pull_request.number
              })).filter(review =>
                review.user.login == 'github-actions[bot]'
              ).map(async (review) => {
                if (review.state == 'CHANGES_REQUESTED') {
                  await github.rest.pulls.dismissReview({
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    pull_number: context.payload.pull_request.number,
                    review_id: review.id,
                    message: 'All cherry-picks are good now, thank you!'
                  })
                }
                await github.graphql(`mutation($node_id:ID!) {
                  minimizeComment(input: {
                    classifier: RESOLVED,
                    subjectId: $node_id
                  })
                  { clientMutationId }
                }`, { node_id: review.node_id })
              })
            )
