Skip to content

GitLab CI/CD

This page describes how to integrate MethodAtlas into a GitLab CI/CD pipeline. The techniques can be used individually or combined:

  • Merge request annotations — findings posted as MR notes via ::notice/::warning commands
  • SARIF upload to Security Dashboard — findings appear in the GitLab MR security widget
  • AI result caching — skips re-classification of unchanged test classes
  • Security test count gate — pipeline fails when security test coverage drops

Prerequisites

Requirement Details
Java runtime Java 21 or later; the examples use the eclipse-temurin:21-jdk Docker image
MethodAtlas Downloaded at job start from the GitHub release; the binary is cached between runs using the cache directive
AI provider API key Stored as a GitLab CI/CD variable (masked); not required for static inventory mode
GitLab tier SARIF upload to the Security Dashboard requires GitLab Ultimate; annotation output has no tier requirement

Project configuration

Store your AI provider API key as a masked, protected CI/CD variable. In the GitLab UI navigate to Settings → CI/CD → Variables and add:

Variable Value Flags
OPENAI_API_KEY Your OpenAI API key Masked, Protected

Use a different variable name and the -ai-provider flag if you are using a provider other than OpenAI. See AI Providers.

Minimal pipeline: static inventory

The following job requires no AI provider and produces a CSV inventory of all test methods with their structural metadata:

methodatlas-inventory:
  image: eclipse-temurin:21-jdk
  stage: test
  cache:
    key: "methodatlas-binary-$CI_COMMIT_REF_SLUG"
    paths:
      - methodatlas.jar
    policy: pull-push
  script:
    - |
      if [ ! -f methodatlas.jar ]; then
        curl -fsSL -o methodatlas.jar \
          https://github.com/Accenture/MethodAtlas/releases/latest/download/methodatlas.jar
      fi
    - java -jar methodatlas.jar src/test/java > inventory.csv
  artifacts:
    paths:
      - inventory.csv
    expire_in: 30 days

AI-enriched scan with SARIF upload

GitLab CI supports SARIF reports as a first-class artefact type through the artifacts.reports.sast key. Reports uploaded this way are parsed by GitLab and displayed in the merge request security widget and the project Security Dashboard (GitLab Ultimate required for the dashboard).

methodatlas-scan:
  image: eclipse-temurin:21-jdk
  stage: test
  cache:
    - key: "methodatlas-binary-$CI_COMMIT_REF_SLUG"
      paths:
        - methodatlas.jar
      policy: pull-push
  script:
    - |
      if [ ! -f methodatlas.jar ]; then
        curl -fsSL -o methodatlas.jar \
          https://github.com/Accenture/MethodAtlas/releases/latest/download/methodatlas.jar
      fi
    - |
      java -jar methodatlas.jar \
        -ai -ai-provider openai -ai-api-key-env OPENAI_API_KEY \
        -sarif -security-only \
        -content-hash \
        src/test/java \
        > methodatlas.sarif
  artifacts:
    reports:
      sast: methodatlas.sarif
    paths:
      - methodatlas.sarif
    expire_in: 90 days
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

GitLab Ultimate

The artifacts.reports.sast integration and the Security Dashboard require GitLab Ultimate. On lower tiers the SARIF file is still produced and available as a downloadable job artefact; it can be reviewed manually or imported into an external code scanning tool.

Caching AI results across runs

Use GitLab's cache directive to persist the MethodAtlas result file across pipeline runs. The cache key is set per branch so that each branch maintains its own classification state:

methodatlas-scan:
  image: eclipse-temurin:21-jdk
  stage: test
  cache:
    - key: "methodatlas-binary-$CI_COMMIT_REF_SLUG"
      paths:
        - methodatlas.jar
      policy: pull-push
    - key: "methodatlas-ai-$CI_COMMIT_REF_SLUG"
      paths:
        - .methodatlas-cache.csv
      policy: pull-push
  script:
    - |
      if [ ! -f methodatlas.jar ]; then
        curl -fsSL -o methodatlas.jar \
          https://github.com/Accenture/MethodAtlas/releases/latest/download/methodatlas.jar
      fi
    - |
      CACHE_ARG=""
      if [ -f .methodatlas-cache.csv ]; then
        CACHE_ARG="-ai-cache .methodatlas-cache.csv"
      fi

      # Pass 1: CSV — refreshes cache, calls AI only for changed classes
      java -jar methodatlas.jar \
        -ai -ai-provider openai -ai-api-key-env OPENAI_API_KEY \
        -content-hash \
        -security-only \
        $CACHE_ARG \
        src/test/java \
        > .methodatlas-cache-new.csv
      mv .methodatlas-cache-new.csv .methodatlas-cache.csv

      # Pass 2: SARIF — reads exclusively from cache, zero AI calls
      java -jar methodatlas.jar \
        -ai -ai-provider openai -ai-api-key-env OPENAI_API_KEY \
        -sarif -security-only \
        -content-hash \
        -ai-cache .methodatlas-cache.csv \
        src/test/java \
        > methodatlas.sarif
  artifacts:
    reports:
      sast: methodatlas.sarif
    paths:
      - methodatlas.sarif
    expire_in: 90 days

On the first run for a branch (cache miss) every class is classified via the AI provider. On subsequent runs only classes whose content_hash has changed incur an API call.

Security test count gate

Fail the pipeline when the number of security-relevant test methods drops compared to the baseline on the default branch:

stages:
  - build
  - test
  - gate

save-baseline:
  image: eclipse-temurin:21-jdk
  stage: test
  cache:
    key: "methodatlas-binary-$CI_COMMIT_REF_SLUG"
    paths:
      - methodatlas.jar
    policy: pull-push
  script:
    - |
      if [ ! -f methodatlas.jar ]; then
        curl -fsSL -o methodatlas.jar \
          https://github.com/Accenture/MethodAtlas/releases/latest/download/methodatlas.jar
      fi
    - |
      java -jar methodatlas.jar \
        -ai -ai-provider openai -ai-api-key-env OPENAI_API_KEY \
        -security-only -content-hash \
        src/test/java > baseline.csv
  artifacts:
    name: methodatlas-baseline
    paths:
      - baseline.csv
    expire_in: 90 days
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

security-gate:
  image: eclipse-temurin:21-jdk
  stage: gate
  needs:
    - job: save-baseline
      artifacts: true
      optional: true
  cache:
    key: "methodatlas-binary-$CI_COMMIT_REF_SLUG"
    paths:
      - methodatlas.jar
    policy: pull
  script:
    - |
      if [ ! -f methodatlas.jar ]; then
        curl -fsSL -o methodatlas.jar \
          https://github.com/Accenture/MethodAtlas/releases/latest/download/methodatlas.jar
      fi
    - |
      java -jar methodatlas.jar \
        -ai -ai-provider openai -ai-api-key-env OPENAI_API_KEY \
        -security-only \
        src/test/java > current.csv

      if [ ! -f baseline.csv ]; then
        echo "No baseline available — skipping count gate."
        exit 0
      fi

      baseline=$(tail -n +2 baseline.csv | wc -l)
      current=$(tail -n +2 current.csv | wc -l)

      echo "Baseline security tests: $baseline"
      echo "Current security tests:  $current"

      if [ "$current" -lt "$baseline" ]; then
        echo "ERROR: Security test count dropped from $baseline to $current"
        exit 1
      fi
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Full pipeline

The following .gitlab-ci.yml combines binary caching, AI result caching, SARIF upload, and the count gate. Adjust stage names and needs references to match your project's existing pipeline structure.

stages:
  - build
  - test

variables:
  METHODATLAS_JAR: methodatlas.jar
  METHODATLAS_VERSION: latest   # pin to a version tag for reproducibility, e.g. 1.2.0

.methodatlas-binary-cache: &methodatlas-binary-cache
  key: "methodatlas-binary-$CI_COMMIT_REF_SLUG"
  paths:
    - $METHODATLAS_JAR
  policy: pull-push

.download-methodatlas: &download-methodatlas
  - |
    if [ ! -f "$METHODATLAS_JAR" ]; then
      curl -fsSL -o "$METHODATLAS_JAR" \
        "https://github.com/Accenture/MethodAtlas/releases/${METHODATLAS_VERSION}/download/methodatlas.jar"
    fi

methodatlas-scan:
  image: eclipse-temurin:21-jdk
  stage: test
  cache:
    - <<: *methodatlas-binary-cache
    - key: "methodatlas-ai-$CI_COMMIT_REF_SLUG"
      paths:
        - .methodatlas-cache.csv
      policy: pull-push
  script:
    - *download-methodatlas
    - |
      CACHE_ARG=""
      if [ -f .methodatlas-cache.csv ]; then
        CACHE_ARG="-ai-cache .methodatlas-cache.csv"
      fi

      # Pass 1: classify and update cache
      java -jar "$METHODATLAS_JAR" \
        -ai -ai-provider openai -ai-api-key-env OPENAI_API_KEY \
        -content-hash \
        -security-only \
        $CACHE_ARG \
        src/test/java \
        > .methodatlas-cache-new.csv
      mv .methodatlas-cache-new.csv .methodatlas-cache.csv

      # Pass 2: emit SARIF from cache (zero AI calls)
      java -jar "$METHODATLAS_JAR" \
        -ai -ai-provider openai -ai-api-key-env OPENAI_API_KEY \
        -sarif -security-only \
        -content-hash \
        -ai-cache .methodatlas-cache.csv \
        src/test/java \
        > methodatlas.sarif

      # Count gate (compare against baseline from previous run on this branch)
      if [ -f baseline.csv ]; then
        baseline=$(tail -n +2 baseline.csv | wc -l)
        current=$(tail -n +2 .methodatlas-cache.csv | wc -l)
        echo "Baseline: $baseline  Current: $current"
        if [ "$current" -lt "$baseline" ]; then
          echo "ERROR: Security test count dropped from $baseline to $current"
          exit 1
        fi
      fi

      cp .methodatlas-cache.csv baseline.csv
  artifacts:
    reports:
      sast: methodatlas.sarif
    paths:
      - methodatlas.sarif
      - baseline.csv
    expire_in: 90 days
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Using a YAML configuration file

For teams that prefer to keep MethodAtlas settings in version control rather than in pipeline YAML, create a methodatlas.yml at the project root:

# methodatlas.yml
aiProvider: openai
aiModel: gpt-4o-mini
contentHash: true
securityOnly: true

Reference it from the pipeline with the -config flag:

java -jar methodatlas.jar -config methodatlas.yml -sarif src/test/java

The API key must still be supplied via an environment variable at runtime; do not store secrets in the configuration file committed to version control.

Further reading