//
··
1#!/usr/bin/env bash
2# Validate build/main.cir.json against the rrxiv CIR schema.
3#
4# Strategy:
5# 1. Prefer `ajv` if available — fast, native JSON Schema 2020-12 support.
6# 2. Fall back to `python -m jsonschema` if Python's jsonschema is installed.
7# 3. As a last resort, ask `uv run --project ../rrxiv-python rrxiv validate`.
8set -euo pipefail
9
10SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
12CIR="$ROOT/build/main.cir.json"
13
14if [[ ! -f "$CIR" ]]; then
15 echo "ERROR: $CIR not found — run scripts/extract-cir.sh first." >&2
16 exit 1
17fi
18
19# Locate the schema. Order:
20# 1. $RRXIV_REPO env var (CI sets this).
21# 2. Sibling rrxiv checkout (local dev).
22# 3. `deps/rrxiv` subdir (CI workspace-relative checkout).
23# 4. Fetch the pinned snapshot from GitHub raw.
24SCHEMA=""
25if [[ -n "${RRXIV_REPO:-}" && -f "$RRXIV_REPO/schema/cir.schema.json" ]]; then
26 SCHEMA="$RRXIV_REPO/schema/cir.schema.json"
27fi
28if [[ -z "$SCHEMA" ]]; then
29 for sibling in \
30 "$ROOT/../rrxiv/schema/cir.schema.json" \
31 "$ROOT/../../rrxiv/schema/cir.schema.json" \
32 "$ROOT/../../repos/rrxiv/schema/cir.schema.json" \
33 "$ROOT/deps/rrxiv/schema/cir.schema.json"; do
34 if [[ -f "$sibling" ]]; then
35 SCHEMA="$sibling"
36 break
37 fi
38 done
39fi
40
41if [[ -z "$SCHEMA" ]]; then
42 TMP="$(mktemp -d)"
43 SCHEMA="$TMP/cir.schema.json"
44 curl -fsSL \
45 "https://raw.githubusercontent.com/random-walks/rrxiv/main/schema/cir.schema.json" \
46 -o "$SCHEMA"
47fi
48
49if command -v ajv >/dev/null 2>&1; then
50 ajv validate \
51 --spec=draft2020 \
52 -s "$SCHEMA" \
53 -d "$CIR" \
54 --allow-union-types
55elif command -v python3 >/dev/null 2>&1 && python3 -c "import jsonschema, referencing" 2>/dev/null; then
56 # Pre-load every *.schema.json in the schema dir into a Registry so
57 # cross-schema $refs (cir → paper → claim → author → ...) resolve
58 # against the local filesystem instead of trying to fetch
59 # https://rrxiv.com/schema/v0/<name>.schema.json over the network.
60 SCHEMA_DIR="$(dirname "$SCHEMA")"
61 python3 - "$SCHEMA" "$CIR" "$SCHEMA_DIR" <<'PY'
62import glob, json, os, sys
63import jsonschema
64from referencing import Registry, Resource
65from referencing.jsonschema import DRAFT202012
66
67schema_path, cir_path, schema_dir = sys.argv[1:4]
68schema = json.load(open(schema_path))
69doc = json.load(open(cir_path))
70
71resources = []
72for path in sorted(glob.glob(os.path.join(schema_dir, "*.schema.json"))):
73 s = json.load(open(path))
74 if "$id" in s:
75 resources.append((s["$id"], Resource(contents=s, specification=DRAFT202012)))
76registry = Registry().with_resources(resources)
77
78jsonschema.Draft202012Validator(schema, registry=registry).validate(doc)
79print(f"OK {cir_path} validates against {os.path.basename(schema_path)}")
80PY
81elif command -v uv >/dev/null 2>&1; then
82 PYREPO="${RRXIV_PYTHON_REPO:-$ROOT/../rrxiv-python}"
83 if [[ ! -f "$PYREPO/pyproject.toml" && -f "$ROOT/deps/rrxiv-python/pyproject.toml" ]]; then
84 PYREPO="$ROOT/deps/rrxiv-python"
85 fi
86 # --all-extras pulls in the CLI's transitive deps (cryptography +
87 # http-message-signatures + fastapi via cli/app.py's eager imports).
88 # `rrxiv validate` itself doesn't need any of them at runtime.
89 uv run --project "$PYREPO" --all-extras rrxiv validate "$CIR"
90else
91 echo "ERROR: no validator available." >&2
92 echo "Install one of:" >&2
93 echo " npm i -g ajv-cli" >&2
94 echo " pip install jsonschema" >&2
95 exit 127
96fi
97
98echo "OK CIR validates."
99