How to trigger an action on push or pull request but not both?

I would like my workflow to be triggered by either a push or a pull-request, but if it a push to a pull-request only trigger one rather than two workflows.

Something like,

on: [push | pull_request]
17 Likes

I’m afraid that you cannot do this directly.  However, you can trigger only on pushes to master, or pull requests to master.  This will prevent builds from happening twice when somebody opens a pull request against master and then pushes updates to their branch.  For example:

on:
  push:
    branches:
    - master
  pull_request:
    branches:
    - master
10 Likes

I found this while searching and thought some people might find my solution helpfull as well.  I have a workflow that has a ‘build’ job and a ‘deploy’ job.  I want to build pull request and deploy merges to master.  Here is my workflow setup:

name: 'foo'
on:
  push:
    branches:
      - master
  pull_request:
env:
  SOME_THING: sweet
jobs:
  build:
    runs-on: ubuntu-latest
    if: github.ref != 'refs/heads/master'
    steps:
      - name: 'Checkout'
        uses: actions/checkout@master
  deploy:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/master'
    steps:
      - name: 'Checkout'
        uses: actions/checkout@master
11 Likes

Thanks for sharing that solution. I have a follow up question. Is it possible to run actions on branches which have not been pull requested, but don’t run the push on the PR than.

Do you now what I mean?

Will this still run builds when the branch for a pull request is updated? That is, I understand the action will run when a pull request is first created for a branch. But if the action is only run for pushes to master, then does that mean it will not run for pushes which update a branch used in a pull request?

The provided code does not work for me, I get

 Error

Invalid type for `on`

Here’s my yml

on:
  schedule:
    - cron: "0 10 * * *" # everyday at 10am
  tags:
    - "v*.*.*"
  push:
    branches:
      - master
  pull_request:
    branches:
      - master

What am I doing wrong? How do I troubleshoot? Thx

I published https://github.com/marketplace/actions/skip-duplicate-actions to solve this problem.
You can give it a try if this is still an issue.

3 Likes

Solution:

on: 
  push:
  pull_request:
    types: [opened]

1 Like

It does prevent additional runs caused by pull_request synchronize later as you push to the feature branch indeed. But doesn’t it still cause two workflow runs initially? (pull_request opened and push to feature branch)

This is a very important issue as you may use some automatization for merging… if you have N checks, created PR as repo owner creates 2N check compare to forked PR creates only N checks…
With limiting push/PR to master you miss all development/testing in branches even as repo owner :confused:

1 Like

This works for me! Thanks @piersy!

@marcosfelt Did it really?

I thought it was working for us but it wasn’t actually letting the forked PRs run.

In fact I came to the conclusion that the “user:branch-name” branch names for forked PRs are a result of the way the github frontend displays branch names and do not actually represent a different class of branch name. In the case of non forked PRs I think github hides the “user:” prefix, because it is redundant. The branch name that is matched against by github actions will be the segment that follows the colon.

Please confirm if I’m incorrect otherwise I will delete the original post as I do not see any way to edit it.

We have now settled for a different solution:

on:
  workflow_dispatch:
  pull_request:

See - Run go workflow on manual trigger and all PRs by piersy · Pull Request #743 · clearmatics/autonity · GitHub

This runs github actions on all PRs and allows us to manually trigger workflows on arbitrary branches. This works out slightly better for us since we didn’t really want to run github actions on every push.

I deleted this post because it contained incorrect information, the solution I provided did not work and I receive confirmation from one other person that it was not working for them.

See this post for the current solution we have adopted - How to trigger an action on push or pull request but not both? - #13 by piersy

1 Like

I have found a work around to this:

My workflow has this at the top:

name: "Build"
on:
  pull_request:
    branches: [ master ]
    types: [ opened, synchronize, closed ]
  push:
    branches-ignore:
      - master
    paths-ignore:
      - README.md
      - CHANGELOG.md  # Should never be edited anyway
      - .gitignore
      - .github/**
      - Jenkinsfile

I have the following steps:

Get Branch Name:

      - name: Extract branch name
        id: extract_branch
        run: |
          echo ${GITHUB_REF##*/}
          echo ::set-output name=BRANCH_NAME::${GITHUB_REF#refs/*/}

Using a JS script (see below)

      - name: Check if Branch is in PR
        if: github.event_name == 'push'
        id: checkbranch
        uses: actions/github-script@v4
        with:
          github-token: ${{ env.GITHUB_TOKEN }}
          script: |
            check_branch_pr = require('./.github/scripts/check_branch_pr.js')
            return await check_branch_pr({github, context, core})
        env:
          GITHUB_USERNAME: ${{ steps.secrets.outputs.GITHUB_USERNAME }}
          GITHUB_TOKEN: ${{ steps.secrets.outputs.GITHUB_TOKEN }}
          GITHUB_ACTOR: ${{ github.actor }}
          GITHUB_EVENT_NAME: ${{ github.event_name }}
          BRANCH_NAME: ${{ steps.extract_branch.outputs.BRANCH_NAME }}

js script (GET request on Github API to see if the branch pushed is in a PR already)

module.exports = async ({github, context, core}) => {
    console.log(process.env.BRANCH_NAME)
    const resp = await github.request('GET /repos/{owner}/{repo}/pulls', {
        owner: context.repo.owner,
        repo: context.repo.repo,
    });
    if (resp.data) {
        const pullRequestsWithBranch = resp.data.filter(it => it.head.ref == process.env.BRANCH_NAME)
        console.log(pullRequestsWithBranch)
        return pullRequestsWithBranch.length >= 1
    }
    return false
}

Set output for the js script

      - name: Setup Output for Branch in PR
        id: inprcheck
        run: |
          echo '::set-output name=BRANCH_IN_PR::${{ env.BRANCH_IN_PR }}'
        env:
          BRANCH_IN_PR: ${{ steps.checkbranch.outputs.result }}

Using this if statement, you can make it so on_push skips all of the steps and exists successfully, but if you push a branch before a PR, the workflow will run as normal. Let me know if you have questions.

- name: foo
   if: steps.inprcheck.outputs.BRANCH_IN_PR != 'true'