From 4bc071b923e286ac28ebe5cbcfd847fe38e17a5d Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Thu, 14 Nov 2024 17:28:02 +0000 Subject: [PATCH] ci: code coverage report --- .github/actions-rs/grcov.yml | 2 + .github/workflows/generate_coverage_pr.yaml | 94 ++++++++++++++++ .github/workflows/upload_coverage_pr.yaml | 119 ++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 .github/actions-rs/grcov.yml create mode 100644 .github/workflows/generate_coverage_pr.yaml create mode 100644 .github/workflows/upload_coverage_pr.yaml diff --git a/.github/actions-rs/grcov.yml b/.github/actions-rs/grcov.yml new file mode 100644 index 0000000..95a8ce9 --- /dev/null +++ b/.github/actions-rs/grcov.yml @@ -0,0 +1,2 @@ +output-type: lcov +output-path: ./lcov.info \ No newline at end of file diff --git a/.github/workflows/generate_coverage_pr.yaml b/.github/workflows/generate_coverage_pr.yaml new file mode 100644 index 0000000..5a0d6a1 --- /dev/null +++ b/.github/workflows/generate_coverage_pr.yaml @@ -0,0 +1,94 @@ +name: Generate Coverage Report (PR) + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + +env: + CARGO_TERM_COLOR: always + +jobs: + coverage: + name: Generate Coverage Report + environment: coverage + runs-on: ubuntu-latest + env: + CARGO_INCREMENTAL: "0" + RUSTFLAGS: "-Cinstrument-coverage" + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install LLVM tools + run: sudo apt-get update && sudo apt-get install -y llvm + + - id: setup + name: Setup Toolchain + uses: dtolnay/rust-toolchain@nightly + with: + toolchain: nightly + components: llvm-tools-preview + + - id: cache + name: Enable Workflow Cache + uses: Swatinem/rust-cache@v2 + + - id: tools + name: Install Tools + uses: taiki-e/install-action@v2 + with: + tool: grcov,cargo-llvm-cov + + - id: build_and_test + name: Build and test + run: | + echo "${{ secrets.CODECOV_TOKEN }}" + cargo clean + cargo build + cargo test -- --nocapture + + - id: coverage + name: Generate Coverage Report + run: | + cargo llvm-cov --lcov --output-path=./lcov.info + + - name: Store PR number and commit SHA + run: | + echo "Storing PR number ${{ github.event.number }}" + echo "${{ github.event.number }}" > pr_number.txt + + echo "Storing commit SHA ${{ github.event.pull_request.head.sha }}" + echo "${{ github.event.pull_request.head.sha }}" > commit_sha.txt + + # Workaround for https://github.com/orgs/community/discussions/25220 + # Triggered sub-workflow is not able to detect the original commit/PR which is available + # in this workflow. + - name: Store PR number + uses: actions/upload-artifact@v4 + with: + name: pr_number + path: pr_number.txt + + - name: Store commit SHA + uses: actions/upload-artifact@v4 + with: + name: commit_sha + path: commit_sha.txt + + # This stores the coverage report in artifacts. The actual upload to Codecov + # is executed by a different workflow `coverage-report.yml`. The reason for this + # split is because `on.pull_request` workflows don't have access to secrets. + - name: Store coverage report in artifacts + uses: actions/upload-artifact@v4 + with: + name: codecov_report + path: ./lcov.info + + - run: | + echo "The coverage report was stored in Github artifacts." + echo "It will be uploaded to Codecov using [coverage-report.yml] workflow shortly." diff --git a/.github/workflows/upload_coverage_pr.yaml b/.github/workflows/upload_coverage_pr.yaml new file mode 100644 index 0000000..7eda8e6 --- /dev/null +++ b/.github/workflows/upload_coverage_pr.yaml @@ -0,0 +1,119 @@ +name: Upload Coverage Report (PR) + +on: + # This workflow is triggered after every successfull execution + # of `Generate Coverage Report` workflow. + workflow_run: + workflows: ["Generate Coverage Report (PR)"] + types: + - completed + +permissions: + actions: write + contents: write + issues: write + pull-requests: write + +jobs: + coverage: + name: Upload Coverage Report + environment: coverage + runs-on: ubuntu-latest + steps: + - name: "Download existing coverage report" + id: prepare_report + uses: actions/github-script@v7 + with: + script: | + var fs = require('fs'); + + // List artifacts of the workflow run that triggered this workflow + var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + + let codecovReport = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "codecov_report"; + }); + + if (codecovReport.length != 1) { + throw new Error("Unexpected number of {codecov_report} artifacts: " + codecovReport.length); + } + + var download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: codecovReport[0].id, + archive_format: 'zip', + }); + fs.writeFileSync('codecov_report.zip', Buffer.from(download.data)); + + let prNumber = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "pr_number"; + }); + + if (prNumber.length != 1) { + throw new Error("Unexpected number of {pr_number} artifacts: " + prNumber.length); + } + + var download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: prNumber[0].id, + archive_format: 'zip', + }); + fs.writeFileSync('pr_number.zip', Buffer.from(download.data)); + + let commitSha = artifacts.data.artifacts.filter((artifact) => { + return artifact.name == "commit_sha"; + }); + + if (commitSha.length != 1) { + throw new Error("Unexpected number of {commit_sha} artifacts: " + commitSha.length); + } + + var download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: commitSha[0].id, + archive_format: 'zip', + }); + fs.writeFileSync('commit_sha.zip', Buffer.from(download.data)); + + - id: parse_previous_artifacts + run: | + unzip codecov_report.zip + unzip pr_number.zip + unzip commit_sha.zip + + echo "Detected PR is: $(> "$GITHUB_OUTPUT" + echo "override_commit=$(> "$GITHUB_OUTPUT" + + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: ${{ steps.parse_previous_artifacts.outputs.override_commit || '' }} + path: repo_root + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 + with: + verbose: true + token: ${{ secrets.CODECOV_TOKEN }} + files: ${{ github.workspace }}/lcov.info + fail_ci_if_error: true + # Manual overrides for these parameters are needed because automatic detection + # in codecov-action does not work for non-`pull_request` workflows. + # In `main` branch push, these default to empty strings since we want to run + # the analysis on HEAD. + override_commit: ${{ steps.parse_previous_artifacts.outputs.override_commit || '' }} + override_pr: ${{ steps.parse_previous_artifacts.outputs.override_pr || '' }} + working-directory: ${{ github.workspace }}/repo_root + # Location where coverage report files are searched for + directory: ${{ github.workspace }}