How to run GitHub workflow on every commit of a push

Viewed 3804

I have some tests that I would like to run on every commit of my repository. I have the following script in my repo:

name: CI

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - run: echo "my tests"

Unfortunately, if I push some new commits to my repository, the tests are only run against the latest commit. Is there a way to test all commits?

2 Answers

It is possible to to this by checking out individual commits and building each one in a single run: step.

In order to do this, the fetch-depth option for the checkout action needs to be 0 to checkout the full git tree.

I did something like this using GitPython to iterate and checkout each commit.

Using just the git command line tool, the rev-list command could be used to create a list of commits.

The tricky part is figuring out the commit range. For pull requests, GitHub actions provides github.head_ref and github.base_ref properties (docs) that could be used to create the commit range. However, these properties are not available for other events, like push (in that case, github.ref could be used with a fixed branch name like origin/main).

Here is a simple example. It may need more a advanced query for rev-list to handle cases where base_ref is not an ancestor of head_ref, but I will leave that for other SO questions to answer.

name: CI

on: [pull_request]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
        with:
          fetch-depth: 0
      - run: |
          for commit in $(git rev-list ${{ github.base_ref }}..${{ github.head_ref }}); do
              git checkout $commit
              echo "run test"
          done

Building on David Lechner's answer:

name: CI

on: 
  push:
    # only trigger on branches, not on tags
    branches: '**'

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
        with:
          # checkout full tree
          fetch-depth: 0
      - run: |
          for commit in $(git rev-list ${{ github.event.before}}..${{ github.sha}}); do
              git checkout $commit
              echo "run test"
          done

As per the docs on the github context and the docs on the push webhook event data {{github.event.before}} is replaced by the commit sha before the push. {{github.sha}} or {{github.event.after}} is replaced by the sha of the latest commit that was pushed:

Push event payload (for a pushed tag; docs)

{
  "ref": "refs/tags/simple-tag",
  "before": "6113728f27ae82c7b1a177c8d03f9e96e0adf246",
  "after": "0000000000000000000000000000000000000000",
  "created": false,
  "deleted": true,
  "forced": false,
  "base_ref": null,
  "compare": "https://github.com/Codertocat/Hello-World/compare/6113728f27ae...000000000000",
  "commits": [],
  "head_commit": null,
 [...]
}
Related