Bespoke SQL linter and formatter for DuckDB, powered by sqlglot
Project description
jarify
Bespoke SQL linter and formatter for DuckDB, built on sqlglot.
Existing SQL formatters can't be configured to enforce a specific team style, and none of them are DuckDB-aware. Jarify parses SQL with the DuckDB dialect and rewrites it through an opinionated, non-configurable formatter — no style debates, consistent output everywhere.
Installation
uv tool install jarify
Or run one-off without installing:
uvx jarify fmt path/to/query.sql
Upgrade
uv tool upgrade jarify
Pin to a specific version
uv tool install 'jarify==0.1.0'
Commands
jarify fmt — format SQL files
jarify fmt [OPTIONS] [FILES]...
Reads each file, formats it, and writes the result back in place.
| Option | Description |
|---|---|
- |
Read from stdin |
--check |
Exit non-zero if any file would change (useful in CI) |
--diff |
Print a unified diff instead of rewriting files |
--stdin-filename NAME |
Label to use in diff output when reading from stdin |
Exit codes: 0 = all files already formatted, 1 = files were reformatted, 2 = error.
# Format a single file
jarify fmt query.sql
# Check all .sql files without modifying them (CI mode)
jarify fmt --check **/*.sql
# Preview changes as a diff
jarify fmt --diff query.sql
# Pipe from stdin
cat query.sql | jarify fmt -
jarify lint — lint SQL files
jarify lint [OPTIONS] [FILES]...
Reports style and semantic violations. Does not modify files.
jarify lint query.sql
jarify init — create a config file
jarify init
Writes a jarify.toml in the current directory. Config is internal to the tool — this is primarily useful for future per-project rule overrides.
jarify show-config — inspect active config
jarify show-config
Prints the effective configuration (syntax-highlighted TOML).
Style and lint rules
Jarify enforces a single, opinionated style. There are no knobs to turn. See the SQL Style Guide for the complete rule reference with bad/good examples for every formatting and lint rule.
Development
git clone https://github.com/amfaro/jarify.git
cd jarify
uv sync --all-groups
mise run test # run tests
mise run lint # ruff check
mise run check # lint + test
Adding a new fixture test
- Create
tests/fixtures/<category>/<name>.input.sqlwith the raw SQL - Run
uv run pytest tests/test_fixtures.py --update-fixturesto generate the expected output - Review
tests/fixtures/<category>/<name>.expected.sqland commit both files
Releases
Releases are fully automated across two workflows — no manual tagging required.
prepare-release.yml runs on every push to main that touches src/** or tests/**. It uses git-cliff to compute the next semver from unreleased conventional commits, bumps the version in pyproject.toml, regenerates CHANGELOG.md, and opens (or updates) a release/vX.Y.Z PR. If there are no new conventional commits since the last tag, it exits silently.
publish.yml triggers when pyproject.toml changes on main (i.e., when the release PR is merged). It builds the package with uv build, publishes to PyPI via OIDC trusted publishing, creates the vX.Y.Z git tag, and creates a GitHub Release with auto-generated notes and dist artifacts.
Human steps:
- Land a feature or fix on
mainusing conventional commit messages (feat:,fix:, etc.) → the release PR is opened automatically. - Review the CHANGELOG entries in the release PR, then merge it → PyPI publish, git tag, and GitHub Release happen automatically.
[!IMPORTANT] Squash-merge PR titles must follow conventional commits format (
feat:,fix:, etc.) sogit-cliffcounts them as releasable commits. A plain-language PR title will cause the release PR to be skipped.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jarify-0.5.4.tar.gz.
File metadata
- Download URL: jarify-0.5.4.tar.gz
- Upload date:
- Size: 79.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b99a00625b12fb41545c13a1269b35e9bc3b806190f1d43fdbd67698d6330647
|
|
| MD5 |
a39545464579e6568af288289758e950
|
|
| BLAKE2b-256 |
a102d8ce83330a9769733662b9d04c655718d49abf7576e6d36a3d3004d4a3a4
|
File details
Details for the file jarify-0.5.4-py3-none-any.whl.
File metadata
- Download URL: jarify-0.5.4-py3-none-any.whl
- Upload date:
- Size: 50.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bdefc738b9103dd7d783f71ca4c01a8eb84e1f8c4ec890344db36b1aec2a07d3
|
|
| MD5 |
1c738b98667cb450bfed5c292c05ab56
|
|
| BLAKE2b-256 |
cb815ff420d19fc37b3cb9ad472324bf095b3a4f77a5df0870cc9402532756b8
|