From 004a3306ecabca072da508e9cb3e27abf367bf3a Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 6 Jun 2021 10:22:45 -0700 Subject: [PATCH 1/5] Move schema pattern for not starting with "arduino" to the general definitions file Since this was previously only used for library.properties, it was in that schema file, but now it will be shared with the package index schema, and so should be in the appropriate file for that usage. --- ...uino-library-properties-definitions-schema.json | 9 ++------- etc/schemas/general-definitions-schema.json | 5 +++++ internal/rule/schema/schemadata/bindata.go | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/etc/schemas/arduino-library-properties-definitions-schema.json b/etc/schemas/arduino-library-properties-definitions-schema.json index 5a38ff6b..a8630aa1 100644 --- a/etc/schemas/arduino-library-properties-definitions-schema.json +++ b/etc/schemas/arduino-library-properties-definitions-schema.json @@ -6,11 +6,6 @@ "definitions": { "general": { "patternObjects": { - "notStartsWithArduino": { - "not": { - "pattern": "^[aA][rR][dD][uU][iI][nN][oO].*$" - } - }, "notContainsArduino": { "not": { "pattern": "^.+[aA][rR][dD][uU][iI][nN][oO].*$" @@ -69,7 +64,7 @@ }, { "$comment": "Only official Arduino libraries are allowed to have names starting with \"Arduino\"", - "$ref": "#/definitions/general/patternObjects/notStartsWithArduino" + "$ref": "general-definitions-schema.json#/definitions/patternObjects/notStartsWithArduino" } ] } @@ -228,7 +223,7 @@ }, { "$comment": "Only official Arduino libraries are allowed to have maintainer field starting with \"Arduino\"", - "$ref": "#/definitions/general/patternObjects/notStartsWithArduino" + "$ref": "general-definitions-schema.json#/definitions/patternObjects/notStartsWithArduino" } ] } diff --git a/etc/schemas/general-definitions-schema.json b/etc/schemas/general-definitions-schema.json index 983972ab..5ecff6d9 100644 --- a/etc/schemas/general-definitions-schema.json +++ b/etc/schemas/general-definitions-schema.json @@ -17,6 +17,11 @@ "containsPropertyReference": { "$comment": "https://arduino.github.io/arduino-cli/dev/platform-specification/#configuration-files-format", "pattern": "{.+}" + }, + "notStartsWithArduino": { + "not": { + "pattern": "^[aA][rR][dD][uU][iI][nN][oO].*$" + } } } } diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index fae8d4cd..3ebd5dba 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -1425,11 +1425,6 @@ var _arduinoLibraryPropertiesDefinitionsSchemaJson = []byte(`{ "definitions": { "general": { "patternObjects": { - "notStartsWithArduino": { - "not": { - "pattern": "^[aA][rR][dD][uU][iI][nN][oO].*$" - } - }, "notContainsArduino": { "not": { "pattern": "^.+[aA][rR][dD][uU][iI][nN][oO].*$" @@ -1488,7 +1483,7 @@ var _arduinoLibraryPropertiesDefinitionsSchemaJson = []byte(`{ }, { "$comment": "Only official Arduino libraries are allowed to have names starting with \"Arduino\"", - "$ref": "#/definitions/general/patternObjects/notStartsWithArduino" + "$ref": "general-definitions-schema.json#/definitions/patternObjects/notStartsWithArduino" } ] } @@ -1647,7 +1642,7 @@ var _arduinoLibraryPropertiesDefinitionsSchemaJson = []byte(`{ }, { "$comment": "Only official Arduino libraries are allowed to have maintainer field starting with \"Arduino\"", - "$ref": "#/definitions/general/patternObjects/notStartsWithArduino" + "$ref": "general-definitions-schema.json#/definitions/patternObjects/notStartsWithArduino" } ] } @@ -4370,6 +4365,11 @@ var _generalDefinitionsSchemaJson = []byte(`{ "containsPropertyReference": { "$comment": "https://arduino.github.io/arduino-cli/dev/platform-specification/#configuration-files-format", "pattern": "{.+}" + }, + "notStartsWithArduino": { + "not": { + "pattern": "^[aA][rR][dD][uU][iI][nN][oO].*$" + } } } } From fa2aa49079d200991e9d68a541d38ab2b129743a Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 6 Jun 2021 10:33:05 -0700 Subject: [PATCH 2/5] Add convenience function for "format" schema result parsing The JSON schema validation result parsing infrastructure provides general purpose capabilities for identifying the instance and schema pointers related to a validation failure. However, when a specific type of schema keyword is used in multiple places, it is convenient to create a dedicated wrapper function to facilitate that use case. Previously the "format" keyword was only used for one thing in the library.properties schemas, which made it not worth a convenience function. The package index schema uses "format" keyword also, so it now becomes worthwhile having a convenience parsing function for it. --- go.mod | 2 +- .../librarypropertiesschemas_test.go | 23 ++++++++++++---- internal/rule/schema/parsevalidationresult.go | 5 ++++ internal/rule/schema/schema_test.go | 26 +++++++++++++++++++ internal/rule/schema/testdata/bindata.go | 12 +++++++++ .../testdata/input/referenced-schema-2.json | 5 ++++ .../input/valid-schema-with-references.json | 7 +++++ 7 files changed, 74 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index d53228e5..9cc0afd9 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/spf13/viper v1.7.1 // indirect github.com/stretchr/testify v1.6.1 github.com/xanzy/ssh-agent v0.3.0 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 go.bug.st/relaxed-semver v0.0.0-20190922224835-391e10178d18 go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.14.0 // indirect diff --git a/internal/project/library/libraryproperties/librarypropertiesschemas_test.go b/internal/project/library/libraryproperties/librarypropertiesschemas_test.go index 4fde2c83..01634f25 100644 --- a/internal/project/library/libraryproperties/librarypropertiesschemas_test.go +++ b/internal/project/library/libraryproperties/librarypropertiesschemas_test.go @@ -79,6 +79,19 @@ func checkPropertyEnumMismatch(propertyName string, testTables []propertyValueTe } } +func checkPropertyFormatMismatch(propertyName string, testTables []propertyValueTestTable, t *testing.T) { + libraryProperties := properties.NewFromHashmap(validLibraryPropertiesMap) + var validationResult map[compliancelevel.Type]schema.ValidationResult + + for _, testTable := range testTables { + validationResult = changeValueUpdateValidationResult(propertyName, testTable.propertyValue, libraryProperties, validationResult) + + t.Run(fmt.Sprintf("%s (%s)", testTable.testName, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.PropertyFormatMismatch(propertyName, validationResult[testTable.complianceLevel])) + }) + } +} + type validationErrorTestTable struct { testName string propertyValue string @@ -322,13 +335,13 @@ func TestPropertiesCategoryEnum(t *testing.T) { } func TestPropertiesUrlFormat(t *testing.T) { - testTables := []validationErrorTestTable{ - {"Invalid URL format", "foo", "/format$", compliancelevel.Permissive, assert.False}, - {"Invalid URL format", "foo", "/format$", compliancelevel.Specification, assert.True}, - {"Invalid URL format", "foo", "/format$", compliancelevel.Strict, assert.True}, + testTables := []propertyValueTestTable{ + {"Invalid URL format", "foo", compliancelevel.Permissive, assert.False}, + {"Invalid URL format", "foo", compliancelevel.Specification, assert.True}, + {"Invalid URL format", "foo", compliancelevel.Strict, assert.True}, } - checkValidationErrorMatch("url", testTables, t) + checkPropertyFormatMismatch("url", testTables, t) } func TestPropertiesDependsPattern(t *testing.T) { diff --git a/internal/rule/schema/parsevalidationresult.go b/internal/rule/schema/parsevalidationresult.go index 4595aeab..169f7bc5 100644 --- a/internal/rule/schema/parsevalidationresult.go +++ b/internal/rule/schema/parsevalidationresult.go @@ -54,6 +54,11 @@ func PropertyDependenciesMissing(propertyName string, validationResult Validatio return ValidationErrorMatch("", "/dependencies/"+propertyName+"/[0-9]+$", "", "", validationResult) } +// PropertyFormatMismatch returns whether the given property has incorrect format. +func PropertyFormatMismatch(propertyName string, validationResult ValidationResult) bool { + return ValidationErrorMatch("^#/"+propertyName+"$", "/format$", "", "", validationResult) +} + // MisspelledOptionalPropertyFound returns whether a misspelled optional property was found. func MisspelledOptionalPropertyFound(validationResult ValidationResult) bool { return ValidationErrorMatch("#/", "/misspelledOptionalProperties/", "", "", validationResult) diff --git a/internal/rule/schema/schema_test.go b/internal/rule/schema/schema_test.go index 100ba76d..f3be4ac2 100644 --- a/internal/rule/schema/schema_test.go +++ b/internal/rule/schema/schema_test.go @@ -16,6 +16,8 @@ package schema import ( + "encoding/json" + "fmt" "regexp" "testing" @@ -25,6 +27,7 @@ import ( "github.com/ory/jsonschema/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/xeipuuv/gojsonpointer" ) var validMap = map[string]string{ @@ -150,6 +153,29 @@ func TestPropertyEnumMismatch(t *testing.T) { assert.True(t, PropertyEnumMismatch("property3", validationResult)) } +func TestPropertyFormatMismatch(t *testing.T) { + propertyName := "TestPropertyFormatMismatch" + instanceTemplate := ` +{ + "%s": "http://example.com" +} +` + rawInstance := fmt.Sprintf(instanceTemplate, propertyName) + var instance map[string]interface{} + json.Unmarshal([]byte(rawInstance), &instance) + + assert.False(t, PropertyFormatMismatch(propertyName, Validate(instance, validSchemaWithReferences)), "Property format is correct") + + // Change property to incorrect type. + pointerString := "/" + propertyName + pointer, err := gojsonpointer.NewJsonPointer(pointerString) + require.NoError(t, err) + _, err = pointer.Set(instance, "foo") + require.NoError(t, err) + + assert.True(t, PropertyFormatMismatch(propertyName, Validate(instance, validSchemaWithReferences)), "Property format is incorrect") +} + func TestPropertyDependenciesMissing(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) validationResult := Validate(general.PropertiesToMap(propertiesMap, 0), validSchemaWithReferences) diff --git a/internal/rule/schema/testdata/bindata.go b/internal/rule/schema/testdata/bindata.go index d2aa48c4..35bb1614 100644 --- a/internal/rule/schema/testdata/bindata.go +++ b/internal/rule/schema/testdata/bindata.go @@ -138,6 +138,11 @@ var _referencedSchema2Json = []byte(`{ ] } }, + "TestPropertyFormatMismatch": { + "object": { + "format": "uri" + } + }, "misspelledOptionalProperties": { "propertyNames": { "not": { @@ -217,6 +222,13 @@ var _validSchemaWithReferencesJson = []byte(`{ "$ref": "referenced-schema-2.json#/definitions/notPatternObject" } ] + }, + "TestPropertyFormatMismatch": { + "allOf": [ + { + "$ref": "referenced-schema-2.json#/definitions/TestPropertyFormatMismatch/object" + } + ] } }, "allOf": [ diff --git a/internal/rule/schema/testdata/input/referenced-schema-2.json b/internal/rule/schema/testdata/input/referenced-schema-2.json index 9b8e2cf2..a768741c 100644 --- a/internal/rule/schema/testdata/input/referenced-schema-2.json +++ b/internal/rule/schema/testdata/input/referenced-schema-2.json @@ -26,6 +26,11 @@ ] } }, + "TestPropertyFormatMismatch": { + "object": { + "format": "uri" + } + }, "misspelledOptionalProperties": { "propertyNames": { "not": { diff --git a/internal/rule/schema/testdata/input/valid-schema-with-references.json b/internal/rule/schema/testdata/input/valid-schema-with-references.json index d07da8e6..de3c486c 100644 --- a/internal/rule/schema/testdata/input/valid-schema-with-references.json +++ b/internal/rule/schema/testdata/input/valid-schema-with-references.json @@ -30,6 +30,13 @@ "$ref": "referenced-schema-2.json#/definitions/notPatternObject" } ] + }, + "TestPropertyFormatMismatch": { + "allOf": [ + { + "$ref": "referenced-schema-2.json#/definitions/TestPropertyFormatMismatch/object" + } + ] } }, "allOf": [ From c0b7847954c8526f8ecb34e9e246252f8b433317 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 6 Jun 2021 10:40:04 -0700 Subject: [PATCH 3/5] Add convenience function for "type" schema result parsing The JSON schema validation result parsing infrastructure provides general purpose capabilities for identifying the instance and schema pointers related to a validation failure. However, when a specific type of schema keyword is used in multiple places, it is convenient to create a dedicated wrapper function to facilitate that use case. Although present in the schemas for the sake of completeness, there are no rules for checking schema validation results for the "type" keyword because the Arduino "properties" data format treats all values as strings. For this reason, there was no convenience function for this keyword. Due to using the JSON data format, which allows for any type, there will be type checking schema-based rules for the package index project type. So it now becomes worthwhile having a convenience parsing function for the "type" keyword. --- internal/rule/schema/parsevalidationresult.go | 5 ++++ internal/rule/schema/schema_test.go | 23 +++++++++++++++++++ internal/rule/schema/testdata/bindata.go | 12 ++++++++++ .../testdata/input/referenced-schema-2.json | 5 ++++ .../input/valid-schema-with-references.json | 7 ++++++ 5 files changed, 52 insertions(+) diff --git a/internal/rule/schema/parsevalidationresult.go b/internal/rule/schema/parsevalidationresult.go index 169f7bc5..7864c89f 100644 --- a/internal/rule/schema/parsevalidationresult.go +++ b/internal/rule/schema/parsevalidationresult.go @@ -54,6 +54,11 @@ func PropertyDependenciesMissing(propertyName string, validationResult Validatio return ValidationErrorMatch("", "/dependencies/"+propertyName+"/[0-9]+$", "", "", validationResult) } +// PropertyTypeMismatch returns whether the given property has incorrect type. +func PropertyTypeMismatch(propertyName string, validationResult ValidationResult) bool { + return ValidationErrorMatch("^#/"+propertyName+"$", "/type$", "", "", validationResult) +} + // PropertyFormatMismatch returns whether the given property has incorrect format. func PropertyFormatMismatch(propertyName string, validationResult ValidationResult) bool { return ValidationErrorMatch("^#/"+propertyName+"$", "/format$", "", "", validationResult) diff --git a/internal/rule/schema/schema_test.go b/internal/rule/schema/schema_test.go index f3be4ac2..88be8279 100644 --- a/internal/rule/schema/schema_test.go +++ b/internal/rule/schema/schema_test.go @@ -153,6 +153,29 @@ func TestPropertyEnumMismatch(t *testing.T) { assert.True(t, PropertyEnumMismatch("property3", validationResult)) } +func TestPropertyTypeMismatch(t *testing.T) { + propertyName := "TestPropertyTypeMismatch" + instanceTemplate := ` +{ + "%s": "foo" +} +` + rawInstance := fmt.Sprintf(instanceTemplate, propertyName) + var instance map[string]interface{} + json.Unmarshal([]byte(rawInstance), &instance) + + assert.False(t, PropertyTypeMismatch(propertyName, Validate(instance, validSchemaWithReferences)), "Property type is correct") + + // Change property to incorrect type. + pointerString := "/" + propertyName + pointer, err := gojsonpointer.NewJsonPointer(pointerString) + require.NoError(t, err) + _, err = pointer.Set(instance, 1) + require.NoError(t, err) + + assert.True(t, PropertyTypeMismatch(propertyName, Validate(instance, validSchemaWithReferences)), "Property type is incorrect") +} + func TestPropertyFormatMismatch(t *testing.T) { propertyName := "TestPropertyFormatMismatch" instanceTemplate := ` diff --git a/internal/rule/schema/testdata/bindata.go b/internal/rule/schema/testdata/bindata.go index 35bb1614..d69ad6fe 100644 --- a/internal/rule/schema/testdata/bindata.go +++ b/internal/rule/schema/testdata/bindata.go @@ -138,6 +138,11 @@ var _referencedSchema2Json = []byte(`{ ] } }, + "TestPropertyTypeMismatch": { + "object": { + "type": "string" + } + }, "TestPropertyFormatMismatch": { "object": { "format": "uri" @@ -223,6 +228,13 @@ var _validSchemaWithReferencesJson = []byte(`{ } ] }, + "TestPropertyTypeMismatch": { + "allOf": [ + { + "$ref": "referenced-schema-2.json#/definitions/TestPropertyTypeMismatch/object" + } + ] + }, "TestPropertyFormatMismatch": { "allOf": [ { diff --git a/internal/rule/schema/testdata/input/referenced-schema-2.json b/internal/rule/schema/testdata/input/referenced-schema-2.json index a768741c..7572dd6a 100644 --- a/internal/rule/schema/testdata/input/referenced-schema-2.json +++ b/internal/rule/schema/testdata/input/referenced-schema-2.json @@ -26,6 +26,11 @@ ] } }, + "TestPropertyTypeMismatch": { + "object": { + "type": "string" + } + }, "TestPropertyFormatMismatch": { "object": { "format": "uri" diff --git a/internal/rule/schema/testdata/input/valid-schema-with-references.json b/internal/rule/schema/testdata/input/valid-schema-with-references.json index de3c486c..1f459c3d 100644 --- a/internal/rule/schema/testdata/input/valid-schema-with-references.json +++ b/internal/rule/schema/testdata/input/valid-schema-with-references.json @@ -31,6 +31,13 @@ } ] }, + "TestPropertyTypeMismatch": { + "allOf": [ + { + "$ref": "referenced-schema-2.json#/definitions/TestPropertyTypeMismatch/object" + } + ] + }, "TestPropertyFormatMismatch": { "allOf": [ { From a11044a0d297d54de05eaa71afe9c2f1230a525e Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 6 Jun 2021 10:44:26 -0700 Subject: [PATCH 4/5] Add convenience function for "additionalProperties" schema result parsing The JSON schema validation result parsing infrastructure provides general purpose capabilities for identifying the instance and schema pointers related to a validation failure. However, when a specific type of schema keyword is used in multiple places, it is convenient to create a dedicated wrapper function to facilitate that use case. Previously the "additionalProperties" keyword was not used in any rules, which made it not worth a convenience function. The package index schema uses the "additionalProperties" keyword, so it now becomes worthwhile having a convenience parsing function for it. --- internal/rule/schema/parsevalidationresult.go | 5 +++ internal/rule/schema/schema_test.go | 43 +++++++++++++++++++ internal/rule/schema/testdata/bindata.go | 16 +++++++ .../input/valid-schema-with-references.json | 16 +++++++ 4 files changed, 80 insertions(+) diff --git a/internal/rule/schema/parsevalidationresult.go b/internal/rule/schema/parsevalidationresult.go index 7864c89f..7d122de5 100644 --- a/internal/rule/schema/parsevalidationresult.go +++ b/internal/rule/schema/parsevalidationresult.go @@ -64,6 +64,11 @@ func PropertyFormatMismatch(propertyName string, validationResult ValidationResu return ValidationErrorMatch("^#/"+propertyName+"$", "/format$", "", "", validationResult) } +// ProhibitedAdditionalProperty returns whether the given property has prohibited additional subproperty(s). +func ProhibitedAdditionalProperties(propertyName string, validationResult ValidationResult) bool { + return ValidationErrorMatch("^#/?"+propertyName+"$", "/additionalProperties$", "", "", validationResult) +} + // MisspelledOptionalPropertyFound returns whether a misspelled optional property was found. func MisspelledOptionalPropertyFound(validationResult ValidationResult) bool { return ValidationErrorMatch("#/", "/misspelledOptionalProperties/", "", "", validationResult) diff --git a/internal/rule/schema/schema_test.go b/internal/rule/schema/schema_test.go index 88be8279..86fd78f1 100644 --- a/internal/rule/schema/schema_test.go +++ b/internal/rule/schema/schema_test.go @@ -19,6 +19,7 @@ import ( "encoding/json" "fmt" "regexp" + "strings" "testing" "github.com/arduino/arduino-lint/internal/project/general" @@ -199,6 +200,48 @@ func TestPropertyFormatMismatch(t *testing.T) { assert.True(t, PropertyFormatMismatch(propertyName, Validate(instance, validSchemaWithReferences)), "Property format is incorrect") } +func TestProhibitedAdditionalProperties(t *testing.T) { + propertyName := "TestProhibitedAdditionalProperties" + instanceTemplate := ` +{ + "%s": { + "additionalPropertiesTrue": { + "fooProperty": "bar" + }, + "additionalPropertiesFalse": { + "fooProperty": "bar" + } + } +} +` + + testTables := []struct { + objectPointerString string + assertion assert.BoolAssertionFunc + }{ + {"/TestProhibitedAdditionalProperties/additionalPropertiesTrue", assert.False}, + {"/TestProhibitedAdditionalProperties/additionalPropertiesFalse", assert.True}, + } + + for _, testTable := range testTables { + rawInstance := fmt.Sprintf(instanceTemplate, propertyName) + var instance map[string]interface{} + json.Unmarshal([]byte(rawInstance), &instance) + + assert.False(t, ProhibitedAdditionalProperties(strings.TrimPrefix(testTable.objectPointerString, "/"), Validate(instance, validSchemaWithReferences)), fmt.Sprintf("No additional properties in %s", testTable.objectPointerString)) + + // Add additional property to object. + pointer, err := gojsonpointer.NewJsonPointer(testTable.objectPointerString + "/fooAdditionalProperty") + require.NoError(t, err) + _, err = pointer.Set(instance, "bar") + require.NoError(t, err) + + t.Run(fmt.Sprintf("Additional property in the %s object", testTable.objectPointerString), func(t *testing.T) { + testTable.assertion(t, ProhibitedAdditionalProperties(strings.TrimPrefix(testTable.objectPointerString, "/"), Validate(instance, validSchemaWithReferences))) + }) + } +} + func TestPropertyDependenciesMissing(t *testing.T) { propertiesMap := properties.NewFromHashmap(validMap) validationResult := Validate(general.PropertiesToMap(propertiesMap, 0), validSchemaWithReferences) diff --git a/internal/rule/schema/testdata/bindata.go b/internal/rule/schema/testdata/bindata.go index d69ad6fe..4702b6a4 100644 --- a/internal/rule/schema/testdata/bindata.go +++ b/internal/rule/schema/testdata/bindata.go @@ -241,6 +241,22 @@ var _validSchemaWithReferencesJson = []byte(`{ "$ref": "referenced-schema-2.json#/definitions/TestPropertyFormatMismatch/object" } ] + }, + "TestProhibitedAdditionalProperties": { + "properties": { + "additionalPropertiesTrue": { + "properties": { + "fooProperty": {} + }, + "additionalProperties": true + }, + "additionalPropertiesFalse": { + "properties": { + "fooProperty": {} + }, + "additionalProperties": false + } + } } }, "allOf": [ diff --git a/internal/rule/schema/testdata/input/valid-schema-with-references.json b/internal/rule/schema/testdata/input/valid-schema-with-references.json index 1f459c3d..1b7408fc 100644 --- a/internal/rule/schema/testdata/input/valid-schema-with-references.json +++ b/internal/rule/schema/testdata/input/valid-schema-with-references.json @@ -44,6 +44,22 @@ "$ref": "referenced-schema-2.json#/definitions/TestPropertyFormatMismatch/object" } ] + }, + "TestProhibitedAdditionalProperties": { + "properties": { + "additionalPropertiesTrue": { + "properties": { + "fooProperty": {} + }, + "additionalProperties": true + }, + "additionalPropertiesFalse": { + "properties": { + "fooProperty": {} + }, + "additionalProperties": false + } + } } }, "allOf": [ From 58619545aa087267eae9f95f3d864fbf1566b092 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 6 Jun 2021 11:28:14 -0700 Subject: [PATCH 5/5] Add JSON schema for package index This schema defines the data structure of the Arduino package index at three compliance levels: - permissive: the minimum accepted format. This allows for a gradual deprecation following specification changes. - specification: the format as defined in the official specification - strict: best practices Helper functions for reading package indexes and validating them against the schema are added, at this point for use in the tests, but also for the schema-based rules to come. --- ...uino-package-index-definitions-schema.json | 1324 +++++++++++++++ ...duino-package-index-permissive-schema.json | 11 + etc/schemas/arduino-package-index-schema.json | 11 + .../arduino-package-index-strict-schema.json | 11 + internal/project/packageindex/packageindex.go | 43 + .../project/packageindex/packageindex_test.go | 8 + .../packageindex/packageindexschemas_test.go | 699 ++++++++ .../testdata/package_valid_index.json | 51 + internal/rule/schema/schemadata/bindata.go | 1437 +++++++++++++++++ 9 files changed, 3595 insertions(+) create mode 100644 etc/schemas/arduino-package-index-definitions-schema.json create mode 100644 etc/schemas/arduino-package-index-permissive-schema.json create mode 100644 etc/schemas/arduino-package-index-schema.json create mode 100644 etc/schemas/arduino-package-index-strict-schema.json create mode 100644 internal/project/packageindex/packageindexschemas_test.go create mode 100644 internal/project/packageindex/testdata/package_valid_index.json diff --git a/etc/schemas/arduino-package-index-definitions-schema.json b/etc/schemas/arduino-package-index-definitions-schema.json new file mode 100644 index 00000000..bbdf1c54 --- /dev/null +++ b/etc/schemas/arduino-package-index-definitions-schema.json @@ -0,0 +1,1324 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-package-index-definitions-schema.json", + "title": "Shared definitions for the Arduino Package Index schemas", + "definitions": { + "root": { + "base": { + "object": { + "type": "object", + "required": ["packages"] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/root/base/object" + }, + { + "properties": { + "packages": { + "$ref": "#/definitions/propertiesObjects/packages/permissive/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/root/base/object" + }, + { + "properties": { + "packages": { + "$ref": "#/definitions/propertiesObjects/packages/specification/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/root/base/object" + }, + { + "properties": { + "packages": { + "$ref": "#/definitions/propertiesObjects/packages/strict/object" + } + }, + "additionalProperties": false + } + ] + } + } + }, + "propertiesObjects": { + "packages": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "$comment": "help property is currently undocumented, so considered optional.", + "required": ["name", "maintainer", "websiteURL", "email", "platforms", "tools"] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/packages/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/packageName/permissive/object" + }, + "maintainer": { + "$ref": "#/definitions/propertiesObjects/maintainer/permissive/object" + }, + "websiteURL": { + "$ref": "#/definitions/propertiesObjects/websiteURL/permissive/object" + }, + "email": { + "$ref": "#/definitions/propertiesObjects/email/permissive/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/permissive/object" + }, + "platforms": { + "$ref": "#/definitions/propertiesObjects/platforms/permissive/object" + }, + "tools": { + "$ref": "#/definitions/propertiesObjects/tools/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/packages/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/packageName/specification/object" + }, + "maintainer": { + "$ref": "#/definitions/propertiesObjects/maintainer/specification/object" + }, + "websiteURL": { + "$ref": "#/definitions/propertiesObjects/websiteURL/specification/object" + }, + "email": { + "$ref": "#/definitions/propertiesObjects/email/specification/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/specification/object" + }, + "platforms": { + "$ref": "#/definitions/propertiesObjects/platforms/specification/object" + }, + "tools": { + "$ref": "#/definitions/propertiesObjects/tools/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/packages/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/packageName/strict/object" + }, + "maintainer": { + "$ref": "#/definitions/propertiesObjects/maintainer/strict/object" + }, + "websiteURL": { + "$ref": "#/definitions/propertiesObjects/websiteURL/strict/object" + }, + "email": { + "$ref": "#/definitions/propertiesObjects/email/strict/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/strict/object" + }, + "platforms": { + "$ref": "#/definitions/propertiesObjects/platforms/strict/object" + }, + "tools": { + "$ref": "#/definitions/propertiesObjects/tools/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "packageName": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/packageName/base/object" + } + }, + "specification": { + "definitions": { + "patternObjects": { + "notArduino": { + "not": { + "pattern": "^[aA][rR][dD][uU][iI][nN][oO]$" + } + } + } + }, + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/packageName/base/object" + }, + { + "$comment": "Only official Arduino packages are allowed to use the \"arduino\" vendor name", + "$ref": "#/definitions/propertiesObjects/packageName/specification/definitions/patternObjects/notArduino" + } + ] + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/packageName/specification/object" + } + } + }, + "maintainer": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/maintainer/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/maintainer/base/object" + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/maintainer/specification/object" + }, + { + "$comment": "Only official Arduino packages are allowed to have maintainer field starting with \"Arduino\"", + "$ref": "general-definitions-schema.json#/definitions/patternObjects/notStartsWithArduino" + } + ] + } + } + }, + "websiteURL": { + "base": { + "object": { + "type": "string", + "format": "uri" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/websiteURL/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/websiteURL/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/websiteURL/specification/object" + } + } + }, + "email": { + "base": { + "object": { + "type": "string" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/email/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/email/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/email/specification/object" + } + } + }, + "help": { + "$comment": "Schema for package and platform help objects.", + "base": { + "object": { + "type": "object", + "required": ["online"] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/help/base/object" + }, + { + "properties": { + "online": { + "$ref": "#/definitions/propertiesObjects/online/permissive/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/help/base/object" + }, + { + "properties": { + "online": { + "$ref": "#/definitions/propertiesObjects/online/specification/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/help/base/object" + }, + { + "properties": { + "online": { + "$ref": "#/definitions/propertiesObjects/online/strict/object" + } + }, + "additionalProperties": false + } + ] + } + } + }, + "online": { + "base": { + "object": { + "type": "string", + "format": "uri" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/online/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/online/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/online/specification/object" + } + } + }, + "platforms": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "required": [ + "name", + "architecture", + "version", + "category", + "help", + "url", + "archiveFileName", + "checksum", + "size", + "boards", + "toolsDependencies" + ] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platforms/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/platformName/permissive/object" + }, + "architecture": { + "$ref": "#/definitions/propertiesObjects/architecture/permissive/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/platformVersion/permissive/object" + }, + "category": { + "$ref": "#/definitions/propertiesObjects/category/permissive/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/permissive/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/permissive/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/permissive/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/permissive/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/permissive/object" + }, + "boards": { + "$ref": "#/definitions/propertiesObjects/boards/permissive/object" + }, + "toolsDependencies": { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platforms/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/platformName/specification/object" + }, + "architecture": { + "$ref": "#/definitions/propertiesObjects/architecture/specification/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/platformVersion/specification/object" + }, + "category": { + "$ref": "#/definitions/propertiesObjects/category/specification/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/specification/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/specification/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/specification/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/specification/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/specification/object" + }, + "boards": { + "$ref": "#/definitions/propertiesObjects/boards/specification/object" + }, + "toolsDependencies": { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platforms/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/platformName/strict/object" + }, + "architecture": { + "$ref": "#/definitions/propertiesObjects/architecture/strict/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/platformVersion/strict/object" + }, + "category": { + "$ref": "#/definitions/propertiesObjects/category/strict/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/strict/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/strict/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/strict/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/strict/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/strict/object" + }, + "boards": { + "$ref": "#/definitions/propertiesObjects/boards/strict/object" + }, + "toolsDependencies": { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "platformName": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/platformName/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/platformName/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/platformName/specification/object" + } + } + }, + "architecture": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/architecture/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/architecture/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/architecture/specification/object" + } + } + }, + "platformVersion": { + "base": { + "object": { + "type": "string" + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platformVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/relaxedSemver" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platformVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/relaxedSemver" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platformVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/semver" + } + ] + } + } + }, + "category": { + "base": { + "object": { + "type": "string", + "enum": ["Contributed"] + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/category/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/category/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/category/specification/object" + } + } + }, + "archiveUrl": { + "$comment": "Schema for platform and tool archive URLs", + "base": { + "object": { + "type": "string", + "format": "uri" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/specification/object" + } + } + }, + "archiveFileName": { + "$comment": "Schema for platform and tool archive filenames.", + "base": { + "definitions": { + "validExtensionPattern": { + "pattern": "^.+\\.(tar\\.bz2|tar\\.gz|zip)$" + } + }, + "object": { + "allOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "$ref": "#/definitions/propertiesObjects/archiveFileName/base/definitions/validExtensionPattern" + } + ] + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/specification/object" + } + } + }, + "checksum": { + "$comment": "Schema for platform and tool archive checksums.", + "base": { + "object": { + "type": "string", + "pattern": "^(MD5|SHA-1|SHA-256):[0-9a-fA-F]+$" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/checksum/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/checksum/base/object" + } + }, + "strict": { + "definitions": { + "patternObjects": { + "usesSHA256": { + "pattern": "^SHA-256:" + } + } + }, + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/checksum/specification/object" + }, + { + "$ref": "#/definitions/propertiesObjects/checksum/strict/definitions/patternObjects/usesSHA256" + } + ] + } + } + }, + "size": { + "$comment": "Schema for platform and tool archive file sizes.", + "base": { + "object": { + "type": "string", + "pattern": "[0-9]+" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/size/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/size/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/size/specification/object" + } + } + }, + "boards": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "required": ["name"] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boards/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardName/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boards/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardName/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boards/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardName/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "boardName": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/boardName/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/boardName/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/boardName/specification/object" + } + } + }, + "toolsDependencies": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "required": ["packager", "name", "version"] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/base/object" + }, + { + "items": { + "properties": { + "packager": { + "$ref": "#/definitions/propertiesObjects/packager/permissive/object" + }, + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/permissive/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/base/object" + }, + { + "items": { + "properties": { + "packager": { + "$ref": "#/definitions/propertiesObjects/packager/specification/object" + }, + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/specification/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/base/object" + }, + { + "items": { + "properties": { + "packager": { + "$ref": "#/definitions/propertiesObjects/packager/strict/object" + }, + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/strict/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "packager": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/packager/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/packager/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/packager/specification/object" + } + } + }, + "tools": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "required": ["name", "version", "systems"] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/tools/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/permissive/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/permissive/object" + }, + "systems": { + "$ref": "#/definitions/propertiesObjects/systems/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/tools/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/specification/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/specification/object" + }, + "systems": { + "$ref": "#/definitions/propertiesObjects/systems/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/tools/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/strict/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/strict/object" + }, + "systems": { + "$ref": "#/definitions/propertiesObjects/systems/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "toolName": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/toolName/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/toolName/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/toolName/specification/object" + } + } + }, + "toolVersion": { + "base": { + "object": { + "type": "string" + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/relaxedSemver" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/relaxedSemver" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/semver" + } + ] + } + } + }, + "systems": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "required": ["host", "url", "archiveFileName", "size", "checksum"] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/systems/base/object" + }, + { + "items": { + "properties": { + "host": { + "$ref": "#/definitions/propertiesObjects/host/permissive/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/permissive/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/permissive/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/permissive/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/systems/base/object" + }, + { + "items": { + "properties": { + "host": { + "$ref": "#/definitions/propertiesObjects/host/specification/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/specification/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/specification/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/specification/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/systems/base/object" + }, + { + "items": { + "properties": { + "host": { + "$ref": "#/definitions/propertiesObjects/host/strict/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/strict/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/strict/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/strict/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "host": { + "base": { + "definitions": { + "patternObjects": { + "validHost": { + "$comment": "https://github.com/arduino/arduino-cli/blob/cdbebe98f895c18146ea2607cfb706d002b01191/arduino/cores/tools.go#L144-L155", + "anyOf": [ + { + "pattern": "^arm.*-linux-gnueabihf$" + }, + { + "pattern": "^(aarch64|arm64)-linux-gnu$" + }, + { + "pattern": "^x86_64-.*linux-gnu$" + }, + { + "pattern": "^i[3456]86-.*linux-gnu$" + }, + { + "pattern": "^i[3456]86-.*(mingw32|cygwin)$" + }, + { + "pattern": "^(amd64|x86_64)-.*(mingw32|cygwin)$" + }, + { + "pattern": "^x86_64-apple-darwin.*$" + }, + { + "pattern": "^i[3456]86-apple-darwin.*$" + }, + { + "pattern": "^arm64-apple-darwin.*$" + }, + { + "pattern": "^arm.*-freebsd[0-9]*$" + }, + { + "pattern": "^i?[3456]86-freebsd[0-9]*$" + }, + { + "pattern": "^amd64-freebsd[0-9]*$" + } + ] + } + } + }, + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/propertiesObjects/host/base/definitions/patternObjects/validHost" + } + ] + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/host/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/host/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/host/specification/object" + } + } + } + } + } +} diff --git a/etc/schemas/arduino-package-index-permissive-schema.json b/etc/schemas/arduino-package-index-permissive-schema.json new file mode 100644 index 00000000..97ddfa0b --- /dev/null +++ b/etc/schemas/arduino-package-index-permissive-schema.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-package-index-permissive-schema.json", + "title": "Arduino Package Index JSON permissive schema", + "description": "Package indexes define Arduino hardware packages. See: https://arduino.github.io/arduino-cli/latest/package_index_json-specification/. This schema defines the minimum accepted data format.", + "allOf": [ + { + "$ref": "arduino-package-index-definitions-schema.json#/definitions/root/permissive/object" + } + ] +} diff --git a/etc/schemas/arduino-package-index-schema.json b/etc/schemas/arduino-package-index-schema.json new file mode 100644 index 00000000..e5a948ef --- /dev/null +++ b/etc/schemas/arduino-package-index-schema.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-package-index-schema.json", + "title": "Arduino Package Index JSON schema", + "description": "Package indexes define Arduino hardware packages. See: https://arduino.github.io/arduino-cli/latest/package_index_json-specification/. This schema defines the data format per the specification.", + "allOf": [ + { + "$ref": "arduino-package-index-definitions-schema.json#/definitions/root/specification/object" + } + ] +} diff --git a/etc/schemas/arduino-package-index-strict-schema.json b/etc/schemas/arduino-package-index-strict-schema.json new file mode 100644 index 00000000..3008876b --- /dev/null +++ b/etc/schemas/arduino-package-index-strict-schema.json @@ -0,0 +1,11 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-package-index-strict-schema.json", + "title": "Arduino Package Index JSON strict schema", + "description": "Package indexes define Arduino hardware packages. See: https://arduino.github.io/arduino-cli/latest/package_index_json-specification/. This schema defines the best practices for the data format, above and beyond the specification.", + "allOf": [ + { + "$ref": "arduino-package-index-definitions-schema.json#/definitions/root/strict/object" + } + ] +} diff --git a/internal/project/packageindex/packageindex.go b/internal/project/packageindex/packageindex.go index febba77a..1d0560df 100644 --- a/internal/project/packageindex/packageindex.go +++ b/internal/project/packageindex/packageindex.go @@ -20,12 +20,55 @@ See: https://arduino.github.io/arduino-cli/latest/package_index_json-specificati package packageindex import ( + "encoding/json" "fmt" "regexp" + "github.com/arduino/arduino-lint/internal/rule/schema" + "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" + "github.com/arduino/arduino-lint/internal/rule/schema/schemadata" "github.com/arduino/go-paths-helper" ) +// Properties parses the package index from the given path and returns the data. +func Properties(packageIndexPath *paths.Path) (map[string]interface{}, error) { + rawIndex, err := packageIndexPath.ReadFile() + if err != nil { + panic(err) + } + var indexData map[string]interface{} + err = json.Unmarshal(rawIndex, &indexData) + if err != nil { + return nil, err + } + + return indexData, nil +} + +var schemaObject = make(map[compliancelevel.Type]schema.Schema) + +// Validate validates boards.txt data against the JSON schema and returns a map of the result for each compliance level. +func Validate(packageIndex map[string]interface{}) map[compliancelevel.Type]schema.ValidationResult { + referencedSchemaFilenames := []string{ + "general-definitions-schema.json", + "arduino-package-index-definitions-schema.json", + } + + var validationResults = make(map[compliancelevel.Type]schema.ValidationResult) + + if schemaObject[compliancelevel.Permissive].Compiled == nil { // Only compile the schemas once. + schemaObject[compliancelevel.Permissive] = schema.Compile("arduino-package-index-permissive-schema.json", referencedSchemaFilenames, schemadata.Asset) + schemaObject[compliancelevel.Specification] = schema.Compile("arduino-package-index-schema.json", referencedSchemaFilenames, schemadata.Asset) + schemaObject[compliancelevel.Strict] = schema.Compile("arduino-package-index-strict-schema.json", referencedSchemaFilenames, schemadata.Asset) + } + + validationResults[compliancelevel.Permissive] = schema.Validate(packageIndex, schemaObject[compliancelevel.Permissive]) + validationResults[compliancelevel.Specification] = schema.Validate(packageIndex, schemaObject[compliancelevel.Specification]) + validationResults[compliancelevel.Strict] = schema.Validate(packageIndex, schemaObject[compliancelevel.Strict]) + + return validationResults +} + var empty struct{} // Reference: https://arduino.github.io/arduino-cli/latest/package_index_json-specification/#naming-of-the-json-index-file diff --git a/internal/project/packageindex/packageindex_test.go b/internal/project/packageindex/packageindex_test.go index 5db7e6be..8d8375e1 100644 --- a/internal/project/packageindex/packageindex_test.go +++ b/internal/project/packageindex/packageindex_test.go @@ -21,6 +21,7 @@ import ( "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var testDataPath *paths.Path @@ -33,6 +34,13 @@ func init() { testDataPath = paths.New(workingDirectory, "testdata") } +func TestProperties(t *testing.T) { + packageIndex, err := Properties(testDataPath.Join("package_valid_index.json")) + require.Nil(t, err) + + assert.NotNil(t, packageIndex) +} + func TestHasValidExtension(t *testing.T) { assert.True(t, HasValidExtension(paths.New("/foo", "bar.json"))) assert.False(t, HasValidExtension(paths.New("/foo", "bar.baz"))) diff --git a/internal/project/packageindex/packageindexschemas_test.go b/internal/project/packageindex/packageindexschemas_test.go new file mode 100644 index 00000000..436a6adb --- /dev/null +++ b/internal/project/packageindex/packageindexschemas_test.go @@ -0,0 +1,699 @@ +// This file is part of Arduino Lint. +// +// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of Arduino Lint. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +// This file contains tests for the package index JSON schemas. +package packageindex_test + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/arduino/arduino-lint/internal/project/packageindex" + "github.com/arduino/arduino-lint/internal/rule/schema" + "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/xeipuuv/gojsonpointer" +) + +var validIndexRaw = []byte(` +{ + "packages": [ + { + "name": "notarduino", + "maintainer": "NotArduino", + "websiteURL": "http://www.arduino.cc/", + "email": "packages@arduino.cc", + "help": { + "online": "http://www.arduino.cc/en/Reference/HomePage" + }, + "platforms": [ + { + "name": "Arduino AVR Boards", + "architecture": "avr", + "version": "1.8.3", + "category": "Contributed", + "help": { + "online": "http://www.arduino.cc/en/Reference/HomePage" + }, + "url": "http://downloads.arduino.cc/cores/avr-1.8.3.tar.bz2", + "archiveFileName": "avr-1.8.3.tar.bz2", + "checksum": "SHA-256:de8a9b982477762d3d3e52fc2b682cdd8ff194dc3f1d46f4debdea6a01b33c14", + "size": "4941548", + "boards": [ + {"name": "Arduino Uno"} + ], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "avr-gcc", + "version": "7.3.0-atmel3.6.1-arduino7" + } + ] + } + ], + "tools": [ + { + "name": "avr-gcc", + "version": "7.3.0-atmel3.6.1-arduino7", + "systems": [ + { + "size": "34683056", + "checksum": "SHA-256:3903553d035da59e33cff9941b857c3cb379cb0638105dfdf69c97f0acc8e7b5", + "host": "arm-linux-gnueabihf", + "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino7-arm-linux-gnueabihf.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-arm-linux-gnueabihf.tar.bz2" + } + ] + } + ] + } + ] +} +`) + +func TestSchemaValid(t *testing.T) { + var validIndex map[string]interface{} + err := json.Unmarshal(validIndexRaw, &validIndex) + require.NoError(t, err) + + validationResult := packageindex.Validate(validIndex) + + assert.Nil(t, validationResult[compliancelevel.Permissive].Result) + assert.Nil(t, validationResult[compliancelevel.Specification].Result) + assert.Nil(t, validationResult[compliancelevel.Strict].Result) +} + +func TestMinLength(t *testing.T) { + testTables := []struct { + propertyPointerString string + minLength int + complianceLevel compliancelevel.Type + }{ + {"/packages/0/name", 1, compliancelevel.Permissive}, + {"/packages/0/name", 1, compliancelevel.Specification}, + {"/packages/0/name", 1, compliancelevel.Strict}, + + {"/packages/0/maintainer", 1, compliancelevel.Permissive}, + {"/packages/0/maintainer", 1, compliancelevel.Specification}, + {"/packages/0/maintainer", 1, compliancelevel.Strict}, + + {"/packages/0/platforms/0/name", 1, compliancelevel.Permissive}, + {"/packages/0/platforms/0/name", 1, compliancelevel.Specification}, + {"/packages/0/platforms/0/name", 1, compliancelevel.Strict}, + + {"/packages/0/platforms/0/architecture", 1, compliancelevel.Permissive}, + {"/packages/0/platforms/0/architecture", 1, compliancelevel.Specification}, + {"/packages/0/platforms/0/architecture", 1, compliancelevel.Strict}, + + {"/packages/0/platforms/0/archiveFileName", 1, compliancelevel.Permissive}, + {"/packages/0/platforms/0/archiveFileName", 1, compliancelevel.Specification}, + {"/packages/0/platforms/0/archiveFileName", 1, compliancelevel.Strict}, + + {"/packages/0/platforms/0/boards/0/name", 1, compliancelevel.Permissive}, + {"/packages/0/platforms/0/boards/0/name", 1, compliancelevel.Specification}, + {"/packages/0/platforms/0/boards/0/name", 1, compliancelevel.Strict}, + + {"/packages/0/platforms/0/toolsDependencies/0/packager", 1, compliancelevel.Permissive}, + {"/packages/0/platforms/0/toolsDependencies/0/packager", 1, compliancelevel.Specification}, + {"/packages/0/platforms/0/toolsDependencies/0/packager", 1, compliancelevel.Strict}, + + {"/packages/0/platforms/0/toolsDependencies/0/name", 1, compliancelevel.Permissive}, + {"/packages/0/platforms/0/toolsDependencies/0/name", 1, compliancelevel.Specification}, + {"/packages/0/platforms/0/toolsDependencies/0/name", 1, compliancelevel.Strict}, + + {"/packages/0/tools/0/systems/0/archiveFileName", 1, compliancelevel.Permissive}, + {"/packages/0/tools/0/systems/0/archiveFileName", 1, compliancelevel.Specification}, + {"/packages/0/tools/0/systems/0/archiveFileName", 1, compliancelevel.Strict}, + + {"/packages/0/tools/0/name", 1, compliancelevel.Permissive}, + {"/packages/0/tools/0/name", 1, compliancelevel.Specification}, + {"/packages/0/tools/0/name", 1, compliancelevel.Strict}, + } + + // Test schema validation results with value length < minimum. + for _, testTable := range testTables { + var packageIndex map[string]interface{} + err := json.Unmarshal(validIndexRaw, &packageIndex) + require.NoError(t, err) + + propertyPointer, err := gojsonpointer.NewJsonPointer(testTable.propertyPointerString) + require.NoError(t, err) + _, err = propertyPointer.Set(packageIndex, strings.Repeat("a", testTable.minLength-1)) + require.NoError(t, err) + + t.Run(fmt.Sprintf("%s less than minimum length of %d (%s)", testTable.propertyPointerString, testTable.minLength, testTable.complianceLevel), func(t *testing.T) { + assert.True(t, schema.PropertyLessThanMinLength(strings.TrimPrefix(testTable.propertyPointerString, "/"), packageindex.Validate(packageIndex)[testTable.complianceLevel])) + }) + + // Test schema validation results with minimum value length. + propertyPointer.Set(packageIndex, strings.Repeat("a", testTable.minLength)) + + t.Run(fmt.Sprintf("%s at minimum length of %d (%s)", testTable.propertyPointerString, testTable.minLength, testTable.complianceLevel), func(t *testing.T) { + assert.False(t, schema.PropertyLessThanMinLength(strings.TrimPrefix(testTable.propertyPointerString, "/"), packageindex.Validate(packageIndex)[testTable.complianceLevel])) + }) + } +} + +func TestRequired(t *testing.T) { + testTables := []struct { + propertyPointerString string + complianceLevel compliancelevel.Type + assertion assert.BoolAssertionFunc + }{ + {"/packages", compliancelevel.Permissive, assert.True}, + {"/packages", compliancelevel.Specification, assert.True}, + {"/packages", compliancelevel.Strict, assert.True}, + + {"/packages/0/name", compliancelevel.Permissive, assert.True}, + {"/packages/0/name", compliancelevel.Specification, assert.True}, + {"/packages/0/name", compliancelevel.Strict, assert.True}, + + {"/packages/0/maintainer", compliancelevel.Permissive, assert.True}, + {"/packages/0/maintainer", compliancelevel.Specification, assert.True}, + {"/packages/0/maintainer", compliancelevel.Strict, assert.True}, + + {"/packages/0/websiteURL", compliancelevel.Permissive, assert.True}, + {"/packages/0/websiteURL", compliancelevel.Specification, assert.True}, + {"/packages/0/websiteURL", compliancelevel.Strict, assert.True}, + + {"/packages/0/email", compliancelevel.Permissive, assert.True}, + {"/packages/0/email", compliancelevel.Specification, assert.True}, + {"/packages/0/email", compliancelevel.Strict, assert.True}, + + {"/packages/0/help", compliancelevel.Permissive, assert.False}, + {"/packages/0/help", compliancelevel.Specification, assert.False}, + {"/packages/0/help", compliancelevel.Strict, assert.False}, + + {"/packages/0/help/online", compliancelevel.Permissive, assert.True}, + {"/packages/0/help/online", compliancelevel.Specification, assert.True}, + {"/packages/0/help/online", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools", compliancelevel.Specification, assert.True}, + {"/packages/0/tools", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/name", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/name", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/name", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/architecture", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/architecture", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/architecture", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/version", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/version", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/version", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/category", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/category", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/category", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/help", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/help", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/help", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/help/online", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/help/online", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/help/online", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/url", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/url", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/url", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/archiveFileName", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/archiveFileName", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/archiveFileName", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/checksum", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/checksum", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/checksum", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/size", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/size", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/size", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/boards", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/boards", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/boards", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/toolsDependencies", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/toolsDependencies", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/toolsDependencies", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/toolsDependencies/0/packager", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/toolsDependencies/0/packager", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/toolsDependencies/0/packager", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/toolsDependencies/0/name", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/toolsDependencies/0/name", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/toolsDependencies/0/name", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/toolsDependencies/0/version", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/toolsDependencies/0/version", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/toolsDependencies/0/version", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/name", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/name", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/name", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/version", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/version", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/version", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/host", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0/host", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0/host", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/url", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0/url", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0/url", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/archiveFileName", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0/archiveFileName", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0/archiveFileName", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/size", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0/size", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0/size", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/checksum", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0/checksum", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0/checksum", compliancelevel.Strict, assert.True}, + } + + for _, testTable := range testTables { + var packageIndex map[string]interface{} + err := json.Unmarshal(validIndexRaw, &packageIndex) + require.NoError(t, err) + + propertyPointer, err := gojsonpointer.NewJsonPointer(testTable.propertyPointerString) + require.NoError(t, err) + _, err = propertyPointer.Delete(packageIndex) + require.NoError(t, err) + + validationResult := packageindex.Validate(packageIndex) + t.Run(fmt.Sprintf("%s (%s)", testTable.propertyPointerString, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.RequiredPropertyMissing(strings.TrimPrefix(testTable.propertyPointerString, "/"), validationResult[testTable.complianceLevel])) + }) + } +} + +func TestEnum(t *testing.T) { + testTables := []struct { + propertyPointerString string + propertyValue string + complianceLevel compliancelevel.Type + assertion assert.BoolAssertionFunc + }{ + {"/packages/0/platforms/0/category", "Contributed", compliancelevel.Permissive, assert.False}, + {"/packages/0/platforms/0/category", "Contributed", compliancelevel.Specification, assert.False}, + {"/packages/0/platforms/0/category", "Contributed", compliancelevel.Strict, assert.False}, + + {"/packages/0/platforms/0/category", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/category", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/category", "foo", compliancelevel.Strict, assert.True}, + } + + for _, testTable := range testTables { + var packageIndex map[string]interface{} + err := json.Unmarshal(validIndexRaw, &packageIndex) + require.NoError(t, err) + + propertyPointer, err := gojsonpointer.NewJsonPointer(testTable.propertyPointerString) + require.NoError(t, err) + _, err = propertyPointer.Set(packageIndex, testTable.propertyValue) + require.NoError(t, err) + + t.Run(fmt.Sprintf("%s: %s (%s)", testTable.propertyPointerString, testTable.propertyValue, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.PropertyEnumMismatch(strings.TrimPrefix(testTable.propertyPointerString, "/"), packageindex.Validate(packageIndex)[testTable.complianceLevel])) + }) + } +} + +func TestPattern(t *testing.T) { + testTables := []struct { + propertyPointerString string + propertyValue string + complianceLevel compliancelevel.Type + assertion assert.BoolAssertionFunc + }{ + {"/packages/0/name", "foo", compliancelevel.Permissive, assert.False}, + {"/packages/0/name", "foo", compliancelevel.Specification, assert.False}, + {"/packages/0/name", "foo", compliancelevel.Strict, assert.False}, + + {"/packages/0/name", "arduino", compliancelevel.Permissive, assert.False}, + {"/packages/0/name", "arduino", compliancelevel.Specification, assert.True}, + {"/packages/0/name", "arduino", compliancelevel.Strict, assert.True}, + + {"/packages/0/name", "Arduino", compliancelevel.Permissive, assert.False}, + {"/packages/0/name", "Arduino", compliancelevel.Specification, assert.True}, + {"/packages/0/name", "Arduino", compliancelevel.Strict, assert.True}, + + {"/packages/0/name", "ARDUINO", compliancelevel.Permissive, assert.False}, + {"/packages/0/name", "ARDUINO", compliancelevel.Specification, assert.True}, + {"/packages/0/name", "ARDUINO", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/archiveFileName", "foo.tar.bz2", compliancelevel.Permissive, assert.False}, + {"/packages/0/platforms/0/archiveFileName", "foo.tar.bz2", compliancelevel.Specification, assert.False}, + {"/packages/0/platforms/0/archiveFileName", "foo.tar.bz2", compliancelevel.Strict, assert.False}, + + {"/packages/0/platforms/0/archiveFileName", "foo.tar.gz", compliancelevel.Permissive, assert.False}, + {"/packages/0/platforms/0/archiveFileName", "foo.tar.gz", compliancelevel.Specification, assert.False}, + {"/packages/0/platforms/0/archiveFileName", "foo.tar.gz", compliancelevel.Strict, assert.False}, + + {"/packages/0/platforms/0/archiveFileName", "foo.zip", compliancelevel.Permissive, assert.False}, + {"/packages/0/platforms/0/archiveFileName", "foo.zip", compliancelevel.Specification, assert.False}, + {"/packages/0/platforms/0/archiveFileName", "foo.zip", compliancelevel.Strict, assert.False}, + + {"/packages/0/platforms/0/archiveFileName", "foo.bar", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/archiveFileName", "foo.bar", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/archiveFileName", "foo.bar", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/checksum", "SHA-256:de8a9b982477762d3d3e52fc2b682cdd8ff194dc3f1d46f4debdea6a01b33c14", compliancelevel.Permissive, assert.False}, + {"/packages/0/platforms/0/checksum", "SHA-256:de8a9b982477762d3d3e52fc2b682cdd8ff194dc3f1d46f4debdea6a01b33c14", compliancelevel.Specification, assert.False}, + {"/packages/0/platforms/0/checksum", "SHA-256:de8a9b982477762d3d3e52fc2b682cdd8ff194dc3f1d46f4debdea6a01b33c14", compliancelevel.Strict, assert.False}, + + {"/packages/0/platforms/0/checksum", "SHA-1:f89bb8563bf86eb097679dce9d2b29b86d06bf66", compliancelevel.Permissive, assert.False}, + {"/packages/0/platforms/0/checksum", "SHA-1:f89bb8563bf86eb097679dce9d2b29b86d06bf66", compliancelevel.Specification, assert.False}, + {"/packages/0/platforms/0/checksum", "SHA-1:f89bb8563bf86eb097679dce9d2b29b86d06bf66", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/checksum", "MD5:6c0f556759894aa1a45e8af423a08ce8", compliancelevel.Permissive, assert.False}, + {"/packages/0/platforms/0/checksum", "MD5:6c0f556759894aa1a45e8af423a08ce8", compliancelevel.Specification, assert.False}, + {"/packages/0/platforms/0/checksum", "MD5:6c0f556759894aa1a45e8af423a08ce8", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/checksum", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/checksum", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/checksum", "foo", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/size", "42", compliancelevel.Permissive, assert.False}, + {"/packages/0/platforms/0/size", "42", compliancelevel.Specification, assert.False}, + {"/packages/0/platforms/0/size", "42", compliancelevel.Strict, assert.False}, + + {"/packages/0/platforms/0/size", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/size", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/size", "foo", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.tar.bz2", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.tar.bz2", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.tar.bz2", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.tar.gz", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.tar.gz", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.tar.gz", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.zip", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.zip", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.zip", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.bar", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.bar", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0/archiveFileName", "foo.bar", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/checksum", "SHA-256:de8a9b982477762d3d3e52fc2b682cdd8ff194dc3f1d46f4debdea6a01b33c14", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/checksum", "SHA-256:de8a9b982477762d3d3e52fc2b682cdd8ff194dc3f1d46f4debdea6a01b33c14", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/checksum", "SHA-256:de8a9b982477762d3d3e52fc2b682cdd8ff194dc3f1d46f4debdea6a01b33c14", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/checksum", "SHA-1:f89bb8563bf86eb097679dce9d2b29b86d06bf66", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/checksum", "SHA-1:f89bb8563bf86eb097679dce9d2b29b86d06bf66", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/checksum", "SHA-1:f89bb8563bf86eb097679dce9d2b29b86d06bf66", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/checksum", "MD5:6c0f556759894aa1a45e8af423a08ce8", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/checksum", "MD5:6c0f556759894aa1a45e8af423a08ce8", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/checksum", "MD5:6c0f556759894aa1a45e8af423a08ce8", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/checksum", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0/checksum", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0/checksum", "foo", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/size", "42", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/size", "42", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/size", "42", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/size", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0/size", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0/size", "foo", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/host", "arm-linux-gnueabihf", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/host", "arm-linux-gnueabihf", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/host", "arm-linux-gnueabihf", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/host", "aarch64-linux-gnu", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/host", "aarch64-linux-gnu", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/host", "aarch64-linux-gnu", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/host", "arm64-linux-gnu", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/host", "arm64-linux-gnu", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/host", "arm64-linux-gnu", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/host", "x86_64-linux-gnu", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/host", "x86_64-linux-gnu", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/host", "x86_64-linux-gnu", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/host", "i686-mingw32", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/host", "i686-mingw32", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/host", "i686-mingw32", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/host", "i686-cygwin", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/host", "i686-cygwin", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/host", "i686-cygwin", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/host", "x86_64-apple-darwin", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/host", "x86_64-apple-darwin", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/host", "x86_64-apple-darwin", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/host", "i386-apple-darwin11", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/host", "i386-apple-darwin11", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/host", "i386-apple-darwin11", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/host", "i386-freebsd11", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/host", "i386-freebsd11", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/host", "i386-freebsd11", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/host", "amd64-freebsd11", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/host", "amd64-freebsd11", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/host", "amd64-freebsd11", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/host", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0/host", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0/host", "foo", compliancelevel.Strict, assert.True}, + } + + for _, testTable := range testTables { + var packageIndex map[string]interface{} + err := json.Unmarshal(validIndexRaw, &packageIndex) + require.NoError(t, err) + + propertyPointer, err := gojsonpointer.NewJsonPointer(testTable.propertyPointerString) + require.NoError(t, err) + _, err = propertyPointer.Set(packageIndex, testTable.propertyValue) + require.NoError(t, err) + + t.Run(fmt.Sprintf("%s: %s (%s)", testTable.propertyPointerString, testTable.propertyValue, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.PropertyPatternMismatch(strings.TrimPrefix(testTable.propertyPointerString, "/"), packageindex.Validate(packageIndex)[testTable.complianceLevel])) + }) + } +} + +func TestType(t *testing.T) { + testTables := []struct { + propertyPointerString string + propertyValue interface{} + assertion assert.BoolAssertionFunc + }{ + {"/packages", 42, assert.True}, + {"/packages/0/name", 42, assert.True}, + {"/packages/0/maintainer", 42, assert.True}, + {"/packages/0/websiteURL", 42, assert.True}, + {"/packages/0/email", 42, assert.True}, + {"/packages/0/help", 42, assert.True}, + {"/packages/0/help/online", 42, assert.True}, + {"/packages/0/platforms", 42, assert.True}, + {"/packages/0/platforms/0/name", 42, assert.True}, + {"/packages/0/platforms/0/architecture", 42, assert.True}, + {"/packages/0/platforms/0/version", 42, assert.True}, + {"/packages/0/platforms/0/help", 42, assert.True}, + {"/packages/0/platforms/0/help/online", 42, assert.True}, + {"/packages/0/platforms/0/category", 42, assert.True}, + {"/packages/0/platforms/0/url", 42, assert.True}, + {"/packages/0/platforms/0/archiveFileName", 42, assert.True}, + {"/packages/0/platforms/0/checksum", 42, assert.True}, + {"/packages/0/platforms/0/size", 42, assert.True}, + {"/packages/0/platforms/0/boards", 42, assert.True}, + {"/packages/0/platforms/0/boards/0/name", 42, assert.True}, + {"/packages/0/platforms/0/toolsDependencies", 42, assert.True}, + {"/packages/0/platforms/0/toolsDependencies/0/packager", 42, assert.True}, + {"/packages/0/tools", 42, assert.True}, + {"/packages/0/tools/0/name", 42, assert.True}, + {"/packages/0/tools/0/version", 42, assert.True}, + {"/packages/0/tools/0/systems", 42, assert.True}, + {"/packages/0/tools/0/systems/0/host", 42, assert.True}, + {"/packages/0/tools/0/systems/0/url", 42, assert.True}, + {"/packages/0/tools/0/systems/0/archiveFileName", 42, assert.True}, + {"/packages/0/tools/0/systems/0/checksum", 42, assert.True}, + {"/packages/0/tools/0/systems/0/size", 42, assert.True}, + } + + for _, testTable := range testTables { + for _, complianceLevel := range []compliancelevel.Type{compliancelevel.Permissive, compliancelevel.Specification, compliancelevel.Strict} { + var packageIndex map[string]interface{} + err := json.Unmarshal(validIndexRaw, &packageIndex) + require.NoError(t, err) + + propertyPointer, err := gojsonpointer.NewJsonPointer(testTable.propertyPointerString) + require.NoError(t, err) + _, err = propertyPointer.Set(packageIndex, testTable.propertyValue) + + t.Run(fmt.Sprintf("%s: %v (%s)", testTable.propertyPointerString, testTable.propertyValue, complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.PropertyTypeMismatch(strings.TrimPrefix(testTable.propertyPointerString, "/"), packageindex.Validate(packageIndex)[complianceLevel])) + }) + } + } +} + +func TestFormat(t *testing.T) { + testTables := []struct { + propertyPointerString string + propertyValue string + complianceLevel compliancelevel.Type + assertion assert.BoolAssertionFunc + }{ + {"/packages/0/websiteURL", "http://example.com", compliancelevel.Permissive, assert.False}, + {"/packages/0/websiteURL", "http://example.com", compliancelevel.Specification, assert.False}, + {"/packages/0/websiteURL", "http://example.com", compliancelevel.Strict, assert.False}, + + {"/packages/0/websiteURL", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/websiteURL", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/websiteURL", "foo", compliancelevel.Strict, assert.True}, + + {"/packages/0/help/online", "http://example.com", compliancelevel.Permissive, assert.False}, + {"/packages/0/help/online", "http://example.com", compliancelevel.Specification, assert.False}, + {"/packages/0/help/online", "http://example.com", compliancelevel.Strict, assert.False}, + + {"/packages/0/help/online", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/help/online", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/help/online", "foo", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/help/online", "http://example.com", compliancelevel.Permissive, assert.False}, + {"/packages/0/platforms/0/help/online", "http://example.com", compliancelevel.Specification, assert.False}, + {"/packages/0/platforms/0/help/online", "http://example.com", compliancelevel.Strict, assert.False}, + + {"/packages/0/platforms/0/help/online", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/help/online", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/help/online", "foo", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/url", "http://example.com/foo.tar.bz2", compliancelevel.Permissive, assert.False}, + {"/packages/0/platforms/0/url", "http://example.com/foo.tar.bz2", compliancelevel.Specification, assert.False}, + {"/packages/0/platforms/0/url", "http://example.com/foo.tar.bz2", compliancelevel.Strict, assert.False}, + + {"/packages/0/platforms/0/url", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/url", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/url", "foo", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0/url", "http://example.com/foo.tar.bz2", compliancelevel.Permissive, assert.False}, + {"/packages/0/tools/0/systems/0/url", "http://example.com/foo.tar.bz2", compliancelevel.Specification, assert.False}, + {"/packages/0/tools/0/systems/0/url", "http://example.com/foo.tar.bz2", compliancelevel.Strict, assert.False}, + + {"/packages/0/tools/0/systems/0/url", "foo", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0/url", "foo", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0/url", "foo", compliancelevel.Strict, assert.True}, + } + + for _, testTable := range testTables { + var packageIndex map[string]interface{} + err := json.Unmarshal(validIndexRaw, &packageIndex) + require.NoError(t, err) + + propertyPointer, err := gojsonpointer.NewJsonPointer(testTable.propertyPointerString) + require.NoError(t, err) + _, err = propertyPointer.Set(packageIndex, testTable.propertyValue) + require.NoError(t, err) + + t.Run(fmt.Sprintf("%s: %s (%s)", testTable.propertyPointerString, testTable.propertyValue, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.PropertyFormatMismatch(strings.TrimPrefix(testTable.propertyPointerString, "/"), packageindex.Validate(packageIndex)[testTable.complianceLevel])) + }) + } +} + +func TestAdditionalProperties(t *testing.T) { + testTables := []struct { + propertyPointerString string + complianceLevel compliancelevel.Type + assertion assert.BoolAssertionFunc + }{ + // Root + {"", compliancelevel.Permissive, assert.True}, + {"", compliancelevel.Specification, assert.True}, + {"", compliancelevel.Strict, assert.True}, + + {"/packages/0", compliancelevel.Permissive, assert.True}, + {"/packages/0", compliancelevel.Specification, assert.True}, + {"/packages/0", compliancelevel.Strict, assert.True}, + + {"/packages/0/help", compliancelevel.Permissive, assert.True}, + {"/packages/0/help", compliancelevel.Specification, assert.True}, + {"/packages/0/help", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/help", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/help", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/help", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/boards/0", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/boards/0", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/boards/0", compliancelevel.Strict, assert.True}, + + {"/packages/0/platforms/0/toolsDependencies/0", compliancelevel.Permissive, assert.True}, + {"/packages/0/platforms/0/toolsDependencies/0", compliancelevel.Specification, assert.True}, + {"/packages/0/platforms/0/toolsDependencies/0", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0", compliancelevel.Strict, assert.True}, + + {"/packages/0/tools/0/systems/0", compliancelevel.Permissive, assert.True}, + {"/packages/0/tools/0/systems/0", compliancelevel.Specification, assert.True}, + {"/packages/0/tools/0/systems/0", compliancelevel.Strict, assert.True}, + } + + for _, testTable := range testTables { + var packageIndex map[string]interface{} + err := json.Unmarshal(validIndexRaw, &packageIndex) + require.NoError(t, err) + + // Add an additional property to the object. + propertyPointer, err := gojsonpointer.NewJsonPointer(testTable.propertyPointerString + "/fooAdditionalProperty") + require.NoError(t, err) + _, err = propertyPointer.Set(packageIndex, "bar") + require.NoError(t, err) + + t.Run(fmt.Sprintf("Additional property in the %s object (%s)", testTable.propertyPointerString, testTable.complianceLevel), func(t *testing.T) { + testTable.assertion(t, schema.ProhibitedAdditionalProperties(strings.TrimPrefix(testTable.propertyPointerString, "/"), packageindex.Validate(packageIndex)[testTable.complianceLevel])) + }) + } +} diff --git a/internal/project/packageindex/testdata/package_valid_index.json b/internal/project/packageindex/testdata/package_valid_index.json new file mode 100644 index 00000000..7938a2aa --- /dev/null +++ b/internal/project/packageindex/testdata/package_valid_index.json @@ -0,0 +1,51 @@ +{ + "packages": [ + { + "name": "notarduino", + "maintainer": "NotArduino", + "websiteURL": "http://www.arduino.cc/", + "email": "packages@arduino.cc", + "help": { + "online": "http://www.arduino.cc/en/Reference/HomePage" + }, + "platforms": [ + { + "name": "Arduino AVR Boards", + "architecture": "avr", + "version": "1.8.3", + "category": "Contributed", + "help": { + "online": "http://www.arduino.cc/en/Reference/HomePage" + }, + "url": "http://downloads.arduino.cc/cores/avr-1.8.3.tar.bz2", + "archiveFileName": "avr-1.8.3.tar.bz2", + "checksum": "SHA-256:de8a9b982477762d3d3e52fc2b682cdd8ff194dc3f1d46f4debdea6a01b33c14", + "size": "4941548", + "boards": [{ "name": "Arduino Uno" }], + "toolsDependencies": [ + { + "packager": "arduino", + "name": "avr-gcc", + "version": "7.3.0-atmel3.6.1-arduino7" + } + ] + } + ], + "tools": [ + { + "name": "avr-gcc", + "version": "7.3.0-atmel3.6.1-arduino7", + "systems": [ + { + "size": "34683056", + "checksum": "SHA-256:3903553d035da59e33cff9941b857c3cb379cb0638105dfdf69c97f0acc8e7b5", + "host": "arm-linux-gnueabihf", + "archiveFileName": "avr-gcc-7.3.0-atmel3.6.1-arduino7-arm-linux-gnueabihf.tar.bz2", + "url": "http://downloads.arduino.cc/tools/avr-gcc-7.3.0-atmel3.6.1-arduino7-arm-linux-gnueabihf.tar.bz2" + } + ] + } + ] + } + ] +} diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index 3ebd5dba..ec16d1cb 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -8,6 +8,10 @@ // etc/schemas/arduino-library-properties-permissive-schema.json // etc/schemas/arduino-library-properties-schema.json // etc/schemas/arduino-library-properties-strict-schema.json +// etc/schemas/arduino-package-index-definitions-schema.json +// etc/schemas/arduino-package-index-permissive-schema.json +// etc/schemas/arduino-package-index-schema.json +// etc/schemas/arduino-package-index-strict-schema.json // etc/schemas/arduino-platform-txt-definitions-schema.json // etc/schemas/arduino-platform-txt-permissive-schema.json // etc/schemas/arduino-platform-txt-schema.json @@ -2530,6 +2534,1431 @@ func arduinoLibraryPropertiesStrictSchemaJson() (*asset, error) { return a, nil } +var _arduinoPackageIndexDefinitionsSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-package-index-definitions-schema.json", + "title": "Shared definitions for the Arduino Package Index schemas", + "definitions": { + "root": { + "base": { + "object": { + "type": "object", + "required": ["packages"] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/root/base/object" + }, + { + "properties": { + "packages": { + "$ref": "#/definitions/propertiesObjects/packages/permissive/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/root/base/object" + }, + { + "properties": { + "packages": { + "$ref": "#/definitions/propertiesObjects/packages/specification/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/root/base/object" + }, + { + "properties": { + "packages": { + "$ref": "#/definitions/propertiesObjects/packages/strict/object" + } + }, + "additionalProperties": false + } + ] + } + } + }, + "propertiesObjects": { + "packages": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "$comment": "help property is currently undocumented, so considered optional.", + "required": ["name", "maintainer", "websiteURL", "email", "platforms", "tools"] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/packages/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/packageName/permissive/object" + }, + "maintainer": { + "$ref": "#/definitions/propertiesObjects/maintainer/permissive/object" + }, + "websiteURL": { + "$ref": "#/definitions/propertiesObjects/websiteURL/permissive/object" + }, + "email": { + "$ref": "#/definitions/propertiesObjects/email/permissive/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/permissive/object" + }, + "platforms": { + "$ref": "#/definitions/propertiesObjects/platforms/permissive/object" + }, + "tools": { + "$ref": "#/definitions/propertiesObjects/tools/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/packages/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/packageName/specification/object" + }, + "maintainer": { + "$ref": "#/definitions/propertiesObjects/maintainer/specification/object" + }, + "websiteURL": { + "$ref": "#/definitions/propertiesObjects/websiteURL/specification/object" + }, + "email": { + "$ref": "#/definitions/propertiesObjects/email/specification/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/specification/object" + }, + "platforms": { + "$ref": "#/definitions/propertiesObjects/platforms/specification/object" + }, + "tools": { + "$ref": "#/definitions/propertiesObjects/tools/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/packages/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/packageName/strict/object" + }, + "maintainer": { + "$ref": "#/definitions/propertiesObjects/maintainer/strict/object" + }, + "websiteURL": { + "$ref": "#/definitions/propertiesObjects/websiteURL/strict/object" + }, + "email": { + "$ref": "#/definitions/propertiesObjects/email/strict/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/strict/object" + }, + "platforms": { + "$ref": "#/definitions/propertiesObjects/platforms/strict/object" + }, + "tools": { + "$ref": "#/definitions/propertiesObjects/tools/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "packageName": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/packageName/base/object" + } + }, + "specification": { + "definitions": { + "patternObjects": { + "notArduino": { + "not": { + "pattern": "^[aA][rR][dD][uU][iI][nN][oO]$" + } + } + } + }, + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/packageName/base/object" + }, + { + "$comment": "Only official Arduino packages are allowed to use the \"arduino\" vendor name", + "$ref": "#/definitions/propertiesObjects/packageName/specification/definitions/patternObjects/notArduino" + } + ] + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/packageName/specification/object" + } + } + }, + "maintainer": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/maintainer/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/maintainer/base/object" + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/maintainer/specification/object" + }, + { + "$comment": "Only official Arduino packages are allowed to have maintainer field starting with \"Arduino\"", + "$ref": "general-definitions-schema.json#/definitions/patternObjects/notStartsWithArduino" + } + ] + } + } + }, + "websiteURL": { + "base": { + "object": { + "type": "string", + "format": "uri" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/websiteURL/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/websiteURL/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/websiteURL/specification/object" + } + } + }, + "email": { + "base": { + "object": { + "type": "string" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/email/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/email/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/email/specification/object" + } + } + }, + "help": { + "$comment": "Schema for package and platform help objects.", + "base": { + "object": { + "type": "object", + "required": ["online"] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/help/base/object" + }, + { + "properties": { + "online": { + "$ref": "#/definitions/propertiesObjects/online/permissive/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/help/base/object" + }, + { + "properties": { + "online": { + "$ref": "#/definitions/propertiesObjects/online/specification/object" + } + }, + "additionalProperties": false + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/help/base/object" + }, + { + "properties": { + "online": { + "$ref": "#/definitions/propertiesObjects/online/strict/object" + } + }, + "additionalProperties": false + } + ] + } + } + }, + "online": { + "base": { + "object": { + "type": "string", + "format": "uri" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/online/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/online/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/online/specification/object" + } + } + }, + "platforms": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "required": [ + "name", + "architecture", + "version", + "category", + "help", + "url", + "archiveFileName", + "checksum", + "size", + "boards", + "toolsDependencies" + ] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platforms/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/platformName/permissive/object" + }, + "architecture": { + "$ref": "#/definitions/propertiesObjects/architecture/permissive/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/platformVersion/permissive/object" + }, + "category": { + "$ref": "#/definitions/propertiesObjects/category/permissive/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/permissive/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/permissive/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/permissive/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/permissive/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/permissive/object" + }, + "boards": { + "$ref": "#/definitions/propertiesObjects/boards/permissive/object" + }, + "toolsDependencies": { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platforms/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/platformName/specification/object" + }, + "architecture": { + "$ref": "#/definitions/propertiesObjects/architecture/specification/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/platformVersion/specification/object" + }, + "category": { + "$ref": "#/definitions/propertiesObjects/category/specification/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/specification/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/specification/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/specification/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/specification/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/specification/object" + }, + "boards": { + "$ref": "#/definitions/propertiesObjects/boards/specification/object" + }, + "toolsDependencies": { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platforms/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/platformName/strict/object" + }, + "architecture": { + "$ref": "#/definitions/propertiesObjects/architecture/strict/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/platformVersion/strict/object" + }, + "category": { + "$ref": "#/definitions/propertiesObjects/category/strict/object" + }, + "help": { + "$ref": "#/definitions/propertiesObjects/help/strict/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/strict/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/strict/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/strict/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/strict/object" + }, + "boards": { + "$ref": "#/definitions/propertiesObjects/boards/strict/object" + }, + "toolsDependencies": { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "platformName": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/platformName/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/platformName/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/platformName/specification/object" + } + } + }, + "architecture": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/architecture/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/architecture/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/architecture/specification/object" + } + } + }, + "platformVersion": { + "base": { + "object": { + "type": "string" + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platformVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/relaxedSemver" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platformVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/relaxedSemver" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/platformVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/semver" + } + ] + } + } + }, + "category": { + "base": { + "object": { + "type": "string", + "enum": ["Contributed"] + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/category/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/category/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/category/specification/object" + } + } + }, + "archiveUrl": { + "$comment": "Schema for platform and tool archive URLs", + "base": { + "object": { + "type": "string", + "format": "uri" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/specification/object" + } + } + }, + "archiveFileName": { + "$comment": "Schema for platform and tool archive filenames.", + "base": { + "definitions": { + "validExtensionPattern": { + "pattern": "^.+\\.(tar\\.bz2|tar\\.gz|zip)$" + } + }, + "object": { + "allOf": [ + { + "type": "string", + "minLength": 1 + }, + { + "$ref": "#/definitions/propertiesObjects/archiveFileName/base/definitions/validExtensionPattern" + } + ] + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/specification/object" + } + } + }, + "checksum": { + "$comment": "Schema for platform and tool archive checksums.", + "base": { + "object": { + "type": "string", + "pattern": "^(MD5|SHA-1|SHA-256):[0-9a-fA-F]+$" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/checksum/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/checksum/base/object" + } + }, + "strict": { + "definitions": { + "patternObjects": { + "usesSHA256": { + "pattern": "^SHA-256:" + } + } + }, + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/checksum/specification/object" + }, + { + "$ref": "#/definitions/propertiesObjects/checksum/strict/definitions/patternObjects/usesSHA256" + } + ] + } + } + }, + "size": { + "$comment": "Schema for platform and tool archive file sizes.", + "base": { + "object": { + "type": "string", + "pattern": "[0-9]+" + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/size/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/size/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/size/specification/object" + } + } + }, + "boards": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "required": ["name"] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boards/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardName/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boards/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardName/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/boards/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/boardName/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "boardName": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/boardName/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/boardName/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/boardName/specification/object" + } + } + }, + "toolsDependencies": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "required": ["packager", "name", "version"] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/base/object" + }, + { + "items": { + "properties": { + "packager": { + "$ref": "#/definitions/propertiesObjects/packager/permissive/object" + }, + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/permissive/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/base/object" + }, + { + "items": { + "properties": { + "packager": { + "$ref": "#/definitions/propertiesObjects/packager/specification/object" + }, + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/specification/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsDependencies/base/object" + }, + { + "items": { + "properties": { + "packager": { + "$ref": "#/definitions/propertiesObjects/packager/strict/object" + }, + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/strict/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "packager": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/packager/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/packager/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/packager/specification/object" + } + } + }, + "tools": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "required": ["name", "version", "systems"] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/tools/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/permissive/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/permissive/object" + }, + "systems": { + "$ref": "#/definitions/propertiesObjects/systems/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/tools/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/specification/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/specification/object" + }, + "systems": { + "$ref": "#/definitions/propertiesObjects/systems/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/tools/base/object" + }, + { + "items": { + "properties": { + "name": { + "$ref": "#/definitions/propertiesObjects/toolName/strict/object" + }, + "version": { + "$ref": "#/definitions/propertiesObjects/toolVersion/strict/object" + }, + "systems": { + "$ref": "#/definitions/propertiesObjects/systems/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "toolName": { + "base": { + "object": { + "type": "string", + "minLength": 1 + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/toolName/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/toolName/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/toolName/specification/object" + } + } + }, + "toolVersion": { + "base": { + "object": { + "type": "string" + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/relaxedSemver" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/relaxedSemver" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolVersion/base/object" + }, + { + "$ref": "general-definitions-schema.json#/definitions/patternObjects/semver" + } + ] + } + } + }, + "systems": { + "base": { + "object": { + "type": "array", + "items": { + "type": "object", + "required": ["host", "url", "archiveFileName", "size", "checksum"] + } + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/systems/base/object" + }, + { + "items": { + "properties": { + "host": { + "$ref": "#/definitions/propertiesObjects/host/permissive/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/permissive/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/permissive/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/permissive/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/permissive/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/systems/base/object" + }, + { + "items": { + "properties": { + "host": { + "$ref": "#/definitions/propertiesObjects/host/specification/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/specification/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/specification/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/specification/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/specification/object" + } + }, + "additionalProperties": false + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/systems/base/object" + }, + { + "items": { + "properties": { + "host": { + "$ref": "#/definitions/propertiesObjects/host/strict/object" + }, + "url": { + "$ref": "#/definitions/propertiesObjects/archiveUrl/strict/object" + }, + "archiveFileName": { + "$ref": "#/definitions/propertiesObjects/archiveFileName/strict/object" + }, + "checksum": { + "$ref": "#/definitions/propertiesObjects/checksum/strict/object" + }, + "size": { + "$ref": "#/definitions/propertiesObjects/size/strict/object" + } + }, + "additionalProperties": false + } + } + ] + } + } + }, + "host": { + "base": { + "definitions": { + "patternObjects": { + "validHost": { + "$comment": "https://github.com/arduino/arduino-cli/blob/cdbebe98f895c18146ea2607cfb706d002b01191/arduino/cores/tools.go#L144-L155", + "anyOf": [ + { + "pattern": "^arm.*-linux-gnueabihf$" + }, + { + "pattern": "^(aarch64|arm64)-linux-gnu$" + }, + { + "pattern": "^x86_64-.*linux-gnu$" + }, + { + "pattern": "^i[3456]86-.*linux-gnu$" + }, + { + "pattern": "^i[3456]86-.*(mingw32|cygwin)$" + }, + { + "pattern": "^(amd64|x86_64)-.*(mingw32|cygwin)$" + }, + { + "pattern": "^x86_64-apple-darwin.*$" + }, + { + "pattern": "^i[3456]86-apple-darwin.*$" + }, + { + "pattern": "^arm64-apple-darwin.*$" + }, + { + "pattern": "^arm.*-freebsd[0-9]*$" + }, + { + "pattern": "^i?[3456]86-freebsd[0-9]*$" + }, + { + "pattern": "^amd64-freebsd[0-9]*$" + } + ] + } + } + }, + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/propertiesObjects/host/base/definitions/patternObjects/validHost" + } + ] + } + }, + "permissive": { + "object": { + "$ref": "#/definitions/propertiesObjects/host/base/object" + } + }, + "specification": { + "object": { + "$ref": "#/definitions/propertiesObjects/host/base/object" + } + }, + "strict": { + "object": { + "$ref": "#/definitions/propertiesObjects/host/specification/object" + } + } + } + } + } +} +`) + +func arduinoPackageIndexDefinitionsSchemaJsonBytes() ([]byte, error) { + return _arduinoPackageIndexDefinitionsSchemaJson, nil +} + +func arduinoPackageIndexDefinitionsSchemaJson() (*asset, error) { + bytes, err := arduinoPackageIndexDefinitionsSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-package-index-definitions-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoPackageIndexPermissiveSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-package-index-permissive-schema.json", + "title": "Arduino Package Index JSON permissive schema", + "description": "Package indexes define Arduino hardware packages. See: https://arduino.github.io/arduino-cli/latest/package_index_json-specification/. This schema defines the minimum accepted data format.", + "allOf": [ + { + "$ref": "arduino-package-index-definitions-schema.json#/definitions/root/permissive/object" + } + ] +} +`) + +func arduinoPackageIndexPermissiveSchemaJsonBytes() ([]byte, error) { + return _arduinoPackageIndexPermissiveSchemaJson, nil +} + +func arduinoPackageIndexPermissiveSchemaJson() (*asset, error) { + bytes, err := arduinoPackageIndexPermissiveSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-package-index-permissive-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoPackageIndexSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-package-index-schema.json", + "title": "Arduino Package Index JSON schema", + "description": "Package indexes define Arduino hardware packages. See: https://arduino.github.io/arduino-cli/latest/package_index_json-specification/. This schema defines the data format per the specification.", + "allOf": [ + { + "$ref": "arduino-package-index-definitions-schema.json#/definitions/root/specification/object" + } + ] +} +`) + +func arduinoPackageIndexSchemaJsonBytes() ([]byte, error) { + return _arduinoPackageIndexSchemaJson, nil +} + +func arduinoPackageIndexSchemaJson() (*asset, error) { + bytes, err := arduinoPackageIndexSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-package-index-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + +var _arduinoPackageIndexStrictSchemaJson = []byte(`{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-package-index-strict-schema.json", + "title": "Arduino Package Index JSON strict schema", + "description": "Package indexes define Arduino hardware packages. See: https://arduino.github.io/arduino-cli/latest/package_index_json-specification/. This schema defines the best practices for the data format, above and beyond the specification.", + "allOf": [ + { + "$ref": "arduino-package-index-definitions-schema.json#/definitions/root/strict/object" + } + ] +} +`) + +func arduinoPackageIndexStrictSchemaJsonBytes() ([]byte, error) { + return _arduinoPackageIndexStrictSchemaJson, nil +} + +func arduinoPackageIndexStrictSchemaJson() (*asset, error) { + bytes, err := arduinoPackageIndexStrictSchemaJsonBytes() + if err != nil { + return nil, err + } + + info := bindataFileInfo{name: "arduino-package-index-strict-schema.json", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)} + a := &asset{bytes: bytes, info: info} + return a, nil +} + var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://raw.githubusercontent.com/arduino/arduino-lint/main/etc/schemas/arduino-platform-txt-definitions-schema.json", @@ -4451,6 +5880,10 @@ var _bindata = map[string]func() (*asset, error){ "arduino-library-properties-permissive-schema.json": arduinoLibraryPropertiesPermissiveSchemaJson, "arduino-library-properties-schema.json": arduinoLibraryPropertiesSchemaJson, "arduino-library-properties-strict-schema.json": arduinoLibraryPropertiesStrictSchemaJson, + "arduino-package-index-definitions-schema.json": arduinoPackageIndexDefinitionsSchemaJson, + "arduino-package-index-permissive-schema.json": arduinoPackageIndexPermissiveSchemaJson, + "arduino-package-index-schema.json": arduinoPackageIndexSchemaJson, + "arduino-package-index-strict-schema.json": arduinoPackageIndexStrictSchemaJson, "arduino-platform-txt-definitions-schema.json": arduinoPlatformTxtDefinitionsSchemaJson, "arduino-platform-txt-permissive-schema.json": arduinoPlatformTxtPermissiveSchemaJson, "arduino-platform-txt-schema.json": arduinoPlatformTxtSchemaJson, @@ -4511,6 +5944,10 @@ var _bintree = &bintree{nil, map[string]*bintree{ "arduino-library-properties-permissive-schema.json": &bintree{arduinoLibraryPropertiesPermissiveSchemaJson, map[string]*bintree{}}, "arduino-library-properties-schema.json": &bintree{arduinoLibraryPropertiesSchemaJson, map[string]*bintree{}}, "arduino-library-properties-strict-schema.json": &bintree{arduinoLibraryPropertiesStrictSchemaJson, map[string]*bintree{}}, + "arduino-package-index-definitions-schema.json": &bintree{arduinoPackageIndexDefinitionsSchemaJson, map[string]*bintree{}}, + "arduino-package-index-permissive-schema.json": &bintree{arduinoPackageIndexPermissiveSchemaJson, map[string]*bintree{}}, + "arduino-package-index-schema.json": &bintree{arduinoPackageIndexSchemaJson, map[string]*bintree{}}, + "arduino-package-index-strict-schema.json": &bintree{arduinoPackageIndexStrictSchemaJson, map[string]*bintree{}}, "arduino-platform-txt-definitions-schema.json": &bintree{arduinoPlatformTxtDefinitionsSchemaJson, map[string]*bintree{}}, "arduino-platform-txt-permissive-schema.json": &bintree{arduinoPlatformTxtPermissiveSchemaJson, map[string]*bintree{}}, "arduino-platform-txt-schema.json": &bintree{arduinoPlatformTxtSchemaJson, map[string]*bintree{}},