> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qlty.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# CI Integration / Uploader

Integrating code coverage reporting into your CI pipeline ensures that coverage data is consistently collected and reported for every build.

Qlty is compatible with every major CI provider.

Qlty offers CI-specific packages for GitHub Actions (GitHub Action) and CircleCI (CircleCI Orb), but works just as well without them.

Qlty provides [environment detection](/coverage/ci#environment-detection) for most CI providers, making integration simpler, but offers simple instructions for those without it.

| CI Provider                                           | Package     | Environment Detection | OIDC |
| ----------------------------------------------------- | ----------- | --------------------- | ---- |
| [GitHub Actions](/coverage/ci#github-actions)         | qlty-action | Yes                   | Yes  |
| [CircleCI](/coverage/ci#circleci)                     | qlty-orb    | Yes                   | No   |
| [Buildkite](/coverage/ci#buildkite)                   |             | Yes                   | No   |
| [Jenkins](/coverage/ci#jenkins)                       |             | Yes                   | No   |
| [CodeFresh](/coverage/ci#other-ci-providers)          |             | Yes                   | No   |
| [SemaphoreCI](/coverage/ci#other-ci-providers)        |             | Yes                   | No   |
| [TravisCI](/coverage/ci#travisci)                     |             | Yes                   | No   |
| [bitrise](/coverage/ci#bitrise)                       |             | Yes                   | No   |
| [Other CI Providers](/coverage/ci#other-ci-providers) |             | No                    | No   |

## CI Provider Integration

### GitHub Actions

Integrate with Qlty using our GitHub Action.

While integration can be done without our GitHub Action, using it allows you to take advantage of [OIDC support](/coverage/tokens).

<CodeGroup>
  ```yaml OIDC-based authentication (recommended) lines theme={"system"}

  # .github/workflows/test.yml
  name: Test and Coverage

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

  permissions:
    contents: read
    id-token: write # Required for OIDC

  jobs:
    test:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v3

        # Run your tests with coverage
        - name: Run tests
          run: npm test -- --coverage

        # Upload coverage to Qlty
        - uses: qltysh/qlty-action/coverage@v2
          with:
            oidc: true
            files: coverage/lcov.info

  ```

  ```yaml Using a Coverage Token lines theme={"system"}
  # .github/workflows/test.yml
  name: Test and Coverage

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

  jobs:
    test:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v3

        # Run your tests with coverage
        - name: Run tests
          run: npm test -- --coverage

        # Upload coverage to Qlty
        - uses: qltysh/qlty-action/coverage@v2
          with:
            token: ${{ secrets.QLTY_COVERAGE_TOKEN }}
            files: coverage/lcov.info
  ```
</CodeGroup>

### CircleCI

Qlty offers a CircleCI Orb for easy integration.

<CodeGroup>
  ```yaml Using Qlty's CircleCI Orb lines theme={"system"}
  # Add the Qlty orb to your `.circleci/config.yml`:
  version: 2.1

  orbs:
    qlty-orb: qltysh/qlty-orb@0.0

  jobs:
    test:
      docker:
        - image: cimg/node:16.13
      steps:
        # Checkout is required
        - checkout
        - run:
            name: Install dependencies
            command: npm ci
        - run:
            name: Run tests with coverage
            command: npm test
        - qlty-orb/coverage_publish:
            files: coverage/lcov.info

  workflows:
    main:
      jobs:
        - test

  ```

  ```yaml Without Qlty's CircleCI Orb lines theme={"system"}
  version: 2.1

  jobs:
    test:
      docker:
        - image: cimg/node:16.13
      steps:
        - checkout
        - run:
            name: Install dependencies
            command: npm ci
        - run:
            name: Run tests with coverage
            command: npm test
        - run:
            name: Install Qlty CLI
            command: curl https://qlty.sh | sh
        - run:
            name: Upload coverage
            command: qlty coverage publish coverage/lcov.info
            environment:
              QLTY_COVERAGE_TOKEN: ${QLTY_COVERAGE_TOKEN}

  workflows:
    main:
      jobs:
        - test
  ```
</CodeGroup>

### Travis CI

Add Qlty coverage reporting to your `.travis.yml`:

```yaml lines theme={"system"}
language: node_js
node_js:
    - 16

script:
    - npm test -- --coverage

after_success:
    - curl https://qlty.sh | sh
    - qlty coverage publish coverage/lcov.info
```

Make sure to add your `QLTY_COVERAGE_TOKEN` in the Travis CI repository settings.

### GitLab CI

Add coverage reporting to your `.gitlab-ci.yml`:

```yaml lines theme={"system"}
test:
    stage: test
    script:
        - npm ci
        - npm test -- --coverage
        - curl https://qlty.sh | sh
        - qlty coverage publish coverage/lcov.info
    artifacts:
        paths:
            - coverage/
```

Add your `QLTY_COVERAGE_TOKEN` as a CI/CD variable in your GitLab project settings.

### Buildkite

Export your QLTY\_COVERAGE\_TOKEN as an environment variable. (See Buildkite's documentation on [managing secrets](https://buildkite.com/docs/pipelines/security/secrets/managing).

One option is to export `QLTY_COVERAGE_TOKEN` in an [environment agent hook](https://buildkite.com/docs/pipelines/security/secrets/managing#without-a-secrets-storage-service-exporting-secrets-with-environment-hooks)):

```bash lines theme={"system"}
#!/usr/bin/env bash
set -e

export QLTY_COVERAGE_TOKEN="YOUR_COVERAGE_TOKEN"
```

Then update your pipeline.yaml appropriately. Here's an example with Ruby test coverage:

```yaml lines theme={"system"}
steps:
    - label: ":ruby: Ruby 3.1 Tests"
      key: "ruby-tests"
      command: |
          bundle install
          bundle exec rspec --require spec_helper
      artifact_paths:
          - "coverage/coverage.json"

    - wait

    - label: ":chart_with_upwards_trend: Coverage Report"
      command: |
          # Download the coverage artifact
          buildkite-agent artifact download "coverage/coverage.json" .

          curl https://qlty.sh | sh
          qlty coverage publish coverage/coverage.json
      depends_on: "ruby-tests"
```

### Jenkins

Jenkins requires a credentials entry for your `QLTY_COVERAGE_TOKEN`. To add this:

1. Navigate to **Manage Jenkins** → **Manage Credentials**
2. Select the appropriate credentials store (usually **Jenkins** → **Global credentials**)
3. Click **Add Credentials**
4. Set the **Kind** to **Secret text**
5. Enter your Qlty coverage token as the **Secret**
6. Set the **ID** to `qlty-coverage-token`
7. Add a **Description** (e.g., "Qlty Coverage Token")
8. Click **Create**

Here's an example `Jenkinsfile` with Ruby test coverage:

```groovy lines theme={"system"}
pipeline {
    agent any

    environment {
        GEM_HOME = "${WORKSPACE}/.gem"
        PATH = "${WORKSPACE}/.gem/bin:${env.PATH}"
        BUNDLE_PATH = "${WORKSPACE}/vendor/bundle"
        QLTY_COVERAGE_TOKEN = credentials('qlty-coverage-token')
    }

    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }

        stage('Install Dependencies') {
            steps {
                sh 'bundle install --jobs 4 --retry 3'
            }
        }

        stage('Run Tests') {
            steps {
                sh 'bundle exec rspec --require spec_helper'
            }
        }

        stage('Upload Coverage') {
            steps {
                sh '''
                    curl -sSL https://qlty.sh | sh
                    ~/.qlty/bin/qlty coverage publish coverage/coverage.json
                '''
            }
        }
    }

    post {
        always {
            cleanWs()
        }
    }
}
```

The `credentials('qlty-coverage-token')` function retrieves the secret text credential you created and makes it available as the `QLTY_COVERAGE_TOKEN` environment variable.

### bitrise

Configure your `QLTY_COVERAGE_TOKEN` as a [project level token](https://docs.bitrise.io/en/bitrise-ci/configure-builds/secrets.html). Check "Exposed to pull requests" in order to see diff coverage and total coverage in pull requests.

The coverage data file(s) must be placed somewhere accessible to future steps. bitwrise's documentation lists [several options for managing files across steps](https://docs.bitrise.io/en/bitrise-ci/run-and-analyze-builds/managing-build-files/using-files-in-your-builds.html).

Assuming a coverage data file located at `coverage.lcov`, the following step uploads coverage to qlty.sh:

```yaml lines theme={"system"}
- script@1:
      inputs:
          - content: |-
                #!/usr/bin/env bash
                set -e
                set -o pipefail
                set -x

                curl https://qlty.sh | sh
                ~/.qlty/bin/qlty coverage publish coverage.lcov
```

### Other CI Providers

As long as your CI environment [meets the system requirements](/cli/system-requirements) to install the Qlt CLI, you can publish coverage.

Publishing coverage on any CI provider follows the same procedure:

1. Ensure a `QLTY_COVERAGE_TOKEN` is available in the environment
2. Install the Qlty CLI
3. Execute `qlty coverage publish` command with the correct arguments

For example:

```bash lines theme={"system"}
# Assumes $QLTY_COVERAGE_TOKEN is set in the environment
curl https://qlty.sh | sh
qlty coverage publish coverage/lcov.info # Replace with path to your coverage data file(s)
```

<Tip>
  For security-sensitive environments, you can [verify the CLI's
  integrity](/cli/integrity-verification) before installation.
</Tip>

The Qlty CLI coverage commands leverage as much information from its environment as possible to ease integration. This includes environment variables as well as git metadata from the current working directory. However, sometimes, such as when attempting to publish coverage from a CI provider not listed above, Qlty is unable to detect this information from its environment. In these cases, you will need to provide the missing metadata to `qlty coverage publish` as additional arguments. Because usually no more than 2 - 4 arguments are needed, we recommend a trial and error process, following the instructions provided by the command when it errors.

Below are some common scenarios you may run into.

#### Path Validation Failures

`qlty coverage publish` validates that the source files specified in your coverage data files match the source files in your repository. This ensures that we're able to properly associate coverage with your source files in Qlty Cloud.

If we're unable to validate that a majority (up to a configurable threshold) of the source files referenced exist in your repository, `qlty coverage publish` will error.

Usually these validate failures this can be addressed with the `--add-prefix` and/or `--strip-prefix` arguments. See [Path Fixing](/coverage/path-fixing) for more information.

#### Commit SHA Missing

A commit sha is always required when sending coverage data to Qlty.sh. The commit sha can be manually specified with:

`--override-commit-sha <COMMIT_SHA>`

#### Reference Missing

When publishing coverage, Qlty associates the coverage data with a single reference. The reference can be one of:

* a branch
* a pull request
* a git tag

If not inferred, provide one of the following:

* pull request: `--override-pr-number <PR_NUMBER>`
* branch: `--override-branch <BRANCH_NAME>` (can be used alongside `--override-pr-number`)
* git tag: `--override-git-tag <TAG_NAME>` (cannot be combined with either `--override-pr-number` or `--override-branch`)

#### Commit Time Missing

A timestamp associated with the commit is also required. This can be specified with:

`--override-commit-time <OVERRIDE_COMMIT_TIME>`

The commit time can be a Unix timestamp (seconds since epoch) or a date in RFC3339/ISO8601 format.

#### Examples

<CodeGroup>
  ```shell Branch Override lines theme={"system"}
  QLTY_COVERAGE_TOKEN=QLTY_TOKEN qlty coverage publish --override-branch=main --override-commit-sha=e83c5163316f89bfbde7d9ab23ca2e25604af290 --override-commit=time=1704067200 lcov.info
  ```

  ```shell Pull Request Override lines theme={"system"}
  QLTY_COVERAGE_TOKEN=QLTY_TOKEN qlty coverage publish --override-pr-number=34 --override-branch=feature-1 --override-commit-sha=e83c5163316f89bfbde7d9ab23ca2e25604af290 --override-commit=time=1704067200 lcov.info
  ```

  ```shell Git Tag Override lines theme={"system"}
  QLTY_COVERAGE_TOKEN=QLTY_TOKEN qlty coverage publish --override-git-tag=v0.2.2 --override-commit-sha=e83c5163316f89bfbde7d9ab23ca2e25604af290 --override-commit=time=1704067200 lcov.info
  ```
</CodeGroup>

## Advanced CI Configuration

### Parallelized Tests

When running tests in parallel, you'll need to merge the coverage reports before uploading. Qlty supports both client-side and server-side merging.

#### Client-side Merging

Collect all coverage reports into a single location and upload them together:

```bash lines theme={"system"}
qlty coverage publish report1.lcov report2.lcov report3.lcov
```

#### Server-side Merging

Upload each part separately and specify the total number of parts:

```bash lines theme={"system"}
# Part 1
qlty coverage publish --total-parts-count=3 report1.lcov

# Part 2
qlty coverage publish --total-parts-count=3 report2.lcov

# Part 3
qlty coverage publish --total-parts-count=3 report3.lcov
```

Learn more about [Coverage Merging](/coverage/merging).

### Multiple Test Suites

If you have different types of tests (unit, integration, e2e), you can track them separately using coverage tags:

```bash lines theme={"system"}
# Upload unit test coverage
qlty coverage publish --tag=unit unit-coverage.lcov

# Upload integration test coverage
qlty coverage publish --tag=integration integration-coverage.lcov
```

Learn more about [Coverage Tags](/coverage/tags).

## Environment Detection

### What is environment detection?

Environment detection is Qlty CLI's ability to infer, without user input, metadata about your build from CI provider-specific environment variables. For example, CircleCI exposes the commit being built in an environment variable `CIRCLE_SHA1`.

Without specific environment detection, you can still use our coverage publisher with a few extra command line arguments. See our documentation on [Other CI Providers](#other-ci-providers).

### Adding Environment Detection

If you want automatic environment detection:

1. Double check [our Qlty repository](https://github.com/qltysh/qlty/tree/main/qlty-coverage/src/ci) for the most up to date list of supported CI providers.
2. Our coverage publisher is open source and you can add support by following [the other provider examples](https://github.com/qltysh/qlty/tree/main/qlty-coverage/src/ci) in under an hour. (Provide an LLM with the other examples and a link to your CI provider's list of available environment variables)

## Troubleshooting

### Common Issues

* **Authentication failures**: Check that your token is correctly set as an environment variable
* **Report format not recognized**: Make sure you're using a [supported coverage format](/coverage/formats)
* **Path mismatches**: Use the `--strip-prefix` and `--add-prefix` options to [fix file paths](/coverage/path-fixing)
* **Missing metadata**: Use `--override-branch`, `--override-commit` and `--override-build-id` if CI detection fails

## See Also

* [Coverage Merging](/coverage/merging) - Combine multiple coverage reports
* [Coverage Tags](/coverage/tags) - Track different test suites separately
