From 67353e68f5da303fd2b89d11e24a417d721974cf Mon Sep 17 00:00:00 2001 From: Brad Jorsch Date: Tue, 28 Jan 2025 12:26:40 -0500 Subject: [PATCH] ci: Comment with code coverage summary (#41231) After uploading the code coverage data, the server will reply with a summary of the code coverage and a recommended state. Post that as a comment to the PR. --- .../files/coverage-munger/upload-coverage.sh | 85 +++++++++++++++++++ .github/workflows/tests.yml | 4 + 2 files changed, 89 insertions(+) diff --git a/.github/files/coverage-munger/upload-coverage.sh b/.github/files/coverage-munger/upload-coverage.sh index 4dace87c5b189..cb6aa1c42119f 100755 --- a/.github/files/coverage-munger/upload-coverage.sh +++ b/.github/files/coverage-munger/upload-coverage.sh @@ -3,9 +3,15 @@ ## Environment used by this script: # # Required: +# - API_TOKEN_GITHUB: GitHub API token. +# - GITHUB_API_URL: GitHub API URL. +# - GITHUB_TOKEN: GitHub API token. +# - GITHUB_REPOSITORY: GitHub repo. # - GITHUB_SHA: Commit SHA. +# - PR_HEAD: SHA for the PR head commit (versus GITHUB_SHA which is a merge commit) # - PR_ID: PR number or "trunk". # - SECRET: Shared secret. +# - STATUS: Status of the coverage run. set -eo pipefail @@ -95,3 +101,82 @@ done do_req "op=finish&token=$TOKEN" TOKEN= echo '::endgroup::' + +if [[ "$PR_ID" != "trunk" ]]; then + echo "::group::Setting GitHub status" + if jq -e '.covinfo' <<<"$JSON" &>/dev/null; then + JSON=$( jq '.covinfo' <<<"$JSON" ) + if [[ "$STATUS" != 'success' ]]; then + JSON=$( jq '.state |= "pending" | .description |= "Waiting for tests to pass" | .msg |= "Cannot generate coverage summary while tests are failing. :zipper_mouth_face:\n\nPlease fix the tests, or re-run the Code coverage job if it was something being flaky."' <<<"$JSON" ) + fi + else + JSON='{"state":"error","description":"No covinfo received from server","msg":"","footer":""}' + fi + jq . <<<"$JSON" + curl -v -L --fail \ + --url "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/statuses/$( jq --arg V "$PR_HEAD" -nr '$V | @uri' )" \ + --header "authorization: Bearer $API_TOKEN_GITHUB" \ + --header 'content-type: application/json' \ + --data "$( jq -c --arg PR "$PR_ID" '{ + context: "Code coverage requirement", + state: .state, + target_url: "https://jetpackcodecoverage.atomicsites.blog/prs/\( $PR | @uri )/", + description: .description, + }' <<<"$JSON" )" + echo "::endgroup::" + + # Find the last comment starting with "### Code Coverage Summary" + echo "::group::Looking for existing comment" + PAGE=1 + while true; do + J=$( curl -v -L fail \ + --url "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/issues/${ID}/comments?per_page=100&page=$PAGE" \ + --header "authorization: Bearer $API_TOKEN_GITHUB" + ) + CID=$( jq -r --arg CID "$CID" '[ { id: $CID }, ( .[] | select( .user.login == "github-actions[bot]" ) | select( .body | test( "^### Code Coverage Summary" ) ) ) ] | last | .id' <<<"$J" ) + if jq -e 'length < 100' <<<"$J" &>/dev/null; then + break + fi + PAGE=$(( PAGE + 1 )) + done + echo "::endgroup::" + if [[ -n "$CID" ]]; then + echo "Existing comment ID=$CID" + else + echo "No existing comment found" + fi + + if jq -e '.msg != ""' <<<"$JSON" &>/dev/null; then + if [[ -n "$CID" ]]; then + echo "::group::Updating comment" + curl -v -L --fail \ + -X PATCH \ + --url "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/issues/comments/${CID}" \ + --header "authorization: Bearer $API_TOKEN_GITHUB" \ + --header 'content-type: application/json' \ + --data "$( jq -c '{ + body: "### Code Coverage Summary\n\n\( .msg )\n\n\( .footer )", + }' <<<"$JSON" )" + echo "::endgroup::" + else + echo "::group::Creating comment" + curl -v -L --fail \ + -X POST \ + --url "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/issues/${ID}/comments" \ + --header "authorization: Bearer $API_TOKEN_GITHUB" \ + --header 'content-type: application/json' \ + --data "$( jq -c '{ + body: "### Code Coverage Summary\n\n\( .msg )\n\n\( .footer )", + }' <<<"$JSON" )" + echo "::endgroup::" + fi + elif [[ -n "$CID" ]]; then + # No message, delete existing comment. + echo "::group::Deleting comment" + curl -v -L --fail \ + -X DELETE \ + --url "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/issues/comments/${CID}" \ + --header "authorization: Bearer $API_TOKEN_GITHUB" + echo "::endgroup::" + fi +fi diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3d7d30bed5a8f..5fd123c99f809 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -57,6 +57,7 @@ jobs: # Note matrix-job outputs are kind of weird. Last-to-run job that sets a non-empty value wins. outputs: did-coverage: ${{ ( steps.run-tests.conclusion != 'cancelled' && steps.process-coverage.conclusion == 'success' && steps.upload-artifacts.conclusion == 'success' ) && 'true' || '' }} + coverage-status: ${{ matrix.script == 'test-coverage' && steps.run-tests.conclusion || '' }} steps: - uses: actions/checkout@v4 @@ -305,6 +306,9 @@ jobs: env: PR_ID: ${{ github.event_name != 'pull_request' && 'trunk' || github.event.pull_request.number }} SECRET: ${{ secrets.CODECOV_SECRET }} + STATUS: ${{ needs.run-tests.outputs.coverage-status }} + PR_HEAD: ${{ github.event.pull_request.head.sha }} + API_TOKEN_GITHUB: ${{ secrets.GITHUB_TOKEN }} run: .github/files/coverage-munger/upload-coverage.sh storybook-test: