Alex
—Jan 07, 2023
I use GitHub Actions quite a lot, for both personal and professional projects, and I often have multiple separate checks that I want to happen, such as linting, testing, building, and benchmarking.
One way to do this is to create separate jobs on GitHub Actions, which will all run in parallel, and one failing won't affect the rest. However, I typically have a bit of common setup code for the separate jobs, and I don't really need them to happen in parallel, so I usually just have them as sequencial steps in the same job.
But what if one of these steps fails? What if my first step (e.g. a lint) fails? Then the rest of the steps won't be run. So I fix the lint, and then the build fails, and the whole CI has to run again. Wouldn't it be handy if all the steps would run, even if a previous step failed?
A quick Google search will bring up this StackOverflow post, which give the headline to add if: always()
to your steps, meaning a step will always run even if a previous step fails
I have been blindly doing this for several months.
Entirely separately (to the point that I didn't put it together until I was cancelling 20 or so jobs in a row) I started noticing that jobs wouldn't cancel properly.
Scrolling down to the bottom of that StackOverflow answer I then found
Additionally, as pointed out below, putting always() will cause the function to run even if the build is canceled.
To achieve my desired behaviour (running a step on previous step failure, but still respecting suite cancellation) you should add if: success() || failure()
to a step.
I think my use-case is quite common, as discussed in this GitHub community discussion, so I thought I would make a post to highlight this.
I'm not sure why always()
is still at the top of that accepted answer (with 256 upvotes) on StackOverflow!
Here is a quick example of a full Rust CI suite in GitHub actions
name: Lint, build, & teston: [ push ]concurrency:# Cancel if I push to the same branchcancel-in-progress: true# Don't cancel on mastergroup: ${{ github.ref == 'refs/heads/master' && format('ci-master-{0}', github.sha) || format('ci-{0}', github.ref) }}env:CARGO_TERM_COLOR: alwaysjobs:lint-build-and-test:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v3- name: Install rustrun: rustup toolchain install stable --profile minimal- name: Cache cargo dependenciesuses: Swatinem/rust-cache@v2- name: Lintrun: cargo clippy --all-targets --all-features -- -D warnings- name: Buildif: ${{ success() || failure() }}run: cargo build- name: Testif: ${{ success() || failure() }}run: cargo test