Running the same SQL checks in a browser, CLI and pull request
I wanted one set of SQL checks to work in three places: while exploring a query, from a terminal and during code review. That became SQL Atlas. It is a local, deterministic SQL analyzer with a browser interface, a CLI and a GitHub Action. This article covers the interfaces, the CI contract and the limits of static SQL analysis. One analyzer, three interfaces The analyzer returns structured data instead of printing messages directly. Each interface decides how to present the same result: The browser explains findings and links them to learning material. The CLI returns text, JSON or Markdown and uses stable exit codes. The GitHub Action converts findings into file annotations and a job summary. Keeping presentation outside the analyzer prevents the CLI and Action from becoming separate implementations with different behavior. A CLI needs a contract The CLI accepts one or more files, or SQL through standard input: npx --yes sql-atlas@0.5.1 analyze query.sql echo "SELECT * FROM customers;" | npx --yes sql-atlas@0.5.1 analyze - It supports PostgreSQL, MySQL, Oracle, SQLite, SQL Server and a generic mode. Output can be text for a person, JSON for another program or Markdown for an issue or report. Exit codes are part of the interface: 0 means analysis completed and the configured policy passed. 1 means analysis completed but a severity or score threshold failed. 2 means the command or input was invalid. This distinction matters in CI. A policy failure is not the same as a broken invocation. Turning findings into pull request feedback The Action runs as a bundled Node 24 program and does not download dependencies at runtime. A minimal workflow looks like this: name : SQL review on : pull_request : paths : - " **/*.sql" permissions : contents : read jobs : sql-atlas : runs-on : ubuntu-latest steps : - uses : actions/checkout@v7 - uses : milekv/sql-atlas@v0.5.1 with : paths : | migrations/**/*.sql schema/**/*.sql dialect : postgresql fail-on : critical min-score : 60 Findin