# GitLab

{% hint style="info" %}
Cortex connects to many third-party vendors whose system interfaces frequently change. As a result, integration behavior or configuration steps may shift without notice. If you encounter unexpected issues, check with your system administrator or refer to the vendor's documentation for the most current information. Additionally, integration sync times vary and are subject to scheduling overrides and timing variance.
{% endhint %}

[GitLab](https://about.gitlab.com/) is a Git-based version control system with cloud and self-hosted options.

Integrating GitLab with Cortex allows you to:

* Discover and track ownership of entities
* Follow a [GitOps](/configure/gitops.md) workflow with GitLab
* View commits alongside events and other data on [entity pages in Cortex](#view-gitlab-data-on-entity-pages-in-cortex)
* View information about merge requests in the [engineering homepage](#engineering-homepage)
* Use GitLab metrics in [Eng Intelligence](#eng-intelligence) to understand key metrics and gain insight into services, incident response, and more
* Create [Scorecards](#scorecards-and-cql) to monitor development maturity relating to GitLab projects

## How to configure GitLab with Cortex

There are two options for integrating GitLab: the default configuration method and Cortex Axon Relay, a relay broker allows you to securely connect your on-premises GitLab data.

### Prerequisites

Before getting started:

* A GitLab user with at least the `Maintainer` role must create a GitLab [personal access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html) or [group access token](https://docs.gitlab.com/ee/user/group/settings/group_access_tokens.html) with the `read_api` scope.
  * We recommend that you create the token at the parent group level, as GitLab does not support using a scoped token to read members from a parent group. If you do not create the token at the parent level, then you will need to manually configure groups in your GitLab settings in order for identity mapping and teams to work as expected.
  * Note that `Maintainer` is sufficient for project/repo data, but retrieving user emails for identity mapping typically requires an `Owner`- or `Admin`-generated token with `api` or `read_user` scope, particularly in enterprise/SSO environments.
* If you're using the Scaffolder for entities in a given GitLab instance, make sure that configuration has the full `api` scope.

#### Self-hosted prerequisites

If you're using a self-hosted instance of GitLab, you'll need to verify that your Cortex instance is able to reach the GitLab instance.\
\
We route our requests through a static IP address. Reach out to support at <help@cortex.io> to receive details about our static IP. If you're unable to directly allowlist our static IP, you can route requests through a secondary proxy in your network that has this IP allowlisted and have that proxy route traffic to your GitLab instance.

{% tabs %}
{% tab title="Standard configuration" %}
**Configure the integration in Cortex**

1. In Cortex, navigate to the [GitLab settings page](https://app.getcortexapp.com/admin/integrations/gitlab):
   * Click **Integrations** from the main nav. Search for and select **GitLab**.
2. Click **Add configuration**.
3. Configure the GitLab integration form:
   * **Account alias**: Enter the alias you will use to tie entity registrations to different configuration accounts.
   * **Token**: Enter your personal or group access token.
   * **Host**: Enter your host. If using a custom GitLab instance, enter the URL without the API path (e.g. `https://gitlab.getcortexapp.com`)
   * **Hide personal projects**: Toggle this setting on if you do not want your personal projects pulled in to Cortex. Toggle this setting off to allow Cortex to pull your personal projects.
4. Click **Save**.
   {% endtab %}

{% tab title="Relay broker" %}
**Configure GitLab with Cortex Axon Relay**

See [Internally hosted integrations](/ingesting-data-into-cortex/integrations/axon-relay.md) for instructions. Make sure to follow the GitLab-specific instructions for the docker-compose.yml file.
{% endtab %}
{% endtabs %}

Once you save your configuration, you'll see it listed on the integration's settings page in Cortex. If you’ve set everything up correctly, you’ll see the option to **Remove Integration** in Settings.

You can also use the **Test all configurations** button to confirm that the configuration was successful. If your configuration is valid, you’ll see a banner that says “Configuration is valid. If you see issues, please see documentation or reach out to Cortex support.”

**Configure the integration for multiple GitLab accounts**[**​**](https://docs.cortex.io/docs/reference/integrations/gitlab#configure-the-integration-for-multiple-propsintegration-accounts)

The GitLab integration has multi-account support. You can add a configuration for each additional by repeating the process above.

Each configuration requires an alias, which Cortex uses to correlate the designated with registrations for various entities. Registrations can also use a default configuration without a listed alias. You can edit aliases and default configurations from the GitLab page in your Cortex settings. Select the edit icon next to a given configuration and toggle **Set as default** on. If you only have one configuration, it will automatically be set as the default.

Cortex supports mapping multiple identities for a single user if you have multiple configurations of GitLab. See the [Identity mapping documentation](/configure/settings/managing-users/identity-mapping.md) for more information.

To modify the integration configuration, see [Modifying an existing integration configuration](/ingesting-data-into-cortex/integrations.md#modifying-an-existing-integration-configuration).

## Enable GitOps for your GitLab integration

Cortex supports a GitOps approach, which allows you to manage entities in Cortex through your version control system. If you would prefer this workflow over the UI for the GitLab integration, you must create a webhook. Please see the [Cortex GitOps documentation](/configure/gitops.md) for instructions.

## How to connect Cortex entities to GitLab

### Import entities

Cortex will discover entities for import from your GitLab configuration(s). These will appear in the import entities workflow.

See the [Create services documentation](/ingesting-data-into-cortex/entities-overview/entities/adding-entities/add-services.md#creating-services) for instructions on importing entities manually.

### Editing the entity descriptor

**Git**

By specifying the `x-cortex-git` field in your Cortex entity descriptor, you'll be able to see Git information in the entity page, including the top language, recent commits, and top contributors.

```yaml
x-cortex-git:
  gitlab:
    repository: cortex/docs
    basepath: myService
    alias: myApp
```

| Field        | Description                                                                                      | Required |
| ------------ | ------------------------------------------------------------------------------------------------ | :------: |
| `repository` | GitLab project ID or `namespace/repo` as defined in GitLab                                       |   **✓**  |
| `basepath`   | Subdirectory for the entity if it is in a monorepo                                               |          |
| `alias`      | Alias for the configuration in Cortex (only needed if you have opted into multi-account support) |          |

Only one repository can be defined for in a given entity's YAML in the `x-cortex-git` block.

**Ownership**

You can define the following block in your Cortex entity descriptor to add your GitLab groups.

Team name should match the group name in GitLab.

```yaml
x-cortex-owners:
  - type: group
    name: Team Name
    provider: GITLAB
    description: This is a description for this owner
```

| Field         | Description                                                 | Required |
| ------------- | ----------------------------------------------------------- | :------: |
| `type`        | Ownership type; must be defined as `group` for GitLab teams |   **✓**  |
| `name`        | GitLab team name                                            |   **✓**  |
| `provider`    | Name of integration (in this case, `GITLAB`)                |   **✓**  |
| `description` | Description for the GitLab team                             |          |

### Identity mapping

Cortex maps users' email addresses to discovered GitLab accounts, so you never need to define email ownership in an entity descriptor.

You can confirm users' GitLab accounts are connected from [GitLab identity mappings in settings](https://app.getcortexapp.com/admin/settings/gitlab-mappings).

If users are not loading in the identity mapping page, make sure that you have created your GitLab personal access token from the parent level as described in the [Prerequisites](#prerequisites).

## Using the GitLab integration

### View GitLab data on entity pages in Cortex

Cortex uses the GitLab integration for a significant amount of data that appears on [entities' detail pages](/ingesting-data-into-cortex/entities-overview/entities/details.md).

The GitLab integration will populate the **Repo** and **Language** detail blocks on an entity's details page.

In the **Recent activity preview**, you'll find the recent commits and releases. These will also appear in the event timeline.

These data will appear for entities imported from a Git source or those that have a Git repo defined in their YAMLs.

#### **Events**

On an entity's **Events** page, you can find all of the commits and releases associated with that entity. Each is hyperlinked to the commit or release page in GitLab and includes a timestamp.

#### **CI/CD**

To see pipeline runs for GitLab, use the [deploys API](/ingesting-data-into-cortex/entities-overview/entities/deploys.md) to add deploy information. After doing this, from the **CI/CD > Deploys** page in the entity's sidebar, you will see a history of pipeline runs.

#### **Repository**

You can access more detailed information pulled from GitLab in the **Repository** page in the entity's sidebar. At the top of the page, you'll find the repo associated with that entity and the most-used language in files for that entity. In the **Top contributors** block, you'll find the three users who have contributed the most code and the number of their contributions.

In the Commits section, you'll find the 10 most recent commits and metadata about each. Below Commits is the Recent releases section, which includes the 5 most recent releases.

#### **Packages**

Packages are automatically scraped from your Git repos or they can be submitted via the [packages API](/api/readme/packages.md). The package file must be in the root of your repository — or, if you're using `basepath`, in the root of the subdirectory — to be scraped by Cortex. You can query an entity's packages in [CQL explorer](https://app.getcortexapp.com/admin/cql-explorer) using `packages()`.

To view packages, click **Packages** in the entity's sidebar.

The following package types are automatically scraped from repositories:

* JavaScript / Node.js: `package.json`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`
* Python: `requirements.txt`, `pipfile.lock`
* .NET (C#): `packages.lock.json`
* Java: `pom.xml`
* Go: `go.sum`

All other files of these types can be added via the [packages API](/api/readme/packages.md).

#### Team pages

When a GitLab team is registered in a team entity descriptor, Cortex will pull GitLab users in to the **Members** tab. When available, Cortex will pull in the profile picture and email address for each user.

If team members are not appearing as expected, make sure that you have created your GitLab personal access token from the parent level as described in the [Prerequisites](#prerequisites).

### Engineering homepage

The GitLab integration enables Cortex to pull information about merge requests into the [homepage](/streamline/homepage.md). You can find your open merge requests and any merge requests assigned to you for review.

Merge requests from GitLab are refreshed every 2 minutes.

### Eng Intelligence

The [Eng Intelligence tool](/improve/eng-intelligence.md) also uses merge request data from GitLab to generate metrics:

* Average MR open to close time
* Avg time to first review
* Avg time to approval
* MRs opened
* Weekly MRs merged
* Avg MRs reviewed/week
* Avg commits per MR
* Avg lines of code changed per MR

You can read more about how Eng Intelligence tracks metrics for teams and users in the Eng Intelligence walkthrough.

To add deployments for your GitLab related entity, you can send a deployment event to the [Cortex API](/api/readme/deploys.md).

### Scorecards and CQL

With the GitLab integration, you can create Scorecard rules and write CQL queries based on GitLab data.

See more examples in the [CQL Explorer](https://app.getcortexapp.com/admin/cql-explorer) in Cortex.

<details>

<summary>Approvals required to merge</summary>

Number of approvals required to merge a pull/merge request into a repository. Defaults to 0 if no approvals are defined.

**Definition**: `git.numOfRequiredApprovals()`

**Examples**

For a security or development maturity Scorecard, you can write a rule to make sure at least one approval is required to merge a pull/merge request:

```
git.numOfRequiredApprovals() > 0
```

By having a rigorous PR process in place for a repo, you can make sure changes aren't made that create vulnerabilities. This kind of rule could also be used in a best practices or project standards Scorecard.

You can also use a similar expression in the Query Builder to find entities lacking approval:

```
git.numOfRequiredApprovals() < 1
```

</details>

<details>

<summary>Git repository set</summary>

Check if an entity has a registered Git repository.

**Definition:** `git (==/!=) null: Boolean`

**Example**

In a Scorecard, you can write a rule that detects whether an entity has a Git repository set:

```
git != null
```

</details>

<details>

<summary>Pipeline build success rate</summary>

The percentage of build pipelines that complete successfully. This is calculated against builds on the default branch, for commits in the last 30 days. The calculation is # successful builds / (# successful + # failed).

**Definition:** `git.percentBuildSuccess(): Number`

**Example**

In a Scorecard, you can write a rule that requires at least 90% of build runs to be successful:

```
git.percentBuildSuccess() > 0.9
```

</details>

<details>

<summary>Branches</summary>

List all live branches with some basic metadata.

* Head
* Is protected
* Name

**Definition**: `git.branches()`

**Example**

For a best practices Scorecard, you can make sure that branches associated with an entity match a standard naming convention:

```
git.branches().all((branch) => branch.name.matches("(main|master|feat-.*|bug-.*|task-.*)"))
```

</details>

<details>

<summary>Branch protection details</summary>

Find details for specified branch, or default branch if none is specified.

* Branch name
* Code owner reviews required
* Dismiss stale reviews
* Required status checks
* Restrictions apply to admin
* Review required

**Definition:** `git.branchProtection()`

**Examples**

For a security Scorecard, you can write a rule to make sure the default branch is protected:

```
git.branchProtection() != null
```

Because vulnerabilities in the default branch are critical, this rule should be in one of the first couple levels.

You can also use the Query Builder to find entities with unprotected default branches:

```
git.branchProtection() = null
```

</details>

<details>

<summary>Commits</summary>

Get the latest commits **(to a maximum of 100)** for a defined lookback period **(defaulting to 7 days)**.

* Date
* Message
* SHA
* URL
* Username

These results can be filtered based on branch name, using the default branch if no other branch is provided.

**Definition:** `git.commits()`

**Example**

You can use the `git.commits()` expression in a security Scorecard to make sure entities have fewer than three commits to a "security-fixes" branch in the last week:

```
git.commits(branch="security-fixes", lookback=duration("P7D")).length < 3
```

Entities passing this rule will include those that haven't needed three or more security fixes. This can indicate that there aren't vulnerabilities in a given entity's code, but could also suggest that fixes aren't being implemented. Using this rule in conjunction with one focused on vulnerabilities could provide the extra context needed to gain a better understanding of what's happening.

</details>

<details>

<summary>Default branch</summary>

Default branch for the repository, or `main` when null.

**Definition:** `git.defaultBranch()`

**Example**

If default branches should always be named "main," you can write a rule to make sure entities follow this practice:

```
git.defaultBranch().matches("main")
```

</details>

<details>

<summary>File contents</summary>

Load the contents of a file from the entity's associated repository.

The contents can be validated by using string comparison operations or parsed by the built-in jq function. The jq function will automatically coerce file contents of JSON or YAML formats.

**Definition:** `git.fileContents()`

**Example**

For a Scorecard focused on development maturity, you could use the `git.fileContents()` rule to enforce that a CI pipeline exists, and that there is a testing step defined in the pipeline.

```
git.fileContents("circleci/config.yml").matches(".*npm test.*")
```

A best practices Scorecard, meanwhile, could use this expression for a number of rules:

* To make sure node engine version in specified in the `package.json` file:

  ```
  jq(git.fileContents("package.json"), ".engines.node") != null
  ```
* To make sure TypeScript projects have a `tsconfig.json` file checked in:

  ```
  jq(git.fileContents("package.json"), ".devDependencies | with_entries(select(.key == \"typescript\")) | length") == 0 or git.fileExists("tsconfig.json")
  ```
* To make sure projects using yarn do not allow NPM:

  ```
  jq(git.fileContents("package.json"), ".engines.yarn") == null or jq(git.fileContents("package.json"), ".engine.npm") = "please-use-yarn"
  ```
* And to ensure the yarn version being used is not deprecated:

  ```
  jq(git.fileContents("package.json"), ".engines.yarn") == null or !(semver("1.2.0") ~= semverRange(jq(git.fileContents("package.json"), ".engines.yarn")))
  ```

</details>

<details>

<summary>File exists</summary>

Check if file exists from within the entity's associated repository.

**Definition:** `git.fileExists()`

**Examples**

For a Scorecard focused on best practices, you can make sure that repositories contain a README.md file:

```
git.fileExists("README.md")
```

This rule would make sense in the first level because it's so essential.

A higher-level rule in a best practices Scorecard might confirm that developers are checking in lockfiles to ensure consistency in package installs:

```
git.fileExists("yarn.lock") OR git.fileExists("package-lock.json")
```

And/or a rule that makes sure there are unit tests enabled:

```
git.fileExists("*Test.java")
```

Finally, you could write a rule to make sure projects have a standard linter:

```
git.fileExists(".prettierrc.json") OR git.fileExists(".eslintrc.js")
```

</details>

<details>

<summary>Number of Git vulnerabilities</summary>

Check the number of vulnerabilities for an entity's associated repository.

**Definition:** `git.numOfVulnerabilities()`

**Examples**

A security-focused Scorecard will likely include a rule making sure there are no Git vulnerabilities:

```
git.numOfVulnerabilities() == 0
```

You can use Scorecard levels to stratify vulnerabilities by risk. An initial level might make sure there are no critical vulnerabilities:

```
git.numOfVulnerabilities(severity=["CRITICAL"]) == 0
```

While a higher level might make sure there are no vulnerability warnings:

```
git.numOfVulnerabilities(severity=["WARNING"]) == 0
```

</details>

<details>

<summary>List of Git vulnerabilities</summary>

Find all vulnerabilities within a repository. Can filter by severity or scan type.

**Definition**: `git.vulnerabilities()`

**Examples**

You could write a Scorecard rule that verifies an entity has fewer than 5 Git vulnerabilities:

```
git.vulnerabilities().length < 5
```

You could write a rule to verify that an entity has no critical vulnerabilities:

```
git.vulnerabilities(severity=["CRITICAL"]).length == 0
```

</details>

<details>

<summary>Has Cortex YAML</summary>

Check if a repository has a valid `cortex.yaml` file checked in at the root directory (when GitOps is enabled).

**Definition:** `git.hasCortexYaml()`

**Example**

If you're using a Scorecard to track a migration from Cortex UI to GitOps, you can use this rule to make sure entities are set up for GitOps management of entity descriptors:

```
git.hasCortexYaml() == true
```

</details>

<details>

<summary>Last commit details</summary>

Provides last commit details.

* Date
* Message
* SHA
* URL
* Username

**Definition:** `git.lastCommit()`

**Examples**

One of the first rules you might write for a Scorecard focused on development maturity or security is one validating that the last commit was within the last month:

```
git.lastCommit().freshness < duration("P1M")
```

As counterintuitive as it may seem, services that are committed too infrequently are actually at more risk. People who are familiar with the service may leave a team, institutional knowledge accumulates, and from a technical standpoint, the service may be running outdated versions of your platform tooling.

Depending on best practices at your organization, you may want to confirm entities are updated within a week:

```
git.lastCommit().freshness < duration("P7D")
```

Confirming whether a service was updated within the last week can help team members catch outdated code sooner. Plus, if there is a security issue, you can quickly determine which services have or have not been updated to patch the vulnerability.

</details>

<details>

<summary>Pull requests</summary>

Lists pull requests opened during a defined lookback period.

* Approval date
* Author
* Date closed
* Date opened
* First review date
* Last updated
* Number of commits
* Number of lines added
* Number of lines deleted
* Organization
* Repository
* Source
* Status
* URL

**Definition:** `git.pullRequests()`

**Example**

You can use the `git.pullRequests()` query to find entities that have a small number of pull requests opened in the last two weeks:

```
git.pullRequests(lookback=duration("P14D")).length < 3
```

This can highlight entities that haven't been updated recently, which may be especially useful when entities have to be updated to address a vulnerability.

</details>

<details>

<summary>Reviews</summary>

List reviews left during a defined lookback period.

* Organization
* Repository
* Review date
* Reviewer

**Definition:** `git.reviews()`

**Examples**

A development maturity Scorecard might use the `git.reviews()` expression to make sure that there is a rigorous review process in place before changes are implemented:

```
git.reviews(lookback=duration("P7D")).length > 25
```

This rule makes sure that there are more than 25 reviews left in the last week.

</details>

<details>

<summary>Workflow runs</summary>

Get workflow runs meeting given filter criteria, including conclusions, statuses, and a lookback period.

* Conclusion
* Name
* Run started at
* Run time
* Run updated at
* Status

Conclusions: `FAILURE`, `SUCCESS`, `TIMED_OUT`

Statuses: `QUEUED`, `IN_PROGRESS`, `COMPLETED`

The lookback period specifies a duration for which returned runs should be created within, defaulting to a period of 3 days.

* The `runTime` of the `WorkflowRun` object represents the difference between `runStartedAt` and `runUpdatedAt` times in seconds.

**Definition:** `git.workflowRuns()`

**Example**

To make sure an entity has had a successful workflow run within the last two weeks, you can write a rule like:

```
git.workflowRuns(conclusions=["SUCCESS"], statuses=["COMPLETED"], lookback=duration("P14D")).length > 0
```

This rule is checking for GitHub workflow runs with a `SUCCESS` conclusion and `COMPLETED` status during a 14-day lookback window.

</details>

**Ownership CQL**

<details>

<summary>All ownership details</summary>

A special built-in type that supports a null check or a count check, used to enforce ownership of entities.

**Definition:** `ownership: Ownership | Null`

**Example**

An initial level in a security Scorecard might include a rule to ensure an entity has at least one team as an owner:

```
ownership.teams().length > 0
```

</details>

<details>

<summary>All owner details</summary>

List of owners, including team members and individual users, for each entity

**Definition:** `ownership.allOwners()`

**Example**

The Scorecard might include a rule to ensure that entity owners all have an email set:

```
ownership.allOwners().all((member) => member.email != null)
```

</details>

<details>

<summary>Team details</summary>

List of teams for each entity

**Definition:** `ownership.teams(): List<Team>`

**Example**

The Scorecard might include a rule to ensure that an entity owners all have a description and are not archived:

```
ownership.teams().all(team => team.description != null and team.isArchived == false)
```

</details>

### View integration logs <a href="#still-need-help" id="still-need-help"></a>

## Background sync

Cortex conducts a background sync of GitLab identities every day at 10 a.m. UTC. Merge requests are refreshed every 2 minutes.

## FAQ and Troubleshooting

**Why is my CQL query `git.branchProtection()` returning no results or a 403 error?**

This can happen if you do not have the `read_api` scope set for your access token, or if the GitLab user who generated the token does not have at minimum the `Maintainer` role.

**Why isn't Cortex pulling in user emails from GitLab (identity mapping / team members missing emails)?**

Email addresses are treated as sensitive user identity data in GitLab, not project data, so a `Maintainer`-level token isn't sufficient in most modern GitLab environments, especially enterprise instances with SSO or stricter privacy controls. Use a token created by an `Owner` or `Admin` user with the `api` (or `read_user`) scope.

Note that `Maintainer` tokens *may* work on loosely configured self-managed instances, but this is not reliable. Email visibility also depends on instance configuration and individual user privacy settings (e.g., hidden emails).

## Still need help?[​](https://docs.cortex.io/docs/reference/integrations/aws#still-need-help) <a href="#still-need-help" id="still-need-help"></a>

The following options are available to get assistance from the Cortex Customer Engineering team:

* **Email**: <help@cortex.io>, or open a support ticket in the in app Resource Center
* **Slack**: Users with a connected Slack channel will have a workflow added to their account. From here, you can either @CortexTechnicalSupport or add a `:ticket:` reaction to a question in Slack, and the team will respond directly.

Don’t have a Slack channel? Talk with your Customer Success Manager.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.cortex.io/ingesting-data-into-cortex/integrations/gitlab.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
