diff --git a/.github/scripts/check-codes-comment.js b/.github/scripts/check-codes-comment.js new file mode 100644 index 00000000000..9daa36e8040 --- /dev/null +++ b/.github/scripts/check-codes-comment.js @@ -0,0 +1,120 @@ +const fs = require("fs"); + +module.exports = async ({ github, context, core }) => { + const prNumberPath = process.env.PULL_REQUEST_NUMBER_PATH; + if (!prNumberPath || !fs.existsSync(prNumberPath) || !fs.statSync(prNumberPath).isFile()) { + core.setFailed("Pull request number file not found."); + return; + } + + const reportsPath = process.env.REPORTS_PATH; + if (!reportsPath || !fs.existsSync(reportsPath) || !fs.statSync(reportsPath).isFile()) { + core.setFailed("Reports file not found."); + return; + } + + const PULL_REQUEST_NUMBER = parseInt(fs.readFileSync(prNumberPath, "utf8").trim(), 10); + if (isNaN(PULL_REQUEST_NUMBER)) { + core.setFailed("Failed to parse the pull request number."); + return; + } + + const { owner, repo } = context.repo; + + let pr; + try { + const response = await github.rest.pulls.get({ + owner, + repo, + pull_number: PULL_REQUEST_NUMBER, + }); + pr = response.data; + } catch (error) { + core.setFailed(`Failed to fetch PR #${PULL_REQUEST_NUMBER}: ${error.message}`); + return; + } + + core.info(`Current PR state: ${pr.state}`); + if (pr.state !== "open") { + core.setFailed("The pull request is not open. Skipping comment creation."); + return; + } + + const run = context.payload.workflow_run; + if (!run) { + core.setFailed("context.payload.workflow_run is undefined. Ensure this script runs on the 'workflow_run' event."); + return; + } + + if (pr.head.sha !== run.head_sha) { + core.setFailed("PR head SHA does not match the workflow run head SHA. Skipping."); + return; + } + + if (pr.head.ref !== run.head_branch) { + core.setFailed("PR head branch does not match the workflow run head branch. Skipping."); + return; + } + + if (pr.head.repo.full_name !== run.head_repository.full_name) { + core.setFailed("PR head repository fullname does not match the workflow run head repository fullname. Skipping."); + return; + } + + const comments = []; + const maxCommentCount = 10; + const reports = fs.readFileSync(reportsPath, "utf8").trim().split("\n").filter(Boolean); + for (const report of reports) { + if (comments.length >= maxCommentCount) break; + try { + const data = JSON.parse(report); + if (typeof data !== "object" || Array.isArray(data)) continue; + + const { location, message, severity, code } = data; + if (typeof location !== "object" || Array.isArray(location)) continue; + if (typeof message !== "string") continue; + const severities = ["UNKNOWN_SEVERITY", "ERROR", "WARNING", "INFO", 0, 1, 2, 3]; + if (!severities.includes(severity)) continue; + const severityText = typeof severity === "string" ? severity : severities[severity]; + if (typeof code !== "object" || Array.isArray(code)) continue; + + const path = location.path; + const line = location.range?.start?.line; + const codeText = code.value ? `[${code.value}] ` : ""; + + if (path && line) { + comments.push({ + path, + line, + side: "RIGHT", + body: `**[${severityText}]** ${codeText}\n\n${message}` + }); + } + } catch (error) { + core.warning(`Failed to parse report: ${error.message}`); + } + } + + if (comments.length === 0) { + core.info("No diagnostics found to report as comments."); + return; + } + + core.info(`Successfully prepared ${comments.length} comment${comments.length > 1 ? "s" : ""} to post.`); + + try { + await github.rest.pulls.createReview({ + owner, + repo, + pull_number: PULL_REQUEST_NUMBER, + commit_id: run.head_sha, + body: "🤖 Static analysis found the following issues (max count 10):", + event: "COMMENT", + comments: comments + }); + core.info("Review comments successfully posted to the pull request."); + } catch (error) { + core.setFailed(`Failed to post review comments to GitHub: ${error.message}`); + return; + } +} diff --git a/.github/workflows/check-codes-comment.yml b/.github/workflows/check-codes-comment.yml new file mode 100644 index 00000000000..f9d21859584 --- /dev/null +++ b/.github/workflows/check-codes-comment.yml @@ -0,0 +1,40 @@ +name: Check Codes Comment + +on: + workflow_run: + workflows: ["Check Codes"] + types: + - completed + +permissions: + actions: read + contents: read + pull-requests: write + +jobs: + comment: + runs-on: ubuntu-latest + if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'failure' + steps: + - name: Checkout Script + uses: actions/checkout@v7 + with: + sparse-checkout: | + .github/scripts/check-codes-comment.js + sparse-checkout-cone-mode: false + - name: Download Artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: checkstyle-artifact + path: ${{ runner.temp }}/checkstyle-artifact + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Generate Review Comment + uses: actions/github-script@v9 + env: + REPORTS_PATH: ${{ runner.temp }}/checkstyle-artifact/ALL_REPORTS.jsonl + PULL_REQUEST_NUMBER_PATH: ${{ runner.temp }}/checkstyle-artifact/PULL_REQUEST_NUMBER + with: + script: | + const script = require("./.github/scripts/check-codes-comment.js"); + await script({ github, context, core }); diff --git a/.github/workflows/check-codes.yml b/.github/workflows/check-codes.yml index bd6044b1afa..3cd88ca7f84 100644 --- a/.github/workflows/check-codes.yml +++ b/.github/workflows/check-codes.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - name: Set up JDK 17 + - name: Setup JDK 17 uses: actions/setup-java@v5 with: distribution: 'zulu' @@ -27,3 +27,29 @@ jobs: cache-cleanup: never - name: Check Codes run: ./gradlew checkstyle checkTranslations --no-daemon --parallel --stacktrace + - if: failure() && github.event_name == 'pull_request' + name: Setup reviewdog + uses: reviewdog/action-setup@d8edfce3dd5e1ec6978745e801f9c50b5ef80252 # v1.4.0 + with: + reviewdog_version: v0.21.0 + - if: failure() && github.event_name == 'pull_request' + name: Generate Artifact + run: | + echo "${{ github.event.number }}" > PULL_REQUEST_NUMBER + > ALL_REPORTS.jsonl + find . -path "*/build/reports/checkstyle/*.xml" | while read -r xml_file; do + if [ -s "$xml_file" ]; then + echo "Processing $xml_file..." + reviewdog -f=checkstyle -name="checkstyle" -reporter=rdjsonl < "$xml_file" >> ALL_REPORTS.jsonl + else + echo "Skipping empty or missing report: $xml_file" + fi + done + - if: failure() && github.event_name == 'pull_request' + name: Upload Artifact + uses: actions/upload-artifact@v7 + with: + name: checkstyle-artifact + path: | + ALL_REPORTS.jsonl + PULL_REQUEST_NUMBER diff --git a/.github/workflows/pr-size-label.yml b/.github/workflows/pr-size-label.yml index 4fc035d419b..452548785e4 100644 --- a/.github/workflows/pr-size-label.yml +++ b/.github/workflows/pr-size-label.yml @@ -21,8 +21,8 @@ jobs: uses: actions/checkout@v7 with: sparse-checkout: | - .github/scripts - sparse-checkout-cone-mode: true + .github/scripts/pr-size-label.js + sparse-checkout-cone-mode: false - name: Label PR by filtered changed lines uses: actions/github-script@v9 with: