Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Pre-commit hooks for the query library pipeline.
#
# One-time setup per clone:
# pip install pre-commit
# pre-commit install
#
# Hooks run only when staged files touch the library sources or the
# committed artifacts. Both use language: python with the pipeline's
# dependencies (keep additional_dependencies in sync with
# query-library/scripts/requirements.txt), so no local pip install of the
# requirements is needed. CI enforces the same checks either way; the hooks
# just fail faster.
repos:
- repo: local
hooks:
- id: query-library-validate
name: query library structural validation
entry: python query-library/scripts/validate.py
language: python
additional_dependencies: ["PyYAML>=6.0", "jsonschema>=4.17"]
files: ^query-library/
pass_filenames: false
- id: query-library-artifacts
name: query library artifacts freshness
entry: python query-library/scripts/precommit-artifacts.py
language: python
additional_dependencies: ["PyYAML>=6.0", "jsonschema>=4.17"]
files: ^(query-library/|static/docs/query-library/)
pass_filenames: false
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ silently diverge from the committed artifact.
python query-library/scripts/validate.py
python query-library/scripts/build-artifacts.py

# Optional commit-time enforcement of the two commands above
# (.pre-commit-config.yaml; per-clone opt-in)
pip install pre-commit && pre-commit install

# Site (no env vars needed)
yarn start # dev server - no postBuild AEO outputs, no JSON-LD
yarn build # full production build
Expand Down
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,23 @@ python query-library/scripts/build-artifacts.py
Commit the regenerated files under `static/docs/query-library/` together with your
entry. CI fails the PR if the committed artifacts do not match the sources.

### Pre-commit hooks (optional but recommended)

The repo ships a `.pre-commit-config.yaml` that runs the same validation and
artifact-freshness checks at commit time, so you find out in seconds instead
of at CI. One-time setup per clone:

```bash
pip install pre-commit
pre-commit install
```

The hooks only fire when a commit touches `query-library/` or the committed
artifacts. On a freshness failure the hook has already regenerated the
artifacts in your working tree - review them, `git add` them, and commit
again. The hooks install their own Python dependencies, and CI enforces the
same checks regardless, so skipping this setup just means slower feedback.

## What CI checks

Per PR:
Expand Down
3 changes: 2 additions & 1 deletion query-library/scripts/nightly-verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ def main() -> int:
args = parser.parse_args()

try:
overrides = json.loads(args.params_json)
# An unset secret reaches us as an empty env var; treat blank as no overrides.
overrides = json.loads(args.params_json.strip() or "{}")
except json.JSONDecodeError as e:
print(f"nightly-verify: bad params JSON: {e}", file=sys.stderr)
return 1
Expand Down
64 changes: 64 additions & 0 deletions query-library/scripts/precommit-artifacts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env python3
"""Pre-commit gate: regenerate the committed artifacts and fail if the result
does not match what is staged.

Runs build-artifacts.py (which validates entries first), then checks git
status for the generated outputs: static/docs/query-library/ and the
per-family .mdx stubs under query-library/. Any diff means the staged
sources and the staged artifacts are out of sync - the same condition the
CI freshness gate fails on. The rebuild is idempotent (build_id is a
content hash; manifest timestamps only change when it does), so a clean
tree passes with no side effects.

pre-commit stashes unstaged changes before running hooks, so a diff here
always means "regenerated artifacts were not staged", never "you have
unrelated local edits".
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[2]
GENERATED_PATHSPECS = ["static/docs/query-library", "query-library/*.mdx"]
HANDWRITTEN = {"query-library/index.mdx"}


def main() -> int:
build = REPO_ROOT / "query-library" / "scripts" / "build-artifacts.py"
if subprocess.run([sys.executable, str(build)], cwd=REPO_ROOT).returncode != 0:
return 1

status = subprocess.run(
["git", "status", "--porcelain", "--", *GENERATED_PATHSPECS],
cwd=REPO_ROOT,
capture_output=True,
text=True,
)
if status.returncode != 0:
print(status.stderr, file=sys.stderr)
return 1

stale = [
line
for line in status.stdout.splitlines()
if line.strip() and line[3:].strip('"') not in HANDWRITTEN
]
if stale:
print("Regenerated artifacts differ from what is staged:", file=sys.stderr)
for line in stale:
print(f" {line}", file=sys.stderr)
print(
"\nThe build has refreshed these files in your working tree.\n"
"Review them, then: git add static/docs/query-library query-library/*.mdx\n"
"and commit again.",
file=sys.stderr,
)
return 1
return 0


if __name__ == "__main__":
sys.exit(main())
4 changes: 2 additions & 2 deletions static/docs/query-library/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"build_id": "ql-ea4d2cbf5ec44568",
"generated_at": "2026-07-31T03:10:30Z",
"library_commit": "3a2c3d9b",
"generated_at": "2026-07-31T21:03:45Z",
"library_commit": "30430712",
"entry_count": 47
}