73

When I push my commits to a PR my tests are triggered for this commit. After that, if I push additional commits to this PR, tests in Github Actions runs on both commits.

I need to cancel the previous run and run only on the most recent pushed commit.

How can I configure my yaml file to achieve that?

Max
  • 875
  • 1
  • 6
  • 9

4 Answers4

99

To cancel a currently running workflow from the same PR, branch or tag when a new workflow is triggered you can use:

name: Workflow X

on: # ...

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: true

jobs: # ...

Explanation:

  • ${{ github.workflow }}: the workflow name is used to generate the concurrency group (e.g. Workflow 1). That allows you to have more than one different workflow (eg. workflow1.yaml and workflow2.yaml) triggered on the same event (e.g. a PR). Otherwise, workflow1.yaml would cancel workflow2.yaml or viceversa.
  • ${{ github.event.pull_request.number: when the trigger event is a PR, it will use the PR number to generate the concurrency group (e.g. workflow1-33).
  • || github.ref }}: when the trigger is not a PR but a push, it will use the branch or tag name to generate the concurrency group (e.g. workflow1-branch1).
  • cancel-in-progress: true: if cancel-in-progress is omitted or set to false, when a new workflow is triggered, the currently running workflow won't be cancelled, and the new workflow will be queued pending until the previous one is done.

Ref: https://docs.github.com/en/actions/using-jobs/using-concurrency

David Miguel
  • 12,154
  • 3
  • 66
  • 68
  • 1
    This is a great explanation, especially because adding `${{ github.workflow}}` is important when there are different workflows to consider. Since `github.ref` for pull requests it is `refs/pull//merge` it is also sufficiently unique. It seems that a slightly simpler group is as effective: `group: ${{ github.workflow }}-${{ github.ref }}` I'm therefore curious why it was necessary for the specific logic to handle `event.pull_request.number` in your answer. – BjornO Mar 02 '23 at 07:33
  • 3
    I would also consider changing `${{ github.ref }}` to `${{ github.ref || github.run_id }}` as mentioned in the GitHub docs on concurrency. This allows for arbitrary events that might not have a ref, such as `workflow_dispatch`, to run the workflow in their own group. So in summary from both of my comments: `group: ${{ github.workflow }}-${{ github.ref || github.run_id }}` – BjornO Mar 02 '23 at 07:40
85

You can use Concurrency:

Concurrency ensures that only a single job or workflow using the same concurrency group will run at a time.

concurrency: 
  group: ${{ github.head_ref }}
  cancel-in-progress: true
EmmanuelMess
  • 1,025
  • 2
  • 17
  • 31
  • Works really well. Note: `concurrency` is "currently in beta and subject to change" as of 05/10/2021. – vhiairrassary Oct 05 '21 at 09:47
  • 3
    what level is this at? Every example just shows it all alone... not in a full file or example. Thank you. – naspinski Oct 07 '21 at 21:07
  • 9
    @naspinski Working example https://github.com/TeamAmaze/AmazeFileManager/blob/release/3.7/.github/workflows/android-feature.yml – EmmanuelMess Oct 08 '21 at 19:14
  • @EmmanuelMess In your answer you say `github.head_ref` whereas in your repo reference you have just `github.ref` just as in https://github.community/t/stop-running-actions-when-new-commit-pushed-to-branch/16455/5. Which one is better? – Csaba Toth Jan 23 '22 at 08:18
  • 3
    @CsabaToth `github.ref` see [here](https://docs.github.com/es/actions/learn-github-actions/contexts#github-context). `head_ref` "is only available when the event that triggers a workflow run is either pull_request or pull_request_target." but `ref` is just "The branch or tag ref that triggered the workflow run." – EmmanuelMess Jan 23 '22 at 21:07
8

You will also want to make sure you account for having multiple workflows in the same repository so that you only cancel in-progress runs of the same workflow.

Snippet from the Using concurrency GitHub docs:

concurrency: 
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Full, working example (Source):

name: CI with in-progress cancellations

on:
  pull_request:
    branches: [ main ]
  workflow_dispatch:

# This is what will cancel the workflow
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Print concurrency group
        run: echo '${{ github.workflow }}-${{ github.ref }}'
      - name: Sleep 15s
        run: sleep 15s
Philip
  • 126
  • 1
  • 4
4

Update

This is no longer valid, GitHub Actions has improved this experience since. Check out the alternative answer first.

Old answer

Like we had "There's an app for that!", now it's "There's an Action for that!":

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.8.0
        with:
          access_token: ${{ github.token }}
      #- name: Run Tests
      #  uses: actions/setup-node@v1
      #  run: node test.js
      # ... etc

https://github.com/styfle/cancel-workflow-action

jessehouwing
  • 106,458
  • 22
  • 256
  • 341