GitLab CI rules:if match condition which fails in Bash - bash

In .gitlab-ci.yml we do have a rule matching a value with regex match. Why is rule-regex job running with if: $EMPTY_VAR =~ "some-value" when Bash tells this condition is false?
If I set EMPTY_VAR: 'x' no job runs.
variables:
EMPTY_VAR: ''
default:
image: container-registry.ubs.net/alpine:3.12.3
# Runs against expectation and prints 'expected false'
rule-regex:
rules:
- if: $EMPTY_VAR =~ "some-value"
script:
- echo "Job has run with EMPTY_VAR='${EMPTY_VAR}'"
- |
if [[ $EMPTY_VAR =~ "some-value" ]]; then
echo "expected true"
else
echo "expected false"
fi
# Does not run as expected
rule-equal:
rules:
- if: $EMPTY_VAR == "some-value"
script:
- echo "Job has run with EMPTY_VAR='${EMPTY_VAR}'"

You can try this
- if: $EMPTY_VAR =~ /^some-value.*/
https://docs.gitlab.com/ee/ci/jobs/job_control.html#compare-a-variable-to-a-regex-pattern

Related

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

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

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

How does the keyword “if” test if a value is true or false?

In bash script
if [ 1 ]
then
echo "Yes"
else
echo "No"
fi
Output: Yes
It represents that '1' is treated as true value.
But in code:
word = Linux
letter = nuxi
if echo "$word" | grep -q "$letter"
then
echo "Yes"
else
echo "No"
fi
Output: No
But echo "$word" | grep -q "$letter" will return 1, so why is the result is No.
How does the keyword if test the value returned by the command after if?
The return value of a command is checked. [ 1 ] has a return value of 0 (true). Any other return value (like 1) indicates an error.
You can display the return value of the last executed command using the $? variable:
true
echo $?
# returned 0
false
echo $?
# returned 1
echo $?
# returned 0 as the last executed command is 'echo', and not 'false'
In unix land, 0 is true and 1 is false.
For your first example:
if [ 1 ]
then
echo "Yes"
else
echo "No"
fi
"If" checks the exit code of the given command for true/false (i.e. zero/non-zero).
The square brackets actually invoke the "test" command (see "man test" for more information) and give the exit code to if.
"test 1" (or indeed "test any_string") returns true (0) so "Yes" is output.
For your second example, this outputs "No" because "nuxi" isn't found in "Linux", if you change "nuxi" to "nux" (perhaps this was a typo?) and remove the spaces around the = then you will get the behaviour you expect. e.g.
word=Linux
letter=nux
if echo "$word" | grep -q "$letter"
then
echo "Yes"
else
echo "No"
fi
This is because the grep failed to find the $letter in $word, hence the exit code is 1. Whenever a process in linux return a code other than 0 then it means it failed. 0 means exited successfully. You can verify this by echo "Linux" | grep -d "nuxi"; echo $?
On the other hand in scripting world 0 means false and 1 mean true. So the grep failed to find the word and send 1 as an exit code to if, which took it as a true value.

Resources