> 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/3.0/troubleshooting-and-resources/tutorials-and-code-samples/ci-ruby.md).

# Ruby

You can build and test a [Ruby](https://www.ruby-lang.org/en/) application using a Linux platform on [Harness Cloud](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/use-harness-cloud-build-infrastructure.md) or a [self-managed Kubernetes cluster](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/k8s-build-infrastructure.md) build infrastructure.

This guide assumes you've [created a Harness CI pipeline](/continuous-integration/3.0/use-harness-ci/use-harness-ci/prep-ci-pipeline-components.md).

### Install dependencies <a href="#install-dependencies" id="install-dependencies"></a>

Run [Bundler](https://bundler.io/guides/getting_started.html) commands in a [Run step](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-step-settings.md) to install dependencies in the build environment.

{% tabs %}
{% tab title="Harness Cloud" %}

```yaml
- step:
    type: Run
    identifier: dependencies
    name: Dependencies
    spec:
      shell: Sh
      command: |-
        bundle check || bundle install
```

{% endtab %}

{% tab title="Self-managed" %}

```yaml
- step:
    type: Run
    identifier: dependencies
    name: Dependencies
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: ruby:latest
      command: |-
        bundle check || bundle install
```

{% endtab %}
{% endtabs %}

### Cache dependencies <a href="#cache-dependencies" id="cache-dependencies"></a>

{% tabs %}
{% tab title="Cache Intelligence" %}
You can cache your Ruby dependencies with [Cache Intelligence](/continuous-integration/3.0/use-harness-ci/use-harness-ci/caching-ci-data/cache-intelligence.md). Add `caching.enabled.true` to your `stage.spec` and specify the cache paths (in `paths` and `sharedPaths`).

```yaml
- stage:
    spec:
      caching:
        enabled: true
        key: cache-{{ checksum "Gemfile.lock" }}
        paths:
          - "vendor/bundle"
      sharedPaths:
        - vendor/bundle
```

{% endtab %}

{% tab title="Save and Restore Cache steps" %}
You can use built-in steps to:

* [Save and Restore Cache from S3](/continuous-integration/3.0/use-harness-ci/use-harness-ci/caching-ci-data/saving-cache.md)
* [Save and Restore Cache from GCS](/continuous-integration/3.0/use-harness-ci/use-harness-ci/caching-ci-data/save-cache-in-gcs.md)

Here's an example of a pipeline with **Save Cache to S3** and **Restore Cache from S3** steps.

```yaml
            steps:
              - step:
                  type: RestoreCacheS3
                  name: Restore Cache From S3
                  identifier: Restore_Cache_From_S3
                  spec:
                    connectorRef: AWS_connector
                    region: us-east-1
                    bucket: some_s3_bucket
                    key: cache-{{ checksum "Gemfile.lock" }}
                    archiveFormat: Tar
              - step:
                  type: Run
                  ...
              - step:
                  type: BuildAndPushDockerRegistry
                  ...
              - step:
                  type: SaveCacheS3
                  name: Save Cache to S3
                  identifier: Save_Cache_to_S3
                  spec:
                    connectorRef: AWS_connector
                    region: us-east-1
                    bucket: some_s3_bucket
                    key: cache-{{ checksum "Gemfile.lock" }}
                    sourcePaths:
                      - vendor/bundle
                    archiveFormat: Tar
```

{% endtab %}
{% endtabs %}

### Build and run tests <a href="#build-and-run-tests" id="build-and-run-tests"></a>

You can use **Run** and **Test** steps to [run tests in Harness CI](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-tests/run-tests-in-ci.md).

The following examples run tests in a **Run** step.

{% tabs %}
{% tab title="Harness Cloud" %}

```yaml
- step:
    type: Run
    name: Run Ruby Tests
    identifier: run_ruby_tests
    spec:
      shell: Sh
      command: |-
        bundle exec rake test
```

{% endtab %}

{% tab title="Self-managed" %}

```yaml
- step:
    type: Run
    name: Run Ruby Tests
    identifier: run_ruby_tests
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: ruby:latest
      shell: Sh
      command: |-
        bundle exec rake test
```

{% endtab %}
{% endtabs %}

#### Visualize test results <a href="#visualize-test-results" id="visualize-test-results"></a>

If you want to [view test results in Harness](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-tests/viewing-tests.md), your test reports must be in JUnit XML format.

If you use a **Run** step to run tests, your **Run** step must include the `reports` specification. The `reports` specification is not required for [Test steps (Test Intelligence)](#run-tests-with-test-intelligence).

The following examples use the [Minitest JUnit Formatter](https://github.com/aespinosa/minitest-junit). For more information and an RSpec example, go to [Format test reports - Ruby](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-tests/test-report-ref.md#ruby).

{% tabs %}
{% tab title="Harness Cloud" %}

```yaml
- step:
    type: Run
    name: Run Ruby Tests
    identifier: run_ruby_tests
    spec:
      shell: Sh
      command: |-
        bundle exec rake test --junit
    reports:
      type: JUnit
      spec:
        paths:
          - report.xml
```

{% endtab %}

{% tab title="Self-managed" %}

```yaml
- step:
    type: Run
    name: Run Ruby Tests
    identifier: run_ruby_tests
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: ruby:latest
      shell: Sh
      command: |-
        bundle exec rake test --junit
      reports:
        type: JUnit
        spec:
          paths:
            - report.xml
```

{% endtab %}
{% endtabs %}

#### Run tests with Test Intelligence <a href="#run-tests-with-test-intelligence" id="run-tests-with-test-intelligence"></a>

[Test Intelligence](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-tests/ti-overview.md) is available for Ruby unit tests.

#### Test splitting <a href="#test-splitting" id="test-splitting"></a>

Harness CI supports [test splitting (parallelism)](/continuous-integration/3.0/use-harness-ci/use-harness-ci/run-tests/speed-up-ci-test-pipelines-using-parallelism.md) for both **Run** and **Test** steps.

### Specify version <a href="#specify-version" id="specify-version"></a>

{% tabs %}
{% tab title="Harness Cloud" %}
Ruby is pre-installed on Harness Cloud runners. For details about all available tools and versions, go to [Platforms and image specifications](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/use-harness-cloud-build-infrastructure.md#platforms-and-image-specifications).

If your application requires a specific Ruby version, add a **Run** step to install it.

<details>

<summary>Install one Ruby version</summary>

```yaml
- step:
    type: Run
    name: Install Ruby
    identifier: installruby
    spec:
      shell: Sh
      command: |-
        curl -fsSL https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-installer | bash
        export PATH=$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH
        rbenv install 3.2.2
        rbenv global 3.2.2
        ruby --version
```

</details>

<details>

<summary>Use multiple Ruby versions</summary>

1. Add a [matrix looping strategy](/harness-ai/use-harness-platform/pipelines/looping-strategies/looping-strategies-matrix-repeat-and-parallelism.md) configuration to your stage.

```yaml
- stage:
    strategy:
      matrix:
        rubyVersion:
          - 3.2.2
          - 2.7.8
```

2. Reference the matrix variable in your steps.

```yaml
- step:
    type: Run
    name: Install Ruby
    identifier: installruby
    spec:
      shell: Sh
      command: |-
        curl -fsSL https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-installer | bash
        export PATH=$HOME/.rbenv/bin:$HOME/.rbenv/shims:$PATH
        rbenv install <+matrix.rubyVersion>
        rbenv global <+matrix.rubyVersion>
        ruby --version
```

</details>
{% endtab %}

{% tab title="Self-managed" %}
Specify the desired [Ruby Docker image](https://hub.docker.com/_/ruby) tag in your steps. There is no need for a separate install step when using Docker.

<details>

<summary>Use a specific Ruby version</summary>

```yaml
- step:
    type: Run
    name: Ruby Version
    identifier: rubyversion
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: ruby:latest
      shell: Sh
      command: |-
        ruby --version
```

</details>

<details>

<summary>Use multiple Ruby versions</summary>

1. Add a [matrix looping strategy](/harness-ai/use-harness-platform/pipelines/looping-strategies/looping-strategies-matrix-repeat-and-parallelism.md) configuration to your stage.

```yaml
- stage:
    strategy:
      matrix:
        rubyVersion:
          - 3.2.2
          - 2.7.8
```

2. Reference the matrix variable in the `image` field of your steps.

```yaml
- step:
    type: Run
    name: Ruby Version
    identifier: rubyversion
    spec:
      connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
      image: ruby:<+ stage.matrix.rubyVersion >
      shell: Sh
      command: |-
        ruby --version
```

</details>
{% endtab %}
{% endtabs %}

### Full pipeline examples <a href="#full-pipeline-examples" id="full-pipeline-examples"></a>

The following YAML examples describe pipelines that install dependencies, run tests, use caching, and build and push images to Docker Hub.

{% tabs %}
{% tab title="Harness Cloud" %}
This pipeline uses [Harness Cloud build infrastructure](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/use-harness-cloud-build-infrastructure.md) and [Cache Intelligence](/continuous-integration/3.0/use-harness-ci/use-harness-ci/caching-ci-data/cache-intelligence.md).

If you copy this example, replace the placeholder values with appropriate values for your connector IDs, account/user names, and repo names. Depending on your project and organization, you may also need to replace `projectIdentifier` and `orgIdentifier`.

```yaml
pipeline:
  name: ruby
  identifier: ruby
  projectIdentifier: default
  orgIdentifier: default
  tags: {}
  properties:
    ci:
      codebase:
        connectorRef: YOUR_CODE_REPO_CONNECTOR_ID
        repoName: YOUR_REPO_NAME
        build: <+input>
  stages:
    - stage:
        name: build
        identifier: build
        description: ""
        type: CI
        spec:
          cloneCodebase: true
          caching:
            enabled: true
            key: cache-{{ checksum "Gemfile.lock" }}
            paths:
              - vendor/bundle
          sharedPaths:
            - vendor/bundle
          platform:
            os: Linux
            arch: Amd64
          runtime:
            type: Cloud
            spec: {}
          execution:
            steps:
              - step:
                  type: Run
                  identifier: dependencies
                  name: Dependencies
                  spec:
                    shell: Sh
                    command: bundle install --path vendor/bundle
              - step:
                  type: Run
                  name: Run Ruby Tests
                  identifier: run_ruby_tests
                  spec:
                    shell: Sh
                    command: bundle exec rake test --junit
                  reports:
                    type: JUnit
                    spec:
                      paths:
                        - report.xml
              - step:
                  type: BuildAndPushDockerRegistry
                  name: BuildAndPushDockerRegistry_1
                  identifier: BuildAndPushDockerRegistry_1
                  spec:
                    connectorRef: YOUR_DOCKER_CONNECTOR_ID
                    repo: YOUR_DOCKER_HUB_USERNAME/YOUR_DOCKER_REPO_NAME
                    tags:
                      - <+pipeline.sequenceId>
```

{% endtab %}

{% tab title="Self-managed" %}
This pipeline uses [self-managed Kubernetes cluster build infrastructure](/continuous-integration/3.0/use-harness-ci/use-harness-ci/set-up-build-infrastructure/k8s-build-infrastructure.md) and [Save and Restore Cache from S3 steps](/continuous-integration/3.0/use-harness-ci/use-harness-ci/caching-ci-data/saving-cache.md).

If you copy this example, replace the placeholder values with appropriate values for your connector IDs, account/user names, repo names, and other settings. Depending on your project and organization, you may also need to replace `projectIdentifier` and `orgIdentifier`.

```yaml
pipeline:
  name: ruby-k8s
  identifier: ruby_k8s
  projectIdentifier: default
  orgIdentifier: default
  tags: {}
  properties:
    ci:
      codebase:
        connectorRef: YOUR_CODE_REPO_CONNECTOR_ID
        repoName: YOUR_REPO_NAME
        build: <+input>
  stages:
    - stage:
        name: build
        identifier: build
        description: ""
        type: CI
        spec:
          cloneCodebase: true
          execution:
            steps:
              - step:
                  type: RestoreCacheS3
                  name: Restore Cache From S3
                  identifier: Restore_Cache_From_S3
                  spec:
                    connectorRef: YOUR_AWS_CONNECTOR_ID
                    region: us-east-1 ## Set to your bucket's AWS region
                    bucket: YOUR_AWS_BUCKET_NAME
                    key: cache-{{ checksum "Gemfile.lock" }}
                    archiveFormat: Tar
              - step:
                  type: Run
                  identifier: dependencies
                  name: Dependencies
                  spec:
                    shell: Sh
                    command: bundle install --path vendor/bundle
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: ruby:latest
              - step:
                  type: Run
                  name: Run Ruby Tests
                  identifier: run_ruby_tests
                  spec:
                    shell: Sh
                    command: bundle exec rake test --junit
                    connectorRef: YOUR_IMAGE_REGISTRY_CONNECTOR
                    image: ruby:latest
                  reports:
                    type: JUnit
                    spec:
                      paths:
                        - report.xml
              - step:
                  type: BuildAndPushDockerRegistry
                  name: BuildAndPushDockerRegistry_1
                  identifier: BuildAndPushDockerRegistry_1
                  spec:
                    connectorRef: YOUR_DOCKER_CONNECTOR_ID
                    repo: YOUR_DOCKER_HUB_USERNAME/YOUR_DOCKER_REPO_NAME
                    tags:
                      - <+pipeline.sequenceId>
              - step:
                  type: SaveCacheS3
                  name: Save Cache to S3
                  identifier: Save_Cache_to_S3
                  spec:
                    connectorRef: YOUR_AWS_CONNECTOR_ID
                    region: us-east-1 ## Set to your bucket's AWS region
                    bucket: YOUR_AWS_BUCKET_NAME
                    key: cache-{{ checksum "Gemfile.lock" }}
                    sourcePaths:
                      - vendor/bundle
                    archiveFormat: Tar
          infrastructure:
            type: KubernetesDirect
            spec:
              connectorRef: YOUR_KUBERNETES_CLUSTER_CONNECTOR_ID
              namespace: YOUR_KUBERNETES_NAMESPACE
              automountServiceAccountToken: true
              nodeSelector: {}
              os: Linux
```

{% endtab %}
{% endtabs %}

### Next steps <a href="#next-steps" id="next-steps"></a>

Now that you have created a pipeline that builds and tests a Ruby app, you could:

* Create [triggers](/harness-ai/use-harness-platform/triggers.md) to automatically run your pipeline.
* Add steps to [build and upload artifacts](/continuous-integration/3.0/use-harness-ci/use-harness-ci/build-and-upload-artifacts.md).
* Add a step to [build and push an image to a Docker registry](/continuous-integration/3.0/use-harness-ci/use-harness-ci/build-and-upload-artifacts/build-and-push/build-and-push-to-docker-registry.md).
