# Index repositories in CI

> For the complete documentation index, see [llms.txt](https://www.jetbrains.com/help/air/llms.txt).

Indexing started from Air Teams or the Air Context CLI on a developer machine is limited by repository size. In CI, the CLI runs with an organization service account instead of a personal login. It indexes much larger repositories, keeps the index current for everyone in your organization, and keeps personal tokens out of your pipelines. Re-runs are incremental: only the changes since the previously indexed revision are processed.

> **Note:**
> CI runs count as on-demand indexing, so on-demand indexing must be allowed for your organization in JetBrains Console. See [how administrators block and allow it](manage-external-indexing.html).

## Prerequisites

* An organization service account with AI access. An administrator [creates the service account](service-accounts.html#add-service-account) in JetBrains Console, [grants it AI access](ai-access.html#grant-ai-access), and makes sure it has an available [credit limit](ai-credit-limits.html). The account doesn't need any roles. Copy its client ID and client secret when they are displayed: the secret is shown only once, and you store both as CI secrets in the procedure below. > **Note:** > Service accounts never get AI access automatically. You must always explicitly [grant AI access](ai-access.html#grant-ai-access) to a service account.

* A checked-out Git repository. The CLI reads the repository ID and revision from Git, so run it in a checkout with a `.git` folder. A shallow clone is fine. A non-Git folder can be indexed too, but its repository ID may change between runs, which produces separate snapshots instead of updating one index.

* A build agent with `git`, and `curl` on Linux and macOS. The CLI is a self-contained native binary; it needs no Docker image or JVM. Standard hosted runners such as `ubuntu-latest` work out of the box. Windows build agents work too, with the PowerShell installer; the examples below are written for Linux and macOS.

Procedure: Set up CI indexing

1. Store the service account's client ID and client secret as masked secrets in your CI system, named `JCP_ORG_SERVICE_CLIENT_ID` and `JCP_ORG_SERVICE_CLIENT_SECRET`. The older names `JCP_ORG_SERVICE_ID` and `JCP_ORG_SERVICE_SECRET` still work.

The CLI picks these environment variables up automatically, so the pipeline doesn't need a `jbcontext login` step.

2. Add a step that installs the CLI:

```BASH
curl -fsSL https://download.jetbrains.com/jetbrains-context/install.sh | bash
```

This installs the latest stable build to `~/.jbcontext/bin/jbcontext`. To pin a version for reproducible pipelines:

```BASH
curl -fsSL https://download.jetbrains.com/jetbrains-context/install.sh | bash -s -- --version=<version>
```

3. Add a step that runs the indexing from the repository checkout:

```BASH
~/.jbcontext/bin/jbcontext --ci index
```

For what the `--ci` option changes, see [What the --ci option changes](#what-ci-changes).

4. Define the trigger, for example on every commit to the default branch or on a schedule. Index the default branch: searches from branches that have no index of their own fall back to it.

## What the --ci option changes

`--ci` marks the run as unattended, for build agents: logs stream to stderr so that the build log keeps them, and `index` reports progress as plain log lines, uses all available CPU, and lifts the client-side repository size limits. It is a global option, so it goes before the command; the `JBCONTEXT_CI=true` environment variable does the same. The server lifts its own size limit only for runs authenticated with an organization service account.

Add `-v` to stream debug output as well.

## Pipeline examples

Each example checks out the repository, installs the CLI, and runs `jbcontext --ci index` with the service account credentials in the environment.

GitHub Actions:

Create the `JCP_ORG_SERVICE_CLIENT_ID` and `JCP_ORG_SERVICE_CLIENT_SECRET` [repository secrets](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets).

```YAML
name: Index code

on: # define your own trigger, for example on every commit to main or on a schedule
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  index:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install Air Context CLI
        run: curl -fsSL https://download.jetbrains.com/jetbrains-context/install.sh | bash
      - name: Index code
        run: ~/.jbcontext/bin/jbcontext --ci index
        env:
          JCP_ORG_SERVICE_CLIENT_ID: ${{ secrets.JCP_ORG_SERVICE_CLIENT_ID }}
          JCP_ORG_SERVICE_CLIENT_SECRET: ${{ secrets.JCP_ORG_SERVICE_CLIENT_SECRET }}
```

TeamCity:

Add `JCP_ORG_SERVICE_CLIENT_ID` and `JCP_ORG_SERVICE_CLIENT_SECRET` as [password-typed parameters](https://www.jetbrains.com/help/teamcity/typed-parameters.html). The example uses the TeamCity Kotlin DSL.

```KOTLIN
object IndexCode : BuildType({
    name = "Index code"

    vcs {
        root(MyVcsRoot) // VCS root to index
        checkoutMode = CheckoutMode.ON_AGENT // index needs the .git directory to identify the repository
    }

    params {
        password("env.JCP_ORG_SERVICE_CLIENT_ID", "credentialsJSON:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
        password("env.JCP_ORG_SERVICE_CLIENT_SECRET", "credentialsJSON:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
    }

    steps {
        script {
            name = "Install Air Context CLI"
            scriptContent = "curl -fsSL https://download.jetbrains.com/jetbrains-context/install.sh | bash"
        }
        script {
            name = "Index code"
            scriptContent = "~/.jbcontext/bin/jbcontext --ci index"
        }
    }

    triggers {
        // Define your own trigger, for example on every commit (vcs {}) or on a schedule
        schedule {
            schedulingPolicy = cron {
                dayOfWeek = "Mon-Fri"
                hours = "0"
                minutes = "0"
            }
            branchFilter = "+:<default>"
            triggerBuild = always()
            withPendingChangesOnly = false
        }
    }

    requirements {
        doesNotContain("teamcity.agent.jvm.os.name", "Windows") // the install step uses curl and bash
    }

    failureConditions {
        executionTimeoutMin = 120 // the first indexing run can take a while
    }
})
```

GitLab CI/CD:

Define the `JCP_ORG_SERVICE_CLIENT_ID` and `JCP_ORG_SERVICE_CLIENT_SECRET` [CI/CD variables](https://docs.gitlab.com/ci/variables/) as masked. GitLab exposes them to the job environment automatically.

```YAML
stages:
  - index

index_code:
  stage: index

  image: ubuntu:24.04 # any Linux image with bash; curl and git are installed below

  # Define your own rules; this runs on pushes, schedules, and manual runs on the default branch
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

  before_script:
    - apt-get update && apt-get install -y curl git ca-certificates
    - curl -fsSL https://download.jetbrains.com/jetbrains-context/install.sh | bash

  script:
    - ~/.jbcontext/bin/jbcontext --ci index
```

Jenkins:

Set up the `git-creds`, `jcp-org-service-client-id`, and `jcp-org-service-client-secret` [credentials](https://www.jenkins.io/doc/book/using/using-credentials/#configuring-credentials); the two service account values are Secret text credentials. The example is a Declarative Pipeline.

```GROOVY
pipeline {

    agent any // needs curl and git available

    environment {
        JCP_ORG_SERVICE_CLIENT_ID = credentials('jcp-org-service-client-id')
        JCP_ORG_SERVICE_CLIENT_SECRET = credentials('jcp-org-service-client-secret')
    }

    stages {
        stage('Checkout code') {
            steps {
                git branch: 'main', credentialsId: 'git-creds', url: 'https://my.git.hosting/repo.git'
            }
        }
        stage('Install Air Context CLI') {
            steps {
                sh 'curl -fsSL https://download.jetbrains.com/jetbrains-context/install.sh | bash'
            }
        }
        stage('Index code') {
            steps {
                sh '~/.jbcontext/bin/jbcontext --ci index'
            }
        }
    }

}
```

## Troubleshooting

JCP service account authentication failed: could not obtain an access token
: The credential exchange was rejected. Check that `JCP_ORG_SERVICE_CLIENT_ID` and `JCP_ORG_SERVICE_CLIENT_SECRET` match the values shown in JetBrains Console and that the service account hasn't been deleted or disabled.

Indexing requests fail with 401 or 403
: The credentials are valid, but the service account isn't allowed to index. Ask an administrator to check that the service account has [AI access](ai-access.html#grant-ai-access).

Repository is too large to be indexed
: The server lifts its size limit only for runs authenticated with an organization service account. Check that `JCP_ORG_SERVICE_CLIENT_ID` and `JCP_ORG_SERVICE_CLIENT_SECRET` are set for the step that runs the indexing.

Project directory is not a git repository
: The CLI didn't find a `.git` folder in the working directory and indexes it as a non-Git folder, so each run may produce a separate snapshot. Make sure that the checkout step runs before the indexing step and that the CLI runs from the repository root, or pass `--project-path`.

For more details, add `-v` to the `index` command: with `--ci`, the debug output streams to the build log. `jbcontext doctor` runs end-to-end self-tests: version, authentication, repository, index, and search. See also [Troubleshooting and FAQ](context-troubleshooting.html).

