Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e4e782f
build: add the Cargo workspace, Python packaging and fensu policy
ampagent Oct 30, 2026
ff1b7ad
feat: add the SQL lint, format and statement facts engine
ampagent Oct 30, 2026
1dd5c7d
feat: add the sqlclean check, fix, format and rules commands
ampagent Oct 30, 2026
fe86c04
feat: add Python custom Rules, their host and test harness
ampagent Oct 30, 2026
85cf182
ci: add verify, release and publish workflows
ampagent Oct 30, 2026
303c89f
docs: document commands, configuration and custom Rules
ampagent Oct 30, 2026
6fdc62f
fix: satisfy clippy nonminimal_bool on the CI toolchain
ampagent Oct 30, 2026
d598a7c
docs: add the logo and a check and fix demo to the README
ampagent Oct 30, 2026
3e01d87
fix: count unsafe fixes apart in the check summary
ampagent Oct 30, 2026
179e62e
fix: locate qualified tables from their first name part
ampagent Oct 30, 2026
1f32207
fix: separate the message and remediation in SARIF results
ampagent Oct 30, 2026
38564c5
perf(cli): format files in parallel
ampagent Oct 30, 2026
12ce1bb
feat(format): indent four spaces and break only overflowing groups
ampagent Oct 30, 2026
a57ec8d
docs: add the SQLClean website, docs and benchmark page
ampagent Oct 30, 2026
9dba1fd
chore(bench): compare SQLClean with sqruff and SQLFluff
ampagent Oct 30, 2026
b240183
chore: keep agent instructions and orb setup out of the repository
ampagent Oct 30, 2026
64fdb10
fix(cli): read a bare --config file name from the working directory
ampagent Oct 30, 2026
966b321
perf(custom): evaluate custom Rules on one host per CPU
ampagent Oct 30, 2026
933e62e
chore(bench): add a custom Rules variant
ampagent Oct 30, 2026
b1b5182
docs(website): lead with the benchmark and custom Rules
ampagent Oct 30, 2026
aadba24
docs(website): show race totals in time and say custom Rules run on t…
ampagent Oct 30, 2026
6930cf2
test(e2e): parametrize the many-files custom Rules test
ampagent Oct 30, 2026
0fdca9a
feat(cli): accept lint as an alias of check
ampagent Oct 30, 2026
a674bd2
docs(website): shorten the hero lede
ampagent Oct 30, 2026
4940635
docs(website): serve the site from sqlclean.io
ampagent Oct 30, 2026
5b1ccbf
chore: trigger the first site preview build
ampagent Oct 30, 2026
6c04ba8
feat(cli): add sqlclean skills to install a bundled agent skill
ampagent Oct 30, 2026
116ffb0
ci: run only the suites a change needs and keep site edits out of rel…
ampagent Oct 30, 2026
261abfb
test: write e2e project files with LF line endings on every platform
ampagent Oct 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
40 changes: 40 additions & 0 deletions .github/RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Releasing

The website and the packages ship separately.

## Website

Cloudflare Pages builds `website/` (`npm ci && npm run build`, output `dist`) from its own Git
integration. A push to `main` deploys https://sqlclean.io; every other branch deploys a preview
at `<branch>.sqlclean.pages.dev`. No workflow here deploys the site, and publishing packages never
touches it.

## Packages

Release Please keeps a release pull request open on `main`. Merging it tags `v0.MINOR.PATCH` and
dispatches `publish.yml`, which builds the `sqlclean-cli` wheels and both sdists and publishes
`sqlclean` and `sqlclean-cli` to PyPI.

Release Please counts every Conventional Commit on `main` since the last release, whatever its
type: `docs:` and `chore:` commits bump the patch version even though their changelog sections
are hidden or not. What keeps a change out of a release is `exclude-paths` in
`release-please-config.json`. A commit whose files all sit under `website/`, `benchmarks/` or
`.github/` is ignored, so site, benchmark and workflow edits never cut a release.

Documentation edits usually do release, on purpose: `make skills` copies the docs into
`crates/sqlclean-cli/skill/`, which the `sqlclean` executable embeds for `sqlclean skills`, so the
installed skill stays in step with the docs. A homepage or styling change touches only `website/`
and does not.

## Verify

`verify.yml` classifies a pull request by the paths it changes and runs only the suites it needs.
The `Verify` job is the one check to require: it passes when every selected suite passed.

| Change | Suites |
|--------|--------|
| Only `website/`, `benchmarks/`, root `*.md`, `.github/demos/`, images, `website.yml` | docs skill check and ruff |
| Also the bundled skill under `crates/sqlclean-cli/skill/` | plus a Linux build and the `sqlclean skills` tests |
| Anything else, including workflows, Rust, Python, tests and lockfiles | everything: `make verify`, minimum Rust, Windows, three-platform package smoke tests and the source build |

A manual or release dispatch has no pull request diff, so it runs everything.
Binary file added .github/demos/check-fix.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions .github/scripts/assert_cli_wheel_tags.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
# Guard the sqlclean-cli wheel tag. The package ships one standalone Rust
# executable that links no libpython, so an interpreter-specific tag makes the
# wheel uninstallable on every other Python and silently forces a source build.
set -euo pipefail
shopt -s nullglob

directory="${1:?usage: assert_cli_wheel_tags.sh <directory>}"
wheels=("${directory}"/sqlclean_cli-*.whl)

if [ ${#wheels[@]} -eq 0 ]; then
echo "No sqlclean-cli wheel found in ${directory}."
exit 1
fi

status=0
for wheel in "${wheels[@]}"; do
name="$(basename "${wheel}")"
case "${name}" in
sqlclean_cli-*-py3-none-*.whl)
echo "ok: ${name}"
;;
*)
echo "error: sqlclean-cli wheel must be tagged py3-none-<platform>: ${name}"
status=1
;;
esac
done

exit "${status}"
55 changes: 55 additions & 0 deletions .github/scripts/classify_changes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Decide which Verify suites a change needs from the paths it touches, one per line on stdin.

Prints `full=` and `skills=` lines for GITHUB_OUTPUT:

- Documentation, website, benchmark and image changes need only the light checks.
- Changes confined to the bundled agent skill also rebuild the executable on Linux and run the
skills command tests, because the executable embeds the skill.
- Any other change, including workflows, Rust, Python, tests and lockfiles, runs every suite.

An empty path list means the diff could not be computed, so every suite runs.
"""

from __future__ import annotations

import fnmatch
import sys

LIGHT: tuple[str, ...] = (
"website/*",
"benchmarks/*",
".github/demos/*",
".github/*.png",
".github/*.md",
".github/workflows/website.yml",
"*.md",
)
SKILL: tuple[str, ...] = ("crates/sqlclean-cli/skill/*",)


def classify(paths: list[str]) -> dict[str, bool]:
"""Return which suites the changed paths need."""

def matches(path: str, patterns: tuple[str, ...]) -> bool:
# `*.md` must only match a root file, while `website/*` matches every depth.
return any(
fnmatch.fnmatch(path, pattern) and ("/" in pattern or "/" not in path)
for pattern in patterns
)

skills: bool = any(matches(path, SKILL) for path in paths)
full: bool = not paths or any(
not matches(path, LIGHT) and not matches(path, SKILL) for path in paths
)
return {"full": full, "skills": skills and not full}


def main() -> int:
paths: list[str] = [line.strip() for line in sys.stdin if line.strip()]
for name, value in classify(paths).items():
print(f"{name}={str(value).lower()}")
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading
Loading