GitHub Actions Job is skipped although all needs succeeded - github

We do have a problem with a GitHub Actions job which is always skipped although all "needed" jobs did run successfully. That's the job:
deploy-api:
needs: [build-test-api, terraform-apply, set-deployment-env]
uses: ./.github/workflows/workflow-api-deploy.yml
To verify that all needs did pass, I have added another job for debugging and printed the result of the needed jobs.
debug-deploy-api:
runs-on: ubuntu-latest
needs: [build-test-api, terraform-apply, set-deployment-env]
if: always() # Had to add this, otherwise it would be skipped just as "deploy-api".
steps:
- run: |
echo "Result of build-test-api: ${{ needs.build-test-api.result }}"
echo "Result of terraform-apply: ${{ needs.terraform-apply.result }}"
echo "Result of set-deployment-env: ${{ needs.set-deployment-env.result }}"
The output is
Result of build-test-api: success
Result of terraform-apply: success
Result of set-deployment-env: success
I don't understand why deploy-api is skipped.
Job began to be skipped after this change
The behavior started after adding a dependency to build-test-api:
With this version of build-test-api, the deploy job did run just fine:
build-test-api:
uses: # reusable WF from internal repo
needs: set-deployment-env
After changing it into
build-test-api:
uses: # reusable WF from internal repo
needs: [set-deployment-env, auto-versioning]
if: |
always() &&
(needs.set-deployment-env.result == 'success') &&
(needs.auto-versioning.result == 'success' || needs.auto-versioning.result == 'skipped')
deploy-api has been skipped always. But build-test-api is, despite that change, still running fine and even appends the created artifact to the workflow run.
Activating runner and step debug logging did not reveal any insights on why the job is still skipped. Any ideas?

Meanwhile I did contact the GitHub Premium Support and they provided a solution:
deploy-api:
if: success('build-test-api') # This line is required, if any of the previous job did not end with status 'success'.
needs: build-test-api
uses: ./.github/workflows/48-reusable-workflow-2.yml
I think I also know why: The documentation says:
You can use the following status check functions as expressions in if conditionals. A default status check of success() is applied unless you include one of these functions.
And definition of success() is as follows:
Returns true when none of the previous steps have failed or been canceled.
The only issue I think is, that is should be:
Returns true when none of the previous steps have failed, canceled or skipped.

Related

how to avoid the duplicated job in github action like build image?

there are some workflows in a repository and triggered by push behavior.(sometime a push will trigger 2 workflows, sometimes will trigger 5 workflows)
but there are some common works like 'build docker image' for integration test.
I'm not sure how to avoid the duplicated work, I tried reuable workflow, but that will build the image 2 or more times。
is there anyway like the needs keywork to make all triggered action depends on the same job result ?
Check if your use case is similar to "Avoid re-running wokflow for the same commit", with Samuel Ryan's workaround:
Yes, you can use the Check Runs API to identify Workflow runs for a ref.
As luck would have it, someone has already built a comprehensive Action for this use-case: fkirc/skip-duplicate-actions.
Add a new “pre” job to your Workflow, this job uses fkirc/skip-duplicate-actions to determine if your main job should be skipped
Add a condition to your main job using the should_skip output of the “pre” job.
For example, adapted from the fkirc/skip-duplicate-actions README:
jobs:
pre_job:
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
- id: skip_check
uses: fkirc/skip-duplicate-actions#v3.4.0
with:
skip_after_successful_duplicate: 'true'
main_job:
needs: pre_job
if: ${{ needs.pre_job.outputs.should_skip != 'true' }}
runs-on: ubuntu-latest
steps:
- run: echo "Running slow tests..." && sleep 30

How to run a script at the end of a job, even if the job was cancelled?

This is the workflow I'm currently using on GitHub
name: Windows10 - CI
on: [ push ]
jobs:
run-test:
runs-on: [ self-hosted, windows, DO ]
steps:
- uses: actions/checkout#v2
with:
clean: false
- name: Run nds2 CI - Sanity Test
if: github.ref == 'refs/heads/master'
run: cd c:\actions-runner\_work\nds2\nds2 ; python3 ci_host.py --master
- name: Run nds2 CI - Build Installer
if: github.ref != 'refs/heads/master'
run: cd c:\actions-runner\_work\nds2\nds2 ; python3 ci_host.py
I have a windows 10 computer which listens for an incoming job by using the GitHub runner.
Upon an incoming job, if a push is being made to the master branch the script ci_host.py is being run with the '--master' flag which spins up a VM and runs multiple tests on it. Eventually, at the end of the tests, the script restores the VM to a pre-configured snap shot.
So basically what I'm trying to achieve is, when the job is being canceled through the GitHub actions web interface then the script which handles the test is being canceled mid-job and doesn't have the chance to restore the VM to its prior clean state (snapshot).
How can I run a script which will be run at the end of the workflow even if the job was canceled?
So no matter what happens my VM could be restored to its clean state
Thanks in advance for your help :)
You can use Job status check functions to execute a step depending on what happened on the job before it (or not):
success: Returns true when none of the previous steps have failed or been canceled.
always: Always returns true, even when canceled. A job or step will not run when a critical failure prevents the task from running. For example, if getting sources failed.
cancelled: Returns true if the workflow was canceled.
failure: Returns true when any previous step of a job fails.
Example of use:
steps:
...
- name: Execute if the job succeeded
if: ${{ success() }}
- name: Execute if the job failed
if: ${{ failure() }}
- name: Execute if the job was cancelled
if: ${{ cancelled() }}
- name: Always execute
if: ${{ always() }}

Github Actions Job being skipped

Using Github Actions for some CI/CD.
Currently I am experiencing strange behavior where my jobs are being skipped despite the conditions being met. deploy-api has two conditions, if code was pushed to master and test-api was a success. But even though we are meeting those conditions, it is still being skipped.
jobs:
test-api:
name: Run tests on API
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v1
- name: Get dependencies
run: npm install
working-directory: ./api
- name: Run tests
run: npm run test
working-directory: ./api
deploy-api:
needs: test-api # other job must finish
if: github.ref == 'refs/heads/master' && needs.test-api.status == 'success' #only run if it's a commit to master AND previous success
As seen in the picture the second job is being skipped despite the push being on the master branch (as seen on the top) AND the previous job being successful.
Am I missing something in the code? Does anyone know of a workaround that can be used?
It would be nice if the UI told the user why it was skipped!
Use needs.test-api.result == 'success' (there is no .status) in the if expression.
See https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#needs-context.

How to run a github-actions step, even if the previous step fails, while still failing the job

I'm trying to follow an example Github has for testing my build with github actions, and then compressing the test results and uploading them as an artifact.
https://help.github.com/en/actions/automating-your-workflow-with-github-actions/persisting-workflow-data-using-artifacts#uploading-build-and-test-artifacts
I'm having trouble with what to do when my tests fail though. This is my action. When my tests pass everything works great, my results are zipped an exported as an artifact, but if my tests fail, it stops the rest of the steps in the job, so my results never get published.
I tried adding the continue-on-error: true https://help.github.com/en/actions/automating-your-workflow-with-github-actions/workflow-syntax-for-github-actions#jobsjob_idstepscontinue-on-error
This makes it continue after it fails and uploads my test results. but then the job is marked as passed, even though my test step failed. Is there some way to have it upload my artifact even if a step fails, while still marking the overall job as failed?
name: CI
on:
pull_request:
branches:
- master
push:
branches:
- master
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v1
- name: Test App
run: ./gradlew test
- name: Archive Rest Results
uses: actions/upload-artifact#v1
with:
name: test-results
path: app/build/reports/tests
You can add
if: always()
to your step to have it run even if a previous step fails
https://docs.github.com/en/actions/learn-github-actions/expressions#status-check-functions
so for a single step it would look like this:
steps:
- name: Build App
run: ./build.sh
- name: Archive Test Results
if: always()
uses: actions/upload-artifact#v1
with:
name: test-results
path: app/build
Or you can add it to a job:
jobs:
job1:
job2:
needs: job1
job3:
if: always()
needs: [job1, job2]
Additionally, as pointed out below, putting always() will cause the function to run even if the build is canceled.
If dont want the function to run when you manually cancel a job, you can instead put:
if: success() || failure()
Other way, you can add continue-on-error: true.
Look like
- name: Job fail
continue-on-error: true
run |
exit 1
- name: Next job
run |
echo Hello
Read more in here.
run a github-actions step, even if the previous step fails
If you only need to execute the step if it succeeds or fails, then:
steps:
- name: Build App
run: ./build.sh
- name: Archive Test Results
if: success() || failure()
uses: actions/upload-artifact#v1
with:
name: test-results
path: app/build
Why use success() || failure() instead of always()?
Reading the Status check functions documentation on Github:
always
Causes the step to always execute, and returns true, even when canceled. A job or step will not run when a critical failure prevents the task from running. For example, if getting sources failed.
Which means the job will run even when it gets cancelled, if that's what you want, then go ahead. Otherwise, success() || failure() would be more suitable.
Note -
The documentation made clear thanks to Vladimir Panteleev in which he submitted the following PR: Github Docs PR #8411
Addon: if you have following sitution. 2 steps i.e. build > deploy and in some cases i.e. workflow_dispatch with input parameters you might want to skip build and proceed with deploy. At the same time you might want deploy to be skipped, when build failed.
Logically that would be something like skipped or not failed as deploy conditional.
if: always() will not work, cause it will always trigger deploy, even if build failed.
Solution is pretty simple:
if: ${{ !failure() }}
Mind that you cannot skip brackets when negating in if:, cause it reports syntax error.
The other answers here are great and work, but you might want a little more granularity.
For instance, ./upload only if ./test ran, even if it failed.
However, if something else failed and prevented the tests from running, don't upload.
# ... Other steps
- run: ./test
id: test
- run: ./upload
if: success() || steps.test.conclusion == 'failure'
steps.*.conclusion will be success, failure, cancelled, or skipped.
success or failure indicate the step ran. cancelled or skipped means it didn't.
Note there is an important caveat that you must test at least one success() or failure() in if.
if: steps.test.conclusion == 'success' || steps.test.conclusion == 'failure' won't work as expected.
you can add || true to your command.
example:
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v1
- name: Test App
run: ./gradlew test || true

How to fail a job in Github Actions?

I'm developing a Github actions workflow. This workflow runs on Linux, Mac, and Windows.
As part of the workflow, I have to check whether 2 environment variables are equal. If they don't - fail the job.
As described here, Github Actions support if: condition:
steps:
- run: # How can I make a cross-platform failure here?
if: ${{ envA }} != ${{ envB }}
How can I make the job fail if the above condition is true?
In the beginning, I thought of a script, but there must be a more elegant way to fail a job.
I'd do run: exit 1. That will simply exit with an exit code of 1, on all three platforms.
Proof that it's cross-platform: https://github.com/rmunn/Testing/runs/220188838 which runs the following workflow:
name: Test exiting on failure
on: [push]
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macOS-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout#v1
- name: Try to fail
run: exit 1
- name: Print message if we don't fail
run: echo Should not get here
(An earlier version of this answer recommended "/bin/false", but that would only work on Linux and macOS).
In 2021, there is perhaps a more graceful way to do this:
- name: A/B Check
if: ${{ envA }} != ${{ envB }}
uses: actions/github-script#v3
with:
script: |
core.setFailed('envA and envB are not equivalent!')
Here, we use the github-script action to provide a one liner script that will fail the job. The "A/B Check" step will only run if the condition in the if line is true, so the script will only run in that case, which is what we want.
The nice thing about this approach is that you will get nicely formatted output in the Actions UI in your repo, showing that the "A/B Check" step caused the failure, and why (i.e. "envA and envB are not equivalent").
Note that if you have additional steps in the job after this, and you do NOT want them to run if the A/B check fails, you'll want to use if: success() on them to prevent them from running in that case.
The Github workflow commands docs gives a hint on this.
Toolkit function
Equivalent workflow command
core.setFailed
Used as a shortcut for ::error and exit 1
Given that, you can do the following without using any external workflows.
steps:
- name: A/B Check
if: ${{ envA }} != ${{ envB }}
run: |
echo "::error file={name},line={line},endLine={endLine},title={title}::{message}"
exit 1