Skip to main content

GitLab CI

Basic pipeline

Add a vipr job to your .gitlab-ci.yml. The job installs Vipr via npx, runs analysis, and uploads the JSON report as an artifact so you can inspect it from the pipeline UI.

vipr:
  image: node:22-slim
  stage: test
  script:
    - npx --yes @vipr/cli analyze "src/**/*.{ts,tsx}"
      --format json
      --output vipr-report.json
      --fail-threshold 70
      --fail-on-critical
      --quiet
  artifacts:
    when: always
    paths:
      - vipr-report.json
    expire_in: 7 days

The job exits non-zero when either gate trips (score below threshold or a critical finding is present), which marks the pipeline as failed and blocks the merge request.

Merge request comments

Use the Markdown format to produce a human-readable report and post it as an MR comment with the GitLab API. This approach requires a CI token with api scope or the built-in CI_JOB_TOKEN.

vipr:
  image: node:22-slim
  stage: test
  script:
    - npx --yes @vipr/cli analyze "src/**/*.{ts,tsx}"
      --format markdown
      --output vipr-comment.md
      --quiet
    - |
      BODY=$(cat vipr-comment.md | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
      curl --silent --fail \
        --request POST \
        --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \
        --header "Content-Type: application/json" \
        --data "{\"body\": ${BODY}}" \
        "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/notes"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  artifacts:
    when: always
    paths:
      - vipr-comment.md
    expire_in: 7 days

Store GITLAB_API_TOKEN as a masked CI variable in Settings > CI/CD > Variables — do not hardcode it in .gitlab-ci.yml.

Changed-files mode

For fast feedback on large repositories, narrow analysis to files changed in the MR branch:

vipr:
  image: node:22-slim
  stage: test
  script:
    - git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
    - npx --yes @vipr/cli analyze "src/**/*.{ts,tsx}"
      --changed origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME
      --format json
      --output vipr-report.json
      --fail-on-critical
      --quiet
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  artifacts:
    when: always
    paths:
      - vipr-report.json
    expire_in: 7 days

Use changed-files mode as a fast path alongside — not instead of — a broader baseline job that runs on the main branch.

Full pipeline example

A two-job setup: a fast MR job on changed files, and a full baseline job on every push to main.

stages:
  - test

vipr:analysis:mr:
  image: node:22-slim
  stage: test
  script:
    - git fetch origin $CI_MERGE_REQUEST_TARGET_BRANCH_NAME
    - npx --yes @vipr/cli analyze "src/**/*.{ts,tsx}"
      --changed origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME
      --format markdown
      --output vipr-comment.md
      --fail-on-critical
      --quiet
    - |
      BODY=$(cat vipr-comment.md | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))")
      curl --silent \
        --request POST \
        --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \
        --header "Content-Type: application/json" \
        --data "{\"body\": ${BODY}}" \
        "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/notes"
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  artifacts:
    when: always
    paths:
      - vipr-comment.md
    expire_in: 7 days

vipr:analysis:baseline:
  image: node:22-slim
  stage: test
  script:
    - npx --yes @vipr/cli analyze "src/**/*.{ts,tsx}"
      --format json
      --output vipr-report.json
      --fail-threshold 70
      --fail-on-critical
      --quiet
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  artifacts:
    when: always
    paths:
      - vipr-report.json
    expire_in: 30 days

Exit codes

Code Meaning
0 Analysis completed and all configured gates passed
1 A gate tripped, or the command could not complete

GitLab treats any non-zero exit code as a job failure. Use allow_failure: true on the job if you want Vipr to report findings without blocking the pipeline.

Documentation