diff --git a/stackinator/schema.py b/stackinator/schema.py index 0f6379d6..90849d9a 100644 --- a/stackinator/schema.py +++ b/stackinator/schema.py @@ -45,7 +45,7 @@ def py2yaml(data, indent): # load recipe yaml schema config_schema = json.load(open(prefix / "schema/config.json")) -config_validator = validator(config_schema) +config_validator = extend_with_default(jsonschema.Draft201909Validator)(config_schema) compilers_schema = json.load(open(prefix / "schema/compilers.json")) compilers_validator = validator(compilers_schema) environments_schema = json.load(open(prefix / "schema/environments.json")) diff --git a/stackinator/schema/config.json b/stackinator/schema/config.json index 49fae9b4..99d7a2b8 100644 --- a/stackinator/schema/config.json +++ b/stackinator/schema/config.json @@ -1,9 +1,9 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "http://json-schema.org/draft-2019-09/schema#", "title": "Schema for Spack Stack config.yaml recipe file", "type" : "object", - "additionalProperties": false, - "required": ["name", "spack"], + "unevaluatedProperties": false, + "required": ["name"], "properties" : { "name" : { "type": "string" @@ -12,38 +12,6 @@ "type" : "string", "default" : "/user-environment" }, - "spack" : { - "type" : "object", - "additionalProperties": false, - "properties" : { - "repo": { - "type": "string" - }, - "commit": { - "oneOf": [ - {"type" : "string"}, - {"type" : "null"} - ], - "default": null - }, - "packages" : { - "type" : "object", - "additionalProperties": false, - "properties" : { - "repo": { - "type": "string" - }, - "commit": { - "oneOf": [ - {"type" : "string"}, - {"type" : "null"} - ], - "default": null - } - } - } - } - }, "mirror" : { "type" : "object", "additionalProperties": false, @@ -75,7 +43,87 @@ }, "version" : { "type": "number", - "default": 1 + "default": 1, + "minimum": 1, + "maximum": 2 + } + }, + "allOf": [ + { + "if": { + "required": ["version"], + "properties": { + "version": {"const": 2 } + } + }, + "then": { + "required": ["spack"], + "properties": { + "spack": { + "$ref": "#/$defs/git-repo", + "unevaluatedProperties": false, + "required": ["packages"], + "properties": { + "packages": { + "$ref": "#/$defs/git-repo", + "unevaluatedProperties": false + } + } + } + } + } + }, + { + "if": { + "oneOf": [ + { + "not": { + "required": ["version"] + } + }, + { + "required": ["version"], + "properties": { + "version": {"const": 1 } + } + } + ] + }, + "then": { + "required": ["spack"], + "properties": { + "spack": { + "$ref": "#/$defs/git-repo", + "unevaluatedProperties": false + } + } + } + }, + { + "if": { + "not": { "properties": { "version": { "minimum": 1, "maximum": 2} } } + }, + "then": { + "properties": { "spack": {} } + } + } + ], + "$defs": { + "git-repo": { + "type" : "object", + "required": ["repo"], + "properties" : { + "repo": { + "type": "string" + }, + "commit": { + "oneOf": [ + {"type" : "string"}, + {"type" : "null"} + ], + "default": null + } } + } } } diff --git a/unittests/test_schema.py b/unittests/test_schema.py index 9bc03264..4ba0e937 100644 --- a/unittests/test_schema.py +++ b/unittests/test_schema.py @@ -40,7 +40,8 @@ def test_config_yaml(yaml_path): # test that the defaults are set as expected with open(yaml_path / "config.defaults.yaml") as fid: raw = yaml.load(fid, Loader=yaml.Loader) - schema.validator(schema.config_schema).validate(raw) + schema.config_validator.validate(raw) + assert raw["version"] == 1 assert raw["store"] == "/user-environment" assert raw["spack"]["commit"] is None assert raw["modules"] == True # noqa: E712 @@ -49,34 +50,45 @@ def test_config_yaml(yaml_path): with open(yaml_path / "config.full.yaml") as fid: raw = yaml.load(fid, Loader=yaml.Loader) - schema.validator(schema.config_schema).validate(raw) + schema.config_validator.validate(raw) + assert raw["version"] == 1 assert raw["store"] == "/alternative-point" assert raw["spack"]["commit"] == "6408b51" assert raw["modules"] == False # noqa: E712 assert raw["mirror"] == {"enable": True, "key": "/home/bob/veryprivate.key"} assert raw["description"] == "a really useful environment" + with open(yaml_path / "config.defaults.v2.yaml") as fid: + raw = yaml.load(fid, Loader=yaml.Loader) + schema.config_validator.validate(raw) + assert raw["store"] == "/user-environment" + assert raw["spack"]["commit"] is None + assert raw["spack"]["packages"]["commit"] is None + assert raw["modules"] == True # noqa: E712 + assert raw["mirror"] == {"enable": True, "key": None} + assert raw["description"] is None + def test_recipe_config_yaml(recipe_paths): # validate the config.yaml in the test recipes for p in recipe_paths: with open(p / "config.yaml") as fid: raw = yaml.load(fid, Loader=yaml.Loader) - schema.validator(schema.config_schema).validate(raw) + schema.config_validator.validate(raw) def test_compilers_yaml(yaml_path): # test that the defaults are set as expected with open(yaml_path / "compilers.defaults.yaml") as fid: raw = yaml.load(fid, Loader=yaml.Loader) - schema.validator(schema.compilers_schema).validate(raw) + schema.compilers_validator.validate(raw) assert raw["bootstrap"] == {"spec": "gcc@11"} assert raw["gcc"] == {"specs": ["gcc@10.2"]} assert raw["llvm"] is None with open(yaml_path / "compilers.full.yaml") as fid: raw = yaml.load(fid, Loader=yaml.Loader) - schema.validator(schema.compilers_schema).validate(raw) + schema.compilers_validator.validate(raw) assert raw["bootstrap"]["spec"] == "gcc@11" assert raw["gcc"] == {"specs": ["gcc@11", "gcc@10.2", "gcc@9.3.0"]} assert raw["llvm"] == { @@ -90,13 +102,13 @@ def test_recipe_compilers_yaml(recipe_paths): for p in recipe_paths: with open(p / "compilers.yaml") as fid: raw = yaml.load(fid, Loader=yaml.Loader) - schema.validator(schema.compilers_schema).validate(raw) + schema.compilers_validator.validate(raw) def test_environments_yaml(yaml_path): with open(yaml_path / "environments.full.yaml") as fid: raw = yaml.load(fid, Loader=yaml.Loader) - schema.validator(schema.environments_schema).validate(raw) + schema.environments_validator.validate(raw) # the defaults-env does not set fields # test that they have been set to the defaults correctly @@ -145,7 +157,7 @@ def test_environments_yaml(yaml_path): jsonschema.exceptions.ValidationError, match=r"Additional properties are not allowed \('providers' was unexpected", ): - schema.validator(schema.environments_schema).validate(raw) + schema.environments_validator.validate(raw) def test_recipe_environments_yaml(recipe_paths): @@ -153,4 +165,4 @@ def test_recipe_environments_yaml(recipe_paths): for p in recipe_paths: with open(p / "environments.yaml") as fid: raw = yaml.load(fid, Loader=yaml.Loader) - schema.validator(schema.environments_schema).validate(raw) + schema.environments_validator.validate(raw) diff --git a/unittests/yaml/config.defaults.v2.yaml b/unittests/yaml/config.defaults.v2.yaml new file mode 100644 index 00000000..9720dcf0 --- /dev/null +++ b/unittests/yaml/config.defaults.v2.yaml @@ -0,0 +1,13 @@ +version: 2 +name: cuda-env +# store: /user-environment +spack: + repo: https://github.com/spack/spack.git + # commit: None + packages: + repo: https://github.com/spack/spack-packages.git + # commit: None +# mirror: + # key: None + # enable: True +# modules: True