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.

Setup in four steps
- Create a bot token
Use a project access token or group access token, or a dedicated bot user's personal access token.
- Scopes:
apiandwrite_repository. - Role: Developer or higher. It needs Maintainer only if fix commits must go to protected branches, and mrsentry deliberately avoids those.
CI_JOB_TOKENcan't post notes or push, so don't use it.
- Scopes:
- Add CI/CD variables (Settings › CI/CD › Variables, masked)
Variable Required Purpose GITLAB_TOKENyes The bot token. MRSENTRY_GITLAB_TOKENalso works and wins if both are set. If your company already stores it under another name, setMRSENTRY_GITLAB__TOKEN_ENVto that name.LLM credentials yes e.g. ANTHROPIC_API_KEYorAZURE_OPENAI_API_KEY. Bedrock uses the AWS role and needs no key.MRSENTRY_CONFIGno A File-type variable holding your org config YAML (LLM profiles, verify commands, limits). MRSENTRY_MODELunset Shortcut for the default profile's model, e.g. bedrock/…orlitellm_proxy/….MRSENTRY_CHILD_TAGSunset Comma-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_SCRIPTunset Lines (one per step) added as the child job's before_script, e.g. installing mrsentry.MRSENTRY_AWS_ROLE_ARNfor ECR / Bedrock IAM role to assume through GitLab OIDC. Not needed on runners that already have an instance profile or IRSA. - 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 - Schedule auto-fix MRs (optional)
Create a pipeline schedule under Build › Pipeline schedules (for example, nightly on
main). Themrsentry:scheduled-fixjob runs only forschedulepipelines and only whenMRSENTRY_ECR_IMAGEis set.
Template variables
| Variable | Default | Effect |
|---|---|---|
MRSENTRY_IMAGE | registry.example.com/platform/mrsentry:full | Job image, and the image used by the generated child fix job. Point it at your mirrored image. |
MRSENTRY_ECR_IMAGE | empty | ECR repository or URI. Empty means review only: no scan, no fix job, no scheduled fix. |
MRSENTRY_FIX_WHEN | on_success | manual makes the generated fix job wait for a click. |
MRSENTRY_SCHEDULED_FIX | true | Set to false to disable the scheduled fix job. |
MRSENTRY_DISABLE_REVIEW | unset | true skips review and fix jobs in MR pipelines. |
MRSENTRY_AWS_ROLE_ARN | unset | Enables 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
| Pipeline | Job | Does |
|---|---|---|
merge_request_event | mrsentry:review | AI review, ECR scan, overview note, Code Quality report, fix-pipeline.yml. |
merge_request_event | mrsentry: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. |
schedule | mrsentry:scheduled-fix | No 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"