I'm attempting to save the result of a GitHub Actions job to be used by another job, like this:
jobs:
job1:
runs-on: ubuntu-latest
# Map a step output to a job output
outputs:
output1: ${{ steps.step1.outputs.test }}
steps:
- id: step1
run: |
./my-command
[ $? == 0 ] && result="Success" || result="Failure"
echo "result=$result" >> $GITHUB_OUTPUT
job2:
runs-on: ubuntu-latest
needs: job1
steps:
- run: echo ${{needs.job1.outputs.output1}}
The issue I'm having is this will never show "Failure" as the output, only ever "Success". If ./my-command has a non-zero exit code, it will show nothing.
Am I missing something?
run steps by default run with bash --noprofile --norc -eo pipefail {0} under the hood (see here); this includes the "error on exit" option, which makes the run step abort if ./my-command isn't successful. To avoid that, you can use a construct like
if ./my-command; then
result="Success"
else
result="Failure"
fi
echo "result=$result" >> "$GITHUB_OUTPUT"
in the run step. A non-zero exit status in a conditional does not trigger error-on-exit behaviour of the -e option.
In addition to what #benjamin-w wrote, you also have a variable named incorrectly in your YAML
jobs:
job1:
runs-on: ubuntu-latest
# Map a step output to a job output
outputs:
# The variable name of the output here needs to match what you write to $GITHUB_OUTPUT
output1: ${{ steps.step1.outputs.result }}
steps:
- id: step1
run: |
./my-command
[ $? == 0 ] && result="Success" || result="Failure"
echo "result=$result" >> $GITHUB_OUTPUT
job2:
runs-on: ubuntu-latest
needs: job1
steps:
- run: echo ${{needs.job1.outputs.output1}}
Notice that I changed steps.step1.outputs.test to steps.step1.outputs.result. You used the name result when you wrote the value out to $GITHUB_OUTPUT.
Related
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
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 }}"
I want to create conditional pr_number variable and after assign it to the Dsonar.pullrequest.key. This is how I am trying to do it, but it's not working: pr_number remaining undefined
name: SonarQube
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
sonarqube:
runs-on: ubuntu-latest
steps:
- name: Run sonarqube
run:
if [[ -z "${{github.event.number}}" ]]; then pr_number=${{ github.event.release }}; else pr_number=${{ github.event.number }}; fi |
sonar-scanner
-Dsonar.pullrequest.key=$pr_number
As a solution i added command arguments using inline if statement conditional and immediately concat it ,inside sonarqube.yml using bash script when github.event.number and pullrequest.base is null
- name: Run sonarqube
run:
sonar-scanner
-Dsonar.dependencyCheck.htmlReportPath=dependency-check-report.html
"-Dsonar.pullrequest.key=`if [ -z "${{github.event.number}}" ]; then echo ${{github.sha}}; else echo ${{github.event.number}}; fi`"
"-Dsonar.pullrequest.branch=`if [ -z "${{github.head_ref}}" ]; then echo ${{github.sha}}; else echo ${{github.head_ref}}; fi`"
-Dsonar.pullrequest.base=${GITHUB_BASE_REF#refs/heads/}
-Dsonar.pullrequest.github.repository=${GITHUB_REPOSITORY}
-Dsonar.pullrequest.github.endpoint=${GITHUB_API_URL}
Given this example .Net CI pipeline configuration for Gitlab
image: mcr.microsoft.com/dotnet/sdk:5.0
stages:
- build
build:
stage: build
script:
- |
BUILD_WARNINGS_LOG_FILE="buildWarnings.log";
dotnet build -consoleloggerparameters:"Summary;Verbosity=normal" -m -p:"WarnLevel=5;EnforceCodeStyleInBuild=true" -t:"clean,build" -fl1 "/flp1:logFile=$BUILD_WARNINGS_LOG_FILE;warningsonly";
if [ $? -eq 0 ] && [ $(wc -l < $BUILD_WARNINGS_LOG_FILE) -gt 0 ]
then
# do stuff here
fi
The pipeline fails because of invalid syntax. This is because it doesn't know there are multiple shell commands to execute.
This .sh file works on my local machine
#!/bin/bash
BUILD_WARNINGS_LOG_FILE="buildWarnings.log";
dotnet build --output build -consoleloggerparameters:"Summary;Verbosity=normal" -m -p:"WarnLevel=5;EnforceCodeStyleInBuild=true" -t:"clean,build" -fl1 "/flp1:logFile=$BUILD_WARNINGS_LOG_FILE;warningsonly";
if [ $? -eq 0 ] && [ $(wc -l < $BUILD_WARNINGS_LOG_FILE) -gt 0 ]
then
# do stuff here
fi
so how can I embed it into a Gitlab ci stage correctly?
There are three ways to do this:
Add your shell script into the image so you can execute it: (in your case that would require creating a new image based on mcr.microsoft.com/dotnet/sdk:5.0)
image: mcr.microsoft.com/dotnet/sdk:5.0
stages:
- build
build:
stage: build
script:
- bash /path/to/your/script.sh
Create the shell script at run-time (just echo the script into a shell file and make it executable)
image: mcr.microsoft.com/dotnet/sdk:5.0
stages:
- build
build:
stage: build
script:
- echo "BUILD_WARNINGS_LOG_FILE="buildWarnings.log";\ndotnet build\n-consoleloggerparameters:"Summary;Verbosity=normal" -m -p:"WarnLevel=5;EnforceCodeStyleInBuild=true" -t:"clean,build" -fl1 "/flp1:logFile=$BUILD_WARNINGS_LOG_FILE;warningsonly";\nif [ $? -eq 0 ] && [ $(wc -l < $BUILD_WARNINGS_LOG_FILE) -gt 0 ]\nthen\nfi" > my-bash.sh
- chmod +X my-bash.sh
- bash ./my-bash.sh
Use the proper syntax of multi-bash command. (note the dash after the pipe)
image: mcr.microsoft.com/dotnet/sdk:5.0
stages:
- build
build:
stage: build
script:
- |-
BUILD_WARNINGS_LOG_FILE="buildWarnings.log";
dotnet build -consoleloggerparameters:"Summary;Verbosity=normal" -m -p:"WarnLevel=5;EnforceCodeStyleInBuild=true" -t:"clean,build" -fl1 "/flp1:logFile=$BUILD_WARNINGS_LOG_FILE;warningsonly";
if [ $? -eq 0 ] && [ $(wc -l < $BUILD_WARNINGS_LOG_FILE) -gt 0 ]
then
# do stuff here
fi
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' }}