Skip to main content
Using GitHub? See GitHub for the GitHub App equivalent.

Why authenticate the CLI?

Two Aspect CLI features post results back 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.
Both authenticate to the Aspect API, which then calls GitLab via the Aspect GitLab App installed for your top-level GitLab group and linked to your Aspect account. Plain commands that don’t touch any of the above — for example, aspect test //... locally, or aspect build //... against a remote cache — work without authentication, so you can skip this page if none of these apply to your usage. Setup, end to end:
  1. Install the Aspect GitLab App for your GitLab top-level group and link it to your Aspect account (one-time per group).
  2. Generate an ASPECT_API_TOKEN — this is what the CLI uses to authenticate to the Aspect API on CI runners.

Setup

1

Create an Aspect account

Authentication is tied to an Aspect account — whenever the CLI authenticates (on CI via ASPECT_API_TOKEN, or locally via aspect auth login), it does so as your Aspect account.
Sign up for free at signup.aspect.build if you don’t have one. A free account works across all Aspect products and unlocks our free Bazel training courses.
The first user to sign up for an Aspect account is automatically granted the Account Admin role, which has the permissions needed for the next step. Subsequent users joining the same Aspect account need an existing admin to assign them either Account Admin or GitLab Integration Admin in the Aspect admin portal before they can install or link the GitLab App.
2

Link the Aspect GitLab App to your GitLab top-level group

The Aspect GitLab App is an OAuth Application Aspect has already registered on gitlab.comyou 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.
Self-hosted GitLab is not yet supported — only gitlab.com works today. See Self-hosted GitLab below.
Use the panel below to start the consent flow: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 clicking Authorize on the consent screen.
  • Aspect side: your Aspect account must have the Account Admin or GitLab Integration Admin role (see the previous step).
Behind the scenes the link flow:
  1. Sends you through the GitLab OAuth consent screen for scope=api read_api against the Aspect 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 FrontEgg 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.
3

Generate an Aspect API token (for CI)

CI runners are non-interactive, so they authenticate with a long-lived token instead of a browser flow. Open the API Tokens portal and click Generate Token. In the dialog:
  • Description — any label that identifies where the token will be used (e.g. ASPECT_API_TOKEN, gl-ci-main).
  • Roles — select one or more GitLab Integration roles. The role you pick gates which GitLab API surfaces the token can use — see Token roles and GitLab scopes below for the mapping. For most CI use cases pick GitLab CI (covers GitlabCommitStatuses and GitlabStatusComments); pick GitLab Integration User if you want a single token with the broadest access.
  • This token will expire — pick a lifetime that matches your secret-rotation policy.
After clicking Create, the portal shows a Client ID and Secret Key.
The Secret Key is shown once and never again. Copy it immediately — if you lose it you’ll need to generate a new token.
The value of ASPECT_API_TOKEN is the two strings joined with a colon:
<CLIENT_ID>:<SECRET_KEY>
4

Provide the token to CI

Store ASPECT_API_TOKEN as a CI/CD variable (Settings → CI/CD → Variables in your project or group) and mark it Masked. The CLI picks it up automatically.
.gitlab-ci.yml
aspect-build:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - aspect build //...
The rules: clause is what makes GitlabCommitStatuses and GitlabStatusComments fire — both features gate on detect_gitlab_mr() returning an MR context, which requires the pipeline to be running for a merge request (CI_MERGE_REQUEST_IID populated). On branch / scheduled / tag pipelines the features skip silently and the build runs normally.For an MR pipeline to be created at all, your project must have Settings → Merge requests → Pipelines for merge requests enabled.
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.

Token roles and GitLab scopes

ASPECT_API_TOKEN does not itself carry GitLab permissions. It authenticates to the Aspect API, and when a feature needs to call GitLab the Aspect API uses the linked App to mint a short-lived GitLab OAuth access token bound to your install’s ownedGroups. Which API surfaces the minted token can hit is determined by the roles assigned to your ASPECT_API_TOKEN in the API Tokens portal, bounded by the App’s own registered scope set (so a feature can never receive more access than the App was granted at install time). Roles are bundles of fine-grained permissions. Pick the smallest bundle that covers what your CI actually does, or pick GitLab Integration User for everything.
RoleGitLab token scopeTypical use
GitLab CIapiRecommended default for CI runners. Covers GitlabCommitStatuses and GitlabStatusComments.
GitLab Integration Userapi, read_apiFull access — equivalent to every GitLab Token: role combined.
GitLab Token: apiapiPosting commit statuses, creating/updating MR notes, anything else under GitLab’s broad api scope.
GitLab Token: external status checksapiPosting External Status Check responses on MRs (Premium/Ultimate tier only).
GitLab Token: MR notesapiCreating and updating merge request notes.
GitLab Token: MR readread_apiRead-only access to merge request metadata + changes.
Unlike the GitHub roles, GitLab token roles do not auto-imply each other. GitLab’s OAuth scope vocabulary is coarser than GitHub’s per-resource permission map, so role narrowing applies primarily as a FrontEgg-side permission gate; the minted token’s effective scope set is whatever the App was registered with for your role’s GitLab scope (today: api read_api).
If a feature needs a role your token doesn’t have, the Aspect API returns 403 and the feature skips gracefully — GitlabCommitStatuses and GitlabStatusComments log Authentication failed for <group>/<project> — <reason>. Add the missing role in the API Tokens portal and re-issue the token to enable the surface.

Configuring the features

Both 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 GitLab App install exists that covers your project’s top-level group.
When any of those preconditions is missing, the features skip silently and the underlying aspect command continues normally. Common knobs (set in .aspect/config.axl):
.aspect/config.axl
load("@aspect//feature/gitlab_commit_statuses.axl", "GitlabCommitStatuses")
load("@aspect//feature/gitlab_status_comments.axl", "GitlabStatusComments")

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

    # Disable a feature entirely:
    # ctx.features[GitlabCommitStatuses].args.enabled = False
Command-line overrides:
aspect build //... \
    --gitlab-commit-statuses:mode=always \      # ci-only (default) | local-only | always
    --gitlab-status-comments:mode=always

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:
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 when posting status back to GitLab from runners that don’t run inside GitLab CI directly.

Local development

aspect auth login walks through a device OAuth flow in your browser and stores credentials so subsequent aspect invocations pick them up automatically:
aspect auth login
The flow needs an interactive browser session and does not work on CI runners; CI must always use ASPECT_API_TOKEN.

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 GitLab App is registered against gitlab.com. If self-hosted support would unblock you, please reach out 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.
SymptomLikely 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 expiredASPECT_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" permissionYour ASPECT_API_TOKEN was issued without the role the feature needs. Re-issue with GitLab CI (or GitLab Integration User).
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 finishedThe 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. For questions or to chat with the team and other users, join our community Slack at slack.aspect.build.