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.json
      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.json ]; then
        CACHE_ARG="-ai-cache .methodatlas-cache.json"
      fi

      # Single combined pass: classify, emit SARIF, and refresh the unified cache.
      # Only changed classes call the AI provider; unchanged classes are served
      # from the cache (classification and, with -detect-secrets, credentials).
      java -jar methodatlas.jar \
        -ai -ai-provider openai -ai-api-key-env OPENAI_API_KEY \
        -sarif -security-only \
        -content-hash \
        $CACHE_ARG \
        -ai-cache-out .methodatlas-cache.json \
        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.json
      policy: pull-push
  script:
    - *download-methodatlas
    - |
      CACHE_ARG=""
      if [ -f .methodatlas-cache.json ]; then
        CACHE_ARG="-ai-cache .methodatlas-cache.json"
      fi

      # Single combined pass: classify, emit SARIF, and refresh the unified cache.
      java -jar "$METHODATLAS_JAR" \
        -ai -ai-provider openai -ai-api-key-env OPENAI_API_KEY \
        -sarif -security-only \
        -content-hash \
        $CACHE_ARG \
        -ai-cache-out .methodatlas-cache.json \
        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

Scanning for hard-coded credentials

-detect-secrets adds a deterministic credential scan alongside the test inventory. The deterministic layer uses no AI and makes no network calls, so it needs no API-key variable and is safe to run on every pipeline. Emit the findings as SARIF — GitLab ingests them through the same artifacts.reports.sast channel as the test scan (SAST reports from multiple jobs are merged), so credential findings appear in the merge-request security widget and the Security Dashboard:

methodatlas-credentials:
  image: eclipse-temurin:21-jdk
  stage: test
  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 \
        -detect-secrets \
        -secrets-out methodatlas-credentials.csv \
        -sarif \
        src/test/java \
        > methodatlas-credentials.sarif
  artifacts:
    reports:
      sast: methodatlas-credentials.sarif
    paths:
      - methodatlas-credentials.sarif
      - methodatlas-credentials.csv
    expire_in: 90 days
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Secret values are masked by default in both the SARIF and the CSV. A deterministic-only run emits every finding at warning so nothing drops below review threshold. (The artifacts.reports.sast dashboard integration requires GitLab Ultimate, exactly as for the test-classification SARIF above; on lower tiers both files remain downloadable job artefacts.)

Optional AI triage

Add -ai -ai-provider openai -ai-api-key-env OPENAI_API_KEY (with the masked OPENAI_API_KEY CI/CD variable) to have the model score each candidate's credibility and attribute it to the endpoint it authenticates against. This transmits the test source to the provider, so for sensitive code prefer a local Ollama model — see the AI trust boundary. -secrets-include <glob> replaces the default test-class set.

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