Github Action job to post pytest coverage as PR comment

Here’s a Github Action job that runs pytest coverage and then posts a summary of the coverage report as a PR comment with Markdown formatting. This is handy as part of a PR workflow to make the test results and coverage summary visible on the PR.

  coverage:
    name: coverage
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: ./.github/actions/setup_python
      - uses: ./.github/actions/job_url
        id: job_url
        with:
          step_name: 'pytest'
      - name: pytest
        run: |
          pytest
      - name: Test coverage summary report
        id: coverage_report
        if: always()
        run: |
          {
            echo "COVERAGE_REPORT<<EOF"
            coverage report --format=markdown --show-missing --skip-covered
            echo "EOF"
          } >> "$GITHUB_OUTPUT"
      - name: Remove previous coverage comments
        uses: ./.github/actions/remove_comments
        if: always()
        with:
          identifier: coverage
      - name: Test coverage PR comment
        id: coverage_comment
        if: always()
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: `[coverage](${{steps.job_url.outputs.job_url}})
              \r\n\r\n${{steps.coverage_report.outputs.COVERAGE_REPORT}}`
            })

Note that this uses separate actions to setup the Python env, get the job URL and to remove previous PR comments.

The bash step that generates the pytest coverage report and captures the output looks like this:

{
  echo "COVERAGE_REPORT<<EOF"
  coverage report --format=markdown --show-missing --skip-covered
  echo "EOF"
} >> "$GITHUB_OUTPUT"

The Github Script step that posts the PR comment looks like this:

github.rest.issues.createComment({
  owner: context.repo.owner,
  repo: context.repo.repo,
  issue_number: context.issue.number,
  body: `[coverage](${{steps.job_url.outputs.job_url}})
  \r\n\r\n${{steps.coverage_report.outputs.COVERAGE_REPORT}}`
})

Tech mentioned