Integration Options

This guide covers CI/CD integration for PR reviews and pipeline gates. For runtime integration (agents, applications checking SQL before execution), see Runtime / Agent Integration.

CI/CD Integration

License Setup

For CI/CD runners, pass your license key via environment variable:

env:
  LEXEGA_LICENSE_KEY: ${{ secrets.LEXEGA_LICENSE_KEY }}  # GitHub
  # LEXEGA_LICENSE_KEY: $LEXEGA_LICENSE_KEY              # GitLab
  # LEXEGA_LICENSE_KEY: $(LEXEGA_LICENSE_KEY)            # Azure DevOps

The LEXEGA_LICENSE_KEY environment variable is checked before the license file on disk, making it ideal for ephemeral CI runners.

Installing the CLI in CI

CI runners are ephemeral, so install the CLI as the first step of the job. lexega-sql is a single static binary with no runtime dependencies — installation is one download.

Pin the version your team has validated (recommended for CI, so the gate's behavior only changes when you choose to upgrade):

VERSION=v1.12.1                # pick from https://github.com/Lexega/releases/releases
ASSET=lexega-sql-linux-x64    # linux/darwin × x64/arm64
curl -sSL -O "https://github.com/Lexega/releases/releases/download/${VERSION}/${ASSET}"
curl -sSL "https://github.com/Lexega/releases/releases/download/${VERSION}/CHECKSUMS.sha256" | grep " ${ASSET}
quot; | sha256sum -c - install -m 755 "${ASSET}" /usr/local/bin/lexega-sql

Or always track the latest release:

curl -sSL https://lexega.com/install.sh | sh

The full workflow examples below include the pinned install step.

One command for CI: lexega-sql ci reads the pipeline event and picks the right analysis — a pull request gets a review of the changed SQL against the auto-detected PR base, a push gets a full-repository analysis. It forwards all options to the underlying command, including --pr-comment (on push runs, where there is no PR, ci notes that it's ignoring the flag — so one command line serves both triggers). The PR examples below use it; you never hand-roll the base ref per platform. The Cloud Storage Pipelines page shows full dual-trigger workflows built on it.

The exception is the code scanning sections further down, which deliberately use analyze: GitHub code scanning and the GitLab security dashboard ingest complete scans of each ref, not diffs, so those jobs scan the full tree on every trigger.

Automatic PR Comments

The easiest integration is using --pr-comment to automatically post review results directly to your PR:

GitHub Actions:

name: SQL Review
on: [pull_request]

jobs:
  review:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Install Lexega
        run: |
          curl -sSL -O https://github.com/Lexega/releases/releases/download/v1.12.1/lexega-sql-linux-x64
          curl -sSL https://github.com/Lexega/releases/releases/download/v1.12.1/CHECKSUMS.sha256 | grep ' lexega-sql-linux-x64
#x27; | sha256sum -c - sudo install -m 755 lexega-sql-linux-x64 /usr/local/bin/lexega-sql - name: SQL Review env: LEXEGA_LICENSE_KEY: ${{ secrets.LEXEGA_LICENSE_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | lexega-sql ci . -r --pr-comment

GitLab CI:

sql-review:
  before_script:
    - curl -sSL -O https://github.com/Lexega/releases/releases/download/v1.12.1/lexega-sql-linux-x64
    - curl -sSL https://github.com/Lexega/releases/releases/download/v1.12.1/CHECKSUMS.sha256 | grep ' lexega-sql-linux-x64
#x27; | sha256sum -c - - install -m 755 lexega-sql-linux-x64 /usr/local/bin/lexega-sql script: - lexega-sql ci . -r --pr-comment rules: - if: $CI_MERGE_REQUEST_IID variables: GIT_DEPTH: "0" # MR reviews need history back to the merge base LEXEGA_LICENSE_KEY: $LEXEGA_LICENSE_KEY GITLAB_TOKEN: $LEXEGA_GITLAB_TOKEN

GITLAB_TOKEN needs a token that can call the Notes API — a project access token with the api scope and at least the Reporter role, stored as a masked CI/CD variable (LEXEGA_GITLAB_TOKEN above). The job's built-in CI_JOB_TOKEN cannot post merge request comments — GitLab restricts it to a small set of endpoints that doesn't include notes.

Bitbucket Pipelines:

pipelines:
  pull-requests:
    '**':
      - step:
          script:
            - curl -sSL -O https://github.com/Lexega/releases/releases/download/v1.12.1/lexega-sql-linux-x64
            - curl -sSL https://github.com/Lexega/releases/releases/download/v1.12.1/CHECKSUMS.sha256 | grep ' lexega-sql-linux-x64
#x27; | sha256sum -c - - install -m 755 lexega-sql-linux-x64 /usr/local/bin/lexega-sql # LEXEGA_LICENSE_KEY and BITBUCKET_TOKEN are repository variables — # Bitbucket injects them as environment variables automatically. - lexega-sql ci . -r --pr-comment

Azure DevOps Pipelines:

# Wire this pipeline into the branch policy ("Build validation") so it runs
# on PRs — YAML `pr:` triggers don't apply to Azure Repos.
trigger: none

pool:
  vmImage: ubuntu-latest

steps:
  - checkout: self
    fetchDepth: 0   # review needs git history; new pipelines default to shallow fetch

  - script: |
      curl -sSL -O https://github.com/Lexega/releases/releases/download/v1.12.1/lexega-sql-linux-x64
      curl -sSL https://github.com/Lexega/releases/releases/download/v1.12.1/CHECKSUMS.sha256 | grep ' lexega-sql-linux-x64
#x27; | sha256sum -c - sudo install -m 755 lexega-sql-linux-x64 /usr/local/bin/lexega-sql displayName: Install Lexega - script: lexega-sql ci . -r --pr-comment displayName: SQL Review env: LEXEGA_LICENSE_KEY: $(LEXEGA_LICENSE_KEY) SYSTEM_ACCESSTOKEN: $(System.AccessToken) # not exposed to scripts unless mapped here

Two pieces of one-time Azure DevOps setup: map $(System.AccessToken) into the step's env as shown (it is not available to scripts otherwise), and grant the project's build service identity Contribute to pull requests on the repository (Project Settings → Repositories → Security). Lexega posts its comment thread pre-resolved, so a "resolve all comments" branch policy is never blocked by the review comment itself.

The --pr-comment flag automatically detects your CI platform and posts/updates a comment on the PR. It uses a marker to update existing comments on subsequent runs rather than creating duplicates (on GitHub, GitLab, and Azure DevOps; Bitbucket posts a new comment each run).

GitHub Code Scanning (Security Tab)

Lexega emits SARIF, so findings can flow directly into GitHub's Security tab as code scanning alerts — with the full alert lifecycle (open, dismissed, fixed), branch tracking, and PR annotations your security team already uses for other scanners.

name: SQL Security Scan
on:
  push:
    branches: [main]
  pull_request:

permissions:
  contents: read
  security-events: write   # required to upload SARIF

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Lexega
        run: |
          curl -sSL -O https://github.com/Lexega/releases/releases/download/v1.12.1/lexega-sql-linux-x64
          curl -sSL https://github.com/Lexega/releases/releases/download/v1.12.1/CHECKSUMS.sha256 | grep ' lexega-sql-linux-x64
#x27; | sha256sum -c - sudo install -m 755 lexega-sql-linux-x64 /usr/local/bin/lexega-sql - name: Analyze SQL env: LEXEGA_LICENSE_KEY: ${{ secrets.LEXEGA_LICENSE_KEY }} run: lexega-sql analyze models/ -r --format sarif > lexega.sarif - name: Upload to code scanning uses: github/codeql-action/upload-sarif@v3 with: sarif_file: lexega.sarif category: lexega-sql

Notes:

  • Alerts don't churn when code moves. Each finding carries a stable fingerprint (partialFingerprints) based on the rule and statement rather than the line number, so adding code above a finding updates the alert's location instead of closing it and opening a duplicate. Upload through github/codeql-action/upload-sarif (as above) rather than the raw API — the action supplements fingerprint data from your source files.
  • category keeps Lexega's alerts separate from any other SARIF-producing tools you upload from the same repository.
  • Severity in the Security tab comes from each rule's security-severity score: critical and high findings display as errors, medium as warnings, low/info as notes. Pass --min-severity to control what gets included (default: high and above).
  • Combining with policy gating: analyze without --policy always exits 0, so the upload step runs. If the same job also enforces a policy (exit code 2 on block), put the upload step before the gate or mark it if: always() so blocked runs still report their findings.

GitLab Security Dashboard (SAST Report)

For GitLab, use --format gl-sast — it emits GitLab's native security report schema, which the merge request security widget and Vulnerability Report ingest directly:

sql-sast:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  variables:
    LEXEGA_LICENSE_KEY: $LEXEGA_LICENSE_KEY
  before_script:
    - curl -sSL -O https://github.com/Lexega/releases/releases/download/v1.12.1/lexega-sql-linux-x64
    - curl -sSL https://github.com/Lexega/releases/releases/download/v1.12.1/CHECKSUMS.sha256 | grep ' lexega-sql-linux-x64
#x27; | sha256sum -c - - install -m 755 lexega-sql-linux-x64 /usr/local/bin/lexega-sql script: - lexega-sql analyze models/ -r --format gl-sast > gl-sast-report.json artifacts: reports: sast: gl-sast-report.json

Notes:

  • Each vulnerability carries the same stable, line-independent identity used in SARIF, so findings track correctly across pipelines as code moves.
  • Run the job from the repository root (GitLab's default) so file paths in the report are repo-relative — that's what the widget links against.
  • The report artifact works on every GitLab tier; the merge request security widget and the Vulnerability Report UI require GitLab Ultimate. On other tiers the report is still produced and downloadable as a job artifact.

Policy-Based Blocking

For stricter enforcement, use policies to fail the pipeline. The default policy from lexega-sql init is permissive (warns only). To enable blocking, edit .lexega/policy.yml and change critical: warn to critical: block:

# .lexega/policy.yml (edit to enable blocking)
severity_actions:
  - critical: block   # Change from 'warn' to 'block'
    high: warn
default_action: allow

See Policy Reference for details on scoping severity actions by path or environment.

lexega-sql ci \
  --policy .lexega/policy.yml \
  --env prod \
  --decision-out .lexega/decisions/$GITHUB_RUN_ID/

A policy block exits with code 2, which fails the CI job directly — no wrapper script or exit-code translation needed (and a wrapper that checks only for 2 would silently pass runs where the analysis itself errored). Note that the runner's local .lexega/ disappears with the job; to keep decision artifacts for the dashboard, write them to cloud storage with a unique per-run prefix — see Cloud Storage Pipelines.

Tip: Set LEXEGA_CI=1 to make snapshot runs (analyze) refuse to run without --policy, preventing an accidentally-ungated mainline job. PR reviews don't check this variable.

Dashboard

The dashboard provides a web UI for viewing analysis results, trends, and findings. The Overview separates gate metrics (PR and runtime decisions) from repository posture (snapshots), every view filters by repository and run type, and the findings view can switch from full history to current standing findings — each dated by its first appearance.

# Start dashboard (reads .lexega/ by default — works out of the box after `init`)
lexega-sql dashboard

# Explicit local directory
lexega-sql dashboard --data-dir .lexega/

# Read from cloud storage (S3, GCS, or Azure)
lexega-sql dashboard --data-dir s3://my-bucket/lexega-data

# Custom port / don't auto-open browser
lexega-sql dashboard --data-dir .lexega/ --port 3000 --no-open

Expected Directory Structure

The dashboard expects decision and report artifacts under decisions/ and reports/ subdirectories. Reports are ingested in JSON, YAML, and SARIF form — the baseline.sarif that init writes shows up automatically.

<data-dir>/
  decisions/
    <run-id>/decision.json
    ...
  reports/
    baseline.sarif
    <run-id>/risk_report.json
    ...

This structure is created automatically when you use --decision-out and --report-out with the right paths:

# Local: write artifacts into the directory the dashboard reads
lexega-sql analyze models/*.sql \
  --policy policy.yml --env prod \
  --decision-out .lexega/decisions/$GITHUB_RUN_ID/ \
  --report-out .lexega/reports/$GITHUB_RUN_ID/

# Cloud: same structure, just an S3/GCS/Azure prefix
lexega-sql analyze models/*.sql \
  --policy policy.yml --env prod \
  --decision-out s3://my-bucket/lexega-data/decisions/$GITHUB_RUN_ID/ \
  --report-out s3://my-bucket/lexega-data/reports/$GITHUB_RUN_ID/

Cloud Storage Support

Pass a cloud URI as --data-dir and the dashboard downloads files on startup and on refresh:

# S3
lexega-sql dashboard --data-dir s3://my-bucket/lexega-data

# GCS
lexega-sql dashboard --data-dir gs://my-bucket/lexega-data

# Azure Blob Storage
lexega-sql dashboard --data-dir az://my-container/lexega-data

Cloud credentials are read from standard environment variables (AWS_* / GOOGLE_APPLICATION_CREDENTIALS / AZURE_STORAGE_*). For complete per-provider walkthroughs — bucket setup, CI authentication via OIDC, full workflow YAML, and reading the bucket back from the dashboard — see Cloud Storage Pipelines.

Note: The dashboard runs on your machine—your data never leaves your infrastructure. When --data-dir points to a cloud URI, files are downloaded locally for display; no data is sent elsewhere.

Need Help?

Can't find what you're looking for? Check out our GitHub or reach out to support.