---
url: /en/build/migrate-to-cnb/migrate-from-github-actions.md
---
## Introduction

Both GitHub Actions and CNB allow you to create workflows that automatically build, test, publish, release, and deploy code. CNB and GitHub Actions workflow configurations have some similarities:

* Workflow configuration files are written in YAML and stored in the repository. (In CNB, workflows are called Pipelines)
* Workflows include one or more jobs. (In CNB, jobs correspond to Stages)
* Jobs include one or more steps or individual commands. (In CNB, steps correspond to Jobs, and each job can execute a series of commands or plugins)

This guide will focus on the differences between the two to help you migrate GitHub Actions to CNB.

## Workflow Configuration

* GitHub Actions configures each workflow as a separate YAML file stored in the `.github/workflows` directory.

* CNB stores all workflow configuration files in a file named [.cnb.yml](../quick-start.md) in the repository root directory (you can also import other configuration files using the [include](../grammar.md#include) keyword).

The following are the main syntax differences between the two:

GitHub Actions Format

```yaml
name: Node.js CI/CD

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run tests
        run: npm test
```

CNB Format

```yaml title=".cnb.yml"
main:
  push: &build
    - name: Node.js CI/CD
      stages:
        - name: build
          image: ubuntu:latest
          jobs:
            - name: Run tests
              script: npm test
  pull_request: *build
```

::: tip
Note: `&` and `*` are YAML syntax used for configuration reuse. See [Advanced YAML Syntax](../simplify-configuration.md#yaml-gao-ji-yu-fa) for details.
:::

## Build Trigger Rules

In GitHub Actions, workflow trigger rules are defined by specifying the `on` field. In CNB, you can define the rules for triggering build pipelines by writing `trigger branches` and `trigger events`. See [Build Trigger Rules](../trigger-rule.md) for details.

GitHub Actions Format

```yaml
name: Node.js CI/CD

on:
  push:
    branches: [main]

jobs:
  build:
    steps:
      - name: echo
        run: echo "do some job"
```

CNB Format

```yaml title=".cnb.yml"
main: # Trigger branch
  push: # Trigger event, corresponds to a build that can contain multiple Pipelines. Can be either an array or an object.
    - stages: # Pipeline 1
        - name: echo
          script: echo "do some job"
```

## Runners

* In GitHub Actions, a Runner refers to a virtual machine that executes jobs, such as `macOS`, `Windows`, `Linux`, etc.
* In CNB, a Runner refers to a build node (by default, a node running docker containers). Currently, the official hosted build nodes provided by CNB only support Linux system docker containers as Runners. The enterprise version (private deployment) of CNB can connect to different machines (`macOS`, `Windows`, `Linux`). See [Build Cluster](/en/build/build-node.md) for details.

GitHub Actions uses the `runs-on` field to specify the Runner, while CNB uses the `runner` field to specify the build node architecture (`amd64` or `arm64`, etc.) and `cpu` and `memory` configuration.

GitHub Actions Format

```yaml
linux_job:
  runs-on: ubuntu-latest
  steps:
    - run: echo "Hello, $USER!"
```

CNB Format

```yaml title=".cnb.yml"
main:
  push:
    - runner:
        tags: cnb:arch:amd64 # Specify execution on amd64 architecture build cluster
        cpus: 16 # Specify allocated CPU cores as 16 (memory is automatically allocated as cores*2 GiB)
      docker:
        image: ubuntu:latest # Specify using ubuntu:latest image as pipeline runtime environment
      stages:
        - name: echo
          script: echo "Hello, $USER!"
```

## Build Environment

* In GitHub Actions, workflows run in a virtual machine environment, so any software needed in the build environment must either be pre-installed on the virtual machine or manually installed.
* In CNB, workflows run in one or more docker containers where you can specify docker images. During the build process, you only need to specify the required image or Dockerfile in the configuration file to complete the build environment setup. See [Build Environment](../build-env.md) for details.

GitHub Actions Format

```yaml
linux_job:
  runs-on: ubuntu-latest
  steps:
    - name: Use Node.js 21
      uses: actions/setup-node@v4
      with:
        node-version: 21
    - run: npm ci
    - run: npm run build --if-present
    - run: npm test
```

CNB Format

```yaml title=".cnb.yml"
main:
  push:
    - docker:
        image: node:21 # Use node:21 as pipeline runtime environment
      stages:
        - name: npm ci
          script: npm ci
        - name: build
          script: npm run build --if-present
        - name: npm test
          script: npm test
```

## Repository Code Preparation

* In GitHub Actions, workflows use `actions/checkout` to pull repository code
* In CNB, workflows pull repository code by default without explicit declaration. See [syntax guide-Repository Configuration](../grammar.md#pipeline-git) for detailed configuration.

GitHub Actions Format

```yaml
linux_job:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: ls -al .
```

CNB Format

```yaml title=".cnb.yml"
main:
  push:
    - stages:
        - name: ls
          script: ls -al .
```

## Conditional Trigger

* In GitHub Actions, workflows use the `if` field to set step execution conditions.
* In CNB, workflows use fields like `if`, `ifModify`, `ifNewBranch` to set step execution conditions. See [syntax guide-if](../grammar.md#stage-if) for details.

GitHub Actions Format

```yaml
jobs:
  deploy_prod:
    if: contains( github.ref, 'master')
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploy to production server"
```

CNB Format

```yaml title=".cnb.yml"
$:
  push:
    - if: echo "$CNB_BRANCH" | grep -q 'master'
      stages:
        - name: Deploy to production server
          script: echo "Deploy to production server"
```

## Job Dependencies

* In GitHub Actions, workflows use the `needs` field to define workflow dependencies.
* In CNB, workflows use two Built-in Plugin `cnb:await` and `cnb:resolve` to define workflow dependencies and pass variables. See [await-resolve Built-in Plugin](../internal-steps/#cnb-await-resolve) for details.

GitHub Actions Format

```yaml
jobs:
  build_a:
    runs-on: ubuntu-latest
    steps:
      - run: echo "This job will be run first."

  build_b:
    runs-on: ubuntu-latest
    steps:
      - run: echo "This job will be run first, in parallel with build_a"

  test_ab:
    runs-on: ubuntu-latest
    needs: [build_a, build_b]
    steps:
      - run: echo "This job will run after build_a and build_b have finished"

  deploy_ab:
    runs-on: ubuntu-latest
    needs: [test_ab]
    steps:
      - run: echo "This job will run after test_ab is complete"
```

CNB Format

```yaml title=".cnb.yml"
main:
  push:
    - name: build_a
      docker:
        image: ubuntu:latest
      stages:
        - name: build_a
          script: echo "This job will be run first."
        - name: resolve for test_ab
          type: cnb:resolve
          options:
            key: build_a
    - name: build_b
      docker:
        image: ubuntu:latest
      stages:
        - name: build_b
          script: echo "This job will be run first, in parallel with build_a"
        - name: resolve for test_ab
          type: cnb:resolve
          options:
            key: build_b
    - name: test_ab
      docker:
        image: ubuntu:latest
      stages:
        - name: wait for build_a
          type: cnb:await
          options:
            key: build_a
        - name: wait for build_b
          type: cnb:await
          options:
            key: build_b
        - name: build_a
          script: echo "This job will run after build_a and build_b have finished"
        - name: resolve for test_ab
          type: cnb:resolve
          options:
            key: test_ab
    - name: deploy_ab
      docker:
        image: ubuntu:latest
      stages:
        - name: wait for test_ab
          type: cnb:await
          options:
            key: test_ab
        - name: deploy_ab
          script: echo "This job will run after test_ab is complete"
```

## Variables and Secrets

* In GitHub Actions, workflows use the `env` field to specify variables and use `secrets` variables to reference secrets.
* In CNB, workflows use the `env` field to specify variables and use the `imports` field to reference external repositories or secret repository files as environment variables. See [Reference Variables](../grammar.md#pipeline-imports) and [Secret Repository](../../repo/secret.md) for details.

GitHub Actions Format

```yaml
jobs:
  deploy_prod:
    runs-on: ubuntu-latest
    env:
      DEPLOYMENT_ENV: production
    steps:
      - run: echo "This job will deploy to the $DEPLOYMENT_ENV server"
```

CNB Format

```yaml title="env.yml"
DEPLOYMENT_ENV: "production"
```

```yaml title=".cnb.yml"
main:
  push:
    - imports:
        - cnb.share.ralphlauren.cn/<your-repo-slug>/-/blob/main/xxx/env.yml
      stages:
        - name: Deploy to the $DEPLOYMENT_ENV server
          script: echo "This job will deploy to the $DEPLOYMENT_ENV server"
```

## Matrix Strategy

* In GitHub Actions, workflows can use matrix strategy to automatically create multiple job runs based on variable combinations using variables in a single job definition.
* In CNB, workflows do not support this strategy. However, you can use YAML's anchor feature to simulate matrix strategy. See [Advanced YAML Syntax](../simplify-configuration.md#yaml-gao-ji-yu-fa) for reference.

GitHub Actions Format

```yaml
jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [15.x, 16.x]
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
      - name: Install node modules
        run: npm install
      - name: Run a one-line script
        run: npm run build
      - name: Run a multi-line script
        run: npm run test
```

CNB Format

```yaml title=".cnb.yml"
main:
  push:
    - docker:
        image: node:14
      stages: &BVT-tesing-stages
        - name: Install node modules
          script: npm install
        - name: Run a one-line script
          script: npm run build
        - name: Run a multi-line script
          script: npm run test
    - docker:
        image: node:16
      stages: *BVT-tesing-stages
```

## Cache

* In GitHub Actions, workflows use caching strategies to temporarily store intermediate artifacts generated during the build process in a cache area for use in subsequent builds.
* In CNB, workflows use `pipeline.runner.volumes` to declare `local node cache` or use the `docker:cache` Built-in Plugin to declare `remote docker image cache` to store dependency caches or intermediate artifacts during the build process. See [Pipeline Cache](../pipeline-cache.md) for details.

GitHub Actions Format

```yaml
jobs:
  deploy_prod:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Cache node modules
        uses: actions/cache@v2
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-
      - name: Build project
        run: npm ci && npm run build
```

CNB Format

```yaml title=".cnb.yml"
# Use `pipeline.runner.volumes` to declare `local node cache`
main:
  push:
    - docker:
        image: node:14
        volumes:
          - /root/.npm # Declare local cache directory
      stages:
        - name: Build project
          script: npm ci && npm run build
```

```yaml title=".cnb.yml"
# Use `docker:cache` Built-in Plugin to declare `remote docker image cache`
main:
  push:
    - docker:
        image: node:14
      stages:
        - name: build cache image
          type: docker:cache
          options:
            dockerfile: cache.dockerfile
            # by supports the following two forms: array, string
            by:
              - package.json
              - package-lock.json
            # versionBy: package-lock.json
            versionBy:
              - package-lock.json
          exports:
            name: DOCKER_CACHE_IMAGE_NAME
        - name: use cache
          image: $DOCKER_CACHE_IMAGE_NAME
          # Copy files from cache for use
          commands:
            - cp -r "$NODE_PATH" ./node_modules
        - name: Build project
          script: npm ci && npm run build
```

Where cache.dockerfile is a Dockerfile used to build cache images, example:

```dockerfile
# Choose a Base image
FROM node:14

# Set working directory
WORKDIR /space

# COPY the file list from by
COPY . .

# Install dependencies based on the COPY files
RUN npm ci

# Set required environment variables
ENV NODE_PATH=/space/node_modules
```

## Artifacts

* In Github Actions, workflows use the `actions/upload-artifact` action to upload artifacts
* In CNB, workflows use the `cnbcool/attachments:latest` plugin to upload artifacts. See [Attachment Plugin](../../../plugin/#public/cnbcool/attachments) for details.

GitHub Actions Format

```yaml
jobs:
  deploy_prod:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Upload a Build Artifact
        uses: actions/upload-artifact@v2
        with:
          name: my-artifact
          path: /path/to/artifact
```

CNB Format

```yaml title=".cnb.yml"
main:
  push:
    - docker:
        image: ubuntu:latest
      stages:
        - name: Upload a Build Artifact
          image: cnbcool/attachments:latest
          settings:
            attachments:
              - /path/to/artifact
```

## Database and Service Containers

* In GitHub Actions, workflows use the `services` field to orchestrate database and service containers.
* In CNB, workflows can declare `docker` services and directly execute `docker` or `docker compose` commands in jobs to start database and service containers. See [docker service](../grammar.md#service-docker) for details.

GitHub Actions Format

```yaml
jobs:
  container-job:
    runs-on: ubuntu-latest
    container: node:20-bookworm-slim

    services:
      postgres:
        image: postgres
        env:
          POSTGRES_PASSWORD: postgres

    steps:
      - name: Check out repository code
        uses: actions/checkout@v4

      # Performs a clean installation of all dependencies
      # in the `package.json` file
      - name: Install dependencies
        run: npm ci

      - name: Connect to PostgreSQL
        # Runs a script that creates a PostgreSQL client,
        # populates the client with data, and retrieves data
        run: node client.js
        env:
          # The hostname used to communicate with the
          # PostgreSQL service container
          POSTGRES_HOST: postgres
          # The default PostgreSQL port
          POSTGRES_PORT: 5432
```

CNB Format

```yaml title=".cnb.yml"
# Directly use `docker compose` commands in jobs to orchestrate database and service containers
main:
  push:
    - docker:
        image: ubuntu:latest
      services:
        - docker # After declaration, the pipeline container will automatically start dind service and actively inject docker cli tools
      stages:
        - name: Start database and service
          script: docker-compose up -d
        - name: Runs a script that creates a PostgreSQL client, populates the client with data, and retrieves data
          script: node client.js
          env:
            POSTGRES_HOST: 127.0.0.1
            POSTGRES_PORT: 5432
```
