Docs › GitLab pipelines

GitLab pipelines

One include: gives every merge request an AI review. Add an ECR image to scan and fix vulnerabilities as well, and a pipeline schedule to get a fix MR every night.

Merge request pipeline with mrsentry:review and a generated child pipeline running mrsentry:fix
The review job decides whether a fix is needed. If it is, the fix runs in a generated child pipeline.

Setup in four steps

  1. Create a bot token

    Use a project access token or group access token, or a dedicated bot user's personal access token.

    • Scopes: api and write_repository.
    • Role: Developer or higher. It needs Maintainer only if fix commits must go to protected branches, and mrsentry deliberately avoids those.
    • CI_JOB_TOKEN can't post notes or push, so don't use it.
  2. Add CI/CD variables (Settings › CI/CD › Variables, masked)
    VariableRequiredPurpose
    GITLAB_TOKENyesThe bot token. MRSENTRY_GITLAB_TOKEN also works and wins if both are set. If your company already stores it under another name, set MRSENTRY_GITLAB__TOKEN_ENV to that name.
    LLM credentialsyese.g. ANTHROPIC_API_KEY or AZURE_OPENAI_API_KEY. Bedrock uses the AWS role and needs no key.
    MRSENTRY_CONFIGnoA File-type variable holding your org config YAML (LLM profiles, verify commands, limits).
    MRSENTRY_MODELunsetShortcut for the default profile's model, e.g. bedrock/… or litellm_proxy/….
    MRSENTRY_CHILD_TAGSunsetComma-separated runner tags for the generated child job. A child pipeline doesn't inherit the parent's default:, so set this when your runners need tags.
    MRSENTRY_CHILD_BEFORE_SCRIPTunsetLines (one per step) added as the child job's before_script, e.g. installing mrsentry.
    MRSENTRY_AWS_ROLE_ARNfor ECR / BedrockIAM role to assume through GitLab OIDC. Not needed on runners that already have an instance profile or IRSA.
  3. Include the template
    include:
      - project: platform/mrsentry          # where you host mrsentry, or `remote: https://…/mrsentry.gitlab-ci.yml`
        ref: main
        file: templates/ci/mrsentry.gitlab-ci.yml
    
    variables:
      MRSENTRY_IMAGE: registry.example.com/platform/mrsentry:full
      MRSENTRY_ECR_IMAGE: 123456789012.dkr.ecr.eu-west-1.amazonaws.com/orders-service   # optional: enables scan + fix
      MRSENTRY_FIX_WHEN: on_success          # or manual: a human clicks before the fix is pushed
  4. Schedule auto-fix MRs (optional)

    Create a pipeline schedule under Build › Pipeline schedules (for example, nightly on main). The mrsentry:scheduled-fix job runs only for schedule pipelines and only when MRSENTRY_ECR_IMAGE is set.

Template variables

VariableDefaultEffect
MRSENTRY_IMAGEregistry.example.com/platform/mrsentry:fullJob image, and the image used by the generated child fix job. Point it at your mirrored image.
MRSENTRY_ECR_IMAGEemptyECR repository or URI. Empty means review only: no scan, no fix job, no scheduled fix.
MRSENTRY_FIX_WHENon_successmanual makes the generated fix job wait for a click.
MRSENTRY_SCHEDULED_FIXtrueSet to false to disable the scheduled fix job.
MRSENTRY_DISABLE_REVIEWunsettrue skips review and fix jobs in MR pipelines.
MRSENTRY_AWS_ROLE_ARNunsetEnables the OIDC web-identity role assumption in before_script.

Any configuration key can also be set as a CI variable using the MRSENTRY_<SECTION>__<KEY> form, for example MRSENTRY_REVIEW__MIN_SEVERITY=high. See the Configuration reference.

What runs where

PipelineJobDoes
merge_request_eventmrsentry:reviewAI review, ECR scan, overview note, Code Quality report, fix-pipeline.yml.
merge_request_eventmrsentry:fix (child)Pushes a dependency-fix commit into the MR. It reuses the parent's scan report, so it needs no AWS credentials. It's skipped when nothing is fixable or the head is already a bot fix.
schedulemrsentry:scheduled-fixNo review. Scans the latest image and opens or refreshes one fix MR.

The jobs are allow_failure: true by default, so a review never blocks a merge. To gate merges on findings, set output.fail_on: high (exit code 1) and override allow_failure: false on mrsentry:review.

Changing the stage

The jobs run in the test stage, because GitLab doesn't expand variables in stage:. To move them, override in your own .gitlab-ci.yml:

.mrsentry:base:
  stage: security
mrsentry:fix:
  stage: security

The generated child pipeline

When something is fixable, mrsentry run mr writes this file. The job uses needs: pipeline: to download the parent's artifacts and runs on the same commit:

# generated by mrsentry
mrsentry:fix:
  image: registry.example.com/platform/mrsentry:full
  stage: test
  variables:
    GIT_DEPTH: "0"
    MRSENTRY_GITLAB__PROJECT: "1234"
    MRSENTRY_GITLAB__MR_IID: "42"
  needs:
    - pipeline: "98213"
      job: "mrsentry:review"
      artifacts: true
  script:
    - mrsentry fix --mode existing-mr --scan-report mrsentry-out/mrsentry-report.json

Otherwise it writes a single mrsentry:no-fix-needed job that prints the reason: nothing fixable, fix disabled, or the head is already a bot fix.

AWS access with GitLab OIDC

The template requests an ID token with audience sts.amazonaws.com. When MRSENTRY_AWS_ROLE_ARN is set, it exports AWS_ROLE_ARN and AWS_WEB_IDENTITY_TOKEN_FILE, so boto3 (ECR) and LiteLLM (Bedrock) assume the role automatically, with no static keys.

Trust policy for the role (replace the GitLab host and project path):

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/gitlab.example.com" },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": { "gitlab.example.com:aud": "sts.amazonaws.com" },
      "StringLike":   { "gitlab.example.com:sub": "project_path:payments/orders-service:ref_type:*:ref:*" }
    }
  }]
}

Least-privilege permissions policy:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Resource": "*",
    "Action": [
      "ecr:DescribeImages", "ecr:DescribeImageScanFindings", "ecr:DescribeRepositories",
      "ecr:GetRegistryScanningConfiguration", "ecr:BatchGetRepositoryScanningConfiguration",
      "ecr:StartImageScan",
      "ecr:GetAuthorizationToken", "ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer",
      "bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"
    ]
  }]
}

The GetAuthorizationToken, BatchGetImage and GetDownloadUrlForLayer actions are needed only for the local Trivy/Grype fallback, which pulls the image. The bedrock: actions are needed only if you use Bedrock. Other credential sources work too: a runner instance profile, IRSA on Kubernetes runners, or AWS_* variables.

Using results in later jobs

The template publishes mrsentry-out/mrsentry.env as a dotenv report, so later jobs can branch on the results:

notify-security:
  needs: [mrsentry:review]
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - 'if [ "$MRSENTRY_REVIEW_MAX_SEVERITY" = "critical" ]; then ./notify.sh "Critical finding in !$CI_MERGE_REQUEST_IID"; fi'

The full template

This is the file you include, shown exactly as it is in the repository:

# mrsentry GitLab CI template.
#
# Include it from any project:
#
#   include:
#     - project: platform/mrsentry          # or `remote: https://.../mrsentry.gitlab-ci.yml`
#       ref: v0.1.0
#       file: templates/ci/mrsentry.gitlab-ci.yml
#   variables:
#     MRSENTRY_ECR_IMAGE: 123456789012.dkr.ecr.eu-west-1.amazonaws.com/orders-service   # optional: enables scan/fix
#
# Required CI/CD variables (Settings > CI/CD > Variables, masked):
#   GITLAB_TOKEN            project/group access token or bot PAT: scopes api + write_repository, role Developer+
#                           (MRSENTRY_GITLAB_TOKEN also works; other names: MRSENTRY_GITLAB__TOKEN_ENV=YOUR_VAR)
#   LLM credentials         e.g. AZURE_OPENAI_API_KEY / ANTHROPIC_API_KEY, or Bedrock via AWS role
#   MRSENTRY_CONFIG         (optional) path/`file` variable with org config YAML (see mrsentry.example.yml)
# AWS (ECR + Bedrock): preferred GitLab OIDC -> set MRSENTRY_AWS_ROLE_ARN; otherwise runner instance profile / IRSA
# / AWS_* variables are used through the standard credential chain.
#
# Flows:
#   merge_request_event  -> mrsentry:review  (AI review + ECR scan + overview) -> mrsentry:fix (child pipeline that
#                           pushes the dependency fix into the MR, only when something is fixable)
#   schedule             -> mrsentry:scheduled-fix (no review; scan latest image; open/refresh one fix MR)
#
# Runners that need tags: the generated child pipeline doesn't inherit `default:`, so set MRSENTRY_CHILD_TAGS
# (comma-separated) and, if the child image lacks mrsentry, MRSENTRY_CHILD_BEFORE_SCRIPT (one step per line).
#
# Jobs run in the `test` stage (GitLab does not expand variables in `stage:`). To move them, override in your
# .gitlab-ci.yml, e.g.  `.mrsentry:base: { stage: security }`  and  `mrsentry:fix: { stage: security }`.

variables:
  MRSENTRY_IMAGE: "registry.example.com/platform/mrsentry:full"   # override with your mirrored image
  MRSENTRY_ECR_IMAGE: ""
  MRSENTRY_FIX_WHEN: "on_success"        # or "manual" to require a click before the fix is pushed
  MRSENTRY_SCHEDULED_FIX: "true"

.mrsentry:base:
  image:
    name: $MRSENTRY_IMAGE
    entrypoint: [""]
  stage: test
  variables:
    GIT_DEPTH: "0"                       # full history so merge-base diffs and pushes work
  id_tokens:
    MRSENTRY_AWS_ID_TOKEN:
      aud: sts.amazonaws.com
  before_script:
    - |
      if [ -n "${MRSENTRY_AWS_ROLE_ARN:-}" ]; then
        printf '%s' "$MRSENTRY_AWS_ID_TOKEN" > /tmp/mrsentry-web-identity
        export AWS_ROLE_ARN="$MRSENTRY_AWS_ROLE_ARN" AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/mrsentry-web-identity
        export AWS_ROLE_SESSION_NAME="mrsentry-${CI_PROJECT_ID}-${CI_PIPELINE_ID}"
      fi
    - mrsentry version
  artifacts:
    when: always
    expire_in: 30 days
    paths: [mrsentry-out/]
    reports:
      codequality: mrsentry-out/gl-code-quality-report.json
      dotenv: mrsentry-out/mrsentry.env
  interruptible: true

mrsentry:review:
  extends: .mrsentry:base
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $MRSENTRY_DISABLE_REVIEW != "true"
  script:
    - mrsentry run mr ${MRSENTRY_ECR_IMAGE:+--image "$MRSENTRY_ECR_IMAGE"} --child-image "$MRSENTRY_IMAGE" --fix-when "$MRSENTRY_FIX_WHEN"
  allow_failure: true                    # set output.fail_on + allow_failure:false to block merges

mrsentry:fix:
  stage: test
  needs:
    - job: mrsentry:review
      artifacts: true
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $MRSENTRY_ECR_IMAGE != "" && $MRSENTRY_DISABLE_REVIEW != "true"
  trigger:
    include:
      - artifact: mrsentry-out/fix-pipeline.yml
        job: mrsentry:review
    strategy: depend
  allow_failure: true

mrsentry:scheduled-fix:
  extends: .mrsentry:base
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule" && $MRSENTRY_ECR_IMAGE != "" && $MRSENTRY_SCHEDULED_FIX == "true"
  script:
    - mrsentry run scheduled --image "$MRSENTRY_ECR_IMAGE"