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

# GitLab App

> Link the Aspect Workflows GitLab App to your GitLab top-level group so authenticated Aspect CLI features can post commit statuses, rolling MR notes, and inline lint comments.

The **Aspect Workflows GitLab App** is what lets the Aspect API call GitLab on your behalf. Link it once for your GitLab top-level group, and authenticated CLI features — `GitlabCommitStatuses`, `GitlabStatusComments`, and `GitlabLintComments` — start posting results to your merge requests.

This page covers linking the App. For the account setup and the `ASPECT_API_TOKEN` your CI uses, see [Authenticating the Aspect CLI](/docs/cli/authentication).

<Note>
  Using GitHub? See the [GitHub App](/docs/cli/authentication-github) for the equivalent.
</Note>

## What the App enables

Once linked, these CLI features can post to GitLab from your CI runs:

* **`GitlabCommitStatuses`** — per-task pass/fail status on the commit under review (the rows GitLab shows in the MR's "Pipelines" / commit status column).
* **`GitlabStatusComments`** — a single rolling MR note that aggregates every task's live status into one comment, updated as jobs complete.
* **`GitlabLintComments`** — inline `aspect lint` findings posted as position-bound discussions on the MR diff, with one-click suggestion blocks. See [`aspect lint`](/docs/cli/tasks/lint#gitlab-mr-comments) for behavior and config.

## Link the App

<Steps>
  <Step title="Have an Aspect account with the right role">
    Linking the App requires an Aspect account with the **Account Admin** or **GitLab Integration Admin** role. If you don't have an account yet, or need a role assigned, see the [account setup notes](/docs/cli/authentication#setup-overview) first.
  </Step>

  <Step title="Link the Aspect Workflows GitLab App to your GitLab top-level group">
    <a id="gitlab-app-installation" />

    The **Aspect Workflows GitLab App** is an OAuth Application Aspect has already registered on `gitlab.com` — **you don't create or register anything on the GitLab side**. The link flow walks you through the standard GitLab OAuth consent screen to authorize the existing Aspect App against the top-level group you own; that consent is what allows the Aspect CLI to call the GitLab API on your behalf.

    <Note>
      **Self-hosted GitLab is not yet supported** — only `gitlab.com` works today. See [Self-hosted GitLab](#self-hosted-gitlab) below.
    </Note>

    Use the panel below to start the consent flow:

    {user.loggedIn && user.tenantMetadata?.gitlabAppInstallations?.length > 0 ? (
          <Note>
          <strong>Aspect Workflows GitLab App is linked</strong> to {user.tenantMetadata.gitlabAppInstallations.length === 1 ? '1 GitLab user' : `${user.tenantMetadata.gitlabAppInstallations.length} GitLab users`}:

          {user.tenantMetadata.gitlabAppInstallations.map((inst) => (
          <div key={inst.id} style={{display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 0', borderBottom: '1px solid var(--border)'}}>
            <span>
              <strong>{inst.login !== 'pending' && inst.login !== 'unknown' ? inst.login : `Installation ${inst.id}`}</strong>
              {' '}
              <span style={{color: 'var(--muted)', fontSize: '14px'}}>
                (owns: {(inst.ownedGroups || []).join(', ') || '—'})
              </span>
            </span>
            <a href={`https://api.aspect.build/gitlab/installations/unlink?installation_id=${inst.id}`} target="_self"
               style={{color: '#dc2626', fontSize: '14px'}}>
              Unlink
            </a>
          </div>
          ))}

          <br />
          <a href="https://api.aspect.build/gitlab/installations/link" target="_self">Link another GitLab account</a> — only top-level groups where you are an **Owner** show up as an authorized target.
          </Note>
          ) : user.loggedIn ? (
          <Info>
          <strong>GitLab App is not yet linked to your account.</strong>
          <br /><br />
          <a href="https://api.aspect.build/gitlab/installations/link" target="_self">Install and link the Aspect Workflows GitLab App</a> — approve on GitLab, then approve the tenant association on Aspect. Only top-level groups where you are an Owner show up as an authorized target.
          </Info>
          ) : (
          <Info>
          To manage the GitLab App integration, please <a href="/login?redirect=%2Fdocs%2Fcli%2Fauthentication-gitlab">log in</a> with your Aspect account.
          </Info>
          )}

    Two permissions are required to complete this step:

    * **GitLab side:** you must be an **Owner of the top-level GitLab group** you want the App to act on. GitLab gates `api`-scoped OAuth authorization on Owner role. No App installation or admin action is required on the GitLab side beyond selecting **Authorize** on the consent screen.
    * **Aspect side:** your Aspect account must have the **Account Admin** or **GitLab Integration Admin** role.

    Behind the scenes the link flow:

    1. Sends you through the GitLab OAuth consent screen for `scope=api read_api` against the Aspect Workflows GitLab App.
    2. Captures the top-level groups you're an Owner of (`min_access_level=50`) at consent time and stores them on your install record as `ownedGroups`.
    3. Sends you through the Aspect consent screen to bind the GitLab identity to your Aspect tenant.

    Each Aspect tenant can carry multiple linked GitLab users (e.g. one user for each top-level group). When the CLI mints a token, the Aspect API picks the install whose `ownedGroups` covers the project's top-level group.
  </Step>

  <Step title="Generate an Aspect API token for CI">
    With the App linked, generate an `ASPECT_API_TOKEN` and wire it into CI. Pick **GitLab CI** roles (covers `GitlabCommitStatuses`, `GitlabStatusComments`, and `GitlabLintComments`).

    See [Generate an Aspect API token](/docs/cli/authentication#generate-an-aspect-api-token-for-ci) for the full walkthrough and the [GitLab token roles and scopes](/docs/cli/authentication#gitlab-token-roles-and-scopes) table. The GitLab tab of that page's `.gitlab-ci.yml` example shows the `rules:` clause both status features need — they gate on a merge-request pipeline.
  </Step>
</Steps>

## When the features fire

All three features ship enabled by default in the Aspect CLI. They only post to GitLab when:

1. **A merge-request pipeline is running** (`CI_MERGE_REQUEST_IID` present), and
2. **`ASPECT_API_TOKEN` is set** with appropriate roles, and
3. **An Aspect Workflows GitLab App install exists** that covers your project's top-level group.

For an MR pipeline to be created at all, your project must have **Settings → Merge requests → Pipelines for merge requests** enabled, and your job's `rules:` must allow `merge_request_event` (see the GitLab tab of the [`.gitlab-ci.yml` example](/docs/cli/authentication#generate-an-aspect-api-token-for-ci)).

`GitlabLintComments` additionally only fires on the `aspect lint` task when lint findings are routed to inline comments — see [`aspect lint` › GitLab MR comments](/docs/cli/tasks/lint#gitlab-mr-comments) for its behavior, the suggestion/dedup/auto-resolve semantics, and the `--gitlab-lint-comments:max-pr-comments` cap.

When any precondition is missing, the features skip silently and the underlying `aspect` command continues normally.

<Tip>
  Storing `ASPECT_API_TOKEN` as a **group-level** CI/CD variable (rather than per-project) lets every project under the group pick it up automatically — useful when you're rolling out across many repos at once.
</Tip>

## Configuring the features

Common knobs (set in `.aspect/config.axl`):

```python title=".aspect/config.axl" theme={null}
load("@aspect//feature/gitlab_commit_statuses.axl", "GitlabCommitStatuses")
load("@aspect//feature/gitlab_status_comments.axl", "GitlabStatusComments")
load("@aspect//feature/gitlab_lint_comments.axl", "GitlabLintComments")

def config(ctx: ConfigContext):
    # Slow the commit-status refresh cadence (default 20s) — useful on
    # busy projects where GitLab API rate limit is tight.
    ctx.features[GitlabCommitStatuses].args.min_update_interval_seconds = 60

    # Same for the rolling MR note (default 20s).
    ctx.features[GitlabStatusComments].args.min_poll_interval_seconds = 30

    # Cap inline lint discussions per run (default 25; 0 disables posting).
    ctx.features[GitlabLintComments].args.max_pr_comments = 50

    # Disable a feature entirely:
    # ctx.features[GitlabCommitStatuses].args.enabled = False
```

Command-line overrides:

```shell theme={null}
aspect build //... \
    --gitlab-commit-statuses:mode=always \      # ci-only (default) | local-only | always
    --gitlab-status-comments:mode=always

aspect lint //... \
    --gitlab-lint-comments:max-pr-comments=50   # GitlabLintComments has no mode flag;
                                                # it gates on the lint task + findings routing
```

### `mode=always` for local testing

Running locally against a real MR without a GitLab CI pipeline — useful when iterating on the integration. Set the override env vars in your shell, then run with `mode=always`:

```shell theme={null}
export ASPECT_GITLAB_MR_IID=123
export ASPECT_GITLAB_PROJECT_PATH=mygroup/myproject
export ASPECT_GITLAB_SHA=$(git rev-parse HEAD)
aspect build //... \
    --gitlab-commit-statuses:mode=always \
    --gitlab-status-comments:mode=always
```

These env vars are the same escape hatch used by [Aspect Workflows](/docs/aspect-workflows/integration/gitlab) when posting status back to GitLab from runners that don't run inside GitLab CI directly.

## Self-hosted GitLab

Only `gitlab.com` is supported today. Self-hosted GitLab instances (`gitlab.example.com`, GitLab Dedicated, etc.) cannot use the features on this page yet — the Aspect Workflows GitLab App is registered against `gitlab.com`.

If self-hosted support would unblock you, please [reach out](mailto:hello@aspect.build) so we can prioritize it.

## Troubleshooting

If authentication is unavailable (missing credentials, App not linked for the project's top-level group, or not an MR context), the features skip their work and the underlying `aspect` command continues normally.

| Symptom                                                                                     | Likely cause                                                                                                                                                                                                                                                                             |
| ------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GitLab status comments: No GitLab MR context — skipping.`                                  | Pipeline isn't an MR pipeline. Confirm `CI_MERGE_REQUEST_IID` is set and that the job's `rules:` allow `merge_request_event`.                                                                                                                                                            |
| `GitLab commit statuses: Authentication failed for <group>/<project> — credentials expired` | `ASPECT_API_TOKEN` expired or wasn't injected into the job. Re-issue from the portal and update the CI/CD variable.                                                                                                                                                                      |
| `Authentication failed ... access denied: No GitLab App installation linked for "<group>"`  | No linked install whose `ownedGroups` covers the project's top-level group. Re-run the link flow from the panel above as an Owner of that group.                                                                                                                                         |
| `Authentication failed ... access denied: requires "aspect.gitlab.token.api" permission`    | Your `ASPECT_API_TOKEN` was issued without the role the feature needs. Re-issue with **GitLab CI** (or **GitLab Integration User**) — see [When a role is missing](/docs/cli/authentication#when-a-role-is-missing).                                                                     |
| `commit_status POST failed (state=running): request failed: 401 "Token was revoked"`        | A third-party AXL feature called `gitlab.authenticate(...)` directly instead of going through `lib/gitlab_token_cache.axl`. GitLab's OAuth refresh-grant revokes prior access tokens on each new mint, so two independent callers race-revoke each other's tokens. Use the cache helper. |
| Commit status row stuck on `pending` long after the task finished                           | The terminal-state POST hit a transient API failure. Re-running the job posts the terminal state on next run; no manual recovery needed.                                                                                                                                                 |

If MR notes or commit statuses aren't appearing as expected, check the GitLab App installation (above panel) and `ASPECT_API_TOKEN` first.

## Support

Hit a bug or have a feature request? Open an issue on [aspect-build/aspect-cli](https://github.com/aspect-build/aspect-cli/issues). For questions or to chat with the team and other users, join our community Slack at [slack.aspect.build](https://slack.aspect.build).
