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 throughgithub/codeql-action/upload-sarif(as above) rather than the raw API — the action supplements fingerprint data from your source files. categorykeeps 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-severityscore: critical and high findings display as errors, medium as warnings, low/info as notes. Pass--min-severityto control what gets included (default: high and above). - Combining with policy gating:
analyzewithout--policyalways 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 itif: 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.