# GitLab

Configure merge request verification, GitLab CI previews, Review Apps, and merge protection.

Test the user journeys affected by a GitLab merge request, using its exact source commit, and keep one results note and commit status current. Configure the project once, then run automatically on updates or request a manual rerun.

## Connect and configure a GitLab project

You need GitLab and Verify enabled for your organization and administrator access in Composal. GitLab.com projects use the same scenario selection, preview admission, run history, findings, recordings, and feedback as GitHub PRs. Connect GitLab through **Repositories → Import → GitLab**, authorize Composal, and import the project. Your GitLab account needs Maintainer or Owner access to manage the signed project webhook, read merge requests and diffs, write notes, and publish commit statuses. OAuth uses the `api` scope. Self-managed GitLab instances are not currently supported.

Wait for the import to become **Ready** before configuring verification. If a repository worker is starting or capacity is temporarily unavailable, the import stays queued and retries automatically at the same repository. A failed import displays its error; resolve the cause and use **Retry**. Pausing an import stops further progress until you resume it.

1. Prepare a [scenario pack](/docs/verification#expectations-as-code) for the connected Composal repository, with journeys, personas, and observable outcomes. Map product areas to source paths and dependencies for focused selection.
2. Prepare a ready **external Verify environment template** for the same repository. Its personas, [credential references](/docs/verification#credentials), and safety policy carry over to each MR URL. Confirm that a browser can reach and sign in to the preview.
3. Open **Verify → Integrations → GitLab** at `/{org}/verify/integrations/gitlab`. Choose the project, including its group/subgroup path, its scenario pack, and its environment template.
4. Choose a preview source below. Use **Advanced Settings** for excluded paths, 1–20 concurrent browsers, and a 30–3,600 second time budget; organization limits still apply. Select **Save Settings** before starting a manual run.
5. Enable **Automatic Verification** for MR updates. Use **Verify an MR** for an existing MR, then **Rerun verification** on its history page for another run.

Members can view settings. Administrators can save them and start runs; the configuring user must retain administrator access for automatic runs to continue. Repository-specific links use `?repository_id={repository_id}`. Switching repositories or leaving the page prompts you to keep or discard unsaved edits.

Saving upgrades the project's existing signed webhook to receive MR events as well as source pushes. Automatic verification responds to MR updates, including new commits, reopening, and becoming ready for review. Draft, closed, and merged MRs cannot start a new browser run. Manual verification remains available when automatic updates are off.

## Choose a preview source

Choose where the ready deployment of the MR source commit comes from:

- **Your CI:** deploy the exact MR source commit, wait until its preview is ready, then hand off the URL or a ready Verify environment using the helper below.
- **GitLab Environment:** configure the GitLab environment name, such as `review/{branch_slug}` or `review/mr-{mr}`. `{mr}` expands to the MR IID, `{branch}` to its source branch, and `{branch_slug}` to GitLab’s lowercase, normalized, 63-character ref slug. Verify uses only an available, non-production environment with a successful latest deployment whose SHA matches the current MR head and an external URL. Use a separate review environment for each MR; a shared environment's newer deployment cannot verify an older commit.
- **Composal Preview:** select an existing preview profile and entry service. The exact MR source commit must be available in the connected Composal repository. Verify provisions, deploys, and waits for that revision before testing.

## Select the affected journeys

Verify compares changed paths and available hunks with your scenario pack’s product areas and dependencies. Configure **Advanced Settings → Paths That Need No Browser Testing** for explicit exclusions, such as `docs/**` and `README.md`. A request is skipped only when every changed path, including rename sources, matches those exclusions. See [how selection works](/docs/verify/source-control#selection).

Incomplete GitLab file listings broaden selection to the full scenario pack. A truncated or oversized diff cannot accidentally qualify for a documentation-only skip. Rename sources count as changed paths.

## Use the GitLab CI helper

Include the versioned template in your project's `.gitlab-ci.yml`, extend `.composal-verify`, and run it after your deployment job. Store an administrator Composal API token as a **masked GitLab CI/CD variable** named `COMPOSAL_TOKEN`. Do not put the token in YAML or artifacts. Protected variables are available only to pipelines GitLab allows to access them; use a dedicated test organization token appropriate for your trusted MR branches. Fork pipelines must not receive it.

```yaml
include:
  - remote: https://composal.ai/ci/verify/v1/gitlab.yml

# Your existing deployment job must deploy CI_COMMIT_SHA and wait for readiness.
# Export its ready URL as PREVIEW_URL in a dotenv artifact.
# deploy-preview:
#   stage: deploy
#   ...
#   artifacts:
#     reports:
#       dotenv: preview.env

verify-preview:
  extends: .composal-verify
  # Keep these rules directly in this file to enable MR pipelines.
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_EVENT_TYPE == "detached" && $CI_PROJECT_ID == $CI_MERGE_REQUEST_SOURCE_PROJECT_ID && $CI_PROJECT_ID == $CI_MERGE_REQUEST_PROJECT_ID'
  needs:
    - job: deploy-preview
      artifacts: true
  variables:
    COMPOSAL_ORG: my-org
    COMPOSAL_REPOSITORY: my-composal-repo-slug
    COMPOSAL_PREVIEW_URL: $PREVIEW_URL
```

GitLab identifies the MR target project with `CI_MERGE_REQUEST_PROJECT_ID`. The rules require both the source project and MR project to match the pipeline project, keeping the token-bearing job out of fork pipelines.

Replace `deploy-preview` with your deployment job, and use the connected **Composal repository slug**, which can differ from the GitLab project name. The helper uses Node 24 and the final `.post` stage, reads the MR IID and source SHA from GitLab, requests verification, supplies the ready preview, and waits for its own run. Failed, blocked, cancelled, superseded, and timed-out results fail the CI job. Passed and explicitly skipped runs succeed. Its `composal-verify.json` artifact contains result links and IDs, without the token or preview URL.

| Variable | Purpose |
| --- | --- |
| `COMPOSAL_TOKEN` | Administrator API token; masked CI/CD variable. |
| `COMPOSAL_ORG` | Composal organization slug. |
| `COMPOSAL_REPOSITORY` | Connected Composal repository slug; defaults to `CI_PROJECT_NAME`. |
| `COMPOSAL_PREVIEW_URL` | Ready URL serving the exact MR source commit. |
| `COMPOSAL_ENVIRONMENT` | Ready Verify preview environment slug, instead of a URL. Its revision must attest the MR source SHA. |
| `COMPOSAL_VERIFY_WAIT` | `true` by default. `false` returns after handoff and does not wait for a verdict. |
| `COMPOSAL_VERIFY_TIMEOUT` | Wait timeout in minutes, 1–60; defaults to 15. Verification continues if the job times out. |

Use ordinary **detached MR pipelines**. Merged-results pipelines and merge trains deploy a synthetic merge commit, so this helper rejects them rather than claiming their preview covers the MR source head. The included job runs only for source branches in the connected project. If you override its rules, the helper still rejects fork pipelines and other pipeline types before calling Composal. Use MR rules for your deployment job too. If your project already defines `workflow: rules`, allow `merge_request_event` there and preserve your existing branch and tag rules. Configure workflow rules to avoid duplicate branch and MR pipelines.

For a custom Node-based job, download `https://composal.ai/ci/verify/v1/verify.mjs` and `https://composal.ai/ci/verify/v1/gitlab.mjs` into the same directory, then execute `node gitlab.mjs` with the variables above. Pin the remote include's `integrity` hash in GitLab when your organization requires immutable includes.

## Use the com CLI or API

[Install the CLI](/docs/quickstart). For local use, run `com login`, then select your organization with `com context switch my-org`. CI uses its secret store instead of an interactive login: expose the Composal token as `VEX_API_TOKEN`, and pass `--org` and `--repo` explicitly. See [CLI configuration](/docs/cli-configuration#config-layers).

Use `com verify merge-requests --help` to check support for the MR command. The existing `pull-requests` spelling remains compatible with a configured GitLab repository. The examples use MR `45`, organization `my-org`, and the connected **Composal repository slug** `my-repo`.

Start an existing MR using its saved preview source:

```sh
com verify merge-requests run 45 --org my-org --repo my-repo \
  --idempotency-key mr-45-review-1 --json
```

For a CI-owned ready Verify environment, pin the deployed source SHA on the request and handoff:

```sh
com verify --org my-org --repo my-repo merge-requests run 45 \
  --head-sha "$CI_COMMIT_SHA" --idempotency-key "gitlab-$CI_PIPELINE_ID" --json

com verify --org my-org --repo my-repo merge-requests preview 45 \
  --head-sha "$CI_COMMIT_SHA" --environment mr-45 --json
```

`run` creates a verification request using the saved preview source. In CI, `--head-sha` refuses an outdated pipeline before it can request a newer revision. Run `preview` only after its Verify environment is ready and records the full deployed SHA in `source_revision`. Keep that preview alive through browser execution and cleanup. Reuse an idempotency key to retry an uncertain response; use a new key for an intentional rerun.

Inspect the stable MR ID returned by `run`, then follow its browser sweep when one exists:

```sh
com verify merge-requests get <mr-id> --org my-org --json
com verify sweeps get <sweep-id> --org my-org --repo my-repo --json
com verify sweeps watch <sweep-id> \
  --org my-org --repo my-repo --timeout-seconds 900 --json
```

A successful submission means **accepted**. `sweeps watch` succeeds when the sweep becomes terminal, even when assertions fail. Use the GitLab CI helper's default waiting behavior to gate a job, or explicitly inspect the final outcome. Skipped and blocked requests can finish before a sweep exists.

The API also exposes `POST /api/v1/organizations/{org}/verify/merge_requests`, `POST .../merge_requests/preview`, `GET .../merge_requests/{id}`, and `POST .../merge_requests/{id}/rerun`. These routes require a GitLab-configured repository. The request and preview fields match the corresponding `pull_requests` endpoints. A stale head returns HTTP 409 and cannot hand a preview to a newer run.

## Read results and protect merges

Composal maintains one MR note with selection, the tested source commit, outcomes, problems, and links to protected recordings and complete run history. Notes are written as the GitLab account that connected the project; reconnect with that account to retain ownership of its result note. **MR !45** and the GitLab group/project identify the source throughout Verify.

The **Composal Verify** commit status follows the latest run for each source commit. Queued and preview-waiting runs are pending; browser runs are running; passed runs succeed; failed or blocked runs fail; superseded or cancelled runs are cancelled; explicit skips are skipped. When the MR has a matching source pipeline, the status is attached to that pipeline.

For merge protection, enable GitLab's **Pipelines must succeed** project setting and keep `verify-preview` blocking with `COMPOSAL_VERIFY_WAIT: "true"`. Verify does not change your project's merge policy. Skipped pipeline behavior depends on your GitLab project settings. Do not override the job with `allow_failure: true` if its verdict should block merging.


## Resolve GitLab setup and CI problems

| What you see | What to check |
| --- | --- |
| Configuration has no GitLab project | Confirm GitLab is enabled for your organization, the project import is **Ready**, and the connecting account retains administrator access in Composal. |
| GitLab Review App never appears | Check the environment name or pattern, non-production tier, available environment, successful latest deployment, exact MR source SHA, and `external_url`. |
| **Waiting for preview** | Deploy the current MR source SHA and supply its ready target, or finish the configured Composal preview rollout. After one hour, request a rerun once the preview is ready. |
| Stale or conflicting handoff | Use the current MR head. Retry the same target for the same request; request a new run to intentionally change targets. A stale head returns HTTP 409. |
| CI token is unavailable | Check masked/protected variable access for the trusted MR branch. Fork pipelines must not receive the token. |
| Helper rejects the pipeline | Use detached MR pipelines in the connected source project. Merged-results pipelines and merge trains use synthetic merge commits. |
| Missing results note or commit status | Check OAuth connection health, the connecting account's GitLab permissions, and the configuring user's Composal administrator access. Reconnect with the same GitLab account to retain note ownership. |
| Login or access blocker | Check preview visibility, personas, and credentials before treating the result as an application defect. |
| CI wait timed out | Open the returned run-history link. The server run continues; increase the helper and job timeouts for later runs. |
| `merge-requests` is unrecognized | Install the current CLI or use the compatible `pull-requests` spelling. |

Continue with the [Source Control overview](/docs/verify/source-control), [scenario setup](/docs/verification), or [preview hosting guide](/docs/hosting#previews).
