> For the complete documentation index, see [llms.txt](https://developer.harness.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.harness.io/continuous-integration/use-harness-ci/migrate-to-harness-ci/migrating-from-gitlab.md).

# Migrate from GitLab CI to Harness CI

Learn how to migrate your existing GitLab workflows to Harness CI.

Harness CI and GitLab CI both allow you to create workflows that automatically build, test, publish, release, and deploy code.

This guide describes the important differences between Harness CI and GitLab CI so that you can migrate your existing GitLab CI workflows to Harness CI.

Go to [What makes Harness CI unique](https://developer.harness.io/continuous-integration/use-harness-ci/migrate-to-harness-ci) for more information.

### Comparison: Workflow architecture <a href="#comparison-workflow-architecture" id="comparison-workflow-architecture"></a>

Both Harness CI and GitLab CI use workflows to organize builds. In Harness CI, these workflows are called pipelines. In both products, workflows are made up of one or more stages, which represent major segments of the workflow. Each stage include one or more jobs or steps, which in turn contain individual commands.

The following truncated examples provide a simple comparison of stage and step structure in GitLab CI and Harness CI.

{% tabs %}
{% tab title="GitLab CI" %}

```yaml
step1:
  stage: stage1
    image: openjdk:17.0-jdk
  only:
  - /^dev-.*$/
  except:
  - branches
  script:
  - echo "this runs on openjdk"
```

{% endtab %}

{% tab title="Harness" %}

```yaml
    - stage:
        name: Stage1
        ...
            steps:
              - step:
                  type: Run
                  name: step1
                  identifier: step1
                  spec:
                    connectorRef: my-docker-hub-connector
                    image: openjdk:17.0-jdk
                    shell: Bash
                    command: echo "this runs on openjdk"
```

{% endtab %}
{% endtabs %}

Go to the [CI key concepts](/continuous-integration/new-to-harness-ci/key-concepts.md) for Harness terminology, features, and pipeline components.

#### Jobs and steps <a href="#jobs-and-steps" id="jobs-and-steps"></a>

Jobs in GitLab CI are similar to steps in Harness CI. In both products, jobs/steps contain commands. Some commands are declared explicitly, whereas others run inherently based on the step type or configuration. An important difference is that GitLab CI jobs run in parallel by default, whereas in Harness CI steps run sequentially. However, you can enable [parallelism and other looping strategies](#comparison-matrix-and-parallelism) in Harness CI.

{% hint style="info" %}
**ROOT AND NON-ROOT USERS**

Steps run as the root user, generally. For example, with Harness Cloud build infrastructure, steps run directly on the host and, therefore, run as the root user.

For services running on containers (which are steps where you specify a **Container Registry** and **Image** to use to execute the step's commands), you can use the **Run as User** setting to specify a user to use for that container.

With Kubernetes cluster build infrastructure, you can use the **Run as User** setting to specify a user to use for individual steps, or you can [set a default user for all steps](/continuous-integration/use-harness-ci/use-harness-ci/set-up-build-infrastructure/k8s-build-infrastructure/set-up-a-kubernetes-cluster-build-infrastructure.md#run-as-non-root-or-a-specific-user) and then override the default user as needed for individual steps.
{% endhint %}

#### Scripts in workflows <a href="#scripts-in-workflows" id="scripts-in-workflows"></a>

Both Harness CI and GitLab CI support running scripts or shell commands in jobs/steps. In GitLab CI, you use the `script` key to declare a script step.

In Harness CI scripts are supplied in the `command` key, and can be supplied for any step type that supports `command`.

#### Writing workflows <a href="#writing-workflows" id="writing-workflows"></a>

Both Harness CI and GitLab CI workflows are written in YAML. Whereas GitLab workflow configurations are always stored in the `.gitlab-ci.yml` file in your code repo's root directory, Harness provides you a choice of inline pipeline storage or [importing pipelines from Git](/harness-platform/use-harness-platform/git-experience/import-a-pipeline.md). Harness also provides both visual and code-based pipeline editors.

* The Harness YAML editor includes schema validation and auto-complete recommendations to simplify and expedite pipeline configuration.
* The Harness visual editor provides a guided experience that enables anyone to easily build, debug, and run pipelines.
* You can switch back and forth between editors.

#### Complete workflow comparison

Here are YAML examples of complete workflows in GitLab CI and Harness CI.

{% tabs %}
{% tab title="GitLab CI" %}
{% code overflow="wrap" expandable="true" %}

```yaml
image: docker:latest
variables:
  pipelinevar1: someval
  POSTGRES_USER: postgres
  POSTGRES_PASSWORD: "$POSTGRES_PASS"
  POSTGRES_DB: postgres
services:
  - postgres:10.8
stages:
  - stage1
  - stage2
  - matrixstage
before_script:
  - docker login $REGISTRY -u $REGISTRY_USER -p $REGISTRY_USER_PASSWORD
step1_stage1:
  stage: stage1
  image: openjdk:17.0-jdk
  only:
    - /^dev-.*$/
  except:
    - branches
  script:
    - echo "this runs on openjdk"
step1_stage2:
  stage: stage2
  image: node:13.0.0
  only:
    - /^prod-.*$/
  except:
    - branches
  script:
    - echo "project level var:" "$PROJ_VAR"
    - echo "pipeline var:" "$PIPELINE_VAR"
    - echo "secret example :" "$DB_PASSWORD"
step1_matrixstage:
  stage: matrixstage
  only:
    - /^prod-.*$/
  except:
    - branches
  script:
    - echo "Testing matrix"
  parallel:
    matrix:
      - PLATFORM: [windows, mac, linux]
```

{% endcode %}
{% endtab %}

{% tab title="Harness CI" %}
{% code overflow="wrap" expandable="true" %}

```yaml
pipeline:
  name: gitlab test
  identifier: gitlab_test
  projectIdentifier: gitlab_test
  orgIdentifier: default
  tags: {}
  stages:
    - stage:
        name: Stage1
        identifier: stage1
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  type: Background
                  name: Background_1
                  identifier: Background_1
                  spec:
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: postgres:10.8
                    envVariables:
                      POSTGRES_USER: postgres
                      POSTGRES_PASSWORD: <+secrets.getValue("DbPasswordSecret")>
                      POSTGRES_DB: postgres
              - step:
                  type: Run
                  name: step1
                  identifier: step1
                  spec:
                    connectorRef: myDockerHubConnector
                    image: openjdk:17.0-jdk
                    shell: Bash
                    command: echo "this runs on openjdk"
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
    - stage:
        name: Stage2
        identifier: Stage2
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  type: Run
                  name: step1
                  identifier: step1
                  spec:
                    connectorRef: myDockerHubConnector
                    image: node:13.0.0
                    shell: Bash
                    command: |-
                      echo "pipeline var:" <+pipeline.variables.pipelinevar1>
                      echo "project level var:" <+variable.proj_var>
                      echo "secret example :" <+secrets.getValue("DbPasswordSecret")>
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
        variables: []
    - stage:
        name: matrix stage
        identifier: Stage4
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  type: Run
                  name: step1
                  identifier: step1
                  spec:
                    shell: Bash
                    command: echo "Testing on <+matrix.testparam>"
                  failureStrategies: []
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
        strategy:
          matrix:
            testparam:
              - node
              - python
              - ubuntu
            maxConcurrency: 3
  properties:
    ci:
      codebase:
        connectorRef: account.myscm
        repoName: test
        build: <+input>
  variables:
    - name: pipelinevar1
      type: String
      description: ""
      value: someval
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Comparison: Specify a Docker image <a href="#comparison-specify-a-docker-image" id="comparison-specify-a-docker-image"></a>

In GitLab CI, images are defined at the job level, and you can specify global images that are used by jobs that do not have an image specified, for example:

```yaml
step1:
  stage: stage1
    image: openjdk:17.0-jdk
```

In Harness CI, you specify *connectors* and images for each step, if necessary. Depending on the step type, the commands you are running, and your build infrastructure, you may not need to specify an image. For example, if a binary is already available on your build farm, you may not need to pull an image for that binary.

For example, the following step includes a `connectorRef` referencing a Docker connector. [Docker connectors](broken://spaces/3F2TpHXhur2QtQnORSM9/pages/LZPXMHJjbQyZXdwC5Omo) are platform-agnostic and can be used to connect to any Docker container registry.

```yaml
              - step:
                  type: Run
                  name: step1
                  identifier: step1
                  spec:
                    connectorRef: my-docker-hub-connector
                    image: openjdk:17.0-jdk
                    shell: Bash
                    command: echo "this runs on openjdk"
```

{% hint style="info" %}
**WHAT ARE CONNECTORS?**

Harness integrates with many different types of repositories and providers. A connection from Harness to other platforms is called a [connector](/harness-platform/use-harness-platform/connectors.md). Connectors can connect to source control providers, cloud providers, container registries, and more.
{% endhint %}

### Comparison: Define a multi-stage pipeline <a href="#comparison-define-a-multi-stage-pipeline" id="comparison-define-a-multi-stage-pipeline"></a>

Both GitLab CI and Harness CI execute workflow stages in a specific order based on the YAML configuration.

{% tabs %}
{% tab title="GitLab CI" %}

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

{% endtab %}

{% tab title="Harness" %}

```yaml
  stages:
    - stage:
        name: Test
        ...
    - stage:
        name: Build
        ...
    - stage:
        name: Upload
        ...
```

{% endtab %}
{% endtabs %}

The following examples compare GitLab CI and Harness CI workflows with multiple stages.

{% tabs %}
{% tab title="GitLab CI" %}
{% code overflow="wrap" expandable="true" %}

```yaml
image: golang:latest
stages:
  - test
  - build
format:
  stage: test
  script:
    - go fmt $(go list ./... | grep -v /vendor/)
    - go vet $(go list ./... | grep -v /vendor/)
    - go test -race $(go list ./... | grep -v /vendor/)
compile:
  stage: build
  script:
    - mkdir -p mybinaries
    - go build -o mybinaries ./...
  artifacts:
    paths:
      - mybinaries
```

{% endcode %}
{% endtab %}

{% tab title="Harness CI" %}
{% code overflow="wrap" expandable="true" %}

```yaml
stages:
  - stage:
      name: test
      identifier: test
      description: ""
      type: CI
      spec:
        cloneCodebase: true
        platform:
          os: Linux
          arch: Amd64
        runtime:
          type: Cloud
          spec: {}
        execution:
          steps:
            - step:
                type: Run
                name: test-step
                identifier: teststep
                spec:
                  shell: Sh
                  command: |
                    go fmt $(go list ./... | grep -v /vendor/)
                    go vet $(go list ./... | grep -v /vendor/)
                    go test -race $(go list ./... | grep -v /vendor/)
  - stage:
      name: build
      identifier: build
      type: CI
      spec:
        cloneCodebase: true
        platform:
          os: Linux
          arch: Amd64
        runtime:
          type: Cloud
          spec: {}
        execution:
          steps:
            - step:
                type: Run
                name: build-step
                identifier: buildstep
                spec:
                  shell: Sh
                  command: |
                    mkdir -p mybinaries
                    go build -o mybinaries ./...
        sharedPaths:
          - mybinaries
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Comparison: Environment variables <a href="#comparison-environment-variables" id="comparison-environment-variables"></a>

In GitLab CI, you can use environment variables to control the behavior of jobs and pipelines, store values you want to reuse, or avoid hard-coding values in your `.gitlab-ci.yml` file. You can use predefined environment variables, define custom variables in the `.gitlab-ci.yml` file, or define variables at the project, group, or instance levels.

This range of variable definition is also possible in Harness CI. In addition to built-in variables, you can define variables within individual pipelines, stages, and steps as well as at the project, organization, and account levels.

{% tabs %}
{% tab title="GitLab CI" %}
This GitLab example demonstrates use of a pre-defined variable as well as a custom variable definition.

```yaml
test_variable:
  stage: test
  script:
    - echo "$CI_JOB_STAGE" #Uses pre-defined variable

# Custom variable definition in .gitlab-ci.yml file <a href="#custom-variable-definition-in-gitlab-ciyml-file" id="custom-variable-definition-in-gitlab-ciyml-file"></a>

variables:
  SA_PASSWORD: $SA_PASSWORD
```

{% endtab %}

{% tab title="Harness" %}
To reference project, organization, and account variables, you use variable expressions formatted as: `<+variable.[scope].[variable_id]>`. Here are the syntax formats for variables declared at different levels:

* Account-level variable reference: `<+variable.account.[var_id]>`
* Organization-level variable reference: `<+variable.org.[var_id]>`
* Project-level variable reference: `<+variable.[var_id]>`

In this example, a step references a project-level environment variable called `ENV_VAR`.

```yaml
              - step:
                  type: Run
                  name: test-variable
                  identifier: test_variable
                  spec:
                    command: echo "environment variable: " <+variable.env_var>
```

Within a single pipeline, you can define variables at the step, stage, and pipeline levels. For example, this step includes environment variable definitions.

```yaml
              - step:
                  type: Background
                  name: Background_1
                  identifier: Background_1
                  spec:
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: postgres:10.8
                    envVariables:
                      POSTGRES_USER: postgres
                      POSTGRES_PASSWORD: <+secrets.getValue("DbPasswordSecret")>
                      POSTGRES_DB: postgres
```

Go to the following for defining and fetching variables:

* [Use Harness expressions](/harness-platform/use-harness-platform/variables-and-expressions/harness-variables.md)
* [Define variables](/harness-platform/use-harness-platform/variables-and-expressions/add-a-variable.md)
  {% endtab %}
  {% endtabs %}

### Comparison: Matrix and parallelism <a href="#comparison-matrix-and-parallelism" id="comparison-matrix-and-parallelism"></a>

GitLab CI jobs run in parallel by default, whereas in Harness CI steps and stages run sequentially unless you enable parallelism. For example, this Harness CI YAML example shows a pipeline with two stages that run in parallel, as indicated by `- parallel`, and two stages that run sequentially after the two parallel stages.

```yaml
  stages:
    - parallel:
        - stage:
            name: Stage1
            ...
        - stage:
            name: Stage2
            ...
    - stage:
        name: Stage3
        ...
    - stage:
        name: Stage4
        ...
```

In addition to parallelism, you can use Harness CI's [looping strategies](/harness-platform/use-harness-platform/pipelines/looping-strategies/looping-strategies-matrix-repeat-and-parallelism.md) to make your pipelines more dynamic and versatile. These strategies allow your pipelines to execute the same set of tasks multiple times for several different configurations without requiring you to intentionally create unique steps or stages for each iteration. This is achieved by mentioning user-defined tags and referencing them in the pipeline using the following expression syntax: `<+matrix.usertag>`

As in GitLab CI, you also can combine parallelism and matrix strategies in Harness CI, as shown in the following examples.

{% tabs %}
{% tab title="GitLab CI" %}

```yaml
test:
  stage: test
  script: run-test $PLATFORM
  parallel:
    matrix:
      - PLATFORM: [windows, mac, linux]
```

{% endtab %}

{% tab title="Harness" %}
The following example describes a stage in a Harness CI pipeline that includes one step with matrix and parallelism strategies. The looping strategies are defined in `strategy` at the stage level. One matrix strategy, called `testparam`, is defined in `matrix` and parallelism is defined by `maxConcurrency: 3`. The script in the `Run` step calls the inputs from the matrix strategy by using the expression `+matrix.testparam>`.

```yaml
  stages:
    - stage:
        name: Stage1
        identifier: Stage1
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  type: Run
                  name: step1
                  identifier: step1
                  spec:
                    connectorRef: dockerconectorref
                    image: dockerimage:tag
                    shell: Bash
                    command: echo "Testing on  <+matrix.testparam>"
        strategy:
          matrix:
            testparam:
              - node
              - python
              - ubuntu
          maxConcurrency: 3
```

{% endtab %}
{% endtabs %}

### Comparison: Triggers <a href="#comparison-triggers" id="comparison-triggers"></a>

Webhooks are a convenient way to trigger CI builds on demand by sending an HTTP post request to specialized URLs. This is particularly useful for event-based triggering, in which a webhook can be called whenever a specified event occurs.

In GitLab CI, pipelines can be triggered only through API.

Harness CI supports webhook, artifact, manifest and schedule triggers. The two most commonly used triggers are webhook triggers based on Git events and scheduled triggers based on `cron` expressions. Go to [Triggers](/harness-platform/use-harness-platform/triggers.md) for creating a trigger.

{% tabs %}
{% tab title="GitLab CI" %}

```yaml
trigger_pipeline:
  stage: deploy
  script:
    - 'curl --fail --request POST --form token=$MY_TRIGGER_TOKEN --form ref=main "https://gitlab.example.com/api/v4/projects/123456/trigger/pipeline"'
  rules:
    - if: $CI_COMMIT_TAG
  environment: production
```

{% endtab %}

{% tab title="Harness" %}

```yaml
trigger:
  name: make build
  identifier: make_build
  enabled: true
  orgIdentifier: default
  projectIdentifier: DhrubaCI
  pipelineIdentifier: Demo3
  source:
    type: Scheduled
    spec:
      type: Cron
      spec:
        expression: 45 13 13 09 2
  inputYaml: |
    pipeline:
      identifier: Demo3
      properties:
        ci:
          codebase:
            build:
              type: branch
              spec:
                branch: main
```

{% endtab %}
{% endtabs %}

### Related articles <a href="#see-also" id="see-also"></a>

* [Harness CI pipeline creation overview](/continuous-integration/use-harness-ci/use-harness-ci/prep-ci-pipeline-components.md)
* [Caching in Harness CI](/continuous-integration/use-harness-ci/use-harness-ci/caching-ci-data.md)
* [Split tests in Run steps](/continuous-integration/use-harness-ci/use-harness-ci/run-tests/speed-up-ci-test-pipelines-using-parallelism.md)
* [Harness Platform documentation](https://app.gitbook.com/s/3F2TpHXhur2QtQnORSM9/README)

{% @harness-feedback/feedback %}
