jobs.<job_id>.steps

10 minute read

A job contains a sequence of tasks called steps that are used to execute commands or actions. Steps execute in containers that share the same job workspace directory. Because step commands run in their own containers, any changes to environment variables or other local filesystem directories are not saved as the workflow runs from step to step.

Steps can run the following:

  • A custom command.

  • A CloudBees action in your repository or a public repository.

Usage example

apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: Greeting from the Hive on: push: branches: - '**' jobs: my-job: steps: - name: Print a greeting uses: docker://alpine:3.18 shell: sh env: FIRST_WORD: Hello SECOND_WORD: there THIRD_WORD: world run: | echo $FIRST_WORD $SECOND_WORD $THIRD_WORD

Step outputs

Steps can produce outputs that subsequent steps in the same job can consume. To consume an output from a step, you can use the outputs field in the corresponding step’s context as shown in the example below.

Usage example

apiVersion: automation.cloudbees.io/v1alpha1 kind: workflow name: Example on: [push] jobs: my-job: steps: - id: generator name: Generate random string uses: docker://alpine:3.23 run: | cat /dev/urandom | tr -dc A-Za-z0-9 | head -c10 > $CLOUDBEES_OUTPUTS/random-value - name: Print random string uses: docker://alpine:3.23 env: RAND_VALUE: ${{ steps.generator.outputs.random-value }} run: | echo "$RAND_VALUE"

In this example, the first step generates a random string and saves it to the $CLOUDBEES_OUTPUTS/random-value file. The value is then available to subsequent steps as an output using the syntax ${{ steps.<step_id>.outputs.<output_name> }}.

jobs.<job_id>.steps[*].id

Use jobs.<job_id>.steps[*].id to uniquely identify a step. You can use the id to reference the step in contexts.

Usage example

In the following example, id: step1 is used to reference My first step in my-output.

jobs: my-job-1: outputs: my-output: ${{ steps.step1.outputs.greeting }} steps: - name: My first step uses: docker://alpine:3.18 id: step1 run: echo "hello world" > "$CLOUDBEES_OUTPUTS/greeting" my-job-2: needs: my-job-1 steps: - uses: docker://alpine:3.18 env: OUTPUT1: ${{needs.my-job-1.outputs.my-output}} run: | echo "my-job-1 said: $OUTPUT1"

jobs.<job_id>.steps[*].continue-on-error

Use jobs.<job_id>.steps[*].continue-on-error to prevent a job from failing when a step fails. Set to true to allow a job to pass when this step fails.

When set to true, the step will be marked as failed, but the job will continue to execute subsequent steps. This is useful for steps that may fail but should not block the rest of the workflow, such as optional cleanup tasks or non-critical operations.

By default, a step failure causes the entire job to fail. Use continue-on-error: true to override this behavior for specific steps.

Usage example

In the following example, the "upstream job" step will fail (due to exit 1), but the job will continue executing any subsequent steps.

jobs: my-job: steps: - name: upstream job uses: docker://alpine shell: bash continue-on-error: true run: | exit 1 # or some other failure - name: This step will still run uses: docker://alpine run: echo "Previous step failed but we continue anyway"

Usage example: Optional cleanup

In the following example, a cleanup step is allowed to fail without affecting the job status.

jobs: build-and-cleanup: steps: - name: Build application uses: docker://maven:3.8 run: mvn clean package - name: Optional cleanup uses: docker://alpine continue-on-error: true run: | # Cleanup that might fail if resources don't exist rm -rf /tmp/build-cache

jobs.<job_id>.steps[*].env

Use jobs.<job_id>.steps[*].env to set environment variables for steps to use in the runner environment. You can also set variables for the entire workflow or a job.

When environment variables have matching names, the CloudBees Unify uses the most specific variable. For example, env defined in a step overrides job and workflow environment variables with the same name, while the step executes. env set in a job definition overrides a workflow variable with a matching name, while the job executes.

Public actions may specify expected variables in the README file.

Usage example

steps: - name: My first action env: GIT_TOKEN: ${{ cloudbees.scm.token }} FIRST_WORD: Hello LAST_WORD: world

jobs.<job_id>.steps[*].if

Use jobs.<job_id>.steps[*].if to require a condition be met before a step is run. You can use any supported context and expression to create a conditional.

When you use expressions in an if conditional, you may omit the ${{ }} expression syntax because CloudBees Unify automatically evaluates the if conditional as an expression. However, this rule does not apply everywhere.

You must use the ${{ }} expression syntax or escape with '', "", or () when the expression starts with !, since ! is reserved notation in YAML format.

Using the ${{ }} expression syntax turns the contents into a string, and strings are truthy. For example, if: true && ${{ false }} will evaluate to true.

Usage example: Contexts

A conditional at the job level cannot reference any step outputs. However, a conditional within a step can reference previous steps in the same job.

In the following example, the step only runs when the event type is a push and the branch is main.

steps: - name: My first step uses: docker://alpine:3.18 if: ${{ cloudbees.event_name == 'push' && cloudbees.scm.ref == 'refs/heads/main' }} run: echo This event is a push on the main branch.

In the following example, the first step is referenced in the second step.

jobs: my-job: steps: - name: First step id: detect_build_type uses: docker://alpine:3.19 run: | if [ '${{ cloudbees.scm.ref }}' = refs/heads/main ]; then echo true > $CLOUDBEES_OUTPUTS/is_release_build fi - name: Second step if: ${{ steps.detect_build_type.outputs.is_release_build }} uses: docker://alpine:3.19 run: | echo Publish a release...

Usage example: Status checks

Use an expression to check the status of a job step. The following expressions are supported:

success()

The default for any step is success(), which returns true if all previous job steps are successful.

failure()

failure() returns true if any previous job step fails. If a job is dependent on another job, a failure in the ancestor job also means failure() returns true.

In the following example, My backup step only runs when the previous step (My first step) of the job fails.

steps: - name: My first step uses: cloudbees/checkout@v1 - name: My backup step if: ${{ failure() }} uses: cloudbees/kaniko@v1
Because success() is automatically applied to all non-status check conditions, you must override this in any extra conditions you add to a step to run after a failure.

always()

Use always() to always execute a step, regardless of the status of any previous step.

CloudBees recommends that you only use always for steps that are not critical to the workflow run, for example, sending a log file or test results.

In the following example, Test results always runs.

steps: - name: Test results uses: https://github.com/cloudbees-io/publish-test-results@v1 if: ${{always()}} with: test-type: playwright folder-name: ${{ cloudbees.workspace }}/results.json

Usage example: Using secrets

You cannot directly reference a secret in jobs.<job_id>.steps[*].if. CloudBees recommends setting secrets as environment variables at the job level, then referencing these environment variables in the conditional, as in the following example:

name: Check secret set status on: push jobs: my-job: env: my_secret: ${{ secrets.my_secret }} steps: - uses: docker://alpine:3.18 if: ${{ env.my_secret != '' }} run: echo 'Step runs only if secret value is set.' - uses: docker://alpine:3.18 if: ${{ env.my_secret == '' }} run: echo 'Step runs only if secret value is not set.'

If a secret is not set, the return value of an expression referencing the secret (such as ${{ secrets.my_secret }} is an empty string.

jobs.<job_id>.steps[*].name

Use jobs.<job_id>.steps[*].name to define a name for your step that is displayed in the CloudBees Unify.

jobs.<job_id>.steps[*].run

Use jobs.<job_id>.steps[*].run to run command-line programs using the shell of the operating system.

run specifications:

  • By default, non-login shells are used to run commands. This affects the scripts run as the environment starts, including scripts that affect both path and environment variables.

  • The run command is executed into the container provided by the corresponding uses: statement for this step.

  • The step name defaults to text specified in the run command if a name is not set.

You can choose a different shell and customize the shell used to run commands, and you can use a run step to run a script.

If a step contains multi-line commands, each line runs in the same shell.

jobs.<job_id>.steps[*].shell

Use jobs.<job_id>.steps[*].shell to override the default shell settings in the runner’s operating system. You can use built-in shell keywords, or else define a custom set of shell options. The shell command that is run internally executes a temporary file that contains the commands specified in the run keyword.

Supported platform

shell parameter

Description

Command run internally

Linux

unspecified

Default shell on Linux platforms. Note this runs a different command versus when bash is explicitly specified. If bash is not found in the path, this is interpreted as sh.

bash -e {0}

All

bash

The default shell on non-Windows platforms. Falls back to sh.

bash --noprofile --norc -eo pipefail {0}

All

python

Runs the python command.

python {0}

Linux

sh

Fallback for Linux platforms if no shell is provided and bash is not found in the path.

sh -e {0}

Overriding the default shell on MacOS or Windows is not currently supported. Only Linux platforms are currently supported.

Usage example

In the following example, each step displays the path.

  • The first step runs using Bash.

  • The second step runs using the Python command.

steps: - name: Run using Bash uses: docker://bash:5 shell: bash run: echo $PATH - name: Run an inline Python script uses: docker://python:3 shell: python run: | import os print(os.environ['PATH'])

In the following example, both echo commands run in the same shell.

my-job: steps: - uses: docker://alpine:3.18 shell: bash run: | echo 'Hello' echo 'world'

Exit codes and error action preference

bash/sh is a default executed by CloudBees Unify-hosted runners and is provided for built-in shell keywords.

CloudBees recommends that you adhere to the following usage guidelines for the bash/sh default:

  • Fail-fast behavior is enforced using set -e for both sh and bash by default.

  • sh-like shells exit with the exit code of the last command executed in a script, which is also the default behavior for actions. The runner reports the status of the step as fail/succeed based on this exit code.

jobs.<job_id>.steps[*].uses

A single workflow job must contain only one of the following execution syntax terms:

Specify an action with uses

Use jobs.<job_id>.steps[*].uses to add an action to run in your step. For more information, refer to CloudBees actions.

When referring to a file within a repository, the repository clone URL and the repository-internal path must be separated by a double slash //. For example, When referring to a file within a repository, the repository clone URL and the repository-internal path must be separated by a double slash (//).

For example:

https://github.com/myorg/myrepo//path/to/my/file.yaml

However, for backward compatibility the double slash // can be omitted for GitHub, Bitbucket, and Bitbucket Datacenter URLs.

You can use an action defined in the same repository as the workflow, a public repository, or in a published Docker container image.

CloudBees strongly recommends that you specify the version with any action you use, to avoid any broken workflows or other unexpected behavior. The version may be in the form of a Git tag, branch name, or commit SHA.

Some actions require inputs that you must set using the with keyword. This information is detailed in the action documentation. Actions are Docker containers, (not Dockerfile actions), and you must run the job in a Linux environment.

Usage example

{owner}/{repo}@{ref}

In the following example:

  • The First step uses an action from a public repository with the commit SHA specified.

  • The Second step uses a CloudBees Unify action with the major version tag specified.

  • The Third step uses the main branch of a subdirectory in a public GitHub repository.

steps: - name: First step uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 - name: Second step uses: https://github.com/cloudbees-io/kaniko@v1 - name: Third step uses: actions/aws/ec2@main

If the action is in the same repository as the workflow, you must first check out your repository, as in the following example:

jobs: my-job: steps: - name: Check out repository uses: https://github.com/cloudbees-io/checkout@v1 - name: Use local action uses: ./.github/actions/my-action

Action contexts

CloudBees Unify allows you to invoke actions defined in a repository using their relative path (for example, uses: ./local-action), instead of fetching them from a dedicated repository (such as https://github.com/cloudbees-io/checkout).

Local scan action
Figure 1. Scan 2, highlighted, uses a local child action.

In your workflows, you can also use composite actions that invoke other actions, either from a dedicated GitHub repository or a local relative path.

Composite action
Figure 2. A composite action referring to a different repository is highlighted.

If a composite action refers to another action in the same repository by using a local action link (uses: ./local-action), then the workflow that uses this action fails, unless it also resides in the same repository.

All local references, whether from the workflow or any action it uses, are resolved to the repository where the workflow resides.

Specify a Docker image with uses

Use jobs.<job_id>.steps[*].uses to add a Docker image published on Docker Hub to your step.

docker://{image}:{tag}

In the case of a hosted image:

docker://{host}/{image}:{tag}

Usage example

In the following example:

  • The First step specifies the alpine:3.8 image.

  • The Second step specifies a public Docker image in the Google Container Registry at gcr.io.

jobs: my-job: steps: - name: First step uses: docker://alpine:3.8 - name: Second step uses: docker://gcr.io/cloud-builders/gradle

Specify a private image with uses

Use jobs.<job_id>.steps[*].uses to add an image stored in a private repository to run in your step. To learn more about using private images, refer to:

Usage example

In the following example, an image stored in Amazon Web Services (AWS) Elastic Container Registry (ECR) is added to the step.

jobs: my-job: permissions: scm-token-own: read id-token: write steps: - name: Checkout - uses: https://github.com/cloudbees-io/checkout@v1 - name: Log in to AWS uses: https://github.com/cloudbees-io/configure-aws-credentials@v1 id: aws-login with: aws-region: us-east-1 role-to-assume: {{vars.MY_AWS_ROLE }} role-duration-seconds: "3600" - name: Run AWS CLI commands uses: docker://<AWS_ACCOUNT_ID>.dkr.ecr.<REGION>/<REPOSITORY>:<TAG> #Replace `<AWS_ACCOUNT_ID>` with your AWS account ID and `<REGION>` with your selected AWS region to specify your AWS ECR hostname. run: | aws sts get-caller-identity

jobs.<job_id>.steps[*].with

Use jobs.<job_id>.steps[*].with to pass key/value pair inputs to an action. The with keyword is also used in the context of a command step, when the uses value is a container image, rather than an action.

with specifications:

  • Each input parameter is a key/value pair.

  • Each variable is converted to upper case.

Non-alphanumeric characters are converted to _ (underscore).

When the uses value is a Docker image (docker://), you can use with to map input parameters that are set as environment variables, for example:

  • with.env

  • with.entrypoint

  • with.args

Usage example: actions

In the following example, the job step configures an action, https://github.com/cloudbees-io/jenkins-run-job@v2. The with keyword passes in action inputs.

jobs: my-jenkins-job: steps: - name: My Jenkins job uses: https://github.com/cloudbees-io/jenkins-run-job@v2 with: username: ${{ secrets.JENKINS_USERNAME }} token: ${{ secrets.JENKINS_TOKEN }} url: ${{ vars.JENKINS_URL }} job-name: my_jenkins_job parameters: '{"ENV_NAME":"DEV"}'

jobs.<job_id>.steps[*].with.args

Use jobs.<job_id>.steps[*].with.args to define the inputs for a step that executes a Docker container.

args specifications:

  • Enter the args parameter as Unicode strings.

  • An array of strings is not supported.

  • Any argument containing spaces must be surrounded by double quotes ("").

  • CloudBees passes all args to the ENTRYPOINT of the Docker container when the container starts up.

The main purpose of CMD in a Dockerfile is to provide defaults for an executing container. Instead, you can provide this instruction by defining args in your job.

If you do use CMD in your Dockerfile, you must use one of the following workflow guidelines:

  1. (Recommended) delete any required arguments from CMD.

  2. Define appropriate defaults so that you can run without specifying any arguments.

  3. If a --help flag, or something similar is available, use that as the default for documentation.

Usage example

In the following example, the step documentation is entered as args.

steps: - name: Document the trigger event for this step uses: docker://alpine:3.18 with: entrypoint: /bin/echo args: This step is triggered by ${{ cloudbees.event_name }}.

jobs.<job_id>.steps[*].with.entrypoint

Use jobs.<job_id>.steps[*].with.entrypoint to set the container entrypoint which will override the ENTRYPOINT specified within the container image.

CloudBees Unify has the following key differences from GitHub Actions (GHA):

  • If your container does not contain a shell, you must specify an entrypoint. Otherwise, your job will fail.

  • CloudBees allows shell and run with uses: docker://, unlike GHA, which forces use of the entrypoint.

Unlike Docker ENTRYPOINT, CloudBees Unify entrypoint instruction accepts only a single string defining the executable to be run.

In the following example, shell is not specified, and entrypoint is defined.

steps: - name: Run a command uses: docker://alpine:3.18 with: entrypoint: /bin/echo run:

jobs.<job_id>.steps[*].timeout-minutes

Use jobs.<job_id>.steps[*].timeout-minutes to set the maximum number of minutes to run a step before ending the process.