georgek24
georgek24

Reputation: 1139

Is there a way to continue on error while still getting correct feedback?

I'm trying to find a way to achieve the following functionality: Whenever a step fails, it will show that it failed (will provide correct feedback) but will still continue to other steps.

At the moment, failure causes the step to stop:

Step's failure prevents next step from starting

I've seen the most popular suggestion is to use continue-on-error, but that seems to make the step's conclusion 'Success', and will not show it failed unless you go into the logs.

Failed step appears to be successful

In the screenshot above, the "Secrets" step failed, and yet it appears to be successful unless entering the logs.

When reading this thread, I came to suspect this feature might not exist yet in GH actions.

I've also tried using conditionals for each step, and/or for the job. For example, I've tried: if: ${{ success() }} || ${{ failure() }} - this simply did not provide the needed functionality, the step failed and the next step did not start.

if: succeeded() || failed() - took this syntax from the GitHub community thread above, but it generated a syntax error (which makes sense, since it isn't compatible with the syntax specified here).

To conclude, I'm looking for a way to make steps that fail indicate they failed, and still make the workflow continue to the next step.

Upvotes: 74

Views: 64775

Answers (6)

Anon
Anon

Reputation: 7164

You can use if: {{ !cancelled() }} on the steps you want to continue to run and perhaps include an extra condition if there is something like an install step that is still critical e.g.:

jobs:
  lint-and-format:
    runs-on: ubuntu-24.04
    steps:
      - name: Install linting and formatting dependencies
        id: install
        run: |
          npm install eslint prettier
      - name: Lint JavaScript files
        if: ${{ !cancelled() && steps.install.conclusion == 'success' }}
        run: |
          npx eslint
      - name: Check JavaScript formatting
        if: ${{ !cancelled() && steps.install.conclusion == 'success' }}
        run: |
          npx prettier -c .

(As the runs.steps[*].if documentation warns the ! in !cancelled must be contained in {{}} / '' / "" / () because it's reserved YAML notation)

if: {{ !cancelled }} is preferable to

  • if: always() because if your task has a critical failure it can stop early rather than being forced to run until the timeout. The GitHub documentation for always() even recommends {{ !cancelled }}):

    Avoid using always for any task that could suffer from a critical failure, for example: getting sources, otherwise the workflow may hang until it times out. If you want to run a job or step regardless of its success or failure, use the recommended alternative: if: ${{ !cancelled() }}

  • continue-on-error within a step because if the step fails it will not cause the job to still be considered successful (see this comment about continue-on-error within a job vs within a step)
  • if: {{ success() || failure() }} because it means the same thing and is shorter

See the answers on How to run a github-actions step, even if the previous step fails, while still failing the job for further discussion of different approaches.

Upvotes: 0

Manuel Miglioranza
Manuel Miglioranza

Reputation: 11

I would suggest you take a look to this discussion solution. I was trying to do something similarly related: Running a step (discord notification) whenever the build fail AND avoiding the subsequent steps from running (failing the job altogether).

In my code workflow:

simple_deployment_pipeline:
  runs-on: ubuntu-20.04

  steps:
    ... some previous steps
    - name: build
      id: buildStep
      run: npm run build
    - name: Discord notification if build fails
      if: ${{ failure() && steps.buildStep.outcome == 'failure'}}
      ...do something

It is similar to your solution but missing the part where, besides checking the failure of the step until that point, it checks the step's outcome.

If the job fails, correct feedback is returned (check the image link)

Hope that helps!

Upvotes: 1

MS Berends
MS Berends

Reputation: 5229

The continue-on-error is indeed popular, and part of the reason is perhaps that you can set it for each step, or for the whole job. I’m not sure you are aware of this, and this solution was the answer I was looking for.

Example 1

This will continue all steps, if either fails:

Job:
  MyRun:
    runs-on: ubuntu-latest

    continue-on-error: true
    
    steps:
      - name: Step One
        run: curl --fail -o dates.csv https://doesnotexist.com/dates.csv
      - name: Step Two
        run: date >> dates.csv

Example 2

This will only continue if “Step One” fails, it will break and stop if e.g. “Step Two” fails:

Job:
  MyRun:
    runs-on: ubuntu-latest
    
    steps:
      - name: Step One
        run: curl --fail -o dates.csv https://doesnotexist.com/dates.csv
        continue-on-error: true
      - name: Step Two
        run: date >> dates.csv

NOTE: If you have a failing build step it depends on where you put continue-on-error whether you get a red or green icon for that failed job:

  • if you put continue-on-error: true in the job's metadata, you'll get a red icon (Example 1)
  • if you put it in one of the steps, you'll get a green icon (Example 2).

Upvotes: 35

hwjp
hwjp

Reputation: 16081

Just in case this helps someone else, and because I struggled to find the right answer to this myself.

It turns out the answer is sort of staring you in the face, but it's just confusing because of the way the documentation phrases things, I think.

But, in short, all you need to do is set

  strategy:
    fail-fast: false

at the top level. there is no need to set continue-on-error at each step, that's a distraction.

see this answer to another SO question: "Marking GitHub actions workflow as failed if a single job fails in matrix".

Upvotes: -2

Noach Magedman
Noach Magedman

Reputation: 2473

Just adding on to this old conversation:

  1. The behavior has changed. My job marked with continue-on-error: true does show a "failure" icon, not a faux "success" icon. The workflow continues on afterward.

  2. If a job punts to an external workflow via uses:, the continue-on-error must go in the jobs in the external file. It cannot go on the job in the main workflow. That is, the following gives a syntax error:

jobs:
  my_continue_on_error_job:
    uses: ./.github/workflows/other_workflow.yml
    continue-on-error: true

It fails with:

The workflow is not valid. .github/workflows/main_workflow.yml (Line: 201, Col: 5): Unexpected value 'continue-on-error'

It worked when I moved the continue-on-error into the jobs in the other_workflow.

Upvotes: 12

georgek24
georgek24

Reputation: 1139

To my surprise, and thanks to @smac89 for suggesting it - adding if: always() seems to do the trick. I'm still wondering why if: ${{ success() }} || ${{ failure() }} failed, but the solution seems to work for the moment. Thank you for the help!

Upvotes: 39

Related Questions