Is there a way to tell if a PR is from a forked repository?

Dear Github Community,

At the end of each of my workflows, a Slack message (using a Slack action) is sent using GITHUB_TOKEN from my base repository. Now if someone wishes to make a PR from a forked repository, this Slack step will fail because GITHUB_TOKEN being inaccessible outside the base repository. As a result, any PR that is made from a forked repository fails.

My question is: can I somehow not run the Slack action step when a PR is made from a forked repository? For example:

 name: Notify Slack
      uses: 8398a7/action-slack@v3
      ...
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
        MATRIX_CONTEXT: ${{ toJson(matrix) }} # Required when using matrix
      # Pick up events if the job succeeds, fails or is canceled, and if this
      # job is not executed in forked repositories
      if: (${{ failure() }} || ${{ success() }} || ${{ cancelled() }}) && ${{ github.repository.is_not_forked }}

I have also thought about allowing the Slack action step to fail, but there seems to be no such feature. continue-on-error will still end up with those red crosses for PRs from forked repos.

Any thoughts?

Assuming you do actually want it to run: This is the type of thing the pull_request_target event is meant to facilitate.

Thanks for your reply. Do I understand correctly that if I append the pull_request_target event to my workflows’ on section, any PRs made from forked repositories will be able to use my base repository’s GITHUB_TOKEN (in this case, to send Slack messages)?

on:
  push:
  pull_request:
    branches:
      - master
  pull_request_target:
    branches:
      - master

jobs:
  build:
    runs-on: ubuntu-18.04

    steps:
    - name: Notify Slack
      uses: 8398a7/action-slack@v3
      ...
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
        MATRIX_CONTEXT: ${{ toJson(matrix) }} # Required when using matrix
      if: always()


Would I still need the regular pull_request event in this case for PRs made from the base repository?

As far as I understand you should be able to use pull_request_target most places your previously used pull_request if it needs access to tokens (it’s like the original + :slight_smile:).

Depending what your workflow does you may need to also ensure you’ve checked out the proper code to analyze/build etc.

      uses: actions/checkout@v2
      with:
        ref: ${{github.event.pull_request.head.ref}}
        repository: ${{github.event.pull_request.head.repo.full_name}}
1 Like

If ref and repository were not specified, then it would checkout the code of the target branch, correct?

instead of running against the workflow and code from the merge commit, the event runs against the workflow and code from the base of the pull request

Yup, that seems to be the case from my experience.

1 Like

@senui ,
When the workflow is triggered by the events that are related to pull request ( pull_request, pull_request_review, pull_request_review_comment or pull_request_target), from the github context of the workflow run, you can get the following three properties:

  • github.event.pull_request.base.repo.full_name - - The full name of PR’s base (target) repository (owner-name/repo-name).

  • github.event.pull_request.head.repo.full_name - - The full name of PR’s head (source) repository (owner-name/repo-name).

  • github.repository - - The full name of the repository where the PR is in. Normally this property is equal to ‘github.event.pull_request.base.repo.full_name’.

When the PR’s head branch and base branch are in the same repository, the above three properties are same.
When the PR’s head branch and base branch are in different repositories, ‘github.event.pull_request.head.repo.full_name’ is different with the other two.
In your case, you can try this:

steps:
  - name: Notify Slack
    if: ${{ github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name }}
    uses: 8398a7/action-slack@v3
    . . .

OR

steps:
  - name: Notify Slack
    if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
    uses: 8398a7/action-slack@v3
    . . .

So I tried to go with the pull_request_target solution, but the main issue is that ny PRs made from the base repository to test changes to any workflow file are ignored. This makes testing of GHA workflows before merging into master not feasible.

I am inclined to go with @brightran’s solution of just ignoring the Slack messages from PRs of forked repos. But I do have a question @brightran:

steps:
 - name: Notify Slack
   if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
   uses: 8398a7/action-slack@v3
   . . .

Will the above step also run if a previous step failed? I used to have as condition if: always(), in order to also catch cases where previous steps failed or were canceled. The Slack message would then notify me of what exactly happened.

Would I have to make a boolean construction as I had in my opening post to take those cases into account? Something like:

if: ${{ ( failure() || success() || cancelled() ) && ( github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name ) }}

Edit:

I can confirm that the above construct does indeed work: the Slack action is ignored for forked PRs and taken into account for PRs from the base repository

Thanks everyone!

@senui,
always()” is OK, instead of “(failure() || success() || cancelled())”. You also can reference here.

if: ${{ github.event.pull_request.head.repo.full_name == github.repository && always() }}
1 Like

Users without write access to your repo shouldn’t be able to execute arbitrarily modified workflows. That’s why pull_request_target uses the base branch’s workflows. As a project maintainer who wants to test changes to workflows, you can use pull_request and push a branch to the base repo instead of a fork, can’t you?

@Simran-B, yes, but wouldn’t a PR on the base repo then trigger for both the pull_request_target and pull_request event?

Good point. Yes, it will trigger both if you do on: [pull_request, pull_request_target].
However, it’s possible to add a condition:

Event Origin Fork
pull_request :heavy_check_mark: :x:
pull_request_target :x: :heavy_check_mark:
jobs:
  job1:
    if: >-
      (
        github.event_name == 'pull_request' &&
        github.event.pull_request.head.repo.full_name == github.repository
      ) || (
        github.event_name == 'pull_request_target' &&
        github.event.pull_request.head.repo.full_name != github.repository
      )

The undesired combinations are skipped. The other two have access to secrets. If the PR branch is in the same repo, then changes to the workflow contained in the PR will apply immediately (i.e. you as maintainer can test workflow changes). If the PR has its branch in a forked repo, then workflow changes won’t be run (the origin version of the workflow is used). In the skipped combination of pull_request and fork, workflow changes would be taken into account, but since there is no access to secrets, external users will not be able to leak them unless you merge malicious changes.