# Getting Started

This document describes how to write GitLab CI/CD pipelines on ONES-AI projects.

## Pipeline Configuration

In your project, create a file named `.gitlab-ci.yml`. This file contains the configuration for your CI/CD pipelines. Your pipelines will run on our [CI/CD runners](/ci-cd/using-runners).

## Jobs

### Stages

Identify the different stages your CI/CD pipeline should have. Common stages include build, test, deploy. You can define custom stages based on your specific workflow.

For NEST-Compiler, we use `check` → `build` → `publish` → `test` stages.

Inside the .gitlab-ci.yml file, define the jobs you want to run and organize them into stages. Specify the commands, scripts, and settings for each job.

Example:

```yaml
stages:
  - build
  - test
  - deploy

build:
  stage: build
  script:
    - echo "Building..."
```

### Images

It is recommended to prepare a docker image that is required to build a project. For this, we separated [NEST-Compiler](https://gitlab.com/ones-ai/nest-compiler/) and [NEST-Compiler SDK](https://gitlab.com/ones-ai/nest-compiler-sdk/). NEST-Compiler SDK project is only used to build and publish the docker images for NEST-Compiler project. For more about writing a SDK images, you can refer to ["Writing SDK Images"](/ci-cd/writing-sdk-images).

If your image is prepared, you can define the job like the following.

```yaml
build:
  stage: build
  image: onesai1/nest-compiler-sdk:1.0.0
  script:
    - cmake -G Ninja ../ -DCMAKE_BUILD_TYPE=Release -DNESTC_WITH_EVTA=ON -DNESTC_EVTA_BUNDLE_TEST=ON -DGLOW_WITH_BUNDLES=ON
```

### Scripts

In GitLab CI/CD, you can define commands with `before_script`, `script`, and `after_script` keywords.

#### `before_script`

- Commands specified in `before_script` are executed before the main script (`script`) of the job.
- It is typically used for setting up the environment, installing dependencies, or configuring settings that are common to all jobs in the pipeline.
- Multiple commands can be specified as a list under `before_script`.

#### `script`

- The main commands that perform the primary tasks of the job are specified in the `script` section.
- This is where the core build, test, or deployment tasks are defined.
- Multiple commands can be specified as a list under `script`.

#### `after_script`

- Commands in `after_script` are executed after the `script` section. This can be useful for cleanup tasks, reporting, or additional actions after the main job logic.
- It is commonly used for tasks such as cleaning up temporary files, generating reports, or performing actions based on the result of the main script.
- Multiple commands can be specified as a list under `after_script`.

#### Working Directory

Since GitLab Runner dynamically identifies job working directories, it's recommended not to rely on fixed locations for builds, such as using a fixed location like `/root/dev`. Instead, to dynamically determine where your scripts run, you can make use of the `CI_PROJECT_DIR` variable. In the provided example, several environment variables are set in the `before_script` section to facilitate dynamic build locations.

### Tags

You can specify the runner to execute this job by using tags. Currently, the ONES-AI group has the tags `cpu-runner`, `gpu-runner`, `demo-board`, and `rvx-board`. Specify the appropriate runner based on the workload you want to execute. If no tag is specified, `cpu-runner` will be used by default. For more details, please refer to the [Using runners](/ci-cd/using-runners) documentation.

### Extend

The `extends` keyword in GitLab CI/CD allows you to reuse configurations from other jobs or templates within your `.gitlab-ci.yml` file. This feature promotes code reusability, reduces redundancy, and makes it easier to maintain consistent configurations across multiple jobs or projects.

```yaml
.builds:
  stage: build
  image: onesai1/nest-compiler-sdk:1.0.0
  before_script:
    - export TVM_HOME=$CI_PROJECT_DIR/tvm
    - export DMLC_CORE=$CI_PROJECT_DIR/tvm/3rdparty/dmlc-core
    - export TVM_BUILD_PATH=$CI_PROJECT_DIR/build_ci/tvm
    - export TVM_LIBRARY_PATH=$CI_PROJECT_DIR/build_ci/tvm
    - export PATH=$CI_PROJECT_DIR/build_ci/tvm:$PATH
    - export PYTHONPATH=$TVM_HOME/python:$PYTHONPATH

build:
  extends: .builds
  script:
    - cmake -G Ninja ../ -DCMAKE_BUILD_TYPE=Release -DNESTC_WITH_EVTA=ON -DNESTC_EVTA_BUNDLE_TEST=ON -DGLOW_WITH_BUNDLES=ON

build_from_aws:
  extends: .builds
  script:
    - cmake -G Ninja ../ -DCMAKE_BUILD_TYPE=Release -DNESTC_WITH_EVTA=ON -DNESTC_EVTA_BUNDLE_TEST=ON -DGLOW_WITH_BUNDLES=ON -DNESTC_USE_PRECOMPILED_BUNDLE=ON -DNESTC_USE_PRECOMPILED_BUNDLE_FROM_AWS=ON
```

For more information about `extends` keyword, you can refer to [this documentation](https://docs.gitlab.com/ee/ci/yaml/index.html#extends).

## Workflow

[GitLab workflow](https://docs.gitlab.com/ee/ci/yaml/workflow.html) controls when pipelines are created. For example, The folllowing workflow create pipelines when 1. merge requests are created, 2. tags are created, 3. push events has occured. This workflow also prevents duplicate pipelines for merge requests and commits.

```yaml
workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_TAG
    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
      when: never
    - if: $CI_COMMIT_BRANCH
```

### Workflow for Merge Requests

It is also possible to control the condition to run a specific job. The example job is triggered with following conditions.

- The pipeline is automatically triggered for merge request events.
- For regular branch commits, it won't be automatically triggered if there are open merge requests for the branch (when: never). Instead, it can be manually triggered when needed (when: manual).

```yaml
test-board:
  stage: test
  script:
    - cmake -DNESTC_WITH_EVTA=ON -DLLVM_DIR=/usr/lib/llvm-8.0/lib/cmake/llvm -DCMAKE_BUILD_TYPE=Release -DNESTC_USE_VTASIM=OFF -DVTA_RESNET18_WITH_SKIPQUANT0=ON -DNESTC_EVTA_RUN_ON_ZCU102=ON -DNESTC_USE_PRECOMPILED_BUNDLE=ON -DNESTC_EVTA_RUN_WITH_GENERIC_BUNDLE=ON ..
    - sudo make check_zcu102
  tags:
    - etri-board
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
      when: never
    - if: $CI_COMMIT_BRANCH
      when: manual
```

### Workflow for Docker Builds

#### Build and Push

When you are building a Docker image in your project, you may also define rules like below:

```yaml
docker-build:
  stage: docker-build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  before_script:
    - echo "{\"auths\":{\"${DOCKER_REGISTRY}\":{\"auth\":\"$(printf "%s:%s" "${DOCKER_USERNAME}" "${DOCKER_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json
  script:
    - /kaniko/executor
      --context "${CI_PROJECT_DIR}"
      --dockerfile "${CI_PROJECT_DIR}/utils/docker/Dockerfile"
      --destination "${DOCKER_IMAGE}:${CI_COMMIT_REF_NAME}"
      --destination "${DOCKER_IMAGE}:latest"
  tags:
    - cpu-runner
  rules:
    - if: $CI_COMMIT_REF_PROTECTED == "true"
      changes:
        - utils/docker/Dockerfile

```

In this case, the job will be executed if the pipeline is associated with a protected branch (`$CI_COMMIT_REF_PROTECTED == "true"`) and if there are changes detected in the specified Dockerfile located at `utils/docker/Dockerfile`. The rule ensures that the job is specifically triggered for protected branches when changes occur in the mentioned Dockerfile.

We use such rules to use protected variables for Docker credentials and to prevent unnecessary duplication of Docker builds.

#### Build Only

On branches that are not protected, you can employ the following rules specifically for testing builds.

```yaml
docker-build-no-push:
  stage: docker-build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - /kaniko/executor
      --context "${CI_PROJECT_DIR}"
      --dockerfile "${CI_PROJECT_DIR}/utils/docker/Dockerfile"
      --no-push
  tags:
    - cpu-runner
  rules:
    - if: $CI_COMMIT_REF_PROTECTED == "false"
      changes:
        - utils/docker/Dockerfile
```

## References

Fore more information about job control, you can refer to the following documents.

1. https://docs.gitlab.com/ee/ci/yaml/workflow.html
2. https://docs.gitlab.com/ee/ci/jobs/job_control.html
3. https://docs.gitlab.com/ee/ci/pipelines/merge_request_pipelines.html#prerequisites