Runner: Job-level "if" condition not evaluated correctly if job in "needs" property is skipped

Created on 20 May 2020  路  4Comments  路  Source: actions/runner

Describe the bug
If a job needs a prior job which has been skipped, the if condition for the dependent job may behave unexpectedly under some conditions. (unsure if condition is evaluated incorrectly or if it's not evaluated at all)

To Reproduce
Steps to reproduce the behavior:
Given a workflow such as this (where job_b needs to complete or be skipped before subsequent jobs run, but subsequent jobs don't depend upon any outputs from job_b)

on: push

jobs:
  job_a:
    runs-on: ubuntu-latest
    outputs:
      truthy_string: ${{ steps.a.outputs.always }}
      null_value: ${{ steps.b.outputs.never }}
    steps:
      - id: a
        run: echo "::set-output name=always::something"
      - id: b
        run: echo "We opt not to set any output at this time"
  job_b:
    runs-on: ubuntu-latest
    needs: job_a
    if: needs.job_a.outputs.null_value
    steps:
      - run: echo "We've ensured this job will be skipped"
  job_c:
    runs-on: ubuntu-latest
    needs: [job_a, job_b]
    if: needs.job_a.outputs.truthy_string
    steps:
      - run: echo "This won't run, even though the IF condition evaluates true."
  job_d:
    runs-on: ubuntu-latest
    needs: [job_a, job_b]
    if: always() && needs.job_a.outputs.truthy_string
    steps:
      - run: echo "This will run, even though we've only changed the condition from `true` to `true && true`"

Examining the output of this workflow, job_a will always run, job_b will always be skipped, job_c will always be skipped, and job_d will run.
The only difference between job_c and job_d is the addition of always() && to the if condition.

Expected behavior
If a job-level conditional evaluates to true, the job should run after all needs'd jobs have completed or been skipped.
_Both_ job_c and job_d should run. The always() && should not be required for job_c, since it doesn't change the ultimate true/false result of the conditional.

OR documentation should be updated to indicate this is expected behavior. The current docks simply says of job needs (emphasis added):

Identifies any jobs that must complete successfully before this job will run. It can be a string or array of strings. If a job fails, all jobs that need it are skipped unless the jobs use a conditional statement that causes the job to continue.
This is relatively ambiguous, but the plain interpretation is that a conditional statement that evaluates to true should cause the job to continue. As seen with job_c in the sample, that isn't always the case.

Runner Version and Platform

Version of your runner?
Unsure - I'm only running via Github Actions, not using a self-hosted runner.

OS of the machine running the runner? OSX/Windows/Linux/...
Linux: ubuntu-latest

What's not working?

Jobs are being skipped even when the job-level conditional evaluates to true.

Job Log Output

Because the job is skipped entirely, there is no job-level output, even if the appropriate DEBUG secret is set.

Runner and Worker's Diagnostic Logs

As above, there is no additional output, even if the appropriate DEBUG secret is set.

Service Bug

Most helpful comment

Stumbled upon this while looking for a different issue, but thought I'd throw in my perspective on the matter.

needs implies a dependent relationship, so if one of the jobs doesn't run (job_b in this case) then logically it seems to me the dependent job should not run either by default, lest there be something missing from job_b's non-existent run. The fact that you can override this behavior by adding the always() check seems like a good feature; you have to explicitly say "This job does not directly depend on the previous job(s) success". I would not call the reported behavior a problem.

The only thing I think would be appropriate and appreciated is a note about this functionality in the docs for the workflow job if field (ie here) noting the relationship between if and needs, and potentially also with the job status check functions. There's a lot of areas in the docs I find where connections like this are completely overlooked, which makes it hard to get up to speed.

All 4 comments

I think this meant to be a feature actually, but it's too subtle in my opinion - it left me scratching my head for an hour over why my workflow step wasn't running. From https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#job-status-check-functions:

If your if expression does not contain any of the status functions it will automatically result with success().

I would have expected the default value of if: success() to be replaced with if: <your_condition>, but it's actually if: success() && <your_condition> _unless_ your condition includes one of the status functions

This problem is still present on version 2.273.1

Stumbled upon this while looking for a different issue, but thought I'd throw in my perspective on the matter.

needs implies a dependent relationship, so if one of the jobs doesn't run (job_b in this case) then logically it seems to me the dependent job should not run either by default, lest there be something missing from job_b's non-existent run. The fact that you can override this behavior by adding the always() check seems like a good feature; you have to explicitly say "This job does not directly depend on the previous job(s) success". I would not call the reported behavior a problem.

The only thing I think would be appropriate and appreciated is a note about this functionality in the docs for the workflow job if field (ie here) noting the relationship between if and needs, and potentially also with the job status check functions. There's a lot of areas in the docs I find where connections like this are completely overlooked, which makes it hard to get up to speed.

Bah, and of course 5 minutes after I file the bug I see my PEBCAK -- I forgot the .outputs in the condition. works fine now, sorry for the noise! Please close this (I'm not allowed to do that myself)

Was this page helpful?
0 / 5 - 0 ratings