---
url: /en/build/env.md
---
`Cloud Native Build` not only supports declaring the build environment but also allows defining environment variables used within the build environment.

`Cloud Native Build` comes with some [default environment variables](./build-in-env.md) for direct use.

## Feature Overview

Environment variables support the following features:

| Feature | Description |
|---------|-------------|
| [Declare Environment Variables](#declaring-environment-variables) | Declare environment variables via `env` |
| [Import Environment Variables](#importing-environment-variables) | Import environment variables from files via `imports` |
| [Export Environment Variables](#exporting-environment-variables) | Export task results as environment variables |
| [Use Environment Variables](#using-environment-variables) | Use environment variables in scripts and configuration |

## Declaring Environment Variables

Declare environment variables via `env`:

* Environment variables declared in `Pipeline` are effective for the entire pipeline
* Environment variables declared in `Job` are only effective for the current task

```yaml title=".cnb.yml"
main:
  push:
    - docker:
        # Declare build environment
        image: node:22
      # Environment variables declared at the pipeline level are available to all tasks in the pipeline
      env:
        PIPELINE_ENV_1: pipeline environment variable 1
        PIPELINE_ENV_2: pipeline environment variable 2
      stages:
        - name: Output build environment information
          script: node -v
        - name: Output pipeline environment variables
          script:
            - echo $PIPELINE_ENV_1
            - echo $PIPELINE_ENV_2
        - name: Output job environment variables
          # Environment variables declared in the job are only effective for the current task
          env:
            JOB_ENV: job environment variable
          script: echo $JOB_ENV
```

## Importing Environment Variables

Use `imports` to import a secrets file, injecting sensitive information into environment variables for subsequent tasks to use.

### Priority Rules

When there's a conflict between `env` and `imports` keys, `env` takes precedence.

### Example

```yaml title=".cnb.yml"
main:
  push:
    - services:
        - docker
      # Import secrets file as environment variables
      imports: https://cnb.share.ralphlauren.cn/<your-repo-slug>/-/blob/main/xxx/envs.yml
      stages:
        - name: docker info
          script: docker info
        - name: docker login
          # Where TEST_DOCKER_DOMAIN, TEST_DOCKER_USER, TEST_DOCKER_PWD are variables from the secrets file
          script: docker login $TEST_DOCKER_DOMAIN -u $TEST_DOCKER_USER -p $TEST_DOCKER_PWD
```

**envs.yml content example**:

```yaml
# Docker registry domain
TEST_DOCKER_DOMAIN: registry.example.com
# Docker username
TEST_DOCKER_USER: your_docker_username
# Docker password
TEST_DOCKER_PWD: your_docker_password
```

## Exporting Environment Variables

After a `Job` completes execution, there is a `result` object. You can use [exports](./grammar.md#job-exports) to export properties from `result` to environment variables, with a lifecycle of the current `Pipeline`.

### Syntax Format

```yaml
exports:
  from-key: to-key
```

**Parameter Description**:

| Parameter | Description |
|-----------|-------------|
| from-key | Property name from the `Job result` object to export, supports environment variables, supports deep property access, similar to `lodash.get` |
| to-key | Variable name to map to in environment variables |

### Result Setting Methods

There are three ways to set result:

| Method | Description |
|--------|-------------|
| Script task execution results | Output results after script execution completes |
| Parse custom variables from output | Output variables through special format |
| Built-in task results | Result objects returned by built-in tasks |

### Script Task Execution Results

After a `script` custom script task executes, the `Job result` properties are:

| Property | Description |
|----------|-------------|
| code | Return code |
| stdout | Standard output |
| stderr | Standard error |
| info | Mixed standard output and standard error in chronological order |

::: tip Output Tips
You can use `printf "%s" "hello\nworld"` to output variables, which removes trailing newlines from standard output while preserving escape characters like `\n`.
:::

**Example**:

```yaml title=".cnb.yml"
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
```

**Properties with Conditional Logic**:

When containing `if`, `ifModify`, `ifNewBranch` and other conditional logic, the properties that can be set are:

| Property | Description |
|----------|-------------|
| skip | If the task execution is skipped after the above conditional logic is evaluated, returns the skip reason, otherwise empty string |

```yaml
- name: use if
  if: exit -1
  exports:
    skip: REASON
- name: tell last
  # $REASON value is the string "if"
  script: echo $REASON
```

### Parsing Custom Variables from Output

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

#### Variable Value Encoding

If the variable value contains newline `\n`, you can encode the variable value with `base64` or `escape`:

| Encoding Method | Description |
|----------------|-------------|
| base64 | Variable value starts with `base64,`, the system will decode the content after it as base64 |
| escape | The system will unescape the variable value |

**Node.js Example**:

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

// Output escape encoded variable value
console.log(`##[set-output redline_msg_escape=${escape(value)}]`)
```

```yaml title=".cnb.yml"
main:
  push:
    - docker:
        image: node:20-alpine
      stages:
        - name: set output env
          script: node test.js
          # Export variables output by 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"
```

**echo Example**:

```yaml title=".cnb.yml"
main:
  push:
    - stages:
        - name: set output env
          script: echo "##[set-output redline_msg_base64=base64,$(echo -e "Test string\ntest string" | base64 -w 0)]"
          exports:
            redline_msg_base64: BASE64_KEY
        - name: echo env
          script:
            - echo -e "BASE64_KEY $BASE64_KEY"
```

::: warning Note
In Unix-like systems, the base64 command adds a newline every 76 characters by default. You can use the `-w 0` option to disable newlines to avoid CI failing to parse variables by line.
:::

**Variable values without `\n`** can be output directly:

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

::: tip Limitations

* Due to system environment variable length limits, excessively large variable values are invalid
* CI will ignore variable values >= `100KB`, you can write to files and parse them yourself
* For sensitive information, it's recommended to use the [read-file](./internal-steps/#read-file) built-in task
  :::

### Exporting Environment Variables from Built-in Tasks

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

```yaml title=".cnb.yml"
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 object property access
            nextRelease.gitTag: CUSTOM_ENV_GIT_TAG
        - name: echo env
          script:
            - echo $CUSTOM_ENV_VERSION
            - echo $CUSTOM_ENV_URL
```

Refer to each built-in task's documentation for `result` content.

### Managing Environment Variables

You can override existing environment variables. Setting to empty string or null deletes them.

```yaml title=".cnb.yml"
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 new
            code: CUSTOM_ENV_DATE_CODE
            # Modify
            info: CUSTOM_ENV_DATE_INFO
            # Delete
            CUSTOM_ENV_FOR_DELETE: null
            # Alternative delete syntax
            # 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

### In Script Tasks

When executing script tasks, pipeline environment variables are available as task execution environment variables.

```yaml title=".cnb.yml"
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 configuration files will undergo variable substitution.

**Substitution Rules**: If there's an environment variable `env_name=env_value`, then `$env_name` in property values will be replaced with `env_value`. If `env_name` has no value, it will be replaced with an empty string.

#### Properties Supporting Variable Substitution

| Property | Description |
|----------|-------------|
| Built-in task options | Property values in built-in task options and optionsFrom will undergo variable substitution |
| Plugin task settings | Property values in plugin task settings and settingsFrom will undergo variable substitution |
| env | Property values declared under env can reference variables from parent env for substitution |
| imports | Property values in imports and in declared files will undergo variable substitution |
| pipeline.runner.tags | Build node tags support variable substitution |
| pipeline.docker | volumes, image and build support variable substitution |
| stage.image and job.image | Environment image configuration supports variable substitution |
| ifModify | File change conditions support variable substitution |
| name | pipeline.name, stage.name and job.name support variable substitution |
| lock.key | Lock key name supports variable substitution |
| allowFailure | Allow failure configuration supports variable substitution |

#### Built-in Task Example

```yaml
# options.yml
name: Nightly
```

```yaml title=".cnb.yml"
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 env
          optionsFrom: $address
          # name from options.yml will be merged into options
          options:
            # $description will be replaced with "publish for xx task" from env
            description: $description
```

**Final options content**:

```yaml
name: Nightly
description: publish for xx task
```

#### Plugin Task Example

```yaml
# settings.yml
robot: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx
```

```yaml title=".cnb.yml"
main:
  push:
    - env:
        address: settings.yml
        message: pr check
      stages:
        - name: notify
          image: tencentcom/wecom-message
          # $address will be replaced with "settings.yml" from env
          settingsFrom: $address
          # robot from settings.yml will be merged into settings
          settings:
            # $message will be replaced with "pr check" from env
            content: $message
```

**Final settings content**:

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

#### settingsFrom in Dockerfile

`settingsFrom` specified in Dockerfile `LABEL` also supports variable substitution:

```dockerfile
FROM node:20

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

#### env Example

Property values declared under env can reference variables from parent env for substitution:

```yaml title=".cnb.yml"
main:
  push:
    - env:
        cat_name: tomcat
      stages:
        - name: echo env
          env:
            # Use cat_name value declared in parent env for substitution
            name: "cat $cat_name"
            # Outputs cat tomcat
            script: echo $name
```

#### imports Example

Property values in imports and in declared files will undergo variable substitution. If imports is an array, variables declared in earlier files are effective for later array elements.

```yaml
# env1.yml
address: env2.yml
platform: amd64
```

```yaml
# env2.yml
# Read platform property value from env1.yml for substitution
action: build for $platform
```

```yaml title=".cnb.yml"
main:
  push:
    - imports:
        - env1.yml
        # env1.yml declared address, $address will be replaced with env2.yml
        - $address
      stages:
        - name: echo action
          # Read action property value from env2.yml for substitution
          script: echo $action
```

#### pipeline.runner.tags Example

```yaml title=".cnb.yml"
# Build 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" below will be declared as values of built-in environment variable CNB_PIPELINE_NAME
    amd64: *build
    "arm64:v8": *build
```

#### pipeline.docker Example

```yaml title=".cnb.yml"
.docker-volume: &docker-volume
  docker:
    # Choose either image or build
    image: $image
    build: $build
    volumes:
      - $volume_path
main:
  push:
    install:
      env:
        volume_path: node_modules
        # image has no value, will use build to create an image
        # image: node:22-alpine
        build: Dockerfile
      <<: *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
        image: node:22-alpine
        # build has no value, will use image
        # build: Dockerfile
      <<: *docker-volume
      stages:
        # Wait for install pipeline
        - name: await
          type: cnb:await
          options:
            key: install
        - name: ls
          script: ls node_modules
```

#### ifModify Example

```yaml title=".cnb.yml"
# Only compile corresponding module when code under different modules has changes
.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 Example

Property values of pipeline.name, stage.name and job.name will undergo variable substitution:

```yaml title=".cnb.yml"
main:
  push:
    - name: build in $CNB_REPO_SLUG
      env:
        platform: amd64
      imports:
        - env1.yml
        - env2.yml
      stages:
        - name: stage_$SOME_ENV
          script: echo "hello world"
```

#### lock.key Example

```yaml
# env.yml
build_key: build key
```

```yaml title=".cnb.yml"
.build: &build
  imports: env.yml
  lock:
    key: $build_key
  stages:
    - name: echo
      script: echo "hello world"
main:
  push:
    # Of these two pipelines, one acquires the lock and executes successfully, the other fails to acquire the lock
    - *build
    - *build
```

#### allowFailure Example

```yaml title=".cnb.yml"
main:
  push:
    - env:
        allow_fail: true
      stages:
        - name: echo
          allowFailure: $allow_fail
          # Script execution will error, but allowFailure is true, task is considered successful
          script: echo1 1
```

### Preventing Variable Substitution

If you don't want `$env_name` to be replaced, you can prevent substitution via `\$`:

```yaml title=".cnb.yml"
main:
  push:
    - stages:
        - name: git release
          type: git:release
          options:
            name: Development
            # Property value is "some code update $description"
            description: some code update \$description
```

## Limitations

Environment variables have the following limitations:

| Limitation Item | Description |
|----------------|-------------|
| Variable name format | Can only contain letters (upper/lower case), numbers and underscore (\_) characters, and cannot start with a number |
| Variable value length | Cannot exceed `100KiB` |

::: warning Note
Variables that don't meet the above rules will be ignored.
:::
