How CI Pipeline Scripts and Exit Codes Interact

Water running out of a pipe

What is An Exit Code?

An exit code, sometimes called a return code, is a number returned by a shell command or script. It tells the caller the status of the command execution. A successful command call usually returns an exit code of 0, whereas an exit code of any other value would indicate an error or failure.

How CI Pipeline Scripts Work

A CI Pipeline is typically defined using either YAML – a type of script widely adopted by CI tools – or raw shell scripts. The format used depends on the CI provider, but YAML is increasingly becoming the de facto standard for pipeline configurations. A YAML script can configure multiple jobs per pipeline, and each of these jobs can be set up to use a shell command, array of commands, or a script. 

Taking a GitLab CI pipeline for example – each job within a YAML file has a script section where a command, array of commands, or script can be added. These command(s) are run in a shell by the pipeline runner. If any command(s) return a non-zero exit code, the job fails and the rest of the commands or scripts in the job do not execute. Depending on the job’s configuration, it may also fail the entire pipeline. In GitLab CI you can set the “allow_failure” option as true in the corresponding job’s YAML file. Doing this is one way to prevent the entire pipeline from failing due to a single job failure. Generally, if a job fails for a valid reason, you probably want to fail the whole pipeline. 

Determine Pipeline Job Results With Exit Codes

You can decide to pass or fail a CI pipeline job from your own scripts by returning an exit code. For example, if you want to fail a job due to the absence of a directory or the number of high severity warnings from static code analysis, all you have to do is to return a non-zero exit code from a script. The following bash script will fail the pipeline job if the directory doesn’t exist, and continue on if it does.

#!/bin/bash

# If a directory exists
if [ -d "/path/to/dir" ]; then
	echo "Directory exists."
	exit 0
else
	echo "ERROR: Directory does not exist."
	exit 1
fi

The following Python script will fail the pipeline job if there is at least one high severity warning from a code analyzer that has been run.

#!/usr/bin/env python3

# If there is at least one high severity warning
if num_of_high_severity_warn > 1:
	print("Number of high severity warnings: {}".format(num_of_high_severity_warn))
	sys.exit(1)
else:
	print("No high severity warnings")
	sys.exit(0)

When a Non-Zero Exit Code Doesn’t Mean The Job Failed

There are times when scripts will return a non-zero value that shouldn’t cause your pipeline job(s) to fail. Let’s look at an example of this using a Powershell script on Windows which invokes Robocopy (a file copy utility).

If we observe the following code, it’s executing Robocopy with the /S option. The /S option tells Robocopy to copy all subdirectories, but exclude empty ones. This means /path/to/source will be copied to /path/to/destination with all the contents except for empty directories. For the sake of this example let’s assume that /path/to/source contains an empty directory.Robocopy is a unique command because non-zero return codes do not necessarily indicate a failure. If we look at the Robocopy documentation, we can see a return code of 1 means “All files were copied successfully”. In the example below, a return code of 3 comes back which indicates: “Some files were copied. Additional files were present. No failure occurred.” This is because in /path/to/source we had an empty directory and the /S option told Robocopy not to copy it. It’s not an error, but exactly what we told Robocopy to do.

# Powershell
PS> robocopy /MOVE /S /path/to/source /path/to/destination
PS> echo $?
False
PS> robocopy /MOVE /S /path/to/source /path/to/destination
PS> echo $lastexitcode
3

The Robocopy documentation link above states that any return codes from 0-7 aren’t considered a failure. If we don’t want the pipeline job to fail, we have to let it know that a return code from Robocopy in the 0-7 range is acceptable. 

To accomplish this, we need two commands in a single line so the pipeline job script sees it as one command. The first command will call Robocopy, and the second command will check whether or not the exit/return code is less than 8. If it’s < 8, the job script will return 0. If it’s >= 8, the job script will return the actual return code from Robocopy. One way to write this is shown in the following code snippet:

( robocopy <Source> <Destination> [<Options>]  );if ($lastexitcode -lt 8){write-host 0}else{write-host $lastexitcode};

The above code snippet is for Windows, but the same concept can be used on other platforms. In Linux, you can do a similar exit code capture to prevent a job from failing:

<command> <arg> || exit_code=$?; if [ $exit_code -ne 0 ]; then echo "The exit code of the previous command is $exit_code"; fi;

These methods aren’t just for jumping through hoops to work with unique commands. You may have a command that isn’t critical to the success of the pipeline job, or even have an intentionally incorrect command to test log output. With either of those scenarios you can use the methods presented in this blog to prevent a pipeline build from failing on non-zero return codes.

By understanding how exit codes and pipelines interact, you will now hopefully have more tools to customize your next CI pipeline!

Next Steps

If you are interested in bringing your project to the next level, let’s schedule a call with our team. Or, you can check out EmbedOps, our CI build platform.

We look forward to hearing from you! -D5

Discover why Dojo Five EmbedOps is the embedded enterprise choice for build tool and test management.

Sign up to receive a free account to the EmbedOps platform and start building with confidence..

  • Connect a repo
  • Use Dev Containers with your Continuous Integration (CI) provider
  • Analyze memory usage
  • Integrate and visualize static analysis results
  • Perform Hardware-in-the-Loop (HIL) tests
  • Install the Command Line Interface for a developer-friendly experience

Subscribe to our Monthly Newsletter

Subscribe to our monthly newsletter for development insights delivered straight to your inbox.

Interested in learning more?

Best-in-class embedded firmware content, resources and best practices

Laptop with some code on screen

I want to write my first embedded program. Where do I start?

The boom in the Internet of Things (IoT) commercial devices and hobbyist platforms like the Raspberry Pi and Arduino have created a lot of options, offering inexpensive platforms with easy to use development tools for creating embedded projects. You have a lot of options to choose from. An embedded development platform is typically a microcontroller chip mounted on a circuit board designed to show off its features. There are typically two types out there: there are inexpensive versions, sometimes called

Read More »
Medical device monitoring vitals

IEC-62304 Medical Device Software – Software Life Cycle Processes Primer – Part 1

IEC-62304 Software Lifecycle requires a lot of self-reflection to scrutinize and document your development processes. There is an endless pursuit of perfection when it comes to heavily regulated industries. How can you guarantee something will have zero defects? That’s a pretty hefty task. The regulatory approach for the medical device industry is process control. The concept essentially states that if you document how every step must be completed, and provide checks to show every step has been completed properly, you

Read More »
Operating room filled with medical devices

IEC-62304 Medical Device Software – Software Life Cycle Processes Primer – Part II

Part I provides some background to IEC-62304. Part II provides a slightly more in-depth look at some of the specifics. The IEC 62304 Medical Device Software – Software Lifecycle Processes looks into your development processes for creating and maintaining your software. The standard is available for purchase here. So what activities does the standard look at? Here are some of the major topics. For any given topic, there will be a lot more specifics. This will look at a few

Read More »