CI and GitHub Actions
GitHub Action
# .github/workflows/sqlsift.yml
name: SQL Lint
on: [push, pull_request]
jobs:
sqlsift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: yukikotani231/sqlsift@main # or pin a release tag
with:
schema: db/schema.sql # or schema-dir: db/migrations
files: queries/**/*.sql
Errors are shown as annotations on the pull request diff. All inputs are optional when you have a sqlsift.toml:
| Input | Description |
|---|---|
files | Query files (space-separated paths or globs) |
schema / schema-dir | Schema files, or a directory of migrations |
dialect | postgresql, mysql, sqlite, or (alpha) snowflake, bigquery, redshift, databricks |
config | Path to sqlsift.toml |
disable | Rules to disable, e.g. E0006 E0008 |
sarif-file | Also write a SARIF report (see below) |
fail-on-error | Fail the step on errors (default true) |
diff-base | Report only diagnostics that are new compared with this branch, tag or commit, e.g. ${{ github.base_ref }} (see below) |
version | sqlsift-cli version from npm (default latest) |
cli-path | Use an existing sqlsift binary instead of installing from npm |
The exit-code output is 0 (clean), 1 (errors found) or 2 (configuration error).
Any CI, with plain commands
npx sqlsift-cli check --schema schema.sql 'queries/**/*.sql'
The exit code is non-zero when errors are found. Inside a GitHub Actions job that runs sqlsift itself (a make lint step, a script, a container), add --format github to get annotations on the pull request diff.
Report only what a pull request introduces
In a project that already has diagnostics, the ones a pull request adds are easy to miss among the old ones. With diff-base, the action checks the pull request's base too and reports only what is new, with no baseline file to commit or keep up to date:
on: [push, pull_request]
jobs:
sqlsift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: yukikotani231/sqlsift@main
with:
schema-dir: db/migrations
files: queries/**/*.sql
diff-base: ${{ github.base_ref }}
A pull request that drops or renames a column fails with the queries it breaks, even when other queries already had problems. github.base_ref is empty outside pull requests, so pushes to main still report every diagnostic.
How it works: the action fetches the base (one commit, from the repository the workflow runs in), checks it out in a temporary git worktree, runs the same check there with --write-baseline into a temporary baseline file, and then checks the pull request against that baseline (see Baseline for how diagnostics are matched). On a pull_request event the checkout is the merge commit, so this compares the result of merging with the base branch as it is now. No token beyond the default one is needed, and pull requests from forks work the same way. The SARIF file (sarif-file) has only the new diagnostics too.
diff-base takes a branch or tag name (fetched from origin), or a commit SHA, e.g. ${{ github.event.pull_request.base.sha }}. Good to know:
- The base is checked with its own
sqlsift.tomland the same inputs, run from the same directory. If it can't be checked at all (nosqlsift.tomlor no query files there yet, a schema file named inschemathat the pull request adds), the action prints a warning and reports every diagnostic. Prefer a glob orschema-dirfor schema files, so that a new migration file doesn't make the base check fail. - Diagnostics are matched by file, rule and statement, so a renamed query file's existing problems are reported as new.
- A committed baseline (
baselineinsqlsift.toml) is not used in this mode: everything the base already reports is hidden anyway.
Rolling out a rule gradually
Make the rule a warning and cap the number of warnings with --max-warnings (or max_warnings in sqlsift.toml), so the backlog can only shrink:
sqlsift check -W missing-required-column --max-warnings 12
Adopting sqlsift on an existing codebase
When a project already has many diagnostics, record them in a baseline and fail CI only on new ones:
sqlsift check --write-baseline # writes sqlsift-baseline.json; commit it
# sqlsift.toml
baseline = "sqlsift-baseline.json"
From then on sqlsift check (and the editor) hides the recorded diagnostics, and new ones fail the check as usual. When a recorded problem is fixed, sqlsift prints a note; re-run --write-baseline to shrink the file. See Baseline.
Re-check everything when the schema changes
sqlsift's main job is catching queries broken by a schema or migration change, and those query files usually aren't in the PR diff. The simplest setup is to always check every query file, as above: sqlsift checks hundreds of files in well under a second. If you only check changed files, check all of them whenever the schema changes:
on: pull_request
jobs:
sqlsift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: changed
env:
BASE: ${{ github.event.pull_request.base.sha }}
run: |
changed=$(git diff --name-only --diff-filter=d "$BASE" HEAD)
if grep -q '^db/' <<< "$changed"; then
files='queries/**/*.sql' # schema changed: check every query
else
files=$(grep '^queries/.*\.sql$' <<< "$changed" | tr '\n' ' ' || true)
fi
echo "files=$files" >> "$GITHUB_OUTPUT"
- if: steps.changed.outputs.files != ''
uses: yukikotani231/sqlsift@main
with:
schema-dir: db/migrations
files: ${{ steps.changed.outputs.files }}
GitHub Code Scanning (SARIF)
Show results in the Security tab and as code scanning alerts:
jobs:
sqlsift:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- uses: yukikotani231/sqlsift@main
with:
schema: db/schema.sql
files: queries/**/*.sql
sarif-file: results.sarif
fail-on-error: "false"
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: results.sarif