Environment Variables

Environment variables can be used during the build process. Cloud Native Build provides some default environment variables.

# Declaring Environment Variables

  1. Declare environment variables using env.
  2. Environment variables declared in the Pipeline are valid for the current Pipeline.
  3. Environment variables declared in the Job are valid for the current Job.
main:
  push:
    - env:
        RELEASE_MSG: release message
      stages:
        - name: release
          type: git:release
          env:
            RELEASE_NAME: release-1
          options:
            description: ${RELEASE_MSG}
            name: ${RELEASE_NAME}

# Importing Environment Variables

  1. Use imports to point to a private Git repository file to inject private tokens into environment variables.
  2. When there is a conflict between env and imports keys, env takes priority.
main:
  push:
    - services:
        - docker
      imports: https://xxx/envs.yml
      stages:
        - name: docker info
          script: docker info
        - name: docker login
          script: docker login ${imageDomain} -u $TEST_DOCKER_USER -p $TEST_DOCKER_PWD

Example content of https://xxx/envs.yml:

TEST_DOCKER_USER: your_docker_username
TEST_DOCKER_PWD: your_docker_password
TEST_NPM_USER: your_npm_username
TEST_NPM_PWD: your_npm_password

whatever_key_you_want: whatever_value_you_want

# Variable Name Restrictions

In Shell, there are certain restrictions on naming environment variables. According to the POSIX standard, environment variable names should adhere to the following rules:

  1. They can only contain letters (both uppercase and lowercase), numbers, and underscores (_).
  2. The first character cannot be a number.

Variables that do not comply with the above rules will be ignored.

# Exporting Environment Variables

After the execution of a Job, there is a result object. You can use exports to export the properties from the result object to environment variables, which will be available during the current Pipeline lifecycle.

The syntax format is as follows:

exports:
  from-key: to-key
  • from-key is the property name from the Job result object that you want to export. It supports environment variables and deep value retrieval, referring to lodash.get.
  • to-key is the variable name to be mapped to the environment variable.

There are three ways to set the result:

  • Script task execution result

  • Custom variables from the parsed output

  • Result from built-in tasks

# Script Task Execution Result

After the execution of a custom script task, the Job result object has the following properties:

  • code: Return code
  • stdout: Standard output
  • stderr: Standard error
  • info: Mixed output of standard output and standard error in chronological order

Note: You can use printf "%s" "hello\nworld" to output variables, which eliminates the trailing newline character in the standard output stream while preserving escape characters like \n.

main:
  push:
    - stages:
        - name: set env
          script: echo -n $(date "+%Y-%m-%d %H:%M")
          exports:
            code: CUSTOM_ENV_DATE_CODE
            info: CUSTOM_ENV_DATE_INFO
        - name: echo env
          script:
            - echo $CUSTOM_ENV_DATE_CODE
            - echo $CUSTOM_ENV_DATE_INFO

When using conditional logic such as if, ifModify, ifNewBranch, the following properties can be set:

  • skip: If the task execution is skipped based on the above conditional logic, it returns the reason for skipping; otherwise, it is an empty string.
- name: use if
  if: exit -1
  exports:
    skip: REASON
- name: tell last
  # The value of $REASON is the string "if"
  script: echo $REASON

# Parsing Custom Variables from Output

CI will recognize the content in the standard output stream in the format ##[set-output key=value] and automatically place it into the result object.

If the variable value contains newline characters \n, you can encode the variable value using base64 or escape.

If the variable value starts with base64,, the Cloud Native Build will decode the content after base64, using base64 decoding. Otherwise, it will decode the variable value using unescape.

Here is an example using Node.js:

// test.js
const value = '测试字符串\ntest string';
// Output the base64 encoded variable value
console.log(`##[set-output redline_msg_base64=base64,${Buffer.from(value, 'utf-8').toString('base64')}]`);

// Output the escape encoded variable value
console.log(`##[set-output redline_msg_escape=${escape(value)}]`)
main:
  push:
    - docker:
        image: node:20-alpine
      stages:
        - name: set output env
          script: node test.js
          # Export the variables output from test.js as environment variables
          exports:
            redline_msg_base64: BASE64_KEY
            redline_msg_escape: ESCAPE_KEY
        - name: echo env
          script:
            - echo "BASE64_KEY $BASE64_KEY"
            - echo "ESCAPE_KEY $ESCAPE_KEY"

Here is an example using echo:

main:
  push:
    - stages:
        - name: set output env
          script: echo "##[set-output redline_msg_base64=base64,$(echo -e "测试字符串\ntest string" | base64 -w 0)]"
          exports:
            redline_msg_base64: BASE64_KEY
        - name: echo env
          script:
            - echo -e "BASE64_KEY $BASE64_KEY"

Note: In Unix-like systems, the base64 command by default adds a newline character after every 76 characters. You can use the -w 0 option to disable the newline character to ensure that the CI can parse the variable on a single line.

For variable values that do not contain \n, you can directly output them:

echo "##[set-output redline_msg=some value]"

TIP

Due to the length limitation of system environment variables, very large variable values may not work.

CI will ignore variable values that are greater than or equal to 100KB. You can write them to a file and parse them manually.

For sensitive information, it is recommended to use the built-in task read-file.

# Exporting Environment Variables in Built-in Tasks

Some built-in tasks have output results that can be exported as environment variables using exports.

main:
  push:
    - stages:
        - name: xxxx
          type: xxx:xxx
          options:
            product: public
            name: cnb
            dist: release/
          exports:
            version: CUSTOM_ENV_VERSION
            url: CUSTOM_ENV_URL
            # Supports deep value retrieval
            nextRelease.gitTag: CUSTOM_ENV_GIT_TAG
        - name: echo env
          script:
            - echo $CUSTOM_ENV_VERSION
            - echo $CUSTOM_ENV_URL

Please refer to the documentation of each built-in task for the content of the result.

# Modifying Environment Variables

You can override existing environment variables by setting them to an empty string or null to delete them.

main:
  push:
    - env:
        CUSTOM_ENV_DATE_INFO: default
        CUSTOM_ENV_FOR_DELETE: default
      stages:
        - name: set env
          script: echo -n $(date "+%Y-%m-%d %H:%M")
          exports:
            # Add
            code: CUSTOM_ENV_DATE_CODE
            # Modify
            info: CUSTOM_ENV_DATE_INFO
            # Delete
            CUSTOM_ENV_FOR_DELETE: null
            # Delete
            # CUSTOM_ENV_FOR_DELETE:
        - name: echo env
          script:
            - echo $CUSTOM_ENV_DATE_CODE
            - echo $CUSTOM_ENV_DATE_INFO
            - echo $CUSTOM_ENV_DATE_STDOUT
            - echo $CUSTOM_ENV_FOR_DELETE
            - echo $CUSTOM_ENV_GIT_TAG

# Using Environment Variables

# Using in Script Tasks

When executing script tasks, the environment variables set in the pipeline are available as environment variables during task execution.

main:
  push:
    - stages:
        - name: test internal env
          # CNB_BRANCH is a built-in environment variable
          script: echo $CNB_BRANCH
        - name: test self defined env
          env:
            cat_name: tomcat
          script: echo $cat_name

# Variable Substitution

Some property values in the configuration file undergo variable substitution.

If there is an environment variable env_name=env_value, then the property value $env_name will be replaced with env_value. If env_name has no value, it will be replaced with an empty string.

The following properties undergo variable substitution:

  • Built-in tasks

The property values within the options of built-in tasks and optionsFrom undergo variable substitution.

# options.yml
name: Nightly
main:
  push:
    - env:
        address: options.yml
        description: publish for xx task
      stages:
        - name: git release
          type: git:release
          # $address will be replaced with "options.yml" from the env
          optionsFrom: $address
          # The name in options.yml will be merged into options
          options:
            # $description will be replaced with "publish for xx task" from the env
            description: $description

The final content of options will be:

name: Nightly
description: publish for xx task
  • Plugin Tasks

The property values within the settings of plugin tasks and settingsFrom undergo variable substitution.

# settings.yml
robot: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx
main:
  push:
    - env:
        address: settings.yml
        message: pr check
      stages:
        - name: notify
          image: tencentcom/wecom-message
          # $address will be replaced with "settings.yml" from the env
          settingsFrom: $address
          # The robot in settings.yml will be merged into settings
          settings:
            # $message will be replaced with "pr check" from the env
            content: $message

The final content of settings will be:

robot: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx
message: pr check

Additionally, the settingsFrom specified in the Dockerfile's LABEL can also undergo variable substitution.

FROM node:20

LABEL cnb.cool/settings-from="$address"
  • env

The property values declared under env can reference variables from the upper-level env for substitution.

# .cnb.yml
main:
  push:
    - env:
        cat_name: tomcat
      stages:
        - name: echo env
          env:
            # Use the cat_name value declared in the upper-level env for substitution
            name: "cat $cat_name"
            # Output: cat tomcat
            script: echo $name
  • imports

The property values within imports and the declared property values in the imported files undergo variable substitution.

If imports is an array, the variables declared in the files before an array element are valid for the subsequent elements.

# env1.yml
address: env2.yml
platform: amd64
# env2.yml
# Read the platform property value from env1.yml for substitution
action: build for $platform
# .cnb.yml
main:
  push:
    - imports:
        - env1.yml
        # $address will be replaced with env2.yml from env1.yml
        - $address
      stages:
        - name: echo action
          # Read the action property value from env2.yml for substitution
          script: echo $action
  • pipeline.runner.tags
# Building images for different architectures
.build: &build
  runner:
    tags: cnb:arch:$CNB_PIPELINE_NAME
  services:
    - docker
  stages:
    - name: docker build
      script: echo "docker build for $CNB_PIPELINE_NAME"
main:
  push:
    # "amd64" and "arm64:v8" will be declared as the values of the built-in environment variable CNB_PIPELINE_NAME
    amd64: *build
    "arm64:v8": *build
  • pipeline.docker.volumes
.docker-volume: &docker-volume
  docker:
    image: node:22-alpine
    volumes:
      - $volume_path
main:
  push:
    install:
      env:
        volume_path: node_modules
      <<: *docker-volume
      stages:
        - name: install
          script: npm install axios
        # Notify other pipelines to execute
        - name: resolve
          type: cnb:resolve
          options:
            key: install
    build:
      env:
        volume_path: node_modules
      <<: *docker-volume
      stages:
        # Wait for the install pipeline
        - name: await
          type: cnb:await
          options:
            key: install
        - name: ls
          script: ls node_modules
  • ifModify
# Only compile the corresponding module if there are changes in the code
.build: &build
  ifModify: $CNB_PIPELINE_NAME/*
  stages:
    - name: build $CNB_PIPELINE_NAME
      script: echo "build $CNB_PIPELINE_NAME"
main:
  push:
    module-a: *build
    module-b: *build
  • name

The property values of pipeline.name and job.name undergo variable substitution.

Unlike other property values, the name property can only undergo variable substitution using built-in environment variables.

main:
  push:
    - name: build in $CNB_REPO_SLUG
      env:
        platform: amd64
      imports:
        - env1.yml
        - env2.yml
      stages:
        - name: echo
          script: echo "hello world"
  • lock.key
# env.yml
build_key: build key
.build: &build
  imports: env.yml
  lock:
    key: $build_key
  stages:
    - name: echo
      script: echo "hello world"
main:
  push:
    # One of the following two pipelines will acquire the lock and execute successfully, while the other will fail to acquire the lock and not execute.
    - *build
    - *build
  • allowFailure
main:
  push:
    - env:
        allow_fail: true
      stages:
        - name: echo
          allowFailure: $allow_fail
          # The script execution will throw an error, but since allowFailure is true, the task will be considered successful
          script: echo1 1

# Preventing Variable Substitution

If you don't want $env_name to be replaced, you can use \$ to prevent substitution.

main:
  push:
    - stages:
        - name: git release
          type: git:release
          options:
            name: Development
            # The property value is "some code update $description"
            description: some code update \$description