GitHub action fails with exit code 1 when echo statement is removed - bash

I have a GitHub action step which looks like this:
- if: ${{ steps.cache-images.outputs.cache-hit == 'true' }}
name: Load saved docker images
run: |
if [[ -f docker-images-backup/apisix-images.tar ]]; then
[[ ${{ steps.test_env.outputs.type }} != first ]] && sudo ./ci/init-${{ steps.test_env.outputs.type }}-test-service.sh before
docker load --input docker-images-backup/apisix-images.tar
make ci-env-up project_compose_ci=ci/pod/docker-compose.${{ steps.test_env.outputs.type }}.yml
echo "loaded docker images"
echo test_type:
[[ ${{ steps.test_env.outputs.type }} != first ]] && sudo ./ci/init-${{ steps.test_env.outputs.type }}-test-service.sh after && echo "executed"
fi
echo "exited if"
Which fails consistently (with exit code 1) if I remove the last echo statement that I added to debug the same error.
I have tried running the script locally with different combinations of values of the variables in this script but it works perfectly fine.
I have studied a little about segfaults in c programming occcuring when removing/adding a print statement. I don't think this is a similar case but I wonder if shell scripting has similar kind of hazard.

So it turns out that GitHub actions evaluate the exit code when a step is executed completely. When the value of ${{ steps.test_env.outputs.type }} is first then [[ ${{ steps.test_env.outputs.type }} != first ]] results in a non-zero exit code (exit code 1). Due to this, the GitHub action fails with exit code 1.
When I add an echo statement at the end, the last exit code becomes zero (and the non-zero exit status due to the previous statement gets covered) due to this the action does not fail.
To fix this you can simply implement conditional execution of code using an if statement:
Replace:
[[ ${{ steps.test_env.outputs.type }} != first ]] && sudo ./ci/init-${{ steps.test_env.outputs.type }}-test-service.sh after && echo "executed"
With:
if [[ ${{ steps.test_env.outputs.type }} != first ]]; then
sudo ./ci/init-${{ steps.test_env.outputs.type }}-test-service.sh after
fi

Related

Skipping subsequent Github Actions

I have a set of GitHub actions jobs that I want to run conditionally on the results of a web lookup. However, I don't want these jobs to fail, I just don't want them to run.
What I'm currently kicking around is this but it doesn't seem to work as intended:
jobs:
check-previous-build:
uses: ./.github/workflows/check-previous-build.yml
bundle-install:
uses: ./.github/workflows/deps-bundle-install.yml
secrets: inherit
needs:
- check-previous-build
if: ${{ needs.check_previous_build.outputs.previous_build == 'true'}}
and the check job looks like this (a 200 means that the job should skip):
- name: Check Previous Build
id: check_previous_build
run: |
sha=$(git show HEAD --format=%T -q)
url=http://example.com/check?id=${sha}
echo "Checking $url .."
status_code=$(curl -s -o /dev/null -w "%{http_code}" $url)
if [[ $status_code -ge 200 && $status_code -le 299 ]]; then
echo -e "\x1B[32m✅ Build has previously completed \x1B[0m"
echo "previous_build=true" >> $GITHUB_OUTPUT
fi
echo "previous_build=false" >> $GITHUB_OUTPUT
I feel there is something missing around the needs.check_previous_build.outputs.previous_build logic but can't figure out what's up here.
You're missing some additional code that's required for getting the outputs of a step as the outputs of a job. Based on Example: Defining outputs for a job, your check-previous-build.yml should look like this:
name: Check Previous Build
on:
workflow_call:
outputs:
previous_build:
description: "Status of the job"
value: ${{ jobs.check-previous-build.outputs.previous_build }}
jobs:
check-previous-build:
runs-on: ubuntu-latest
outputs:
previous_build: ${{ steps.check-previous-build.outputs.previous_build }}
steps:
- name: Check Previous Build
id: check-previous-build
run: |
url=http://example.com/
status_code=$(curl -s -o /dev/null -w "%{http_code}" $url)
if [[ $status_code -ge 200 && $status_code -le 299 ]]; then
echo -e "\x1B[32m✅ Build has previously completed \x1B[0m"
echo "previous_build=success" >> $GITHUB_OUTPUT
else
echo "previous_build=fail" >> $GITHUB_OUTPUT
fi
The outputs object on the job needs to be explicitly defined and tied to the output(s) of a step in that job. Re-usable workflows require a similar treatment, but with the outputs object in the workflow_call block. See Using outputs from a reusable workflow.
This is more minor, but your conditional statement also needed an else block, otherwise the previous_build variable was always going to be false (or fail, in this case).
You had the right idea in the main workflow. I omitted the ${{ }} syntax in the if conditional, which is automatically evaluated as an expression (About expressions).
main-workflow.yml:
name: Ed's Workflow
on:
push:
workflow_dispatch:
jobs:
check-previous-build:
uses: ./.github/workflows/check-previous-build.yml
bundle-install:
needs: check-previous-build
runs-on: ubuntu-latest
if: needs.check-previous-build.outputs.previous_build == 'success'
steps:
- name: deps-bundle-install
run: echo "${{ needs.check-previous-build.outputs.previous_build }}"

How to execute an if condition within a yaml in gitlab ci

I have a script and a condition where the branch name changes based on which branch you're using.
test:ui:with_sauce:
...
script:
- export MASTER_URL=https://masterurlexample.io
- export TEST_PREVIEW_APP=$CI_COMMIT_REF_SLUG
- cd $MAVEN_DIRECTORY
- if [ "$CI_COMMIT_BRANCH" == "master" || "$EMULATE_BRANCH" == "master" ]; then
export TEST_PREVIEW_APP=$MASTER_URL;
fi;
- echo "Testing on $TEST_PREVIEW_APP"
- echo "starting test"
- sleep 30
- mvn -U $MAVEN_CLI_OPTS ...
When this job runs I don't believe the condition doesn't execute.
/bin/bash: line 210: [: missing `]'
/bin/bash: line 210: : command not found
Not sure if it's looking for specific quotes around the variables.
Either write the expression in a single line or use a multiline string - |.
There's also a small issue with your bash (you need [[ and ]])
- |
if [[ "$CI_COMMIT_BRANCH" == "master" || "$EMULATE_BRANCH" == "master" ]]; then
export TEST_PREVIEW_APP=$MASTER_URL
fi
- ...
or
- if [[ "$CI_COMMIT_BRANCH" == "master" || "$EMULATE_BRANCH" == "master" ]]; then export TEST_PREVIEW_APP=$MASTER_URL; fi

How can I easily make Github Actions skip subsequent jobs execution on certain condition?

I have an YAML Github Action script, which consists on three jobs. The nightly script should check if there are any user commits (which are not coming from an automatic jobs) and then perform nightly release build and deploy the build to the testing environment.
I am struggling with using one single point where I could skip the execution of the whole second and the third jobs if there are no recent commits in the repositories others than autocommits.
As far as I understand, I should either fail the script to skip any further actions or set the if condition to every step of every job I have, which doesn't look concise.
I tried to put the if condition on the job itself, but it does not work. The job executes even if the if condition value is false. Is there any better or elegant solution other to fail the job if repository is stale?
name: Nightly script
on:
workflow_dispatch:
schedule:
- cron: "0 1 * * *"
jobs:
check-if-there-are-commits:
runs-on: ubuntu-latest
outputs:
alive: ${{ steps.check.outputs.alive }}
steps:
### Activity check
### uses GitHub API to check last non-automagic commit in repository
### if it's older than a week, all other steps are skipped
- name: Activity check
id: "check"
run: |
curl -sL -H "Authorization: bearer ${{secrets.REPO_BEARER_TOKEN}}" https://api.github.com/repos/$GITHUB_REPOSITORY/commits?sha=dev | jq -r '[.[] | select(.author.type != "Bot")][0]' > $HOME/commit.json
echo $GITHUB_REPOSITORY
echo $HOME
echo $(cat $HOME/commit.json)
date="$(jq -r '.commit.author.date' $HOME/commit.json)"
echo "Date: $date"
timestamp=$(date --utc -d "$date" +%s)
echo "Timestamp: $timestamp"
echo "Current date: $(date --utc +%s)"
echo "Difference between the current date and time of the last commit: $(( ( $(date --utc +%s) - $timestamp ) ))"
days=$(( ( $(date --utc +%s) - $timestamp ) / 86400 ))
echo "Days: $days"
alive=0
echo "Date: $date"
echo "timestamp: $timestamp"
echo "days: $days"
if [ $days -lt 1 ]; then
echo Repository active : $days days
alive=1
else
echo "[WARNING] Repository not updated : event<${{ github.event_name }}> not allowed to modify stale repository"
fi
echo "Alive? $alive"
if [ $alive -eq 1 ]; then
echo "REPO_ALIVE=true" >> $GITHUB_ENV
echo "::set-output name=alive::true"
else
echo "REPO_ALIVE=false" >> $GITHUB_ENV
echo "::set-output name=alive::false"
fi
echo "REPO_ACTIVITY=$days" >> $
echo "::set-output name=days::$days"
release:
needs: check-if-there-are-commits
if: ${{needs.check-if-there-are-commits.outputs.alive}} == 'true'
runs-on: ubuntu-latest
steps:
- name: "Verify"
run: |
echo "Alive? ${{needs.check-if-there-are-commits.outputs.alive}}"
alive=${{needs.check-if-there-are-commits.outputs.alive}}
if [ $alive == "true" ]; then
echo "Alive"
else
echo "Dead"
exit 1;
fi
- name: Next step
if: ${{needs.check-if-there-are-commits.outputs.alive}} == 'true'
run: |
...
#- other steps...
deploy:
needs: [check-if-there-are-commits, release]
if: ${{needs.check-if-there-are-commits.outputs.alive}} == 'true'
runs-on: ubuntu-latest
steps:
#- other steps
According to the documentation:
When you use expressions in an if conditional, you may omit the
expression syntax (${{ }}) because GitHub automatically evaluates the
if conditional as an expression, unless the expression contains any
operators. If the expression contains any operators, the expression
must be contained within ${{ }} to explicitly mark it for evaluation.
That means your if must be defined as:
if: ${{ needs.check-if-there-are-commits.outputs.alive == 'true' }}

Running script conditionally does not work in travis.yml, why?

The following causes travis to not build at all. When I try to validate the travis.yml file, it complains that the line just above the if statement is missing a - character at column 3, but the error has to do with the if statement below.
Do I have to move the if statement out to a script?
# Deploy
after_success:
- ./tools/docker-push-container.sh
- if [ $TRAVIS_BRANCH == "master" && $TRAVIS_PULL_REQUEST == "false" ]; then
./.travis/success_message.sh
fi
You're making some assumptions about YAML syntax that are causing you problems. If you "exend" a line of YAML by indenting subsequent lines, like this:
- The quick brown fox
jumped over the
lazy dog.
It is exactly like you instead wrote this:
- The quick brown fox jumped over the lazy dog.
This means that your shell fragment, which you've written as:
- if [ $TRAVIS_BRANCH == "master" && $TRAVIS_PULL_REQUEST == "false" ]; then
./.travis/success_message.sh
fi
Actually becomes:
if [ $TRAVIS_BRANCH == "master" && $TRAVIS_PULL_REQUEST == "false" ]; then ./.travis/success_message.sh fi
And if you try run that line in the shell, you get:
sh: -c: line 1: syntax error: unexpected end of file
If you want to include a multi-line shell script in your YAML document, your best bet is probably to use the verbatim block operator, |, like this:
- |
if [ $TRAVIS_BRANCH == "master" && $TRAVIS_PULL_REQUEST == "false" ]; then
./.travis/success_message.sh
fi
Which will result, as intended, in:
if [ $TRAVIS_BRANCH == "master" && $TRAVIS_PULL_REQUEST == "false" ]; then
./.travis/success_message.sh
fi
Alternatively, you could just make proper use of semicolons:
- if [ $TRAVIS_BRANCH == "master" && $TRAVIS_PULL_REQUEST == "false" ]; then
./.travis/success_message.sh;
fi
Note the new ; before the terminal fi. This results in:
if [ $TRAVIS_BRANCH == "master" && $TRAVIS_PULL_REQUEST == "false" ]; then ./.travis/success_message.sh; fi
...which is perfectly valid shell syntax.
I tried the above solution by larsks but, it did not work for me and could be because in bash when you use && and || you need to separate the conditions.
I had the following (according to above solution)
- if [ $TRAVIS_PULL_REQUEST == false && $TRAVIS_BRANCH == "development" ]; then
echo "# Bump version and flyway migrate db";
else
echo "Skip version increment!";
fi
And I also checked the .travis.yml in Travis Lint and it shows as valid but, according to bash the above can be separated as shown below, also mentioned in this SO Question and I changed to following
- if [ $TRAVIS_PULL_REQUEST == false ] && [ $TRAVIS_BRANCH == "development" ]; then
echo "# Bump version and flyway migrate db";
else
echo "Skip version increment!";
fi
And travis build worked. It kind of make sense because above is valid way to use && and || in bash. Just sharing in case someone else come across a similar issue.
Raf's solution worked for me with separating the conditionals with []
[ $TRAVIS_PULL_REQUEST == false ] && [ $TRAVIS_BRANCH == "develop" ];
None of the suggested workarounds worked for me, I used this instead:
- if [[ ( "$TRAVIS_OS_NAME" == "osx" ) && ( "$TOXENV" == "py36" ) ]]; then brew update; fi

Bash ternary operation gives same output on either Boolean condition

So in my bash script, I output status report to terminal as well as write it to the log file. I wanted to use a bash ternary operator that will output to terminal as well as write a log file if variable LOG_TO_TERMINAL is true, and if that is set to false, just write to a log file without outputting status to the terminal.
My sample code looks like this:
[[ $LOG_TO_TERMINAL ]] && echo "error message" >> $LOG_FILE || echo "error message" | tee -a $LOG_FILE
which just logs the file instead of echoing to the terminal no matter whether I set LOG_TO_TERMINAL to true or false.
To isolate the problem, I tried simplifying the code to:
[[ $LOG_TO_TERMINAL ]] && echo "log to terminal" || echo "don't log to terminal"
But this code snippet also echoes "log to terminal" no matter what its value is.
The test [[ $LOG_TO_TERMINAL ]] tests whether LOG_TO_TERMINAL has a value or not. Nothing else. The shell doesn't treat false (or 0 or null etc.) as special false-y values.
If you want some other test you need to test specifically for that.
[[ $LOG_TO_TERMINAL = true ]]
or
[[ $LOG_TO_TERMINAL != false ]]
or
[[ $LOG_TO_TERMINAL = 1 ]]
etc.
If you were expecting to use the return code from the true and/or false commands then you need $LOG_TO_TERMINAL && Y || Z or similar to run the command stored in the variable (though I wouldn't recommend this version of this test).
Also note that X && Y || Z is not a ternary operation in the shell. See the Shellcheck wiki for warning SC2015 for more about this.
You want this:
[[ $LOG_TO_TERMINAL = 1 ]] && echo "log to terminal" || echo "don't log to terminal"

Resources