From bfe8a98e48ca0f75d9e29abfd75e5a190d9c8cfb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 30 May 2025 10:11:21 +0200 Subject: [PATCH] jsonSchemaCatalogs: init --- .../js/json-schema-catalog-rs/package.nix | 4 + .../catalogs/json-patch-schemastore.nix | 33 +++++ .../data/json-schema/catalogs/json-schema.nix | 39 ++++++ pkgs/data/json-schema/default.nix | 28 ++++ pkgs/data/json-schema/lib.nix | 120 ++++++++++++++++++ pkgs/data/json-schema/tests.nix | 69 ++++++++++ pkgs/top-level/all-packages.nix | 7 + 7 files changed, 300 insertions(+) create mode 100644 pkgs/data/json-schema/catalogs/json-patch-schemastore.nix create mode 100644 pkgs/data/json-schema/catalogs/json-schema.nix create mode 100644 pkgs/data/json-schema/default.nix create mode 100644 pkgs/data/json-schema/lib.nix create mode 100644 pkgs/data/json-schema/tests.nix diff --git a/pkgs/by-name/js/json-schema-catalog-rs/package.nix b/pkgs/by-name/js/json-schema-catalog-rs/package.nix index 179906351b01..b441646e7283 100644 --- a/pkgs/by-name/js/json-schema-catalog-rs/package.nix +++ b/pkgs/by-name/js/json-schema-catalog-rs/package.nix @@ -1,6 +1,7 @@ { callPackage, fetchFromGitHub, + jsonSchemaCatalogs, lib, nix-update-script, rustPlatform, @@ -28,6 +29,9 @@ rustPlatform.buildRustPackage (finalAttrs: { passthru = { tests = { run = callPackage ./test-run.nix { json-schema-catalog-rs = finalAttrs.finalPackage; }; + jsonSchemaCatalogs = jsonSchemaCatalogs.tests.override { + json-schema-catalog-rs = finalAttrs.finalPackage; + }; }; updateScript = nix-update-script { }; diff --git a/pkgs/data/json-schema/catalogs/json-patch-schemastore.nix b/pkgs/data/json-schema/catalogs/json-patch-schemastore.nix new file mode 100644 index 000000000000..7eea0445eb76 --- /dev/null +++ b/pkgs/data/json-schema/catalogs/json-patch-schemastore.nix @@ -0,0 +1,33 @@ +{ + fetchurl, + lib, + newCatalog, +}: + +newCatalog { + name = "json-patch-schemastore"; + displayName = "JSON Patch as maintained by the https://www.schemastore.org community"; + version = "2024-11-26"; + groups = { + "JSON Patch" = { + "https://json.schemastore.org/json-patch.json" = fetchurl { + name = "json-patch-schema"; + # Note that we don't use the json.schemastore.org URLs directly, as those are mutable resources. + # Make sure to update the version above! + url = "https://github.com/schemastore/schemastore/raw/138439cd4f9d7bf57d6747b674ce5bbcffbfafdc/src/schemas/json/json-patch.json"; + hash = "sha256-vrqlgvlU61aEO6jd1SLLEqJDZG1k+eTBozcyPmhUp2U="; + }; + }; + }; + extraDescription = '' + A JSON Patch is a sequence of editing and checking operations to perform against another JSON document. + It is specified in IETF RFC 6902: https://datatracker.ietf.org/doc/html/rfc6902/ + + The schema is maintained by the community at https://www.schemastore.org, which is a collection of JSON Schemas for various purposes. + ''; + meta = { + maintainers = with lib.maintainers; [ roberth ]; + license = lib.licenses.asl20; + changelog = "https://github.com/schemastore/schemastore/commits/master/src/schemas/json/json-patch.json"; + }; +} diff --git a/pkgs/data/json-schema/catalogs/json-schema.nix b/pkgs/data/json-schema/catalogs/json-schema.nix new file mode 100644 index 000000000000..68defdc0b301 --- /dev/null +++ b/pkgs/data/json-schema/catalogs/json-schema.nix @@ -0,0 +1,39 @@ +{ + fetchurl, + lib, + newCatalog, +}: + +newCatalog { + name = "json-schema"; + displayName = "JSON Schema Metaschemas"; + groups = { + "JSON Schema" = { + "http://json-schema.org/draft-04/schema#" = fetchurl { + name = "json-schema-draft-04"; + url = "https://json-schema.org/draft-04/schema"; + hash = "sha256-4UidC0dV8CeTMCWR0/y48Htok6gqlPJIlfjk7fEbguI="; + }; + "http://json-schema.org/draft-06/schema#" = fetchurl { + name = "json-schema-draft-06"; + url = "https://json-schema.org/draft-06/schema"; + hash = "sha256-AGTogLohS/8c7lEeZ4++SMmCbdNZ6R4kI/w23mey6+E="; + }; + "http://json-schema.org/draft-07/schema#" = fetchurl { + name = "json-schema-draft-07"; + url = "https://json-schema.org/draft-07/schema"; + hash = "sha256-aS4dFl5Hr8tfEbLOHGOWNf+oNANdbstrzzCHSB2uhAQ="; + }; + # Unclear how newer metaschemas should be handled, so leaving them out for now. + # https://github.com/roberth/json-schema-catalog-rs/issues/8 + }; + }; + meta = { + maintainers = with lib.maintainers; [ roberth ]; + # https://github.com/json-schema-org/json-schema-spec/blob/main/LICENSE + license = [ + lib.licenses.bsd3 + lib.licenses.afl3 + ]; + }; +} diff --git a/pkgs/data/json-schema/default.nix b/pkgs/data/json-schema/default.nix new file mode 100644 index 000000000000..f12bab479074 --- /dev/null +++ b/pkgs/data/json-schema/default.nix @@ -0,0 +1,28 @@ +{ + lib, + newScope, + json-schema-catalog-rs, + jsonschema-cli, +}: +let + inherit (lib) concatMapAttrs optionalAttrs; + inherit (lib.strings) hasSuffix removeSuffix; + + jsonSchemaCatalogs = lib.makeScope newScope ( + self: + { + inherit ((self.callPackage ./lib.nix { }).lib) newCatalog; + tests = self.callPackage ./tests.nix { }; + } + // concatMapAttrs ( + k: v: + optionalAttrs (v == "regular" && hasSuffix ".nix" k) { + ${removeSuffix ".nix" k} = self.callPackage (./catalogs + "/${k}") { }; + } + ) (builtins.readDir ./catalogs) + ); +in +{ + # Exported to `pkgs` + inherit jsonSchemaCatalogs; +} diff --git a/pkgs/data/json-schema/lib.nix b/pkgs/data/json-schema/lib.nix new file mode 100644 index 000000000000..6dfd96b90981 --- /dev/null +++ b/pkgs/data/json-schema/lib.nix @@ -0,0 +1,120 @@ +{ + lib, + json-schema-catalog-rs, + runCommand, + jq, +}: +let + + /** + A somewhat opinionated method for constructing a JSON Schema Catalog from files in a Nix store. + + The input is a slightly simpler format: + + ```nix + { + name = "my-catalog"; # derivation name, default displayName + displayName = "My Catalog"; # optional + groups = { + "Group One" = { + "https://example.com/schemas/one-v1.json" = pkgs.fetchurl { ... }; + "https://example.com/schemas/one-v2.json" = pkgs.fetchurl { ... }; + "https://example.com/schemas/one-common.json" = pkgs.fetchurl { ... }; + }; + "Group Two" = { + "https://example.com/schemas/two-v1.json" = ./two-v1.json; # Files can be local + }; + }; + } + ``` + */ + newCatalog = + { + name, + displayName ? name, + groups, + version ? null, + extraDescription ? null, + meta ? { }, + }: + let + # lazyDerivation tidies up the package attributes + package = lib.lazyDerivation { + derivation = drv; + passthru = { + name = "catalog-${name}"; + internals = drv; + } + // lib.optionalAttrs (version != null) { + inherit version; + }; + meta = { + description = "JSON Schema Catalog for ${displayName}"; + longDescription = + let + licenses = lib.toList meta.license; + show = license: license.fullName or license; + theLicensesApply = + if lib.length licenses == 1 then + "The package license, ${show (lib.head licenses)}, applies" + else + "The package licenses, ${lib.concatMapStringsSep " / " show licenses}, apply"; + in + '' + A JSON Schema Catalog is a mapping from URIs to JSON Schema documents. + It enables offline use, e.g. in build processes, and therefore it improves performance, robustness and safety. + ${lib.optionalString (extraDescription != null) "\n${extraDescription}\n"} + ${theLicensesApply} to the schemas in this catalog. The catalog file itself is licensed under the terms of the Nix expression that governs it, e.g. MIT in the case of Nixpkgs. + ''; + } + // meta; + }; + + drvArgs = { + pname = name; + catalogJson = builtins.toJSON { + name = displayName; + groups = lib.mapAttrsToList (name: group: { + inherit name; + # TODO dedup the longest common prefix by putting it in baseLocation + baseLocation = "/"; + schemas = lib.mapAttrsToList (id: location: { + inherit id; + inherit location; + }) group; + }) groups; + }; + passAsFile = [ "catalogJson" ]; + passthru = { + inherit groups; + }; + nativeBuildInputs = [ + jq + json-schema-catalog-rs + ]; + } + // lib.optionalAttrs (version != null) { + inherit version; + }; + + drv = runCommand "${package.name}${lib.optionalString (version != null) "-${version}"}" drvArgs '' + out_dir="$out/share/json-schema-catalogs" + out_file="$out_dir/$name.json" + + mkdir -p "$out_dir" + + # Write the catalog JSON. `jq` formats it nicely. + jq . <"$catalogJsonPath" >"$out_file" + + json-schema-catalog check "$out_file" + ''; + in + package; +in +{ + lib = + # Exported as part of `pkgs.jsonSchemaCatalogs` + { + inherit newCatalog; + }; +} diff --git a/pkgs/data/json-schema/tests.nix b/pkgs/data/json-schema/tests.nix new file mode 100644 index 000000000000..4929c9cf9cf5 --- /dev/null +++ b/pkgs/data/json-schema/tests.nix @@ -0,0 +1,69 @@ +{ + json-schema, + lib, + json-schema-catalog-rs, + runCommand, +}: + +lib.recurseIntoAttrs { + test-with-json-schema-catalog-rs = + runCommand "json-schema-catalogs-integration-test" + { + nativeBuildInputs = [ + json-schema + json-schema-catalog-rs + ]; + } + '' + cat >example.json <<"EOF" + { + "$id": "https://example.com/schemas/integration-test.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Integration Test", + "type": "object", + "oneOf": [ + { + "$ref": "http://json-schema.org/draft-07/schema#" + }, + { + "$ref": "http://json-schema.org/draft-07/schema#/definitions/yolo" + }, + { + "$ref": "./foo.json#/definitions/bar" + } + ] + } + EOF + cat >example.json.expected <<"EOF" + { + "$id": "https://example.com/schemas/integration-test.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "oneOf": [ + { + "$ref": "file://${ + json-schema.internals.groups."JSON Schema"."http://json-schema.org/draft-07/schema#" + }#" + }, + { + "$ref": "file://${ + json-schema.internals.groups."JSON Schema"."http://json-schema.org/draft-07/schema#" + }#/definitions/yolo" + }, + { + "$ref": "./foo.json#/definitions/bar" + } + ], + "title": "Integration Test", + "type": "object" + } + EOF + ( set -x; + ! grep '##' example.json.expected + ) + + json-schema-catalog replace --verbose example.json > example.json.out + + diff -U3 --color=always example.json.expected example.json.out + touch $out + ''; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 6f4de0910ec4..6b79decd22cf 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -11474,6 +11474,13 @@ with pkgs; iosevka-comfy = recurseIntoAttrs (callPackages ../data/fonts/iosevka/comfy.nix { }); + /** + A JSON Schema Catalog is a mapping from URIs to JSON Schema documents. + + It enables offline use, e.g. in build processes, and it improves performance, robustness and safety. + */ + inherit (callPackage ../data/json-schema/default.nix { }) jsonSchemaCatalogs; + kde-rounded-corners = kdePackages.callPackage ../data/themes/kwin-decorations/kde-rounded-corners { };