Using `workflow_run` in GitHub Actions

March 01, 2023#Software Development
Article
Author image.

Sarah Dutkiewicz, Senior Trainer

While I was looking at the Ardalis.GuardClauses repo, I noticed that the build process was failing. Talking with Steve, I noticed that the build was failing on pull requests from forks. Being an open source project and wanting to have others contribute, this is a problem.

Problem: Writing code coverage results to the PR

Part of the build process is writing code coverage results to the pull request. The build uses marocchino/sticky-pull-request-comment to keep the code coverage to one comment in the pull request. However, the error message - as seen in this run is:

Error: Resource not accessible by integration

That “not accessible” made me think it was a permissions issue. I needed to explore further.

Check the GITHUB_TOKEN permissions

Knowing that the action uses the GITHUB_TOKEN by default, I needed to look at the permissions set under the “Set up job” step.

Screenshot of the 'Set up job' step from the GitHub Actions workflow. The GITHUB_TOKEN permissions are highlighted. Metadata and PullRequests are marked as 'read'.

In order to write to the pull request, there should be write permissions. Let’s look at this further.

Looking at GITHUB_TOKEN permissions issues

GitHub had announced changes to the default behavior of GITHUB_TOKEN in workflows in February 2023. With this being an existing repo, I don’t think this is a problem.

I ended up creating a test PR to help test possible solutions. We tried looking at the permissions attribute in the YML at the workflow level and at the job level. You can read more about assigning permissions to jobs. However, this still didn’t solve our problem with the forks from new contributors.

Steve opened an issue on the sticky-pull-request-comment action, and marocchino gave us some hints that led me to explore a bit further.

Solution: Separating the reads and the writes

I did some further reading and came across this post: Keeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests. In the post, the GitHub Security Lab explains that you can use the workflow_run trigger for building untrusted code and writing to a PR. This breaks the build into two workflows:

  • A read-only repo token that runs the build and tests
  • A write-access workflow_run trigger that runs after the first one does and writes the code coverage to the PR

What does this look like?

The commenting workflow does need access to some of the files created by the first workflow, so I’ve added upload and download artifacts and am using the dawidd6/action-download-artifact action to get the artifacts from another workflow.

I added an if expression to only run that job when it is a pull request situation. When pushing to main, that workflow will appear as skipped.

build.yml

name: .NET Core

on:
  workflow_dispatch:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - name: Setup .NET Core
        uses: actions/setup-dotnet@v3
        with:
          dotnet-version: "7.x"
      - name: Install dependencies
        run: dotnet restore
      - name: Build
        id: build
        run: dotnet build --configuration Release --no-restore

        # See https://josh-ops.com/posts/github-code-coverage/
        # Add coverlet.collector nuget package to test project - 'dotnet add <TestProject.cspoj> package coverlet
      - name: Test
        id: test
        if: steps.build.outcome == 'success'
        run: dotnet test --no-restore --verbosity normal --collect:"XPlat Code Coverage" --logger trx --results-directory coverage

        # Copy code coverage always - regardless of test failures or successes
      - name: Copy Coverage To Predictable Location
        if: success() || failure()
        run: cp coverage/*/coverage.cobertura.xml coverage/coverage.cobertura.xml

      - name: Code Coverage Summary Report
        uses: irongut/CodeCoverageSummary@v1.3.0
        if: success() || failure()
        with:
          filename: coverage/coverage.cobertura.xml
          badge: true
          format: "markdown"
          output: "both"

comment-on-pr.yml

name: Comment on the Pull Request

# read-write repo token
# See: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
on:
  workflow_run:
    workflows: [".NET Core"]
    types:
      - completed

jobs:
  comment:
    runs-on: ubuntu-latest

    # Only comment on the PR if this is a PR event
    if: github.event.workflow_run.event == 'pull_request'

    steps:
      - name: Get the PR Number artifact
        uses: dawidd6/action-download-artifact@v2
        with:
          workflow: ${{ github.event.workflow_run.workflow_id }}
          workflow_conclusion: ""
          name: pr-number
      - name: Read PR Number into GitHub environment variables
        run: echo "PR_NUMBER=$(cat pr-number.txt)" >> $GITHUB_ENV
      - name: Confirm the PR Number (Debugging)
        run: echo $PR_NUMBER
      - name: Get the code coverage results file
        uses: dawidd6/action-download-artifact@v2
        with:
          workflow: ${{ github.event.workflow_run.workflow_id }}
          workflow_conclusion: ""
          name: code-coverage-results
      - name: Add Coverage PR Comment
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          number: ${{ env.PR_NUMBER }}
          recreate: true
          path: code-coverage-results.md

Conclusion

Remember, it is never “simply a permissions issue”. If you ever think that, the permissions will remind you otherwise. When you see Error: Resource not accessible by integration, consider these things:

  • Does your GITHUB_TOKEN have the write permissions where it is needed?
  • Do you need to isolate your actions into separate workflows to grant only the permissions needed for the steps?

Thanks to the GitHub Security Lab, we have the guidance on how to separate our workflows using their example as a guide.


Copyright © 2024 NimblePros - All Rights Reserved