From ad66228ae28f2e54e9252a077c634bfb03fdd95e Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 29 Aug 2021 04:50:49 -0700 Subject: [PATCH 01/10] Fix typos in Go code doc comment Copy/paste errors. --- internal/project/general/general.go | 2 +- internal/project/projectdata/platform.go | 2 +- internal/rule/rulefunction/platform.go | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/project/general/general.go b/internal/project/general/general.go index 6cefa62a..f51f6e8e 100644 --- a/internal/project/general/general.go +++ b/internal/project/general/general.go @@ -25,7 +25,7 @@ PropertiesToMap converts properties.Map data structures to map[string]interface The Arduino project configuration fields have an odd usage of the properties.Map format. Dots may sometimes indicate nested keys, but in other cases they are merely a character in the key string. There are cases where a completely programmatic recursion of the properties into a fully nested structure would result in the impossibility of some keys -having bot a string and a map type, which is not supported. For this reason, it's necessary to manually configure the +having both a string and a map type, which is not supported. For this reason, it's necessary to manually configure the recursion of key levels on a case-by-case basis. In the event a full recursion of key levels is desired, set the levels argument to a value <1. */ diff --git a/internal/project/projectdata/platform.go b/internal/project/projectdata/platform.go index b22b9273..a44f23a4 100644 --- a/internal/project/projectdata/platform.go +++ b/internal/project/projectdata/platform.go @@ -145,7 +145,7 @@ func ProgrammersTxtProgrammerIds() []string { var platformTxtExists bool -// PlatformTxtExists returns whether the platform contains a programmer.txt file. +// PlatformTxtExists returns whether the platform contains a platform.txt file. func PlatformTxtExists() bool { return platformTxtExists } diff --git a/internal/rule/rulefunction/platform.go b/internal/rule/rulefunction/platform.go index 63aec64f..6c575671 100644 --- a/internal/rule/rulefunction/platform.go +++ b/internal/rule/rulefunction/platform.go @@ -1657,7 +1657,7 @@ func PlatformTxtUploadParamsVerboseMissing() (result ruleresult.Type, output str return ruleresult.Pass, "" } -// PlatformTxtUploadParamsQuietMissing checks if any of the programmers are missing upload.params.quiet properties. +// PlatformTxtUploadParamsQuietMissing checks if any of the tools are missing upload.params.quiet properties. func PlatformTxtUploadParamsQuietMissing() (result ruleresult.Type, output string) { if !projectdata.PlatformTxtExists() { return ruleresult.Skip, "Platform has no platform.txt" @@ -1680,7 +1680,7 @@ func PlatformTxtUploadParamsQuietMissing() (result ruleresult.Type, output strin return ruleresult.Pass, "" } -// PlatformTxtUploadPatternMissing checks if any of the programmers are missing upload.pattern properties. +// PlatformTxtUploadPatternMissing checks if any of the tools are missing upload.pattern properties. func PlatformTxtUploadPatternMissing() (result ruleresult.Type, output string) { if !projectdata.PlatformTxtExists() { return ruleresult.Skip, "Platform has no platform.txt" @@ -1726,7 +1726,7 @@ func PlatformTxtProgramParamsVerboseMissing() (result ruleresult.Type, output st return ruleresult.Pass, "" } -// PlatformTxtProgramParamsQuietMissing checks if any of the programmers are missing program.params.quiet properties. +// PlatformTxtProgramParamsQuietMissing checks if any of the tools are missing program.params.quiet properties. func PlatformTxtProgramParamsQuietMissing() (result ruleresult.Type, output string) { if !projectdata.PlatformTxtExists() { return ruleresult.Skip, "Platform has no platform.txt" @@ -1749,7 +1749,7 @@ func PlatformTxtProgramParamsQuietMissing() (result ruleresult.Type, output stri return ruleresult.Pass, "" } -// PlatformTxtProgramPatternMissing checks if any of the programmers are missing program.pattern properties. +// PlatformTxtProgramPatternMissing checks if any of the tools are missing program.pattern properties. func PlatformTxtProgramPatternMissing() (result ruleresult.Type, output string) { if !projectdata.PlatformTxtExists() { return ruleresult.Skip, "Platform has no platform.txt" @@ -1795,7 +1795,7 @@ func PlatformTxtEraseParamsVerboseMissing() (result ruleresult.Type, output stri return ruleresult.Pass, "" } -// PlatformTxtEraseParamsQuietMissing checks if any of the programmers are missing erase.params.quiet properties. +// PlatformTxtEraseParamsQuietMissing checks if any of the tools are missing erase.params.quiet properties. func PlatformTxtEraseParamsQuietMissing() (result ruleresult.Type, output string) { if !projectdata.PlatformTxtExists() { return ruleresult.Skip, "Platform has no platform.txt" @@ -1818,7 +1818,7 @@ func PlatformTxtEraseParamsQuietMissing() (result ruleresult.Type, output string return ruleresult.Pass, "" } -// PlatformTxtErasePatternMissing checks if any of the programmers are missing erase.pattern properties. +// PlatformTxtErasePatternMissing checks if any of the tools are missing erase.pattern properties. func PlatformTxtErasePatternMissing() (result ruleresult.Type, output string) { if !projectdata.PlatformTxtExists() { return ruleresult.Skip, "Platform has no platform.txt" @@ -1864,7 +1864,7 @@ func PlatformTxtBootloaderParamsVerboseMissing() (result ruleresult.Type, output return ruleresult.Pass, "" } -// PlatformTxtBootloaderParamsQuietMissing checks if any of the programmers are missing bootloader.params.quiet properties. +// PlatformTxtBootloaderParamsQuietMissing checks if any of the tools are missing bootloader.params.quiet properties. func PlatformTxtBootloaderParamsQuietMissing() (result ruleresult.Type, output string) { if !projectdata.PlatformTxtExists() { return ruleresult.Skip, "Platform has no platform.txt" @@ -1887,7 +1887,7 @@ func PlatformTxtBootloaderParamsQuietMissing() (result ruleresult.Type, output s return ruleresult.Pass, "" } -// PlatformTxtBootloaderPatternMissing checks if any of the programmers are missing bootloader.pattern properties. +// PlatformTxtBootloaderPatternMissing checks if any of the tools are missing bootloader.pattern properties. func PlatformTxtBootloaderPatternMissing() (result ruleresult.Type, output string) { if !projectdata.PlatformTxtExists() { return ruleresult.Skip, "Platform has no platform.txt" From b5950b70c3ea68e08ff090a3b8cacc29d82250ff Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 29 Aug 2021 15:13:49 -0700 Subject: [PATCH 02/10] Add capability to test for required subproperties in platform.txt An optional property may have a required subproperty. Previously, the test only allowed testing for required root properties, by removing the property entirely. In order to test for required subproperties, it is necessary to add a root property that is missing the subproperty in addition to removing the required property. --- .../platformtxt/platformtxtschema_test.go | 154 +++++++++--------- 1 file changed, 79 insertions(+), 75 deletions(-) diff --git a/internal/project/platform/platformtxt/platformtxtschema_test.go b/internal/project/platform/platformtxt/platformtxtschema_test.go index 8c13640b..d9a6ed44 100644 --- a/internal/project/platform/platformtxt/platformtxtschema_test.go +++ b/internal/project/platform/platformtxt/platformtxtschema_test.go @@ -147,115 +147,119 @@ func TestMinLength(t *testing.T) { func TestRequired(t *testing.T) { testTables := []struct { propertyName string + replacementPropertyName string // Used for testing subkeys that are required only when the higher level key is present. validationErrorPropertyName string complianceLevel compliancelevel.Type assertion assert.BoolAssertionFunc }{ - {"name", "name", compliancelevel.Permissive, assert.True}, - {"name", "name", compliancelevel.Specification, assert.True}, - {"name", "name", compliancelevel.Strict, assert.True}, + {"name", "", "name", compliancelevel.Permissive, assert.True}, + {"name", "", "name", compliancelevel.Specification, assert.True}, + {"name", "", "name", compliancelevel.Strict, assert.True}, - {"version", "version", compliancelevel.Permissive, assert.True}, - {"version", "version", compliancelevel.Specification, assert.True}, - {"version", "version", compliancelevel.Strict, assert.True}, + {"version", "", "version", compliancelevel.Permissive, assert.True}, + {"version", "", "version", compliancelevel.Specification, assert.True}, + {"version", "", "version", compliancelevel.Strict, assert.True}, - {"compiler.warning_flags.none", "compiler\\.warning_flags\\.none", compliancelevel.Permissive, assert.False}, - {"compiler.warning_flags.none", "compiler\\.warning_flags\\.none", compliancelevel.Specification, assert.False}, - {"compiler.warning_flags.none", "compiler\\.warning_flags\\.none", compliancelevel.Strict, assert.True}, + {"compiler.warning_flags.none", "", "compiler\\.warning_flags\\.none", compliancelevel.Permissive, assert.False}, + {"compiler.warning_flags.none", "", "compiler\\.warning_flags\\.none", compliancelevel.Specification, assert.False}, + {"compiler.warning_flags.none", "", "compiler\\.warning_flags\\.none", compliancelevel.Strict, assert.True}, - {"compiler.warning_flags.default", "compiler\\.warning_flags\\.default", compliancelevel.Permissive, assert.False}, - {"compiler.warning_flags.default", "compiler\\.warning_flags\\.default", compliancelevel.Specification, assert.False}, - {"compiler.warning_flags.default", "compiler\\.warning_flags\\.default", compliancelevel.Strict, assert.True}, + {"compiler.warning_flags.default", "", "compiler\\.warning_flags\\.default", compliancelevel.Permissive, assert.False}, + {"compiler.warning_flags.default", "", "compiler\\.warning_flags\\.default", compliancelevel.Specification, assert.False}, + {"compiler.warning_flags.default", "", "compiler\\.warning_flags\\.default", compliancelevel.Strict, assert.True}, - {"compiler.warning_flags.more", "compiler\\.warning_flags\\.more", compliancelevel.Permissive, assert.False}, - {"compiler.warning_flags.more", "compiler\\.warning_flags\\.more", compliancelevel.Specification, assert.False}, - {"compiler.warning_flags.more", "compiler\\.warning_flags\\.more", compliancelevel.Strict, assert.True}, + {"compiler.warning_flags.more", "", "compiler\\.warning_flags\\.more", compliancelevel.Permissive, assert.False}, + {"compiler.warning_flags.more", "", "compiler\\.warning_flags\\.more", compliancelevel.Specification, assert.False}, + {"compiler.warning_flags.more", "", "compiler\\.warning_flags\\.more", compliancelevel.Strict, assert.True}, - {"compiler.warning_flags.all", "compiler\\.warning_flags\\.all", compliancelevel.Permissive, assert.False}, - {"compiler.warning_flags.all", "compiler\\.warning_flags\\.all", compliancelevel.Specification, assert.False}, - {"compiler.warning_flags.all", "compiler\\.warning_flags\\.all", compliancelevel.Strict, assert.True}, + {"compiler.warning_flags.all", "", "compiler\\.warning_flags\\.all", compliancelevel.Permissive, assert.False}, + {"compiler.warning_flags.all", "", "compiler\\.warning_flags\\.all", compliancelevel.Specification, assert.False}, + {"compiler.warning_flags.all", "", "compiler\\.warning_flags\\.all", compliancelevel.Strict, assert.True}, - {"recipe.c.o.pattern", "recipe\\.c\\.o\\.pattern", compliancelevel.Permissive, assert.True}, - {"recipe.c.o.pattern", "recipe\\.c\\.o\\.pattern", compliancelevel.Specification, assert.True}, - {"recipe.c.o.pattern", "recipe\\.c\\.o\\.pattern", compliancelevel.Strict, assert.True}, + {"recipe.c.o.pattern", "", "recipe\\.c\\.o\\.pattern", compliancelevel.Permissive, assert.True}, + {"recipe.c.o.pattern", "", "recipe\\.c\\.o\\.pattern", compliancelevel.Specification, assert.True}, + {"recipe.c.o.pattern", "", "recipe\\.c\\.o\\.pattern", compliancelevel.Strict, assert.True}, - {"recipe.cpp.o.pattern", "recipe\\.cpp\\.o\\.pattern", compliancelevel.Permissive, assert.True}, - {"recipe.cpp.o.pattern", "recipe\\.cpp\\.o\\.pattern", compliancelevel.Specification, assert.True}, - {"recipe.cpp.o.pattern", "recipe\\.cpp\\.o\\.pattern", compliancelevel.Strict, assert.True}, + {"recipe.cpp.o.pattern", "", "recipe\\.cpp\\.o\\.pattern", compliancelevel.Permissive, assert.True}, + {"recipe.cpp.o.pattern", "", "recipe\\.cpp\\.o\\.pattern", compliancelevel.Specification, assert.True}, + {"recipe.cpp.o.pattern", "", "recipe\\.cpp\\.o\\.pattern", compliancelevel.Strict, assert.True}, - {"recipe.S.o.pattern", "recipe\\.S\\.o\\.pattern", compliancelevel.Permissive, assert.True}, - {"recipe.S.o.pattern", "recipe\\.S\\.o\\.pattern", compliancelevel.Specification, assert.True}, - {"recipe.S.o.pattern", "recipe\\.S\\.o\\.pattern", compliancelevel.Strict, assert.True}, + {"recipe.S.o.pattern", "", "recipe\\.S\\.o\\.pattern", compliancelevel.Permissive, assert.True}, + {"recipe.S.o.pattern", "", "recipe\\.S\\.o\\.pattern", compliancelevel.Specification, assert.True}, + {"recipe.S.o.pattern", "", "recipe\\.S\\.o\\.pattern", compliancelevel.Strict, assert.True}, - {"recipe.ar.pattern", "recipe\\.ar\\.pattern", compliancelevel.Permissive, assert.True}, - {"recipe.ar.pattern", "recipe\\.ar\\.pattern", compliancelevel.Specification, assert.True}, - {"recipe.ar.pattern", "recipe\\.ar\\.pattern", compliancelevel.Strict, assert.True}, + {"recipe.ar.pattern", "", "recipe\\.ar\\.pattern", compliancelevel.Permissive, assert.True}, + {"recipe.ar.pattern", "", "recipe\\.ar\\.pattern", compliancelevel.Specification, assert.True}, + {"recipe.ar.pattern", "", "recipe\\.ar\\.pattern", compliancelevel.Strict, assert.True}, - {"recipe.c.combine.pattern", "recipe\\.c\\.combine\\.pattern", compliancelevel.Permissive, assert.True}, - {"recipe.c.combine.pattern", "recipe\\.c\\.combine\\.pattern", compliancelevel.Specification, assert.True}, - {"recipe.c.combine.pattern", "recipe\\.c\\.combine\\.pattern", compliancelevel.Strict, assert.True}, + {"recipe.c.combine.pattern", "", "recipe\\.c\\.combine\\.pattern", compliancelevel.Permissive, assert.True}, + {"recipe.c.combine.pattern", "", "recipe\\.c\\.combine\\.pattern", compliancelevel.Specification, assert.True}, + {"recipe.c.combine.pattern", "", "recipe\\.c\\.combine\\.pattern", compliancelevel.Strict, assert.True}, - {"recipe.output.tmp_file", "recipe\\.output\\.tmp_file", compliancelevel.Permissive, assert.True}, - {"recipe.output.tmp_file", "recipe\\.output\\.tmp_file", compliancelevel.Specification, assert.True}, - {"recipe.output.tmp_file", "recipe\\.output\\.tmp_file", compliancelevel.Strict, assert.True}, + {"recipe.output.tmp_file", "", "recipe\\.output\\.tmp_file", compliancelevel.Permissive, assert.True}, + {"recipe.output.tmp_file", "", "recipe\\.output\\.tmp_file", compliancelevel.Specification, assert.True}, + {"recipe.output.tmp_file", "", "recipe\\.output\\.tmp_file", compliancelevel.Strict, assert.True}, - {"tools.avrdude.upload.pattern", "tools/avrdude/upload/pattern", compliancelevel.Permissive, assert.True}, - {"tools.avrdude.upload.pattern", "tools/avrdude/upload/pattern", compliancelevel.Specification, assert.True}, - {"tools.avrdude.upload.pattern", "tools/avrdude/upload/pattern", compliancelevel.Strict, assert.True}, + {"tools.avrdude.upload.pattern", "", "tools/avrdude/upload/pattern", compliancelevel.Permissive, assert.True}, + {"tools.avrdude.upload.pattern", "", "tools/avrdude/upload/pattern", compliancelevel.Specification, assert.True}, + {"tools.avrdude.upload.pattern", "", "tools/avrdude/upload/pattern", compliancelevel.Strict, assert.True}, - {"tools.avrdude.program.params.verbose", "tools/avrdude/program/params\\.verbose", compliancelevel.Permissive, assert.True}, - {"tools.avrdude.program.params.verbose", "tools/avrdude/program/params\\.verbose", compliancelevel.Specification, assert.True}, - {"tools.avrdude.program.params.verbose", "tools/avrdude/program/params\\.verbose", compliancelevel.Strict, assert.True}, + {"tools.avrdude.program.params.verbose", "", "tools/avrdude/program/params\\.verbose", compliancelevel.Permissive, assert.True}, + {"tools.avrdude.program.params.verbose", "", "tools/avrdude/program/params\\.verbose", compliancelevel.Specification, assert.True}, + {"tools.avrdude.program.params.verbose", "", "tools/avrdude/program/params\\.verbose", compliancelevel.Strict, assert.True}, - {"tools.avrdude.program.params.quiet", "tools/avrdude/program/params\\.quiet", compliancelevel.Permissive, assert.True}, - {"tools.avrdude.program.params.quiet", "tools/avrdude/program/params\\.quiet", compliancelevel.Specification, assert.True}, - {"tools.avrdude.program.params.quiet", "tools/avrdude/program/params\\.quiet", compliancelevel.Strict, assert.True}, + {"tools.avrdude.program.params.quiet", "", "tools/avrdude/program/params\\.quiet", compliancelevel.Permissive, assert.True}, + {"tools.avrdude.program.params.quiet", "", "tools/avrdude/program/params\\.quiet", compliancelevel.Specification, assert.True}, + {"tools.avrdude.program.params.quiet", "", "tools/avrdude/program/params\\.quiet", compliancelevel.Strict, assert.True}, - {"tools.avrdude.program.pattern", "tools/avrdude/program/pattern", compliancelevel.Permissive, assert.True}, - {"tools.avrdude.program.pattern", "tools/avrdude/program/pattern", compliancelevel.Specification, assert.True}, - {"tools.avrdude.program.pattern", "tools/avrdude/program/pattern", compliancelevel.Strict, assert.True}, + {"tools.avrdude.program.pattern", "", "tools/avrdude/program/pattern", compliancelevel.Permissive, assert.True}, + {"tools.avrdude.program.pattern", "", "tools/avrdude/program/pattern", compliancelevel.Specification, assert.True}, + {"tools.avrdude.program.pattern", "", "tools/avrdude/program/pattern", compliancelevel.Strict, assert.True}, - {"tools.bossac.upload.pattern", "tools/bossac/upload/pattern", compliancelevel.Permissive, assert.True}, - {"tools.bossac.upload.pattern", "tools/bossac/upload/pattern", compliancelevel.Specification, assert.True}, - {"tools.bossac.upload.pattern", "tools/bossac/upload/pattern", compliancelevel.Strict, assert.True}, + {"tools.bossac.upload.pattern", "", "tools/bossac/upload/pattern", compliancelevel.Permissive, assert.True}, + {"tools.bossac.upload.pattern", "", "tools/bossac/upload/pattern", compliancelevel.Specification, assert.True}, + {"tools.bossac.upload.pattern", "", "tools/bossac/upload/pattern", compliancelevel.Strict, assert.True}, - {"compiler.c.extra_flags", "compiler.c.extra_flags", compliancelevel.Permissive, assert.False}, - {"compiler.c.extra_flags", "compiler.c.extra_flags", compliancelevel.Specification, assert.False}, - {"compiler.c.extra_flags", "compiler.c.extra_flags", compliancelevel.Strict, assert.True}, + {"compiler.c.extra_flags", "", "compiler.c.extra_flags", compliancelevel.Permissive, assert.False}, + {"compiler.c.extra_flags", "", "compiler.c.extra_flags", compliancelevel.Specification, assert.False}, + {"compiler.c.extra_flags", "", "compiler.c.extra_flags", compliancelevel.Strict, assert.True}, - {"compiler.c.elf.extra_flags", "compiler.c.elf.extra_flags", compliancelevel.Permissive, assert.False}, - {"compiler.c.elf.extra_flags", "compiler.c.elf.extra_flags", compliancelevel.Specification, assert.False}, - {"compiler.c.elf.extra_flags", "compiler.c.elf.extra_flags", compliancelevel.Strict, assert.True}, + {"compiler.c.elf.extra_flags", "", "compiler.c.elf.extra_flags", compliancelevel.Permissive, assert.False}, + {"compiler.c.elf.extra_flags", "", "compiler.c.elf.extra_flags", compliancelevel.Specification, assert.False}, + {"compiler.c.elf.extra_flags", "", "compiler.c.elf.extra_flags", compliancelevel.Strict, assert.True}, - {"compiler.S.extra_flags", "compiler.S.extra_flags", compliancelevel.Permissive, assert.False}, - {"compiler.S.extra_flags", "compiler.S.extra_flags", compliancelevel.Specification, assert.False}, - {"compiler.S.extra_flags", "compiler.S.extra_flags", compliancelevel.Strict, assert.True}, + {"compiler.S.extra_flags", "", "compiler.S.extra_flags", compliancelevel.Permissive, assert.False}, + {"compiler.S.extra_flags", "", "compiler.S.extra_flags", compliancelevel.Specification, assert.False}, + {"compiler.S.extra_flags", "", "compiler.S.extra_flags", compliancelevel.Strict, assert.True}, - {"compiler.cpp.extra_flags", "compiler.cpp.extra_flags", compliancelevel.Permissive, assert.False}, - {"compiler.cpp.extra_flags", "compiler.cpp.extra_flags", compliancelevel.Specification, assert.False}, - {"compiler.cpp.extra_flags", "compiler.cpp.extra_flags", compliancelevel.Strict, assert.True}, + {"compiler.cpp.extra_flags", "", "compiler.cpp.extra_flags", compliancelevel.Permissive, assert.False}, + {"compiler.cpp.extra_flags", "", "compiler.cpp.extra_flags", compliancelevel.Specification, assert.False}, + {"compiler.cpp.extra_flags", "", "compiler.cpp.extra_flags", compliancelevel.Strict, assert.True}, - {"compiler.ar.extra_flags", "compiler.ar.extra_flags", compliancelevel.Permissive, assert.False}, - {"compiler.ar.extra_flags", "compiler.ar.extra_flags", compliancelevel.Specification, assert.False}, - {"compiler.ar.extra_flags", "compiler.ar.extra_flags", compliancelevel.Strict, assert.True}, + {"compiler.ar.extra_flags", "", "compiler.ar.extra_flags", compliancelevel.Permissive, assert.False}, + {"compiler.ar.extra_flags", "", "compiler.ar.extra_flags", compliancelevel.Specification, assert.False}, + {"compiler.ar.extra_flags", "", "compiler.ar.extra_flags", compliancelevel.Strict, assert.True}, - {"recipe.size.pattern", "recipe.size.pattern", compliancelevel.Permissive, assert.False}, - {"recipe.size.pattern", "recipe.size.pattern", compliancelevel.Specification, assert.False}, - {"recipe.size.pattern", "recipe.size.pattern", compliancelevel.Strict, assert.True}, + {"recipe.size.pattern", "", "recipe.size.pattern", compliancelevel.Permissive, assert.False}, + {"recipe.size.pattern", "", "recipe.size.pattern", compliancelevel.Specification, assert.False}, + {"recipe.size.pattern", "", "recipe.size.pattern", compliancelevel.Strict, assert.True}, - {"recipe.size.regex", "recipe.size.regex", compliancelevel.Permissive, assert.False}, - {"recipe.size.regex", "recipe.size.regex", compliancelevel.Specification, assert.False}, - {"recipe.size.regex", "recipe.size.regex", compliancelevel.Strict, assert.True}, + {"recipe.size.regex", "", "recipe.size.regex", compliancelevel.Permissive, assert.False}, + {"recipe.size.regex", "", "recipe.size.regex", compliancelevel.Specification, assert.False}, + {"recipe.size.regex", "", "recipe.size.regex", compliancelevel.Strict, assert.True}, - {"recipe.size.regex.data", "recipe.size.regex.data", compliancelevel.Permissive, assert.False}, - {"recipe.size.regex.data", "recipe.size.regex.data", compliancelevel.Specification, assert.False}, - {"recipe.size.regex.data", "recipe.size.regex.data", compliancelevel.Strict, assert.True}, + {"recipe.size.regex.data", "", "recipe.size.regex.data", compliancelevel.Permissive, assert.False}, + {"recipe.size.regex.data", "", "recipe.size.regex.data", compliancelevel.Specification, assert.False}, + {"recipe.size.regex.data", "", "recipe.size.regex.data", compliancelevel.Strict, assert.True}, } for _, testTable := range testTables { platformTxt, err := properties.LoadFromBytes(validPlatformTxtRaw) require.Nil(t, err) platformTxt.Remove(testTable.propertyName) + if testTable.replacementPropertyName != "" { + platformTxt.Set(testTable.replacementPropertyName, "foo") + } validationResult := platformtxt.Validate(platformTxt) t.Run(fmt.Sprintf("%s (%s)", testTable.propertyName, testTable.complianceLevel), func(t *testing.T) { From 72520b3cf79c0f5aea26109ce524446d36321fa9 Mon Sep 17 00:00:00 2001 From: per1234 Date: Sun, 29 Aug 2021 23:17:58 -0700 Subject: [PATCH 03/10] Add utility function for parsing properties lists Used as a convention for years, the integer terminal subproperty's use as an array syntax has now been formalized as a "list" type. Although the properties package provides a function to parse this data type, it still takes some code acrobatics to convert that into the map[string]interface{} type consumed by the JSON schema parser, which is a candidate for a function. Another "set" data type has also been added, but I don't have an immediate need for it, so will wait to add support. --- internal/project/general/general.go | 14 ++++++++++++++ internal/project/general/general_test.go | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/internal/project/general/general.go b/internal/project/general/general.go index f51f6e8e..c2e28446 100644 --- a/internal/project/general/general.go +++ b/internal/project/general/general.go @@ -51,3 +51,17 @@ func PropertiesToMap(flatProperties *properties.Map, levels int) map[string]inte return propertiesInterface } + +// PropertiesToList parses a property that has a list data type and returns it in the map[string]interface{} type +// consumed by the JSON schema parser. +func PropertiesToList(flatProperties *properties.Map, key string) map[string]interface{} { + list := flatProperties.ExtractSubIndexLists(key) + // Convert the slice to the required interface type + listInterface := make([]interface{}, len(list)) + for i, v := range list { + listInterface[i] = v + } + mapInterface := make(map[string]interface{}) + mapInterface[key] = listInterface + return mapInterface +} diff --git a/internal/project/general/general_test.go b/internal/project/general/general_test.go index bb4c5ba1..58226e79 100644 --- a/internal/project/general/general_test.go +++ b/internal/project/general/general_test.go @@ -102,3 +102,23 @@ func TestPropertiesToMap(t *testing.T) { assert.True(t, reflect.DeepEqual(expectedMapOutput, PropertiesToMap(propertiesInput, 3))) assert.True(t, reflect.DeepEqual(expectedMapOutput, PropertiesToMap(propertiesInput, 0))) } + +func TestPropertiesToList(t *testing.T) { + rawProperties := []byte(` + hello=world + foo.1=asdf + foo.2=zxcv + `) + propertiesInput, err := properties.LoadFromBytes(rawProperties) + require.Nil(t, err) + + expectedMapOutput := map[string]interface{}{ + "hello": []interface{}{"world"}, + } + assert.True(t, reflect.DeepEqual(expectedMapOutput, PropertiesToList(propertiesInput, "hello"))) + + expectedMapOutput = map[string]interface{}{ + "foo": []interface{}{"asdf", "zxcv"}, + } + assert.True(t, reflect.DeepEqual(expectedMapOutput, PropertiesToList(propertiesInput, "foo"))) +} From ce7a60f29717f838439e396e9ff27f6ae3e43163 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 30 Aug 2021 03:41:49 -0700 Subject: [PATCH 04/10] Add rules for the `platform.txt` `pluggable_discovery.*` properties The new pluggable discovery system for Arduino boards platforms introduces a new set of `pluggable_discovery.*` properties to the platform.txt configuration file, which have requirements to be enforced via Arduino Lint rules. --- ...duino-platform-txt-definitions-schema.json | 247 +++++++++++++++++ ...rduino-platform-txt-permissive-schema.json | 3 + etc/schemas/arduino-platform-txt-schema.json | 3 + .../arduino-platform-txt-strict-schema.json | 3 + .../platform/platformtxt/platformtxt.go | 25 +- .../platform/platformtxt/platformtxt_test.go | 12 + .../platformtxt/platformtxtschema_test.go | 21 ++ .../platformtxt/testdata/valid/platform.txt | 2 + internal/project/projectdata/platform.go | 9 + internal/project/projectdata/platform_test.go | 32 ++- .../platforms/valid-platform.txt/platform.txt | 2 + .../ruleconfiguration/ruleconfiguration.go | 34 +++ internal/rule/rulefunction/platform.go | 54 ++++ internal/rule/rulefunction/platform_test.go | 25 ++ .../boards.txt | 23 ++ .../platform.txt | 54 ++++ .../boards.txt | 23 ++ .../platform.txt | 53 ++++ .../boards.txt | 23 ++ .../platform.txt | 52 ++++ .../boards.txt | 23 ++ .../platform.txt | 55 ++++ .../platforms/valid-platform.txt/platform.txt | 2 + .../boards.txt | 23 ++ .../platform.txt | 53 ++++ internal/rule/schema/schemadata/bindata.go | 256 ++++++++++++++++++ 26 files changed, 1094 insertions(+), 18 deletions(-) create mode 100644 internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-n-platform.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-n-platform.txt/platform.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-platform.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-platform.txt/platform.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/no-pluggable-discoveries-platform.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/no-pluggable-discoveries-platform.txt/platform.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/pluggable-discovery-discovery-id-pattern-missing-platform.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/pluggable-discovery-discovery-id-pattern-missing-platform.txt/platform.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/valid-with-manual-installation-pluggable-discoveries-platform.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/valid-with-manual-installation-pluggable-discoveries-platform.txt/platform.txt diff --git a/etc/schemas/arduino-platform-txt-definitions-schema.json b/etc/schemas/arduino-platform-txt-definitions-schema.json index 2fbc4b33..50140cc8 100644 --- a/etc/schemas/arduino-platform-txt-definitions-schema.json +++ b/etc/schemas/arduino-platform-txt-definitions-schema.json @@ -700,6 +700,215 @@ } } }, + "pluggableDiscovery": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscovery/base/object" + }, + { + "properties": { + "required": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/permissive/object" + } + }, + "additionalProperties": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/permissive/object" + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscovery/base/object" + }, + { + "properties": { + "required": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/specification/object" + } + }, + "additionalProperties": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/specification/object" + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscovery/base/object" + }, + { + "properties": { + "required": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/strict/object" + } + }, + "additionalProperties": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/strict/object" + } + } + ] + } + } + }, + "pluggableDiscoveryDiscoveryName": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/permissive/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/specification/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/strict/object" + } + ] + } + } + }, + "pluggableDiscoveryRequired": { + "base": { + "object": { + "allOf": [ + { + "type": "array" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/base/object" + }, + { + "items": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/permissive/object" + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/base/object" + }, + { + "items": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/specification/object" + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/base/object" + }, + { + "items": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/strict/object" + } + } + ] + } + } + }, + "pluggableDiscoveryRequiredN": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "pattern": "^.+:.+$" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/base/object" + } + ] + } + } + }, "tools": { "base": { "object": { @@ -1145,6 +1354,44 @@ } } }, + "pluggableDiscoveryDiscoveryName": { + "base": { + "object": { + "allOf": [ + { + "required": ["pattern"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/base/object" + } + ] + } + } + }, "toolsToolNameActionName": { "base": { "object": { diff --git a/etc/schemas/arduino-platform-txt-permissive-schema.json b/etc/schemas/arduino-platform-txt-permissive-schema.json index 50e85c5d..af541a7a 100644 --- a/etc/schemas/arduino-platform-txt-permissive-schema.json +++ b/etc/schemas/arduino-platform-txt-permissive-schema.json @@ -54,6 +54,9 @@ "recipe.size.pattern": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/recipeSizePattern/permissive/object" }, + "pluggable_discovery": { + "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/pluggableDiscovery/permissive/object" + }, "tools": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/tools/permissive/object" } diff --git a/etc/schemas/arduino-platform-txt-schema.json b/etc/schemas/arduino-platform-txt-schema.json index d66b9c52..fc299a13 100644 --- a/etc/schemas/arduino-platform-txt-schema.json +++ b/etc/schemas/arduino-platform-txt-schema.json @@ -54,6 +54,9 @@ "recipe.size.pattern": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/recipeSizePattern/specification/object" }, + "pluggable_discovery": { + "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/pluggableDiscovery/specification/object" + }, "tools": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/tools/specification/object" } diff --git a/etc/schemas/arduino-platform-txt-strict-schema.json b/etc/schemas/arduino-platform-txt-strict-schema.json index 871d1453..05f71286 100644 --- a/etc/schemas/arduino-platform-txt-strict-schema.json +++ b/etc/schemas/arduino-platform-txt-strict-schema.json @@ -54,6 +54,9 @@ "recipe.size.pattern": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/recipeSizePattern/strict/object" }, + "pluggable_discovery": { + "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/pluggableDiscovery/strict/object" + }, "tools": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/tools/strict/object" } diff --git a/internal/project/platform/platformtxt/platformtxt.go b/internal/project/platform/platformtxt/platformtxt.go index d6d9b513..fd3b6976 100644 --- a/internal/project/platform/platformtxt/platformtxt.go +++ b/internal/project/platform/platformtxt/platformtxt.go @@ -57,13 +57,20 @@ func Validate(platformTxt *properties.Map) map[compliancelevel.Type]schema.Valid validation package. Even though platform.txt has a multi-level nested data structure, the format has the odd characteristic of allowing a key to be both an object and a string simultaneously, which is not compatible with Golang maps or JSON. So the - data structure used is a selective map, using a flat map except for the tools key, which can contain any number of - arbitrary tool name subproperties which must be linted. + data structure used is a selective map, using a flat map except for the tools and pluggable_discovery keys, which + can contain any number of arbitrary subproperties which must be linted. */ platformTxtInterface := make(map[string]interface{}) keys := platformTxt.Keys() for _, key := range keys { - if strings.HasPrefix(key, "tools.") { + if strings.HasPrefix(key, "pluggable_discovery.") { + if key == "pluggable_discovery.required" || strings.HasPrefix(key, "pluggable_discovery.required.") { + platformTxtInterface["pluggable_discovery"] = general.PropertiesToList(platformTxt.SubTree("pluggable_discovery"), "required") + } else { + // It is a pluggable_discovery.DISCOVERY_ID property. + platformTxtInterface["pluggable_discovery"] = general.PropertiesToMap(platformTxt.SubTree("pluggable_discovery"), 2) + } + } else if strings.HasPrefix(key, "tools.") { platformTxtInterface["tools"] = general.PropertiesToMap(platformTxt.SubTree("tools"), 3) } else { platformTxtInterface[key] = platformTxt.Get(key) @@ -77,6 +84,18 @@ func Validate(platformTxt *properties.Map) map[compliancelevel.Type]schema.Valid return validationResults } +// PluggableDiscoveryNames returns the list of pluggable discovery names from the given platform.txt properties. +func PluggableDiscoveryNames(platformTxt *properties.Map) []string { + names := platformTxt.SubTree("pluggable_discovery").FirstLevelKeys() + for i := range names { + for i < len(names) && names[i] == "required" { + names = append(names[:i], names[i+1:]...) + } + } + + return names +} + // ToolNames returns the list of tool names from the given platform.txt properties. func ToolNames(platformTxt *properties.Map) []string { return platformTxt.SubTree("tools").FirstLevelKeys() diff --git a/internal/project/platform/platformtxt/platformtxt_test.go b/internal/project/platform/platformtxt/platformtxt_test.go index a55168fc..221d1a6e 100644 --- a/internal/project/platform/platformtxt/platformtxt_test.go +++ b/internal/project/platform/platformtxt/platformtxt_test.go @@ -62,6 +62,8 @@ func init() { "recipe.size.pattern": "asdf", "recipe.size.regex": "asdf", "recipe.size.regex.data": "asdf", + "pluggable_discovery.required.0": "builtin:serial-discovery", + "pluggable_discovery.required.1": "builtin:mdns-discovery", "tools.avrdude.upload.params.verbose": "-v", "tools.avrdude.upload.params.quiet": "-q -q", "tools.avrdude.upload.pattern": "asdf", @@ -90,6 +92,16 @@ func TestValidate(t *testing.T) { assert.NotNil(t, validationResult[compliancelevel.Strict].Result, "Invalid (strict)") } +func TestPluggableDiscoveryNames(t *testing.T) { + platformTxt := properties.NewFromHashmap(validPlatformTxtMap) + + assert.ElementsMatch(t, []string{}, PluggableDiscoveryNames(platformTxt), "No elements for pluggable_discovery.required properties.") + + platformTxt.Set("pluggable_discovery.foo_discovery.pattern", "asdf") + platformTxt.Set("pluggable_discovery.bar_discovery.pattern", "zxcv") + assert.ElementsMatch(t, []string{"foo_discovery", "bar_discovery"}, PluggableDiscoveryNames(platformTxt), "pluggable_discovery.DISCOVERY_ID properties add elements for each DISCOVERY_ID.") +} + func TestToolNames(t *testing.T) { platformTxt := properties.NewFromHashmap(validPlatformTxtMap) diff --git a/internal/project/platform/platformtxt/platformtxtschema_test.go b/internal/project/platform/platformtxt/platformtxtschema_test.go index d9a6ed44..513afa57 100644 --- a/internal/project/platform/platformtxt/platformtxtschema_test.go +++ b/internal/project/platform/platformtxt/platformtxtschema_test.go @@ -240,6 +240,14 @@ func TestRequired(t *testing.T) { {"compiler.ar.extra_flags", "", "compiler.ar.extra_flags", compliancelevel.Specification, assert.False}, {"compiler.ar.extra_flags", "", "compiler.ar.extra_flags", compliancelevel.Strict, assert.True}, + {"pluggable_discovery.foo.pattern", "pluggable_discovery.foo.bar", "pluggable_discovery/foo/pattern", compliancelevel.Permissive, assert.True}, + {"pluggable_discovery.foo.pattern", "pluggable_discovery.foo.bar", "pluggable_discovery/foo/pattern", compliancelevel.Specification, assert.True}, + {"pluggable_discovery.foo.pattern", "pluggable_discovery.foo.bar", "pluggable_discovery/foo/pattern", compliancelevel.Strict, assert.True}, + // Property is only required when there is a pluggable_discovery.foo object + {"pluggable_discovery.foo.pattern", "", "pluggable_discovery/foo/pattern", compliancelevel.Permissive, assert.False}, + {"pluggable_discovery.foo.pattern", "", "pluggable_discovery/foo/pattern", compliancelevel.Specification, assert.False}, + {"pluggable_discovery.foo.pattern", "", "pluggable_discovery/foo/pattern", compliancelevel.Strict, assert.False}, + {"recipe.size.pattern", "", "recipe.size.pattern", compliancelevel.Permissive, assert.False}, {"recipe.size.pattern", "", "recipe.size.pattern", compliancelevel.Specification, assert.False}, {"recipe.size.pattern", "", "recipe.size.pattern", compliancelevel.Strict, assert.True}, @@ -387,6 +395,19 @@ func TestPattern(t *testing.T) { {"recipe.preproc.macros", "recipe\\.preproc\\.macros", "foo", compliancelevel.Permissive, assert.False}, {"recipe.preproc.macros", "recipe\\.preproc\\.macros", "foo", compliancelevel.Specification, assert.False}, {"recipe.preproc.macros", "recipe\\.preproc\\.macros", "foo", compliancelevel.Strict, assert.True}, + + {"pluggable_discovery.required", "pluggable_discovery/required", "foo:bar", compliancelevel.Permissive, assert.False}, + {"pluggable_discovery.required", "pluggable_discovery/required", "foo:bar", compliancelevel.Specification, assert.False}, + {"pluggable_discovery.required", "pluggable_discovery/required", "foo:bar", compliancelevel.Strict, assert.False}, + {"pluggable_discovery.required", "pluggable_discovery/required", "foo", compliancelevel.Permissive, assert.True}, + {"pluggable_discovery.required", "pluggable_discovery/required", "foo", compliancelevel.Specification, assert.True}, + {"pluggable_discovery.required", "pluggable_discovery/required", "foo", compliancelevel.Strict, assert.True}, + {"pluggable_discovery.required.1", "pluggable_discovery/required", "foo:bar", compliancelevel.Permissive, assert.False}, + {"pluggable_discovery.required.1", "pluggable_discovery/required", "foo:bar", compliancelevel.Specification, assert.False}, + {"pluggable_discovery.required.1", "pluggable_discovery/required", "foo:bar", compliancelevel.Strict, assert.False}, + {"pluggable_discovery.required.1", "pluggable_discovery/required", "foo", compliancelevel.Permissive, assert.True}, + {"pluggable_discovery.required.1", "pluggable_discovery/required", "foo", compliancelevel.Specification, assert.True}, + {"pluggable_discovery.required.1", "pluggable_discovery/required", "foo", compliancelevel.Strict, assert.True}, } for _, testTable := range testTables { diff --git a/internal/project/platform/platformtxt/testdata/valid/platform.txt b/internal/project/platform/platformtxt/testdata/valid/platform.txt index 3adfb9b6..ddbab5ea 100644 --- a/internal/project/platform/platformtxt/testdata/valid/platform.txt +++ b/internal/project/platform/platformtxt/testdata/valid/platform.txt @@ -23,6 +23,8 @@ recipe.output.save_file=asdf recipe.size.pattern=asdf recipe.size.regex=asdf recipe.size.regex.data=asdf +pluggable_discovery.required.0=builtin:serial-discovery +pluggable_discovery.required.1=builtin:mdns-discovery tools.avrdude.upload.params.verbose=-v tools.avrdude.upload.params.quiet=-q -q tools.avrdude.upload.pattern=asdf diff --git a/internal/project/projectdata/platform.go b/internal/project/projectdata/platform.go index a44f23a4..e7728cfb 100644 --- a/internal/project/projectdata/platform.go +++ b/internal/project/projectdata/platform.go @@ -58,10 +58,12 @@ func InitializeForPlatform(project project.Type) { if platformTxtLoadError != nil { logrus.Tracef("Error loading platform.txt from %s: %s", project.Path, platformTxtLoadError) platformTxtSchemaValidationResult = nil + platformTxtPluggableDiscoveryNames = nil platformTxtToolNames = nil } else { platformTxtSchemaValidationResult = platformtxt.Validate(platformTxt) + platformTxtPluggableDiscoveryNames = platformtxt.PluggableDiscoveryNames(platformTxt) platformTxtToolNames = platformtxt.ToolNames(platformTxt) } } @@ -171,6 +173,13 @@ func PlatformTxtSchemaValidationResult() map[compliancelevel.Type]schema.Validat return platformTxtSchemaValidationResult } +var platformTxtPluggableDiscoveryNames []string + +// PlatformTxtPluggableDiscoveryNames returns the list of pluggable discoveries present in the platform's platform.txt. +func PlatformTxtPluggableDiscoveryNames() []string { + return platformTxtPluggableDiscoveryNames +} + var platformTxtToolNames []string // PlatformTxtToolNames returns the list of tools present in the platform's platform.txt. diff --git a/internal/project/projectdata/platform_test.go b/internal/project/projectdata/platform_test.go index 8effadeb..bb2fac0d 100644 --- a/internal/project/projectdata/platform_test.go +++ b/internal/project/projectdata/platform_test.go @@ -36,22 +36,23 @@ func init() { func TestInitializeForPlatform(t *testing.T) { testTables := []struct { - testName string - platformFolderName string - boardsTxtAssertion assert.ValueAssertionFunc - boardsTxtLoadErrorAssertion assert.ValueAssertionFunc - platformTxtExistsAssertion assert.BoolAssertionFunc - platformTxtAssertion assert.ValueAssertionFunc - platformTxtLoadErrorAssertion assert.ValueAssertionFunc - platformTxtSchemaValidationResultAssertion assert.ValueAssertionFunc - platformTxtToolNamesAssertion []string + testName string + platformFolderName string + boardsTxtAssertion assert.ValueAssertionFunc + boardsTxtLoadErrorAssertion assert.ValueAssertionFunc + platformTxtExistsAssertion assert.BoolAssertionFunc + platformTxtAssertion assert.ValueAssertionFunc + platformTxtLoadErrorAssertion assert.ValueAssertionFunc + platformTxtSchemaValidationResultAssertion assert.ValueAssertionFunc + platformTxtPluggableDiscoveryNamesAssertion []string + platformTxtToolNamesAssertion []string }{ - {"Valid boards.txt", "valid-boards.txt", assert.NotNil, assert.Nil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil}, - {"Invalid boards.txt", "invalid-boards.txt", assert.Nil, assert.NotNil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil}, - {"Missing boards.txt", "missing-boards.txt", assert.Nil, assert.NotNil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil}, - {"Valid platform.txt", "valid-platform.txt", assert.NotNil, assert.Nil, assert.True, assert.NotNil, assert.Nil, assert.NotNil, []string{"avrdude", "bossac"}}, - {"Invalid platform.txt", "invalid-platform.txt", assert.NotNil, assert.Nil, assert.True, assert.Nil, assert.NotNil, assert.Nil, nil}, - {"Missing platform.txt", "missing-platform.txt", assert.NotNil, assert.Nil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil}, + {"Valid boards.txt", "valid-boards.txt", assert.NotNil, assert.Nil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil}, + {"Invalid boards.txt", "invalid-boards.txt", assert.Nil, assert.NotNil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil}, + {"Missing boards.txt", "missing-boards.txt", assert.Nil, assert.NotNil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil}, + {"Valid platform.txt", "valid-platform.txt", assert.NotNil, assert.Nil, assert.True, assert.NotNil, assert.Nil, assert.NotNil, []string{"foo_discovery", "bar_discovery"}, []string{"avrdude", "bossac"}}, + {"Invalid platform.txt", "invalid-platform.txt", assert.NotNil, assert.Nil, assert.True, assert.Nil, assert.NotNil, assert.Nil, nil, nil}, + {"Missing platform.txt", "missing-platform.txt", assert.NotNil, assert.Nil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil}, } for _, testTable := range testTables { @@ -72,6 +73,7 @@ func TestInitializeForPlatform(t *testing.T) { testTable.platformTxtAssertion(t, PlatformTxt(), testTable.testName) testTable.platformTxtLoadErrorAssertion(t, PlatformTxtLoadError(), testTable.testName) testTable.platformTxtSchemaValidationResultAssertion(t, PlatformTxtSchemaValidationResult(), testTable.testName) + assert.Equal(t, testTable.platformTxtPluggableDiscoveryNamesAssertion, PlatformTxtPluggableDiscoveryNames(), testTable.testName) assert.Equal(t, testTable.platformTxtToolNamesAssertion, PlatformTxtToolNames(), testTable.testName) } } diff --git a/internal/project/projectdata/testdata/platforms/valid-platform.txt/platform.txt b/internal/project/projectdata/testdata/platforms/valid-platform.txt/platform.txt index 14781796..27fe7d29 100644 --- a/internal/project/projectdata/testdata/platforms/valid-platform.txt/platform.txt +++ b/internal/project/projectdata/testdata/platforms/valid-platform.txt/platform.txt @@ -23,6 +23,8 @@ recipe.output.save_file=asdf recipe.size.pattern=asdf recipe.size.regex=asdf recipe.size.regex.data=asdf +pluggable_discovery.foo_discovery.pattern=asdf +pluggable_discovery.bar_discovery.pattern=zxcv tools.avrdude.upload.params.verbose=-v tools.avrdude.upload.params.quiet=-q -q tools.avrdude.upload.pattern=asdf diff --git a/internal/rule/ruleconfiguration/ruleconfiguration.go b/internal/rule/ruleconfiguration/ruleconfiguration.go index bed0553c..fa21761d 100644 --- a/internal/rule/ruleconfiguration/ruleconfiguration.go +++ b/internal/rule/ruleconfiguration/ruleconfiguration.go @@ -2829,6 +2829,40 @@ var configurations = []Type{ ErrorModes: []rulemode.Type{rulemode.Strict}, RuleFunction: rulefunction.PlatformTxtRecipeSizeRegexDataMissing, }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "platform.txt", + ID: "PF090", + Brief: "invalid pluggable_discovery.required format", + Description: "The tool dependency reference for a pluggable discovery in the platform's `platform.txt` configuration file has an invalid format.", + MessageTemplate: "Value for tool dependency reference of pluggable discovery(s) {{.}} is invalid.", + Reference: "https://arduino.github.io/arduino-cli/latest/platform-specification/#pluggable-discovery", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.PlatformTxtPluggableDiscoveryRequiredInvalid, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "platform.txt", + ID: "PF091", + Brief: "missing pluggable_discovery.DISCOVERY_ID.pattern", + Description: "A manual installation pluggable discovery in the platform's `platform.txt` configuration file is missing the `pattern` property.", + MessageTemplate: "Missing pattern for {{.}} pluggable discovery(s).", + Reference: "https://arduino.github.io/arduino-cli/latest/platform-specification/#pluggable-discovery", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.PlatformTxtPluggableDiscoveryDiscoveryIDPatternMissing, + }, { ProjectType: projecttype.Platform, SuperprojectType: projecttype.All, diff --git a/internal/rule/rulefunction/platform.go b/internal/rule/rulefunction/platform.go index 6c575671..2c79dd93 100644 --- a/internal/rule/rulefunction/platform.go +++ b/internal/rule/rulefunction/platform.go @@ -16,6 +16,7 @@ package rulefunction import ( + "fmt" "strings" "github.com/arduino/arduino-lint/internal/project/projectdata" @@ -1680,6 +1681,59 @@ func PlatformTxtUploadParamsQuietMissing() (result ruleresult.Type, output strin return ruleresult.Pass, "" } +// PlatformTxtPluggableDiscoveryRequiredInvalid checks if any of the pluggable discovery tool references have invalid format. +func PlatformTxtPluggableDiscoveryRequiredInvalid() (result ruleresult.Type, output string) { + if !projectdata.PlatformTxtExists() { + return ruleresult.Skip, "Platform has no platform.txt" + } + + if projectdata.PlatformTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load platform.txt" + } + + if !projectdata.PlatformTxt().ContainsKey("pluggable_discovery.required") && projectdata.PlatformTxt().SubTree("pluggable_discovery.required").Size() == 0 { + return ruleresult.Skip, "Property not present" + } + + if schema.PropertyPatternMismatch("pluggable_discovery/required", projectdata.PlatformTxtSchemaValidationResult()[compliancelevel.Specification]) { + return ruleresult.Fail, "" + } + + return ruleresult.Pass, "" +} + +// PlatformTxtPluggableDiscoveryDiscoveryIDPatternMissing checks if any of the manual installation pluggable discoveries +// are missing pattern properties. +func PlatformTxtPluggableDiscoveryDiscoveryIDPatternMissing() (result ruleresult.Type, output string) { + if !projectdata.PlatformTxtExists() { + return ruleresult.Skip, "Platform has no platform.txt" + } + + if projectdata.PlatformTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load platform.txt" + } + + if len(projectdata.PlatformTxtPluggableDiscoveryNames()) == 0 { + return ruleresult.Skip, "platform.txt has no manual installation pluggable discoveries" + } + + nonCompliant := []string{} + for _, discovery := range projectdata.PlatformTxtPluggableDiscoveryNames() { + if schema.RequiredPropertyMissing( + fmt.Sprintf("pluggable_discovery/%s/pattern", discovery), + projectdata.PlatformTxtSchemaValidationResult()[compliancelevel.Specification], + ) { + nonCompliant = append(nonCompliant, discovery) + } + } + + if len(nonCompliant) > 0 { + return ruleresult.Fail, strings.Join(nonCompliant, ", ") + } + + return ruleresult.Pass, "" +} + // PlatformTxtUploadPatternMissing checks if any of the tools are missing upload.pattern properties. func PlatformTxtUploadPatternMissing() (result ruleresult.Type, output string) { if !projectdata.PlatformTxtExists() { diff --git a/internal/rule/rulefunction/platform_test.go b/internal/rule/rulefunction/platform_test.go index e7277977..3e0fa705 100644 --- a/internal/rule/rulefunction/platform_test.go +++ b/internal/rule/rulefunction/platform_test.go @@ -967,6 +967,31 @@ func TestPlatformTxtRecipeSizeRegexDataMissing(t *testing.T) { checkPlatformRuleFunction(PlatformTxtRecipeSizeRegexDataMissing, testTables, t) } +func TestPlatformTxtPluggableDiscoveryRequiredInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-platform.txt", ruleresult.Skip, ""}, + {"Invalid", "invalid-platform.txt", ruleresult.NotRun, ""}, + {"Property absent", "no-pluggable-discoveries-platform.txt", ruleresult.Skip, ""}, + {"Property invalid (root prop)", "invalid-pluggable-discovery-required-platform.txt", ruleresult.Fail, "^$"}, + {"Property invalid (list prop)", "invalid-pluggable-discovery-required-n-platform.txt", ruleresult.Fail, "^$"}, + {"Valid", "valid-platform.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(PlatformTxtPluggableDiscoveryRequiredInvalid, testTables, t) +} + +func TestPlatformTxtPluggableDiscoveryDiscoveryIDPatternMissing(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-platform.txt", ruleresult.Skip, ""}, + {"Invalid", "invalid-platform.txt", ruleresult.NotRun, ""}, + {"No pluggable discoveries", "no-pluggable-discoveries-platform.txt", ruleresult.Skip, ""}, + {"Property missing", "pluggable-discovery-discovery-id-pattern-missing-platform.txt", ruleresult.Fail, "^foo_discovery, baz_discovery$"}, + {"Valid", "valid-with-manual-installation-pluggable-discoveries-platform.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(PlatformTxtPluggableDiscoveryDiscoveryIDPatternMissing, testTables, t) +} + func TestPlatformTxtUploadPatternMissing(t *testing.T) { testTables := []platformRuleFunctionTestTable{ {"Missing", "missing-platform.txt", ruleresult.Skip, ""}, diff --git a/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-n-platform.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-n-platform.txt/boards.txt new file mode 100644 index 00000000..1866d3c4 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-n-platform.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-n-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-n-platform.txt/platform.txt new file mode 100644 index 00000000..a5cb9730 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-n-platform.txt/platform.txt @@ -0,0 +1,54 @@ +name=Arduino AVR Boards +version=1.8.3 +compiler.warning_flags.none=asdf +compiler.warning_flags.default=asdf +compiler.warning_flags.more=asdf +compiler.warning_flags.all=asdf +compiler.optimization_flags.debug= +compiler.optimization_flags.release= +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= +recipe.c.o.pattern=asdf {compiler.c.extra_flags} +recipe.cpp.o.pattern=asdf {compiler.cpp.extra_flags} +recipe.S.o.pattern=asdf {compiler.S.extra_flags} +recipe.ar.pattern=asdf {compiler.ar.extra_flags} +recipe.c.combine.pattern=asdf {compiler.c.elf.extra_flags} +recipe.preproc.macros=asdf {compiler.cpp.extra_flags} +recipe.objcopy.eep.pattern=asdf +recipe.objcopy.hex.pattern=asdf +recipe.output.tmp_file=asdf +recipe.output.save_file=asdf +recipe.size.pattern=asdf +recipe.size.regex=asdf +recipe.size.regex.data=asdf +pluggable_discovery.required.1=asdf +pluggable_discovery.required.2=foo:bar +tools.avrdude.upload.params.verbose=-v +tools.avrdude.upload.params.quiet=-q -q +tools.avrdude.upload.pattern=asdf +tools.bossac.upload.params.verbose=-v +tools.bossac.upload.params.quiet=-q -q +tools.bossac.upload.pattern=asdf +tools.avrdude.program.params.verbose=-v +tools.avrdude.program.params.quiet=-q -q +tools.avrdude.program.pattern=asdf +tools.bossac.program.params.verbose=-v +tools.bossac.program.params.quiet=-q -q +tools.bossac.program.pattern=asdf +tools.avrdude.erase.params.verbose=-v +tools.avrdude.erase.params.quiet=-q -q +tools.avrdude.erase.pattern=asdf +tools.bossac.erase.params.verbose=-v +tools.bossac.erase.params.quiet=-q -q +tools.bossac.erase.pattern=asdf +tools.avrdude.bootloader.params.verbose=-v +tools.avrdude.bootloader.params.quiet=-q -q +tools.avrdude.bootloader.pattern=asdf +tools.bossac.bootloader.params.verbose=-v +tools.bossac.bootloader.params.quiet=-q -q +tools.bossac.bootloader.pattern=asdf diff --git a/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-platform.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-platform.txt/boards.txt new file mode 100644 index 00000000..1866d3c4 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-platform.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-platform.txt/platform.txt new file mode 100644 index 00000000..a4ca8bb8 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/invalid-pluggable-discovery-required-platform.txt/platform.txt @@ -0,0 +1,53 @@ +name=Arduino AVR Boards +version=1.8.3 +compiler.warning_flags.none=asdf +compiler.warning_flags.default=asdf +compiler.warning_flags.more=asdf +compiler.warning_flags.all=asdf +compiler.optimization_flags.debug= +compiler.optimization_flags.release= +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= +recipe.c.o.pattern=asdf {compiler.c.extra_flags} +recipe.cpp.o.pattern=asdf {compiler.cpp.extra_flags} +recipe.S.o.pattern=asdf {compiler.S.extra_flags} +recipe.ar.pattern=asdf {compiler.ar.extra_flags} +recipe.c.combine.pattern=asdf {compiler.c.elf.extra_flags} +recipe.preproc.macros=asdf {compiler.cpp.extra_flags} +recipe.objcopy.eep.pattern=asdf +recipe.objcopy.hex.pattern=asdf +recipe.output.tmp_file=asdf +recipe.output.save_file=asdf +recipe.size.pattern=asdf +recipe.size.regex=asdf +recipe.size.regex.data=asdf +pluggable_discovery.required=asdf +tools.avrdude.upload.params.verbose=-v +tools.avrdude.upload.params.quiet=-q -q +tools.avrdude.upload.pattern=asdf +tools.bossac.upload.params.verbose=-v +tools.bossac.upload.params.quiet=-q -q +tools.bossac.upload.pattern=asdf +tools.avrdude.program.params.verbose=-v +tools.avrdude.program.params.quiet=-q -q +tools.avrdude.program.pattern=asdf +tools.bossac.program.params.verbose=-v +tools.bossac.program.params.quiet=-q -q +tools.bossac.program.pattern=asdf +tools.avrdude.erase.params.verbose=-v +tools.avrdude.erase.params.quiet=-q -q +tools.avrdude.erase.pattern=asdf +tools.bossac.erase.params.verbose=-v +tools.bossac.erase.params.quiet=-q -q +tools.bossac.erase.pattern=asdf +tools.avrdude.bootloader.params.verbose=-v +tools.avrdude.bootloader.params.quiet=-q -q +tools.avrdude.bootloader.pattern=asdf +tools.bossac.bootloader.params.verbose=-v +tools.bossac.bootloader.params.quiet=-q -q +tools.bossac.bootloader.pattern=asdf diff --git a/internal/rule/rulefunction/testdata/platforms/no-pluggable-discoveries-platform.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/no-pluggable-discoveries-platform.txt/boards.txt new file mode 100644 index 00000000..1866d3c4 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/no-pluggable-discoveries-platform.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/no-pluggable-discoveries-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/no-pluggable-discoveries-platform.txt/platform.txt new file mode 100644 index 00000000..dec708ad --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/no-pluggable-discoveries-platform.txt/platform.txt @@ -0,0 +1,52 @@ +name=Arduino AVR Boards +version=1.8.3 +compiler.warning_flags.none=asdf +compiler.warning_flags.default=asdf +compiler.warning_flags.more=asdf +compiler.warning_flags.all=asdf +compiler.optimization_flags.debug= +compiler.optimization_flags.release= +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= +recipe.c.o.pattern=asdf {compiler.c.extra_flags} +recipe.cpp.o.pattern=asdf {compiler.cpp.extra_flags} +recipe.S.o.pattern=asdf {compiler.S.extra_flags} +recipe.ar.pattern=asdf {compiler.ar.extra_flags} +recipe.c.combine.pattern=asdf {compiler.c.elf.extra_flags} +recipe.preproc.macros=asdf {compiler.cpp.extra_flags} +recipe.objcopy.eep.pattern=asdf +recipe.objcopy.hex.pattern=asdf +recipe.output.tmp_file=asdf +recipe.output.save_file=asdf +recipe.size.pattern=asdf +recipe.size.regex=asdf +recipe.size.regex.data=asdf +tools.avrdude.upload.params.verbose=-v +tools.avrdude.upload.params.quiet=-q -q +tools.avrdude.upload.pattern=asdf +tools.bossac.upload.params.verbose=-v +tools.bossac.upload.params.quiet=-q -q +tools.bossac.upload.pattern=asdf +tools.avrdude.program.params.verbose=-v +tools.avrdude.program.params.quiet=-q -q +tools.avrdude.program.pattern=asdf +tools.bossac.program.params.verbose=-v +tools.bossac.program.params.quiet=-q -q +tools.bossac.program.pattern=asdf +tools.avrdude.erase.params.verbose=-v +tools.avrdude.erase.params.quiet=-q -q +tools.avrdude.erase.pattern=asdf +tools.bossac.erase.params.verbose=-v +tools.bossac.erase.params.quiet=-q -q +tools.bossac.erase.pattern=asdf +tools.avrdude.bootloader.params.verbose=-v +tools.avrdude.bootloader.params.quiet=-q -q +tools.avrdude.bootloader.pattern=asdf +tools.bossac.bootloader.params.verbose=-v +tools.bossac.bootloader.params.quiet=-q -q +tools.bossac.bootloader.pattern=asdf diff --git a/internal/rule/rulefunction/testdata/platforms/pluggable-discovery-discovery-id-pattern-missing-platform.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/pluggable-discovery-discovery-id-pattern-missing-platform.txt/boards.txt new file mode 100644 index 00000000..1866d3c4 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/pluggable-discovery-discovery-id-pattern-missing-platform.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/pluggable-discovery-discovery-id-pattern-missing-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/pluggable-discovery-discovery-id-pattern-missing-platform.txt/platform.txt new file mode 100644 index 00000000..d25b65ff --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/pluggable-discovery-discovery-id-pattern-missing-platform.txt/platform.txt @@ -0,0 +1,55 @@ +name=Arduino AVR Boards +version=1.8.3 +compiler.warning_flags.none=asdf +compiler.warning_flags.default=asdf +compiler.warning_flags.more=asdf +compiler.warning_flags.all=asdf +compiler.optimization_flags.debug= +compiler.optimization_flags.release= +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= +recipe.c.o.pattern=asdf {compiler.c.extra_flags} +recipe.cpp.o.pattern=asdf {compiler.cpp.extra_flags} +recipe.S.o.pattern=asdf {compiler.S.extra_flags} +recipe.ar.pattern=asdf {compiler.ar.extra_flags} +recipe.c.combine.pattern=asdf {compiler.c.elf.extra_flags} +recipe.preproc.macros=asdf {compiler.cpp.extra_flags} +recipe.objcopy.eep.pattern=asdf +recipe.objcopy.hex.pattern=asdf +recipe.output.tmp_file=asdf +recipe.output.save_file=asdf +recipe.size.pattern=asdf +recipe.size.regex=asdf +recipe.size.regex.data=asdf +pluggable_discovery.foo_discovery.bar=asdf +pluggable_discovery.bar_discovery.pattern=zxcv +pluggable_discovery.baz_discovery.qux=sdfg +tools.avrdude.upload.params.verbose=-v +tools.avrdude.upload.params.quiet=-q -q +tools.avrdude.upload.pattern=asdf +tools.bossac.upload.params.verbose=-v +tools.bossac.upload.params.quiet=-q -q +tools.bossac.upload.pattern=asdf +tools.avrdude.program.params.verbose=-v +tools.avrdude.program.params.quiet=-q -q +tools.avrdude.program.pattern=asdf +tools.bossac.program.params.verbose=-v +tools.bossac.program.params.quiet=-q -q +tools.bossac.program.pattern=asdf +tools.avrdude.erase.params.verbose=-v +tools.avrdude.erase.params.quiet=-q -q +tools.avrdude.erase.pattern=asdf +tools.bossac.erase.params.verbose=-v +tools.bossac.erase.params.quiet=-q -q +tools.bossac.erase.pattern=asdf +tools.avrdude.bootloader.params.verbose=-v +tools.avrdude.bootloader.params.quiet=-q -q +tools.avrdude.bootloader.pattern=asdf +tools.bossac.bootloader.params.verbose=-v +tools.bossac.bootloader.params.quiet=-q -q +tools.bossac.bootloader.pattern=asdf diff --git a/internal/rule/rulefunction/testdata/platforms/valid-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/valid-platform.txt/platform.txt index dec708ad..c672acdb 100644 --- a/internal/rule/rulefunction/testdata/platforms/valid-platform.txt/platform.txt +++ b/internal/rule/rulefunction/testdata/platforms/valid-platform.txt/platform.txt @@ -26,6 +26,8 @@ recipe.output.save_file=asdf recipe.size.pattern=asdf recipe.size.regex=asdf recipe.size.regex.data=asdf +pluggable_discovery.required.0=builtin:serial-discovery +pluggable_discovery.required.1=builtin:mdns-discovery tools.avrdude.upload.params.verbose=-v tools.avrdude.upload.params.quiet=-q -q tools.avrdude.upload.pattern=asdf diff --git a/internal/rule/rulefunction/testdata/platforms/valid-with-manual-installation-pluggable-discoveries-platform.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/valid-with-manual-installation-pluggable-discoveries-platform.txt/boards.txt new file mode 100644 index 00000000..1866d3c4 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/valid-with-manual-installation-pluggable-discoveries-platform.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/valid-with-manual-installation-pluggable-discoveries-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/valid-with-manual-installation-pluggable-discoveries-platform.txt/platform.txt new file mode 100644 index 00000000..fdc29f4c --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/valid-with-manual-installation-pluggable-discoveries-platform.txt/platform.txt @@ -0,0 +1,53 @@ +name=Arduino AVR Boards +version=1.8.3 +compiler.warning_flags.none=asdf +compiler.warning_flags.default=asdf +compiler.warning_flags.more=asdf +compiler.warning_flags.all=asdf +compiler.optimization_flags.debug= +compiler.optimization_flags.release= +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= +recipe.c.o.pattern=asdf {compiler.c.extra_flags} +recipe.cpp.o.pattern=asdf {compiler.cpp.extra_flags} +recipe.S.o.pattern=asdf {compiler.S.extra_flags} +recipe.ar.pattern=asdf {compiler.ar.extra_flags} +recipe.c.combine.pattern=asdf {compiler.c.elf.extra_flags} +recipe.preproc.macros=asdf {compiler.cpp.extra_flags} +recipe.objcopy.eep.pattern=asdf +recipe.objcopy.hex.pattern=asdf +recipe.output.tmp_file=asdf +recipe.output.save_file=asdf +recipe.size.pattern=asdf +recipe.size.regex=asdf +recipe.size.regex.data=asdf +pluggable_discovery.foo_discovery.pattern=asdf +tools.avrdude.upload.params.verbose=-v +tools.avrdude.upload.params.quiet=-q -q +tools.avrdude.upload.pattern=asdf +tools.bossac.upload.params.verbose=-v +tools.bossac.upload.params.quiet=-q -q +tools.bossac.upload.pattern=asdf +tools.avrdude.program.params.verbose=-v +tools.avrdude.program.params.quiet=-q -q +tools.avrdude.program.pattern=asdf +tools.bossac.program.params.verbose=-v +tools.bossac.program.params.quiet=-q -q +tools.bossac.program.pattern=asdf +tools.avrdude.erase.params.verbose=-v +tools.avrdude.erase.params.quiet=-q -q +tools.avrdude.erase.pattern=asdf +tools.bossac.erase.params.verbose=-v +tools.bossac.erase.params.quiet=-q -q +tools.bossac.erase.pattern=asdf +tools.avrdude.bootloader.params.verbose=-v +tools.avrdude.bootloader.params.quiet=-q -q +tools.avrdude.bootloader.pattern=asdf +tools.bossac.bootloader.params.verbose=-v +tools.bossac.bootloader.params.quiet=-q -q +tools.bossac.bootloader.pattern=asdf diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index 96acd7b8..7cfdec8d 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -4780,6 +4780,215 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ } } }, + "pluggableDiscovery": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscovery/base/object" + }, + { + "properties": { + "required": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/permissive/object" + } + }, + "additionalProperties": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/permissive/object" + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscovery/base/object" + }, + { + "properties": { + "required": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/specification/object" + } + }, + "additionalProperties": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/specification/object" + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscovery/base/object" + }, + { + "properties": { + "required": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/strict/object" + } + }, + "additionalProperties": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/strict/object" + } + } + ] + } + } + }, + "pluggableDiscoveryDiscoveryName": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/permissive/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/specification/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryDiscoveryName/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/strict/object" + } + ] + } + } + }, + "pluggableDiscoveryRequired": { + "base": { + "object": { + "allOf": [ + { + "type": "array" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/base/object" + }, + { + "items": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/permissive/object" + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/base/object" + }, + { + "items": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/specification/object" + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequired/base/object" + }, + { + "items": { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/strict/object" + } + } + ] + } + } + }, + "pluggableDiscoveryRequiredN": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "pattern": "^.+:.+$" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/pluggableDiscoveryRequiredN/base/object" + } + ] + } + } + }, "tools": { "base": { "object": { @@ -5225,6 +5434,44 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ } } }, + "pluggableDiscoveryDiscoveryName": { + "base": { + "object": { + "allOf": [ + { + "required": ["pattern"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/pluggableDiscoveryDiscoveryName/base/object" + } + ] + } + } + }, "toolsToolNameActionName": { "base": { "object": { @@ -5377,6 +5624,9 @@ var _arduinoPlatformTxtPermissiveSchemaJson = []byte(`{ "recipe.size.pattern": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/recipeSizePattern/permissive/object" }, + "pluggable_discovery": { + "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/pluggableDiscovery/permissive/object" + }, "tools": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/tools/permissive/object" } @@ -5463,6 +5713,9 @@ var _arduinoPlatformTxtSchemaJson = []byte(`{ "recipe.size.pattern": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/recipeSizePattern/specification/object" }, + "pluggable_discovery": { + "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/pluggableDiscovery/specification/object" + }, "tools": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/tools/specification/object" } @@ -5549,6 +5802,9 @@ var _arduinoPlatformTxtStrictSchemaJson = []byte(`{ "recipe.size.pattern": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/recipeSizePattern/strict/object" }, + "pluggable_discovery": { + "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/pluggableDiscovery/strict/object" + }, "tools": { "$ref": "arduino-platform-txt-definitions-schema.json#/definitions/propertiesObjects/tools/strict/object" } From d2749539aa2c4ee97db8d5a80b3bfcbdcf6b3569 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 30 Aug 2021 05:48:22 -0700 Subject: [PATCH 05/10] Add parent check capability to required boards.txt property helper function Due to the complexity introduced to the data format via the custom board options system, I was not able to find any way to define some of the boards.txt rules via the JSON schema. These were implemented in pure Go code. The requirement has emerged to check whether a required property exists either as a terminal property or as a parent property (e.g., `uno.upload.tool` or `uno.upload.tool.serial`). A helper function is already present with the logic for handling the check of whether the property exists either directly based on the board ID or as a custom board option property, but it didn't have the parent property support, which is added here. --- internal/rule/rulefunction/platform.go | 33 +++++++++++++++++++------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/internal/rule/rulefunction/platform.go b/internal/rule/rulefunction/platform.go index 2c79dd93..347fcafd 100644 --- a/internal/rule/rulefunction/platform.go +++ b/internal/rule/rulefunction/platform.go @@ -23,6 +23,7 @@ import ( "github.com/arduino/arduino-lint/internal/rule/ruleresult" "github.com/arduino/arduino-lint/internal/rule/schema" "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" + "github.com/arduino/go-properties-orderedmap" "github.com/sirupsen/logrus" ) @@ -104,7 +105,7 @@ func BoardsTxtBoardIDBuildBoardMissing() (result ruleresult.Type, output string) return ruleresult.Skip, "boards.txt has no boards" } - nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtBoardIds(), "build.board") + nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtBoardIds(), "build.board", false) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") @@ -142,7 +143,7 @@ func BoardsTxtBoardIDBuildCoreMissing() (result ruleresult.Type, output string) return ruleresult.Skip, "boards.txt has no visible boards" } - nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtVisibleBoardIds(), "build.core") + nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtVisibleBoardIds(), "build.core", false) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") @@ -285,7 +286,7 @@ func BoardsTxtBoardIDUploadToolMissing() (result ruleresult.Type, output string) return ruleresult.Skip, "boards.txt has no visible boards" } - nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtVisibleBoardIds(), "upload.tool") + nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtVisibleBoardIds(), "upload.tool", false) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") @@ -323,7 +324,7 @@ func BoardsTxtBoardIDUploadMaximumSizeMissing() (result ruleresult.Type, output return ruleresult.Skip, "boards.txt has no visible boards" } - nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtVisibleBoardIds(), "upload.maximum_size") + nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtVisibleBoardIds(), "upload.maximum_size", false) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") @@ -361,7 +362,7 @@ func BoardsTxtBoardIDUploadMaximumDataSizeMissing() (result ruleresult.Type, out return ruleresult.Skip, "boards.txt has no visible boards" } - nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtVisibleBoardIds(), "upload.maximum_data_size") + nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtVisibleBoardIds(), "upload.maximum_data_size", false) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") @@ -1691,7 +1692,7 @@ func PlatformTxtPluggableDiscoveryRequiredInvalid() (result ruleresult.Type, out return ruleresult.NotRun, "Couldn't load platform.txt" } - if !projectdata.PlatformTxt().ContainsKey("pluggable_discovery.required") && projectdata.PlatformTxt().SubTree("pluggable_discovery.required").Size() == 0 { + if !containsKeyOrParent(projectdata.PlatformTxt(), "pluggable_discovery.required") { return ruleresult.Skip, "Property not present" } @@ -1970,12 +1971,20 @@ Unlike iDMissingRequiredProperty(), this function does a direct check on the pro This is necessary because JSON schema does not have the capability to account for the custom board options system. This function should not be used in cases where the JSON schema does cover a required property. */ -func boardIDMissingRequiredProperty(boardIDs []string, propertyName string) []string { +func boardIDMissingRequiredProperty(boardIDs []string, propertyName string, parentOK bool) []string { + containsKey := func(key string) bool { + if parentOK { + return containsKeyOrParent(projectdata.BoardsTxt(), key) + } + + return projectdata.BoardsTxt().ContainsKey(key) + } + nonCompliantBoardIDs := []string{} for _, boardID := range boardIDs { logrus.Tracef("Board ID: %s", boardID) boardIDHasProperty := func(boardID string, propertyName string) bool { - if projectdata.BoardsTxt().ContainsKey(boardID + "." + propertyName) { + if containsKey(boardID + "." + propertyName) { logrus.Trace("Property defined at top level\n") return true // The board has a first level definition of the property. No need to check custom board options. @@ -1992,7 +2001,7 @@ func boardIDMissingRequiredProperty(boardIDs []string, propertyName string) []st boardOptionProperties := boardMenuProperties.SubTree(boardMenuID) boardOptionIDs := boardOptionProperties.FirstLevelKeys() for _, boardOptionID := range boardOptionIDs { - if !boardOptionProperties.ContainsKey(boardOptionID + "." + propertyName) { + if !containsKey(boardOptionID + "." + propertyName) { logrus.Tracef("Option ID %s doesn't provide property\n", boardOptionID) menuProvidesProperty = false // Every option associated with the menuID must define the property. break @@ -2067,6 +2076,12 @@ func toolNameMissingRequiredProperty(propertyNameQuery string, complianceLevel c return nonCompliantTools } +// containsKeyOrParent returns whether the given properties contain a key of the given name, or whether the given key is +// a first level of a key in the properties. +func containsKeyOrParent(propertiesMap *properties.Map, key string) bool { + return propertiesMap.ContainsKey(key) || propertiesMap.SubTree(key).Size() > 0 +} + // iDMissingRequiredProperty returns the list of first level keys missing the given required property. func iDMissingRequiredProperty(iDs []string, propertyNameQuery string, validationResult schema.ValidationResult) []string { nonCompliantIDs := []string{} From f5274ecf08195e0918950be6879e21e177653264 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 30 Aug 2021 06:50:05 -0700 Subject: [PATCH 06/10] Add support for `upload.tool.` This boards.txt property is the replacement for `upload.tool`. `upload.tool` will still be supported, and there are no additional requirements on the new property style. So it is only necessary to make the schema and rules apply to either. --- ...arduino-boards-txt-definitions-schema.json | 36 +++++++++---------- .../boardstxt/boardstxtschema_test.go | 8 +++++ .../ruleconfiguration/ruleconfiguration.go | 12 +++---- internal/rule/rulefunction/platform.go | 4 +-- .../boards.txt | 4 +-- .../platforms/valid-boards.txt/boards.txt | 2 +- internal/rule/schema/schemadata/bindata.go | 36 +++++++++---------- 7 files changed, 55 insertions(+), 47 deletions(-) diff --git a/etc/schemas/arduino-boards-txt-definitions-schema.json b/etc/schemas/arduino-boards-txt-definitions-schema.json index 0468b254..9f746fa0 100644 --- a/etc/schemas/arduino-boards-txt-definitions-schema.json +++ b/etc/schemas/arduino-boards-txt-definitions-schema.json @@ -155,9 +155,6 @@ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/permissive/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/permissive/object" }, @@ -168,6 +165,9 @@ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/permissive/object" } @@ -220,9 +220,6 @@ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/specification/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/specification/object" }, @@ -233,6 +230,9 @@ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/specification/object" } @@ -285,9 +285,6 @@ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/strict/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/strict/object" }, @@ -298,6 +295,9 @@ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/strict/object" } @@ -634,9 +634,6 @@ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/permissive/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/permissive/object" }, @@ -647,6 +644,9 @@ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/permissive/object" } @@ -687,9 +687,6 @@ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/specification/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/specification/object" }, @@ -700,6 +697,9 @@ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/specification/object" } @@ -740,9 +740,6 @@ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/strict/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/strict/object" }, @@ -753,6 +750,9 @@ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/strict/object" } diff --git a/internal/project/platform/boardstxt/boardstxtschema_test.go b/internal/project/platform/boardstxt/boardstxtschema_test.go index c4e09df5..93c9709c 100644 --- a/internal/project/platform/boardstxt/boardstxtschema_test.go +++ b/internal/project/platform/boardstxt/boardstxtschema_test.go @@ -88,9 +88,17 @@ func TestMinLength(t *testing.T) { {"foo.upload.tool", "foo/upload\\.tool", 1, compliancelevel.Specification}, {"foo.upload.tool", "foo/upload\\.tool", 1, compliancelevel.Strict}, + {"foo.upload.tool.serial", "foo/upload\\.tool.serial", 1, compliancelevel.Permissive}, + {"foo.upload.tool.serial", "foo/upload\\.tool.serial", 1, compliancelevel.Specification}, + {"foo.upload.tool.serial", "foo/upload\\.tool.serial", 1, compliancelevel.Strict}, + {"foo.menu.bar.baz.upload.tool", "foo/menu/bar/baz/upload\\.tool", 1, compliancelevel.Permissive}, {"foo.menu.bar.baz.upload.tool", "foo/menu/bar/baz/upload\\.tool", 1, compliancelevel.Specification}, {"foo.menu.bar.baz.upload.tool", "foo/menu/bar/baz/upload\\.tool", 1, compliancelevel.Strict}, + + {"foo.menu.bar.baz.upload.tool.serial", "foo/menu/bar/baz/upload\\.tool.serial", 1, compliancelevel.Permissive}, + {"foo.menu.bar.baz.upload.tool.serial", "foo/menu/bar/baz/upload\\.tool.serial", 1, compliancelevel.Specification}, + {"foo.menu.bar.baz.upload.tool.serial", "foo/menu/bar/baz/upload\\.tool.serial", 1, compliancelevel.Strict}, } // Test schema validation results with value length < minimum. diff --git a/internal/rule/ruleconfiguration/ruleconfiguration.go b/internal/rule/ruleconfiguration/ruleconfiguration.go index fa21761d..e40988c5 100644 --- a/internal/rule/ruleconfiguration/ruleconfiguration.go +++ b/internal/rule/ruleconfiguration/ruleconfiguration.go @@ -1781,9 +1781,9 @@ var configurations = []Type{ Category: "configuration files", Subcategory: "boards.txt", ID: "PF016", - Brief: "missing upload.tool", - Description: "A board definition in the platform's `boards.txt` configuration file is missing the `upload.tool` property.", - MessageTemplate: "Missing upload.tool property for board ID(s) {{.}}", + Brief: "missing upload.tool.", + Description: "A board definition in the platform's `boards.txt` configuration file is missing the `upload.tool.` property.", + MessageTemplate: "Missing upload.tool. property for board ID(s) {{.}}", Reference: "https://arduino.github.io/arduino-cli/latest/platform-specification/#sketch-upload-configuration", DisableModes: nil, EnableModes: []rulemode.Type{rulemode.Default}, @@ -1798,9 +1798,9 @@ var configurations = []Type{ Category: "configuration files", Subcategory: "boards.txt", ID: "PF017", - Brief: "upload.tool < min length", - Description: "The `upload.tool` property for a board definition in the platform's `boards.txt` configuration file is shorter than the minimum length.", - MessageTemplate: "upload.tool value for board ID(s) {{.}} is less than the minimum length.", + Brief: "upload.tool. < min length", + Description: "The `upload.tool.` property for a board definition in the platform's `boards.txt` configuration file is shorter than the minimum length.", + MessageTemplate: "upload.tool. value for board ID(s) {{.}} is less than the minimum length.", Reference: "https://arduino.github.io/arduino-cli/latest/platform-specification/#sketch-upload-configuration", DisableModes: nil, EnableModes: []rulemode.Type{rulemode.Default}, diff --git a/internal/rule/rulefunction/platform.go b/internal/rule/rulefunction/platform.go index 347fcafd..fb5c21ea 100644 --- a/internal/rule/rulefunction/platform.go +++ b/internal/rule/rulefunction/platform.go @@ -286,7 +286,7 @@ func BoardsTxtBoardIDUploadToolMissing() (result ruleresult.Type, output string) return ruleresult.Skip, "boards.txt has no visible boards" } - nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtVisibleBoardIds(), "upload.tool", false) + nonCompliantBoardIDs := boardIDMissingRequiredProperty(projectdata.BoardsTxtVisibleBoardIds(), "upload.tool", true) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") @@ -305,7 +305,7 @@ func BoardsTxtBoardIDUploadToolLTMinLength() (result ruleresult.Type, output str return ruleresult.Skip, "boards.txt has no boards" } - nonCompliantBoardIDs := boardIDValueLTMinLength(projectdata.BoardsTxtBoardIds(), "upload\\.tool", compliancelevel.Specification) + nonCompliantBoardIDs := boardIDValueLTMinLength(projectdata.BoardsTxtBoardIds(), "upload\\.tool(\\..+)?", compliancelevel.Specification) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-LT-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-LT-boards.txt/boards.txt index 098c8075..2cf60e7c 100644 --- a/internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-LT-boards.txt/boards.txt +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-tool-LT-boards.txt/boards.txt @@ -2,7 +2,7 @@ buno.name=Buno buno.build.board=BUNO buno.build.core=arduino buno.build.variant=standard -buno.upload.tool= +buno.upload.tool.serial= buno.upload.maximum_size=32256 buno.upload.maximum_data_size=2048 @@ -18,7 +18,7 @@ funo.name=Funo funo.build.board=FUNO funo.build.core=arduino funo.build.variant=standard -funo.upload.tool= +funo.menu.foo.bar.upload.tool.serial= funo.upload.maximum_size=32256 funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/valid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/valid-boards.txt/boards.txt index 1866d3c4..8f48a305 100644 --- a/internal/rule/rulefunction/testdata/platforms/valid-boards.txt/boards.txt +++ b/internal/rule/rulefunction/testdata/platforms/valid-boards.txt/boards.txt @@ -2,9 +2,9 @@ buno.name=Buno buno.build.board=BUNO buno.build.core=arduino buno.build.variant=standard -buno.upload.tool=avrdude buno.upload.maximum_size=32256 buno.upload.maximum_data_size=2048 +buno.upload.tool.serial=avrdude uno.name=Arduino Uno uno.build.board=UNO diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index 7cfdec8d..d3f18ea7 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -230,9 +230,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/permissive/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/permissive/object" }, @@ -243,6 +240,9 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/permissive/object" } @@ -295,9 +295,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/specification/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/specification/object" }, @@ -308,6 +305,9 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/specification/object" } @@ -360,9 +360,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/strict/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/strict/object" }, @@ -373,6 +370,9 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/strict/object" } @@ -709,9 +709,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/permissive/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/permissive/object" }, @@ -722,6 +719,9 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/permissive/object" } @@ -762,9 +762,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/specification/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/specification/object" }, @@ -775,6 +772,9 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/specification/object" } @@ -815,9 +815,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/strict/object" }, - "upload.tool": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" - }, "upload.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/strict/object" }, @@ -828,6 +825,9 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, { "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" + }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/strict/object" } From 7c18e57d5249198d095b86441e17fa9c658d2879 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 31 Aug 2021 03:36:07 -0700 Subject: [PATCH 07/10] Move the "booleanString" schema to the general definitions file This is an enum that contains the supported string representations of Boolean values. It was previously used only by the boards.txt schema, and so was hosted in its file, but now there is a need for it in the platform.txt specification as well, so it goes in the file that holds the shared definitions. --- .../arduino-boards-txt-definitions-schema.json | 13 ++++--------- etc/schemas/general-definitions-schema.json | 5 +++++ internal/rule/schema/schemadata/bindata.go | 18 +++++++++--------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/etc/schemas/arduino-boards-txt-definitions-schema.json b/etc/schemas/arduino-boards-txt-definitions-schema.json index 9f746fa0..76d197cc 100644 --- a/etc/schemas/arduino-boards-txt-definitions-schema.json +++ b/etc/schemas/arduino-boards-txt-definitions-schema.json @@ -773,7 +773,7 @@ "type": "string" }, { - "$ref": "#/definitions/enumObjects/booleanString" + "$ref": "general-definitions-schema.json#/definitions/enumObjects/booleanString" } ] } @@ -814,7 +814,7 @@ "type": "string" }, { - "$ref": "#/definitions/enumObjects/booleanString" + "$ref": "general-definitions-schema.json#/definitions/enumObjects/booleanString" } ] } @@ -1016,7 +1016,7 @@ "type": "string" }, { - "$ref": "#/definitions/enumObjects/booleanString" + "$ref": "general-definitions-schema.json#/definitions/enumObjects/booleanString" } ] } @@ -1057,7 +1057,7 @@ "type": "string" }, { - "$ref": "#/definitions/enumObjects/booleanString" + "$ref": "general-definitions-schema.json#/definitions/enumObjects/booleanString" } ] } @@ -1215,11 +1215,6 @@ } } } - }, - "enumObjects": { - "booleanString": { - "enum": ["true", "false"] - } } } } diff --git a/etc/schemas/general-definitions-schema.json b/etc/schemas/general-definitions-schema.json index 5ecff6d9..7e0ab5c2 100644 --- a/etc/schemas/general-definitions-schema.json +++ b/etc/schemas/general-definitions-schema.json @@ -23,6 +23,11 @@ "pattern": "^[aA][rR][dD][uU][iI][nN][oO].*$" } } + }, + "enumObjects": { + "booleanString": { + "enum": ["true", "false"] + } } } } diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index d3f18ea7..11898e0c 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -848,7 +848,7 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "type": "string" }, { - "$ref": "#/definitions/enumObjects/booleanString" + "$ref": "general-definitions-schema.json#/definitions/enumObjects/booleanString" } ] } @@ -889,7 +889,7 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "type": "string" }, { - "$ref": "#/definitions/enumObjects/booleanString" + "$ref": "general-definitions-schema.json#/definitions/enumObjects/booleanString" } ] } @@ -1091,7 +1091,7 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "type": "string" }, { - "$ref": "#/definitions/enumObjects/booleanString" + "$ref": "general-definitions-schema.json#/definitions/enumObjects/booleanString" } ] } @@ -1132,7 +1132,7 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "type": "string" }, { - "$ref": "#/definitions/enumObjects/booleanString" + "$ref": "general-definitions-schema.json#/definitions/enumObjects/booleanString" } ] } @@ -1290,11 +1290,6 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ } } } - }, - "enumObjects": { - "booleanString": { - "enum": ["true", "false"] - } } } } @@ -6175,6 +6170,11 @@ var _generalDefinitionsSchemaJson = []byte(`{ "pattern": "^[aA][rR][dD][uU][iI][nN][oO].*$" } } + }, + "enumObjects": { + "booleanString": { + "enum": ["true", "false"] + } } } } From 1bdac6cb25203bab96336019f4d334919f031069 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 31 Aug 2021 13:45:48 -0700 Subject: [PATCH 08/10] Increase mapping recursion depth of platform.txt tools.* properties The Arduino project configuration files make an unusual usage of the "properties" data format in that sometimes a `.` in a key name indicates a nested data level, while other times it is only a character. There are cases where a completely programmatic recursion of the properties into a fully nested structure would result in the impossibility of some keys having both a string and a map type, which is not supported by the Go map type that holds the recursed data, nor the JSON data format it is validated against. For this reason, it's necessary to manually configure the recursion of key levels on a case-by-case basis and the approach is taken of recursing only when necessary. Previously, it was possible to treat the params.verbose/quiet component of the `tools.TOOL_NAME.ACTION_NAME.params.verbose/quiet` properties as being just key names. However, the addition of the `tools.TOOL_NAME.upload.field.FIELD_NAME` and `tools.TOOL_NAME.upload.field.FIELD_NAME.secret` properties requires one more level of recursion in order to deal with the fact that the FIELD_NAME component is arbitrary. This resulted in splitting the params.verbose/quiet property, which required adjustments to their schema and rules. --- ...duino-platform-txt-definitions-schema.json | 150 +++++++++++++++++- .../platform/platformtxt/platformtxt.go | 2 +- .../platformtxt/platformtxtschema_test.go | 12 +- internal/rule/rulefunction/platform.go | 16 +- internal/rule/schema/schemadata/bindata.go | 150 +++++++++++++++++- 5 files changed, 313 insertions(+), 17 deletions(-) diff --git a/etc/schemas/arduino-platform-txt-definitions-schema.json b/etc/schemas/arduino-platform-txt-definitions-schema.json index 50140cc8..51e86c9d 100644 --- a/etc/schemas/arduino-platform-txt-definitions-schema.json +++ b/etc/schemas/arduino-platform-txt-definitions-schema.json @@ -1120,6 +1120,13 @@ { "$ref": "#/definitions/propertiesObjects/toolsToolNameProgram/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/permissive/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/permissive/object" } @@ -1132,6 +1139,13 @@ { "$ref": "#/definitions/propertiesObjects/toolsToolNameProgram/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/specification/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/specification/object" } @@ -1144,6 +1158,13 @@ { "$ref": "#/definitions/propertiesObjects/toolsToolNameProgram/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/strict/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/strict/object" } @@ -1167,6 +1188,13 @@ { "$ref": "#/definitions/propertiesObjects/toolsToolNameErase/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/permissive/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/permissive/object" } @@ -1179,6 +1207,13 @@ { "$ref": "#/definitions/propertiesObjects/toolsToolNameErase/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/specification/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/specification/object" } @@ -1191,6 +1226,13 @@ { "$ref": "#/definitions/propertiesObjects/toolsToolNameErase/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/strict/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/strict/object" } @@ -1214,6 +1256,13 @@ { "$ref": "#/definitions/propertiesObjects/toolsToolNameBootloader/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/permissive/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/permissive/object" } @@ -1226,6 +1275,13 @@ { "$ref": "#/definitions/propertiesObjects/toolsToolNameBootloader/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/specification/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/specification/object" } @@ -1238,12 +1294,66 @@ { "$ref": "#/definitions/propertiesObjects/toolsToolNameBootloader/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/strict/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/strict/object" } ] } } + }, + "toolsToolNameActionNameParams": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/permissive/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/specification/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/strict/object" + } + ] + } + } } }, "dependenciesObjects": { @@ -1397,7 +1507,7 @@ "object": { "allOf": [ { - "required": ["params.verbose", "params.quiet", "pattern"] + "required": ["params", "pattern"] } ] } @@ -1430,6 +1540,44 @@ } } }, + "toolsToolNameActionNameParams": { + "base": { + "object": { + "allOf": [ + { + "required": ["verbose", "quiet"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/base/object" + } + ] + } + } + }, "toolsToolNameUpload": { "base": { "object": { diff --git a/internal/project/platform/platformtxt/platformtxt.go b/internal/project/platform/platformtxt/platformtxt.go index fd3b6976..13cb7fcd 100644 --- a/internal/project/platform/platformtxt/platformtxt.go +++ b/internal/project/platform/platformtxt/platformtxt.go @@ -71,7 +71,7 @@ func Validate(platformTxt *properties.Map) map[compliancelevel.Type]schema.Valid platformTxtInterface["pluggable_discovery"] = general.PropertiesToMap(platformTxt.SubTree("pluggable_discovery"), 2) } } else if strings.HasPrefix(key, "tools.") { - platformTxtInterface["tools"] = general.PropertiesToMap(platformTxt.SubTree("tools"), 3) + platformTxtInterface["tools"] = general.PropertiesToMap(platformTxt.SubTree("tools"), 4) } else { platformTxtInterface[key] = platformTxt.Get(key) } diff --git a/internal/project/platform/platformtxt/platformtxtschema_test.go b/internal/project/platform/platformtxt/platformtxtschema_test.go index 513afa57..2b9b3e49 100644 --- a/internal/project/platform/platformtxt/platformtxtschema_test.go +++ b/internal/project/platform/platformtxt/platformtxtschema_test.go @@ -204,13 +204,13 @@ func TestRequired(t *testing.T) { {"tools.avrdude.upload.pattern", "", "tools/avrdude/upload/pattern", compliancelevel.Specification, assert.True}, {"tools.avrdude.upload.pattern", "", "tools/avrdude/upload/pattern", compliancelevel.Strict, assert.True}, - {"tools.avrdude.program.params.verbose", "", "tools/avrdude/program/params\\.verbose", compliancelevel.Permissive, assert.True}, - {"tools.avrdude.program.params.verbose", "", "tools/avrdude/program/params\\.verbose", compliancelevel.Specification, assert.True}, - {"tools.avrdude.program.params.verbose", "", "tools/avrdude/program/params\\.verbose", compliancelevel.Strict, assert.True}, + {"tools.avrdude.program.params.verbose", "", "tools/avrdude/program/params/verbose", compliancelevel.Permissive, assert.True}, + {"tools.avrdude.program.params.verbose", "", "tools/avrdude/program/params/verbose", compliancelevel.Specification, assert.True}, + {"tools.avrdude.program.params.verbose", "", "tools/avrdude/program/params/verbose", compliancelevel.Strict, assert.True}, - {"tools.avrdude.program.params.quiet", "", "tools/avrdude/program/params\\.quiet", compliancelevel.Permissive, assert.True}, - {"tools.avrdude.program.params.quiet", "", "tools/avrdude/program/params\\.quiet", compliancelevel.Specification, assert.True}, - {"tools.avrdude.program.params.quiet", "", "tools/avrdude/program/params\\.quiet", compliancelevel.Strict, assert.True}, + {"tools.avrdude.program.params.quiet", "", "tools/avrdude/program/params/quiet", compliancelevel.Permissive, assert.True}, + {"tools.avrdude.program.params.quiet", "", "tools/avrdude/program/params/quiet", compliancelevel.Specification, assert.True}, + {"tools.avrdude.program.params.quiet", "", "tools/avrdude/program/params/quiet", compliancelevel.Strict, assert.True}, {"tools.avrdude.program.pattern", "", "tools/avrdude/program/pattern", compliancelevel.Permissive, assert.True}, {"tools.avrdude.program.pattern", "", "tools/avrdude/program/pattern", compliancelevel.Specification, assert.True}, diff --git a/internal/rule/rulefunction/platform.go b/internal/rule/rulefunction/platform.go index fb5c21ea..adc7c1e5 100644 --- a/internal/rule/rulefunction/platform.go +++ b/internal/rule/rulefunction/platform.go @@ -1650,7 +1650,7 @@ func PlatformTxtUploadParamsVerboseMissing() (result ruleresult.Type, output str return ruleresult.Skip, "platform.txt has no tools" } - nonCompliantTools := toolNameMissingRequiredProperty("upload/params\\.verbose", compliancelevel.Specification) + nonCompliantTools := toolNameMissingRequiredProperty("upload/params/verbose", compliancelevel.Specification) if len(nonCompliantTools) > 0 { return ruleresult.Fail, strings.Join(nonCompliantTools, ", ") @@ -1673,7 +1673,7 @@ func PlatformTxtUploadParamsQuietMissing() (result ruleresult.Type, output strin return ruleresult.Skip, "platform.txt has no tools" } - nonCompliantTools := toolNameMissingRequiredProperty("upload/params\\.quiet", compliancelevel.Specification) + nonCompliantTools := toolNameMissingRequiredProperty("upload/params/quiet", compliancelevel.Specification) if len(nonCompliantTools) > 0 { return ruleresult.Fail, strings.Join(nonCompliantTools, ", ") @@ -1772,7 +1772,7 @@ func PlatformTxtProgramParamsVerboseMissing() (result ruleresult.Type, output st return ruleresult.Skip, "platform.txt has no tools" } - nonCompliantTools := toolNameMissingRequiredProperty("program/params\\.verbose", compliancelevel.Specification) + nonCompliantTools := toolNameMissingRequiredProperty("program/params/verbose", compliancelevel.Specification) if len(nonCompliantTools) > 0 { return ruleresult.Fail, strings.Join(nonCompliantTools, ", ") @@ -1795,7 +1795,7 @@ func PlatformTxtProgramParamsQuietMissing() (result ruleresult.Type, output stri return ruleresult.Skip, "platform.txt has no tools" } - nonCompliantTools := toolNameMissingRequiredProperty("program/params\\.quiet", compliancelevel.Specification) + nonCompliantTools := toolNameMissingRequiredProperty("program/params/quiet", compliancelevel.Specification) if len(nonCompliantTools) > 0 { return ruleresult.Fail, strings.Join(nonCompliantTools, ", ") @@ -1841,7 +1841,7 @@ func PlatformTxtEraseParamsVerboseMissing() (result ruleresult.Type, output stri return ruleresult.Skip, "platform.txt has no tools" } - nonCompliantTools := toolNameMissingRequiredProperty("erase/params\\.verbose", compliancelevel.Specification) + nonCompliantTools := toolNameMissingRequiredProperty("erase/params/verbose", compliancelevel.Specification) if len(nonCompliantTools) > 0 { return ruleresult.Fail, strings.Join(nonCompliantTools, ", ") @@ -1864,7 +1864,7 @@ func PlatformTxtEraseParamsQuietMissing() (result ruleresult.Type, output string return ruleresult.Skip, "platform.txt has no tools" } - nonCompliantTools := toolNameMissingRequiredProperty("erase/params\\.quiet", compliancelevel.Specification) + nonCompliantTools := toolNameMissingRequiredProperty("erase/params/quiet", compliancelevel.Specification) if len(nonCompliantTools) > 0 { return ruleresult.Fail, strings.Join(nonCompliantTools, ", ") @@ -1910,7 +1910,7 @@ func PlatformTxtBootloaderParamsVerboseMissing() (result ruleresult.Type, output return ruleresult.Skip, "platform.txt has no tools" } - nonCompliantTools := toolNameMissingRequiredProperty("bootloader/params\\.verbose", compliancelevel.Specification) + nonCompliantTools := toolNameMissingRequiredProperty("bootloader/params/verbose", compliancelevel.Specification) if len(nonCompliantTools) > 0 { return ruleresult.Fail, strings.Join(nonCompliantTools, ", ") @@ -1933,7 +1933,7 @@ func PlatformTxtBootloaderParamsQuietMissing() (result ruleresult.Type, output s return ruleresult.Skip, "platform.txt has no tools" } - nonCompliantTools := toolNameMissingRequiredProperty("bootloader/params\\.quiet", compliancelevel.Specification) + nonCompliantTools := toolNameMissingRequiredProperty("bootloader/params/quiet", compliancelevel.Specification) if len(nonCompliantTools) > 0 { return ruleresult.Fail, strings.Join(nonCompliantTools, ", ") diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index 11898e0c..85b12cf7 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -5195,6 +5195,13 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ { "$ref": "#/definitions/propertiesObjects/toolsToolNameProgram/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/permissive/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/permissive/object" } @@ -5207,6 +5214,13 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ { "$ref": "#/definitions/propertiesObjects/toolsToolNameProgram/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/specification/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/specification/object" } @@ -5219,6 +5233,13 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ { "$ref": "#/definitions/propertiesObjects/toolsToolNameProgram/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/strict/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/strict/object" } @@ -5242,6 +5263,13 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ { "$ref": "#/definitions/propertiesObjects/toolsToolNameErase/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/permissive/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/permissive/object" } @@ -5254,6 +5282,13 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ { "$ref": "#/definitions/propertiesObjects/toolsToolNameErase/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/specification/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/specification/object" } @@ -5266,6 +5301,13 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ { "$ref": "#/definitions/propertiesObjects/toolsToolNameErase/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/strict/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/strict/object" } @@ -5289,6 +5331,13 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ { "$ref": "#/definitions/propertiesObjects/toolsToolNameBootloader/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/permissive/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/permissive/object" } @@ -5301,6 +5350,13 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ { "$ref": "#/definitions/propertiesObjects/toolsToolNameBootloader/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/specification/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/specification/object" } @@ -5313,12 +5369,66 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ { "$ref": "#/definitions/propertiesObjects/toolsToolNameBootloader/base/object" }, + { + "properties": { + "params": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/strict/object" + } + } + }, { "$ref": "#/definitions/requiredObjects/toolsToolNameActionName/strict/object" } ] } } + }, + "toolsToolNameActionNameParams": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/permissive/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/specification/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameActionNameParams/base/object" + }, + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/strict/object" + } + ] + } + } } }, "dependenciesObjects": { @@ -5472,7 +5582,7 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ "object": { "allOf": [ { - "required": ["params.verbose", "params.quiet", "pattern"] + "required": ["params", "pattern"] } ] } @@ -5505,6 +5615,44 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ } } }, + "toolsToolNameActionNameParams": { + "base": { + "object": { + "allOf": [ + { + "required": ["verbose", "quiet"] + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/requiredObjects/toolsToolNameActionNameParams/base/object" + } + ] + } + } + }, "toolsToolNameUpload": { "base": { "object": { From c7182c1970f1e448d42d46baa8e4d4b86abcba84 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 31 Aug 2021 19:31:26 -0700 Subject: [PATCH 09/10] Add rules for user defined fields of Arduino platforms Some upload recipes might require custom fields that must be provided by the user. The platform author can define such fields via two new property types that have been added to the Arduino platform system with the expanded pluggable discovery support. There are some requirements for these fields, which are enforced via a couple of new rules. --- ...duino-platform-txt-definitions-schema.json | 171 ++++++++++++++++++ .../platform/platformtxt/platformtxt.go | 15 ++ .../platform/platformtxt/platformtxt_test.go | 22 +++ .../platformtxt/platformtxtschema_test.go | 44 +++++ internal/project/projectdata/platform.go | 9 + internal/project/projectdata/platform_test.go | 15 +- .../platforms/valid-platform.txt/platform.txt | 2 + .../ruleconfiguration/ruleconfiguration.go | 34 ++++ internal/rule/rulefunction/platform.go | 68 +++++++ internal/rule/rulefunction/platform_test.go | 25 +++ .../boards.txt | 23 +++ .../platform.txt | 54 ++++++ .../boards.txt | 23 +++ .../platform.txt | 56 ++++++ .../boards.txt | 23 +++ .../platform.txt | 61 +++++++ .../boards.txt | 23 +++ .../platform.txt | 64 +++++++ .../platforms/valid-platform.txt/platform.txt | 3 + internal/rule/schema/schemadata/bindata.go | 171 ++++++++++++++++++ 20 files changed, 900 insertions(+), 6 deletions(-) create mode 100644 internal/rule/rulefunction/testdata/platforms/no-user-defined-field-platform.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/no-user-defined-field-platform.txt/platform.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/no-user-defined-field-secret-platform.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/no-user-defined-field-secret-platform.txt/platform.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/user-defined-field-GT-platform.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/user-defined-field-GT-platform.txt/platform.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/user-defined-field-secret-invalid-platform.txt/boards.txt create mode 100644 internal/rule/rulefunction/testdata/platforms/user-defined-field-secret-invalid-platform.txt/platform.txt diff --git a/etc/schemas/arduino-platform-txt-definitions-schema.json b/etc/schemas/arduino-platform-txt-definitions-schema.json index 51e86c9d..57ce303e 100644 --- a/etc/schemas/arduino-platform-txt-definitions-schema.json +++ b/etc/schemas/arduino-platform-txt-definitions-schema.json @@ -1075,6 +1075,13 @@ }, { "$ref": "#/definitions/requiredObjects/toolsToolNameUpload/permissive/object" + }, + { + "properties": { + "field": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/permissive/object" + } + } } ] } @@ -1087,6 +1094,13 @@ }, { "$ref": "#/definitions/requiredObjects/toolsToolNameUpload/specification/object" + }, + { + "properties": { + "field": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/specification/object" + } + } } ] } @@ -1099,6 +1113,163 @@ }, { "$ref": "#/definitions/requiredObjects/toolsToolNameUpload/strict/object" + }, + { + "properties": { + "field": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/strict/object" + } + } + } + ] + } + } + }, + "toolsToolNameUploadField": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/base/object" + }, + { + "patternProperties": { + "^.+([^.].*|\\.([^s].*)?|\\.se([^c].*)?|\\.sec([^r].*)?|\\.secr([^e].*)?|\\.secre([^t].*)?|\\.secret.+)$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/permissive/object" + }, + "^.+\\.secret$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/permissive/object" + } + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/base/object" + }, + { + "patternProperties": { + "^.+([^.].*|\\.([^s].*)?|\\.se([^c].*)?|\\.sec([^r].*)?|\\.secr([^e].*)?|\\.secre([^t].*)?|\\.secret.+)$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/specification/object" + }, + "^.+\\.secret$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/specification/object" + } + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/base/object" + }, + { + "patternProperties": { + "^.+([^.].*|\\.([^s].*)?|\\.se([^c].*)?|\\.sec([^r].*)?|\\.secr([^e].*)?|\\.secre([^t].*)?|\\.secret.+)$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/strict/object" + }, + "^.+\\.secret$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/strict/object" + } + } + } + ] + } + } + }, + "toolsToolNameUploadFieldFieldName": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/base/object" + }, + { + "maxLength": 50 + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/specification/object" + } + ] + } + } + }, + "toolsToolNameUploadFieldFieldNameSecret": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "general-definitions-schema.json#/definitions/enumObjects/booleanString" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/base/object" } ] } diff --git a/internal/project/platform/platformtxt/platformtxt.go b/internal/project/platform/platformtxt/platformtxt.go index 13cb7fcd..aa5f4541 100644 --- a/internal/project/platform/platformtxt/platformtxt.go +++ b/internal/project/platform/platformtxt/platformtxt.go @@ -20,6 +20,7 @@ See: https://arduino.github.io/arduino-cli/latest/platform-specification/#platfo package platformtxt import ( + "fmt" "strings" "github.com/arduino/arduino-lint/internal/project/general" @@ -96,6 +97,20 @@ func PluggableDiscoveryNames(platformTxt *properties.Map) []string { return names } +// UserProvidedFieldNames returns the list of user provided field names platform.txt properties, mapped by tool name. +func UserProvidedFieldNames(platformTxt *properties.Map) map[string][]string { + fieldNames := make(map[string][]string) + toolsProps := platformTxt.SubTree("tools") + for _, tool := range toolsProps.FirstLevelKeys() { + fieldProps := toolsProps.SubTree(fmt.Sprintf("%s.upload.field", tool)) + for _, fieldName := range fieldProps.FirstLevelKeys() { + fieldNames[tool] = append(fieldNames[tool], fieldName) + } + } + + return fieldNames +} + // ToolNames returns the list of tool names from the given platform.txt properties. func ToolNames(platformTxt *properties.Map) []string { return platformTxt.SubTree("tools").FirstLevelKeys() diff --git a/internal/project/platform/platformtxt/platformtxt_test.go b/internal/project/platform/platformtxt/platformtxt_test.go index 221d1a6e..7a108ff5 100644 --- a/internal/project/platform/platformtxt/platformtxt_test.go +++ b/internal/project/platform/platformtxt/platformtxt_test.go @@ -16,6 +16,7 @@ package platformtxt import ( + "reflect" "testing" "github.com/arduino/arduino-lint/internal/rule/schema/compliancelevel" @@ -102,6 +103,27 @@ func TestPluggableDiscoveryNames(t *testing.T) { assert.ElementsMatch(t, []string{"foo_discovery", "bar_discovery"}, PluggableDiscoveryNames(platformTxt), "pluggable_discovery.DISCOVERY_ID properties add elements for each DISCOVERY_ID.") } +func TestUserProvidedFieldNames(t *testing.T) { + platformTxt := properties.NewFromHashmap(validPlatformTxtMap) + + assertion := make(map[string][]string) + assert.True(t, reflect.DeepEqual(assertion, UserProvidedFieldNames(platformTxt))) + + platformTxt.Set("tools.avrdude.upload.field.foo_field_name", "Some field label") + assertion = map[string][]string{ + "avrdude": {"foo_field_name"}, + } + assert.True(t, reflect.DeepEqual(assertion, UserProvidedFieldNames(platformTxt))) + + platformTxt.Set("tools.avrdude.upload.field.bar_field_name", "Some field label") + platformTxt.Set("tools.bossac.upload.field.baz_field_name", "Some field label") + assertion = map[string][]string{ + "avrdude": {"foo_field_name", "bar_field_name"}, + "bossac": {"baz_field_name"}, + } + assert.True(t, reflect.DeepEqual(assertion, UserProvidedFieldNames(platformTxt))) +} + func TestToolNames(t *testing.T) { platformTxt := properties.NewFromHashmap(validPlatformTxtMap) diff --git a/internal/project/platform/platformtxt/platformtxtschema_test.go b/internal/project/platform/platformtxt/platformtxtschema_test.go index 2b9b3e49..c687949c 100644 --- a/internal/project/platform/platformtxt/platformtxtschema_test.go +++ b/internal/project/platform/platformtxt/platformtxtschema_test.go @@ -57,6 +57,8 @@ var validPlatformTxtRaw = []byte(` recipe.size.regex.data=asdf tools.avrdude.upload.params.verbose=-v tools.avrdude.upload.params.quiet=-q -q + tools.avrdude.upload.field.foo_field_name=Some field label + tools.avrdude.upload.field.foo_field_name.secret=true tools.avrdude.upload.pattern=asdf tools.avrdude.program.params.verbose=-v tools.avrdude.program.params.quiet=-q -q @@ -144,6 +146,38 @@ func TestMinLength(t *testing.T) { } } +func TestMaxLength(t *testing.T) { + testTables := []struct { + propertyName string + validationErrorPropertyName string + maxLength int + complianceLevel compliancelevel.Type + }{ + {"tools.avrdude.upload.field.foo_field_name", "tools/avrdude/upload/field/foo_field_name", 50, compliancelevel.Specification}, + {"tools.avrdude.upload.field.foo_field_name", "tools/avrdude/upload/field/foo_field_name", 50, compliancelevel.Strict}, + } + + // Test schema validation results with value length > maximum. + for _, testTable := range testTables { + platformTxt, err := properties.LoadFromBytes(validPlatformTxtRaw) + require.Nil(t, err) + platformTxt.Set(testTable.propertyName, strings.Repeat("a", testTable.maxLength+1)) + + t.Run(fmt.Sprintf("%s greater than maximum length of %d (%s)", testTable.propertyName, testTable.maxLength, testTable.complianceLevel), func(t *testing.T) { + assert.True(t, schema.PropertyGreaterThanMaxLength(testTable.validationErrorPropertyName, platformtxt.Validate(platformTxt)[testTable.complianceLevel])) + }) + + // Test schema validation results with maximum value length. + platformTxt, err = properties.LoadFromBytes(validPlatformTxtRaw) + require.Nil(t, err) + platformTxt.Set(testTable.propertyName, strings.Repeat("a", testTable.maxLength)) + + t.Run(fmt.Sprintf("%s at maximum length of %d (%s)", testTable.propertyName, testTable.maxLength, testTable.complianceLevel), func(t *testing.T) { + assert.False(t, schema.PropertyGreaterThanMaxLength(testTable.validationErrorPropertyName, platformtxt.Validate(platformTxt)[testTable.complianceLevel])) + }) + } +} + func TestRequired(t *testing.T) { testTables := []struct { propertyName string @@ -318,6 +352,16 @@ func TestEnum(t *testing.T) { {"compiler.ar.extra_flags", "compiler\\.ar\\.extra_flags", "foo", compliancelevel.Permissive, assert.False}, {"compiler.ar.extra_flags", "compiler\\.ar\\.extra_flags", "foo", compliancelevel.Specification, assert.False}, {"compiler.ar.extra_flags", "compiler\\.ar\\.extra_flags", "foo", compliancelevel.Strict, assert.True}, + + {"tools.avrdude.upload.field.foo_field_name.secret", "tools/avrdude/upload/field/foo_field_name\\.secret", "true", compliancelevel.Permissive, assert.False}, + {"tools.avrdude.upload.field.foo_field_name.secret", "tools/avrdude/upload/field/foo_field_name\\.secret", "true", compliancelevel.Specification, assert.False}, + {"tools.avrdude.upload.field.foo_field_name.secret", "tools/avrdude/upload/field/foo_field_name\\.secret", "true", compliancelevel.Strict, assert.False}, + {"tools.avrdude.upload.field.foo_field_name.secret", "tools/avrdude/upload/field/foo_field_name\\.secret", "false", compliancelevel.Permissive, assert.False}, + {"tools.avrdude.upload.field.foo_field_name.secret", "tools/avrdude/upload/field/foo_field_name\\.secret", "false", compliancelevel.Specification, assert.False}, + {"tools.avrdude.upload.field.foo_field_name.secret", "tools/avrdude/upload/field/foo_field_name\\.secret", "false", compliancelevel.Strict, assert.False}, + {"tools.avrdude.upload.field.foo_field_name.secret", "tools/avrdude/upload/field/foo_field_name\\.secret", "foo", compliancelevel.Permissive, assert.True}, + {"tools.avrdude.upload.field.foo_field_name.secret", "tools/avrdude/upload/field/foo_field_name\\.secret", "foo", compliancelevel.Specification, assert.True}, + {"tools.avrdude.upload.field.foo_field_name.secret", "tools/avrdude/upload/field/foo_field_name\\.secret", "foo", compliancelevel.Strict, assert.True}, } for _, testTable := range testTables { diff --git a/internal/project/projectdata/platform.go b/internal/project/projectdata/platform.go index e7728cfb..aa4da58a 100644 --- a/internal/project/projectdata/platform.go +++ b/internal/project/projectdata/platform.go @@ -59,11 +59,13 @@ func InitializeForPlatform(project project.Type) { logrus.Tracef("Error loading platform.txt from %s: %s", project.Path, platformTxtLoadError) platformTxtSchemaValidationResult = nil platformTxtPluggableDiscoveryNames = nil + platformTxtUserProvidedFieldNames = nil platformTxtToolNames = nil } else { platformTxtSchemaValidationResult = platformtxt.Validate(platformTxt) platformTxtPluggableDiscoveryNames = platformtxt.PluggableDiscoveryNames(platformTxt) + platformTxtUserProvidedFieldNames = platformtxt.UserProvidedFieldNames(platformTxt) platformTxtToolNames = platformtxt.ToolNames(platformTxt) } } @@ -180,6 +182,13 @@ func PlatformTxtPluggableDiscoveryNames() []string { return platformTxtPluggableDiscoveryNames } +var platformTxtUserProvidedFieldNames map[string][]string + +// PlatformTxtUserProvidedFieldNames returns the list of user provided field names present in the platform's platform.txt, mapped by board name. +func PlatformTxtUserProvidedFieldNames() map[string][]string { + return platformTxtUserProvidedFieldNames +} + var platformTxtToolNames []string // PlatformTxtToolNames returns the list of tools present in the platform's platform.txt. diff --git a/internal/project/projectdata/platform_test.go b/internal/project/projectdata/platform_test.go index bb2fac0d..5f8e0ae6 100644 --- a/internal/project/projectdata/platform_test.go +++ b/internal/project/projectdata/platform_test.go @@ -16,6 +16,7 @@ package projectdata import ( + "reflect" "testing" "github.com/arduino/arduino-lint/internal/project" @@ -45,14 +46,15 @@ func TestInitializeForPlatform(t *testing.T) { platformTxtLoadErrorAssertion assert.ValueAssertionFunc platformTxtSchemaValidationResultAssertion assert.ValueAssertionFunc platformTxtPluggableDiscoveryNamesAssertion []string + platformTxtUserProvidedFieldNamesAssertion map[string][]string platformTxtToolNamesAssertion []string }{ - {"Valid boards.txt", "valid-boards.txt", assert.NotNil, assert.Nil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil}, - {"Invalid boards.txt", "invalid-boards.txt", assert.Nil, assert.NotNil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil}, - {"Missing boards.txt", "missing-boards.txt", assert.Nil, assert.NotNil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil}, - {"Valid platform.txt", "valid-platform.txt", assert.NotNil, assert.Nil, assert.True, assert.NotNil, assert.Nil, assert.NotNil, []string{"foo_discovery", "bar_discovery"}, []string{"avrdude", "bossac"}}, - {"Invalid platform.txt", "invalid-platform.txt", assert.NotNil, assert.Nil, assert.True, assert.Nil, assert.NotNil, assert.Nil, nil, nil}, - {"Missing platform.txt", "missing-platform.txt", assert.NotNil, assert.Nil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil}, + {"Valid boards.txt", "valid-boards.txt", assert.NotNil, assert.Nil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil, nil}, + {"Invalid boards.txt", "invalid-boards.txt", assert.Nil, assert.NotNil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil, nil}, + {"Missing boards.txt", "missing-boards.txt", assert.Nil, assert.NotNil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil, nil}, + {"Valid platform.txt", "valid-platform.txt", assert.NotNil, assert.Nil, assert.True, assert.NotNil, assert.Nil, assert.NotNil, []string{"foo_discovery", "bar_discovery"}, map[string][]string{"avrdude": {"foo_field_name"}}, []string{"avrdude", "bossac"}}, + {"Invalid platform.txt", "invalid-platform.txt", assert.NotNil, assert.Nil, assert.True, assert.Nil, assert.NotNil, assert.Nil, nil, nil, nil}, + {"Missing platform.txt", "missing-platform.txt", assert.NotNil, assert.Nil, assert.False, assert.Nil, assert.NotNil, assert.Nil, nil, nil, nil}, } for _, testTable := range testTables { @@ -74,6 +76,7 @@ func TestInitializeForPlatform(t *testing.T) { testTable.platformTxtLoadErrorAssertion(t, PlatformTxtLoadError(), testTable.testName) testTable.platformTxtSchemaValidationResultAssertion(t, PlatformTxtSchemaValidationResult(), testTable.testName) assert.Equal(t, testTable.platformTxtPluggableDiscoveryNamesAssertion, PlatformTxtPluggableDiscoveryNames(), testTable.testName) + assert.True(t, reflect.DeepEqual(testTable.platformTxtUserProvidedFieldNamesAssertion, PlatformTxtUserProvidedFieldNames()), testTable.testName) assert.Equal(t, testTable.platformTxtToolNamesAssertion, PlatformTxtToolNames(), testTable.testName) } } diff --git a/internal/project/projectdata/testdata/platforms/valid-platform.txt/platform.txt b/internal/project/projectdata/testdata/platforms/valid-platform.txt/platform.txt index 27fe7d29..2a217d81 100644 --- a/internal/project/projectdata/testdata/platforms/valid-platform.txt/platform.txt +++ b/internal/project/projectdata/testdata/platforms/valid-platform.txt/platform.txt @@ -25,6 +25,8 @@ recipe.size.regex=asdf recipe.size.regex.data=asdf pluggable_discovery.foo_discovery.pattern=asdf pluggable_discovery.bar_discovery.pattern=zxcv +tools.avrdude.upload.field.foo_field_name=Some field label +tools.avrdude.upload.field.foo_field_name.secret=true tools.avrdude.upload.params.verbose=-v tools.avrdude.upload.params.quiet=-q -q tools.avrdude.upload.pattern=asdf diff --git a/internal/rule/ruleconfiguration/ruleconfiguration.go b/internal/rule/ruleconfiguration/ruleconfiguration.go index e40988c5..89721672 100644 --- a/internal/rule/ruleconfiguration/ruleconfiguration.go +++ b/internal/rule/ruleconfiguration/ruleconfiguration.go @@ -2863,6 +2863,40 @@ var configurations = []Type{ ErrorModes: []rulemode.Type{rulemode.Default}, RuleFunction: rulefunction.PlatformTxtPluggableDiscoveryDiscoveryIDPatternMissing, }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "platform.txt", + ID: "PF092", + Brief: "upload.field.FIELD_NAME > max length", + Description: "The `tools.UPLOAD_RECIPE_ID.upload.field.FIELD_NAME` property in the platform's `platform.txt` configuration file is longer than the maximum length.", + MessageTemplate: "upload.UPLOAD_RECIPE_ID.upload.field.FIELD_NAME property is longer than the maximum length for fields: {{.}}", + Reference: "https://arduino.github.io/arduino-cli/dev/platform-specification/#user-provided-fields", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.PlatformTxtUploadFieldFieldNameGTMaxLength, + }, + { + ProjectType: projecttype.Platform, + SuperprojectType: projecttype.All, + Category: "configuration files", + Subcategory: "platform.txt", + ID: "PF093", + Brief: "upload.field.FIELD_NAME.secret invalid", + Description: "The `tools.UPLOAD_RECIPE_ID.upload.field.FIELD_NAME.secret` property in the platform's `platform.txt` configuration file has an invalid value.", + MessageTemplate: "tools.UPLOAD_RECIPE_ID.upload.field.FIELD_NAME.secret value is invalid or field(s) {{.}}", + Reference: "https://arduino.github.io/arduino-cli/dev/platform-specification/#user-provided-fields", + DisableModes: nil, + EnableModes: []rulemode.Type{rulemode.Default}, + InfoModes: nil, + WarningModes: nil, + ErrorModes: []rulemode.Type{rulemode.Default}, + RuleFunction: rulefunction.PlatformTxtUploadFieldFieldNameSecretInvalid, + }, { ProjectType: projecttype.Platform, SuperprojectType: projecttype.All, diff --git a/internal/rule/rulefunction/platform.go b/internal/rule/rulefunction/platform.go index adc7c1e5..357e18d8 100644 --- a/internal/rule/rulefunction/platform.go +++ b/internal/rule/rulefunction/platform.go @@ -1735,6 +1735,74 @@ func PlatformTxtPluggableDiscoveryDiscoveryIDPatternMissing() (result ruleresult return ruleresult.Pass, "" } +// PlatformTxtUploadFieldFieldNameGTMaxLength checks if any platform.txt tools.UPLOAD_RECIPE_ID.upload.field.FIELD_NAME property value is greater than the maximum length. +func PlatformTxtUploadFieldFieldNameGTMaxLength() (result ruleresult.Type, output string) { + if !projectdata.PlatformTxtExists() { + return ruleresult.Skip, "Platform has no platform.txt" + } + + if projectdata.PlatformTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load platform.txt" + } + + if len(projectdata.PlatformTxtUserProvidedFieldNames()) == 0 { + return ruleresult.Skip, "Property not present" + } + + nonCompliant := []string{} + for _, toolName := range projectdata.PlatformTxtToolNames() { + for _, fieldName := range projectdata.PlatformTxtUserProvidedFieldNames()[toolName] { + if schema.PropertyGreaterThanMaxLength(fmt.Sprintf("tools/%s/upload/field/%s", toolName, fieldName), projectdata.PlatformTxtSchemaValidationResult()[compliancelevel.Strict]) { + nonCompliant = append(nonCompliant, fmt.Sprintf("%s >> %s", toolName, fieldName)) + } + } + } + + if len(nonCompliant) > 0 { + return ruleresult.Fail, strings.Join(nonCompliant, ", ") + } + + return ruleresult.Pass, "" +} + +// PlatformTxtUploadFieldFieldNameSecretInvalid checks if any of the platform.txt tools.UPLOAD_RECIPE_ID.upload.field.FIELD_NAME.secret property values have invalid format. +func PlatformTxtUploadFieldFieldNameSecretInvalid() (result ruleresult.Type, output string) { + if !projectdata.PlatformTxtExists() { + return ruleresult.Skip, "Platform has no platform.txt" + } + + if projectdata.PlatformTxtLoadError() != nil { + return ruleresult.NotRun, "Couldn't load platform.txt" + } + + if len(projectdata.PlatformTxtUserProvidedFieldNames()) == 0 { + return ruleresult.Skip, "Property not present" + } + + found := false + nonCompliant := []string{} + for _, toolName := range projectdata.PlatformTxtToolNames() { + for _, fieldName := range projectdata.PlatformTxtUserProvidedFieldNames()[toolName] { + if projectdata.PlatformTxt().ContainsKey(fmt.Sprintf("tools.%s.upload.field.%s.secret", toolName, fieldName)) { + found = true + if schema.PropertyEnumMismatch(fmt.Sprintf("tools/%s/upload/field/%s\\.secret", toolName, fieldName), projectdata.PlatformTxtSchemaValidationResult()[compliancelevel.Strict]) { + nonCompliant = append(nonCompliant, fmt.Sprintf("%s >> %s", toolName, fieldName)) + } + } + } + } + + if !found { + return ruleresult.Skip, "Property not present" + } + + if len(nonCompliant) > 0 { + return ruleresult.Fail, strings.Join(nonCompliant, ", ") + } + + return ruleresult.Pass, "" +} + // PlatformTxtUploadPatternMissing checks if any of the tools are missing upload.pattern properties. func PlatformTxtUploadPatternMissing() (result ruleresult.Type, output string) { if !projectdata.PlatformTxtExists() { diff --git a/internal/rule/rulefunction/platform_test.go b/internal/rule/rulefunction/platform_test.go index 3e0fa705..da7aca93 100644 --- a/internal/rule/rulefunction/platform_test.go +++ b/internal/rule/rulefunction/platform_test.go @@ -992,6 +992,31 @@ func TestPlatformTxtPluggableDiscoveryDiscoveryIDPatternMissing(t *testing.T) { checkPlatformRuleFunction(PlatformTxtPluggableDiscoveryDiscoveryIDPatternMissing, testTables, t) } +func TestPlatformTxtUploadFieldFieldNameGTMaxLength(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-platform.txt", ruleresult.Skip, ""}, + {"Invalid", "invalid-platform.txt", ruleresult.NotRun, ""}, + {"No field", "no-user-defined-field-platform.txt", ruleresult.Skip, ""}, + {"Property GT max", "user-defined-field-GT-platform.txt", ruleresult.Fail, "^avrdude >> foo_field_name, bossac >> bar_field_name$"}, + {"Valid", "valid-platform.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(PlatformTxtUploadFieldFieldNameGTMaxLength, testTables, t) +} + +func TestPlatformTxtUploadFieldFieldNameSecretInvalid(t *testing.T) { + testTables := []platformRuleFunctionTestTable{ + {"Missing", "missing-platform.txt", ruleresult.Skip, ""}, + {"Invalid", "invalid-platform.txt", ruleresult.NotRun, ""}, + {"No field", "no-user-defined-field-platform.txt", ruleresult.Skip, ""}, + {"No field secret", "no-user-defined-field-secret-platform.txt", ruleresult.Skip, ""}, + {"Property invalid", "user-defined-field-secret-invalid-platform.txt", ruleresult.Fail, "^avrdude >> foo_field_name, footool >> qux_field_name$"}, + {"Valid", "valid-platform.txt", ruleresult.Pass, ""}, + } + + checkPlatformRuleFunction(PlatformTxtUploadFieldFieldNameSecretInvalid, testTables, t) +} + func TestPlatformTxtUploadPatternMissing(t *testing.T) { testTables := []platformRuleFunctionTestTable{ {"Missing", "missing-platform.txt", ruleresult.Skip, ""}, diff --git a/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-platform.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-platform.txt/boards.txt new file mode 100644 index 00000000..1866d3c4 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-platform.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-platform.txt/platform.txt new file mode 100644 index 00000000..c672acdb --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-platform.txt/platform.txt @@ -0,0 +1,54 @@ +name=Arduino AVR Boards +version=1.8.3 +compiler.warning_flags.none=asdf +compiler.warning_flags.default=asdf +compiler.warning_flags.more=asdf +compiler.warning_flags.all=asdf +compiler.optimization_flags.debug= +compiler.optimization_flags.release= +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= +recipe.c.o.pattern=asdf {compiler.c.extra_flags} +recipe.cpp.o.pattern=asdf {compiler.cpp.extra_flags} +recipe.S.o.pattern=asdf {compiler.S.extra_flags} +recipe.ar.pattern=asdf {compiler.ar.extra_flags} +recipe.c.combine.pattern=asdf {compiler.c.elf.extra_flags} +recipe.preproc.macros=asdf {compiler.cpp.extra_flags} +recipe.objcopy.eep.pattern=asdf +recipe.objcopy.hex.pattern=asdf +recipe.output.tmp_file=asdf +recipe.output.save_file=asdf +recipe.size.pattern=asdf +recipe.size.regex=asdf +recipe.size.regex.data=asdf +pluggable_discovery.required.0=builtin:serial-discovery +pluggable_discovery.required.1=builtin:mdns-discovery +tools.avrdude.upload.params.verbose=-v +tools.avrdude.upload.params.quiet=-q -q +tools.avrdude.upload.pattern=asdf +tools.bossac.upload.params.verbose=-v +tools.bossac.upload.params.quiet=-q -q +tools.bossac.upload.pattern=asdf +tools.avrdude.program.params.verbose=-v +tools.avrdude.program.params.quiet=-q -q +tools.avrdude.program.pattern=asdf +tools.bossac.program.params.verbose=-v +tools.bossac.program.params.quiet=-q -q +tools.bossac.program.pattern=asdf +tools.avrdude.erase.params.verbose=-v +tools.avrdude.erase.params.quiet=-q -q +tools.avrdude.erase.pattern=asdf +tools.bossac.erase.params.verbose=-v +tools.bossac.erase.params.quiet=-q -q +tools.bossac.erase.pattern=asdf +tools.avrdude.bootloader.params.verbose=-v +tools.avrdude.bootloader.params.quiet=-q -q +tools.avrdude.bootloader.pattern=asdf +tools.bossac.bootloader.params.verbose=-v +tools.bossac.bootloader.params.quiet=-q -q +tools.bossac.bootloader.pattern=asdf diff --git a/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-secret-platform.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-secret-platform.txt/boards.txt new file mode 100644 index 00000000..1866d3c4 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-secret-platform.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-secret-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-secret-platform.txt/platform.txt new file mode 100644 index 00000000..1eb51ea8 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/no-user-defined-field-secret-platform.txt/platform.txt @@ -0,0 +1,56 @@ +name=Arduino AVR Boards +version=1.8.3 +compiler.warning_flags.none=asdf +compiler.warning_flags.default=asdf +compiler.warning_flags.more=asdf +compiler.warning_flags.all=asdf +compiler.optimization_flags.debug= +compiler.optimization_flags.release= +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= +recipe.c.o.pattern=asdf {compiler.c.extra_flags} +recipe.cpp.o.pattern=asdf {compiler.cpp.extra_flags} +recipe.S.o.pattern=asdf {compiler.S.extra_flags} +recipe.ar.pattern=asdf {compiler.ar.extra_flags} +recipe.c.combine.pattern=asdf {compiler.c.elf.extra_flags} +recipe.preproc.macros=asdf {compiler.cpp.extra_flags} +recipe.objcopy.eep.pattern=asdf +recipe.objcopy.hex.pattern=asdf +recipe.output.tmp_file=asdf +recipe.output.save_file=asdf +recipe.size.pattern=asdf +recipe.size.regex=asdf +recipe.size.regex.data=asdf +pluggable_discovery.required.0=builtin:serial-discovery +pluggable_discovery.required.1=builtin:mdns-discovery +tools.avrdude.upload.field.foo_field_name=Some field label +tools.avrdude.upload.params.verbose=-v +tools.avrdude.upload.params.quiet=-q -q +tools.avrdude.upload.pattern=asdf +tools.bossac.upload.field.bar_field_name=Some other field label +tools.bossac.upload.params.verbose=-v +tools.bossac.upload.params.quiet=-q -q +tools.bossac.upload.pattern=asdf +tools.avrdude.program.params.verbose=-v +tools.avrdude.program.params.quiet=-q -q +tools.avrdude.program.pattern=asdf +tools.bossac.program.params.verbose=-v +tools.bossac.program.params.quiet=-q -q +tools.bossac.program.pattern=asdf +tools.avrdude.erase.params.verbose=-v +tools.avrdude.erase.params.quiet=-q -q +tools.avrdude.erase.pattern=asdf +tools.bossac.erase.params.verbose=-v +tools.bossac.erase.params.quiet=-q -q +tools.bossac.erase.pattern=asdf +tools.avrdude.bootloader.params.verbose=-v +tools.avrdude.bootloader.params.quiet=-q -q +tools.avrdude.bootloader.pattern=asdf +tools.bossac.bootloader.params.verbose=-v +tools.bossac.bootloader.params.quiet=-q -q +tools.bossac.bootloader.pattern=asdf diff --git a/internal/rule/rulefunction/testdata/platforms/user-defined-field-GT-platform.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/user-defined-field-GT-platform.txt/boards.txt new file mode 100644 index 00000000..1866d3c4 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/user-defined-field-GT-platform.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/user-defined-field-GT-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/user-defined-field-GT-platform.txt/platform.txt new file mode 100644 index 00000000..bb9e4388 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/user-defined-field-GT-platform.txt/platform.txt @@ -0,0 +1,61 @@ +name=Arduino AVR Boards +version=1.8.3 +compiler.warning_flags.none=asdf +compiler.warning_flags.default=asdf +compiler.warning_flags.more=asdf +compiler.warning_flags.all=asdf +compiler.optimization_flags.debug= +compiler.optimization_flags.release= +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= +recipe.c.o.pattern=asdf {compiler.c.extra_flags} +recipe.cpp.o.pattern=asdf {compiler.cpp.extra_flags} +recipe.S.o.pattern=asdf {compiler.S.extra_flags} +recipe.ar.pattern=asdf {compiler.ar.extra_flags} +recipe.c.combine.pattern=asdf {compiler.c.elf.extra_flags} +recipe.preproc.macros=asdf {compiler.cpp.extra_flags} +recipe.objcopy.eep.pattern=asdf +recipe.objcopy.hex.pattern=asdf +recipe.output.tmp_file=asdf +recipe.output.save_file=asdf +recipe.size.pattern=asdf +recipe.size.regex=asdf +recipe.size.regex.data=asdf +pluggable_discovery.required.0=builtin:serial-discovery +pluggable_discovery.required.1=builtin:mdns-discovery +tools.avrdude.upload.field.foo_field_name=012345678901234567890123456789012345678901234567890123456789 +tools.avrdude.upload.field.foo_field_name.secret=true +tools.avrdude.upload.params.verbose=-v +tools.avrdude.upload.params.quiet=-q -q +tools.avrdude.upload.pattern=asdf +tools.bossac.upload.field.bar_field_name=012345678901234567890123456789012345678901234567890123456789 +tools.bossac.upload.field.baz_field_name=Some field label of OK length +tools.bossac.upload.params.verbose=-v +tools.bossac.upload.params.quiet=-q -q +tools.bossac.upload.pattern=asdf +tools.footool.upload.params.verbose=-v +tools.footool.upload.params.quiet=-q -q +tools.footool.upload.pattern=asdf +tools.avrdude.program.params.verbose=-v +tools.avrdude.program.params.quiet=-q -q +tools.avrdude.program.pattern=asdf +tools.bossac.program.params.verbose=-v +tools.bossac.program.params.quiet=-q -q +tools.bossac.program.pattern=asdf +tools.avrdude.erase.params.verbose=-v +tools.avrdude.erase.params.quiet=-q -q +tools.avrdude.erase.pattern=asdf +tools.bossac.erase.params.verbose=-v +tools.bossac.erase.params.quiet=-q -q +tools.bossac.erase.pattern=asdf +tools.avrdude.bootloader.params.verbose=-v +tools.avrdude.bootloader.params.quiet=-q -q +tools.avrdude.bootloader.pattern=asdf +tools.bossac.bootloader.params.verbose=-v +tools.bossac.bootloader.params.quiet=-q -q +tools.bossac.bootloader.pattern=asdf diff --git a/internal/rule/rulefunction/testdata/platforms/user-defined-field-secret-invalid-platform.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/user-defined-field-secret-invalid-platform.txt/boards.txt new file mode 100644 index 00000000..1866d3c4 --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/user-defined-field-secret-invalid-platform.txt/boards.txt @@ -0,0 +1,23 @@ +buno.name=Buno +buno.build.board=BUNO +buno.build.core=arduino +buno.build.variant=standard +buno.upload.tool=avrdude +buno.upload.maximum_size=32256 +buno.upload.maximum_data_size=2048 + +uno.name=Arduino Uno +uno.build.board=UNO +uno.build.core=arduino +uno.build.variant=standard +uno.upload.tool=avrdude +uno.upload.maximum_size=32256 +uno.upload.maximum_data_size=2048 + +funo.name=Funo +funo.build.board=FUNO +funo.build.core=arduino +funo.build.variant=standard +funo.upload.tool=avrdude +funo.upload.maximum_size=32256 +funo.upload.maximum_data_size=2048 diff --git a/internal/rule/rulefunction/testdata/platforms/user-defined-field-secret-invalid-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/user-defined-field-secret-invalid-platform.txt/platform.txt new file mode 100644 index 00000000..746491dd --- /dev/null +++ b/internal/rule/rulefunction/testdata/platforms/user-defined-field-secret-invalid-platform.txt/platform.txt @@ -0,0 +1,64 @@ +name=Arduino AVR Boards +version=1.8.3 +compiler.warning_flags.none=asdf +compiler.warning_flags.default=asdf +compiler.warning_flags.more=asdf +compiler.warning_flags.all=asdf +compiler.optimization_flags.debug= +compiler.optimization_flags.release= +compiler.c.extra_flags= +compiler.c.elf.extra_flags= +compiler.S.extra_flags= +compiler.cpp.extra_flags= +compiler.ar.extra_flags= +compiler.objcopy.eep.extra_flags= +compiler.elf2hex.extra_flags= +recipe.c.o.pattern=asdf {compiler.c.extra_flags} +recipe.cpp.o.pattern=asdf {compiler.cpp.extra_flags} +recipe.S.o.pattern=asdf {compiler.S.extra_flags} +recipe.ar.pattern=asdf {compiler.ar.extra_flags} +recipe.c.combine.pattern=asdf {compiler.c.elf.extra_flags} +recipe.preproc.macros=asdf {compiler.cpp.extra_flags} +recipe.objcopy.eep.pattern=asdf +recipe.objcopy.hex.pattern=asdf +recipe.output.tmp_file=asdf +recipe.output.save_file=asdf +recipe.size.pattern=asdf +recipe.size.regex=asdf +recipe.size.regex.data=asdf +pluggable_discovery.required.0=builtin:serial-discovery +pluggable_discovery.required.1=builtin:mdns-discovery +tools.avrdude.upload.field.foo_field_name=Some field label +tools.avrdude.upload.field.foo_field_name.secret=foo +tools.avrdude.upload.params.verbose=-v +tools.avrdude.upload.params.quiet=-q -q +tools.avrdude.upload.pattern=asdf +tools.bossac.upload.field.bar_field_name=Some other field label +tools.bossac.upload.params.verbose=-v +tools.bossac.upload.params.quiet=-q -q +tools.bossac.upload.pattern=asdf +tools.footool.upload.field.baz_field_name=Some other field label +tools.footool.upload.field.baz_field_name.secret=false +tools.footool.upload.field.qux_field_name=Some other other field label +tools.footool.upload.field.qux_field_name.secret=1234 +tools.footool.upload.params.verbose=-v +tools.footool.upload.params.quiet=-q -q +tools.footool.upload.pattern=asdf +tools.avrdude.program.params.verbose=-v +tools.avrdude.program.params.quiet=-q -q +tools.avrdude.program.pattern=asdf +tools.bossac.program.params.verbose=-v +tools.bossac.program.params.quiet=-q -q +tools.bossac.program.pattern=asdf +tools.avrdude.erase.params.verbose=-v +tools.avrdude.erase.params.quiet=-q -q +tools.avrdude.erase.pattern=asdf +tools.bossac.erase.params.verbose=-v +tools.bossac.erase.params.quiet=-q -q +tools.bossac.erase.pattern=asdf +tools.avrdude.bootloader.params.verbose=-v +tools.avrdude.bootloader.params.quiet=-q -q +tools.avrdude.bootloader.pattern=asdf +tools.bossac.bootloader.params.verbose=-v +tools.bossac.bootloader.params.quiet=-q -q +tools.bossac.bootloader.pattern=asdf diff --git a/internal/rule/rulefunction/testdata/platforms/valid-platform.txt/platform.txt b/internal/rule/rulefunction/testdata/platforms/valid-platform.txt/platform.txt index c672acdb..ff72f156 100644 --- a/internal/rule/rulefunction/testdata/platforms/valid-platform.txt/platform.txt +++ b/internal/rule/rulefunction/testdata/platforms/valid-platform.txt/platform.txt @@ -28,9 +28,12 @@ recipe.size.regex=asdf recipe.size.regex.data=asdf pluggable_discovery.required.0=builtin:serial-discovery pluggable_discovery.required.1=builtin:mdns-discovery +tools.avrdude.upload.field.foo_field_name=Some field label +tools.avrdude.upload.field.foo_field_name.secret=true tools.avrdude.upload.params.verbose=-v tools.avrdude.upload.params.quiet=-q -q tools.avrdude.upload.pattern=asdf +tools.bossac.upload.field.bar_field_name=Some other field label tools.bossac.upload.params.verbose=-v tools.bossac.upload.params.quiet=-q -q tools.bossac.upload.pattern=asdf diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index 85b12cf7..985e1062 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -5150,6 +5150,13 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ }, { "$ref": "#/definitions/requiredObjects/toolsToolNameUpload/permissive/object" + }, + { + "properties": { + "field": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/permissive/object" + } + } } ] } @@ -5162,6 +5169,13 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ }, { "$ref": "#/definitions/requiredObjects/toolsToolNameUpload/specification/object" + }, + { + "properties": { + "field": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/specification/object" + } + } } ] } @@ -5174,6 +5188,163 @@ var _arduinoPlatformTxtDefinitionsSchemaJson = []byte(`{ }, { "$ref": "#/definitions/requiredObjects/toolsToolNameUpload/strict/object" + }, + { + "properties": { + "field": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/strict/object" + } + } + } + ] + } + } + }, + "toolsToolNameUploadField": { + "base": { + "object": { + "allOf": [ + { + "type": "object" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/base/object" + }, + { + "patternProperties": { + "^.+([^.].*|\\.([^s].*)?|\\.se([^c].*)?|\\.sec([^r].*)?|\\.secr([^e].*)?|\\.secre([^t].*)?|\\.secret.+)$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/permissive/object" + }, + "^.+\\.secret$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/permissive/object" + } + } + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/base/object" + }, + { + "patternProperties": { + "^.+([^.].*|\\.([^s].*)?|\\.se([^c].*)?|\\.sec([^r].*)?|\\.secr([^e].*)?|\\.secre([^t].*)?|\\.secret.+)$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/specification/object" + }, + "^.+\\.secret$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/specification/object" + } + } + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadField/base/object" + }, + { + "patternProperties": { + "^.+([^.].*|\\.([^s].*)?|\\.se([^c].*)?|\\.sec([^r].*)?|\\.secr([^e].*)?|\\.secre([^t].*)?|\\.secret.+)$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/strict/object" + }, + "^.+\\.secret$": { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/strict/object" + } + } + } + ] + } + } + }, + "toolsToolNameUploadFieldFieldName": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/base/object" + }, + { + "maxLength": 50 + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldName/specification/object" + } + ] + } + } + }, + "toolsToolNameUploadFieldFieldNameSecret": { + "base": { + "object": { + "allOf": [ + { + "type": "string" + }, + { + "$ref": "general-definitions-schema.json#/definitions/enumObjects/booleanString" + } + ] + } + }, + "permissive": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/base/object" + } + ] + } + }, + "specification": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/base/object" + } + ] + } + }, + "strict": { + "object": { + "allOf": [ + { + "$ref": "#/definitions/propertiesObjects/toolsToolNameUploadFieldFieldNameSecret/base/object" } ] } From cc2887c44165222a2e183c597d43623a959c8411 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 31 Aug 2021 22:32:21 -0700 Subject: [PATCH 10/10] Detect invalid `upload..x` properties Pluggable discovery brings support for variants of the upload properties for each protocol. In addition to the previous property names, the same rules must now be applied to the properties with an arbitrary protocol name inserted. --- ...arduino-boards-txt-definitions-schema.json | 120 +++++++++--------- .../boardstxt/boardstxtschema_test.go | 40 ++++++ internal/rule/rulefunction/platform.go | 8 +- .../boards.txt | 4 +- .../boards.txt | 4 +- internal/rule/schema/schemadata/bindata.go | 120 +++++++++--------- 6 files changed, 168 insertions(+), 128 deletions(-) diff --git a/etc/schemas/arduino-boards-txt-definitions-schema.json b/etc/schemas/arduino-boards-txt-definitions-schema.json index 76d197cc..e3144e51 100644 --- a/etc/schemas/arduino-boards-txt-definitions-schema.json +++ b/etc/schemas/arduino-boards-txt-definitions-schema.json @@ -145,6 +145,13 @@ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/permissive/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/permissive/object" @@ -152,21 +159,14 @@ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/permissive/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/permissive/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/permissive/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/permissive/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/permissive/object" @@ -210,6 +210,13 @@ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/specification/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/specification/object" @@ -217,21 +224,14 @@ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/specification/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/specification/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/specification/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/specification/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/specification/object" @@ -275,6 +275,13 @@ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/strict/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/strict/object" @@ -282,21 +289,14 @@ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/strict/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/strict/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/strict/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/strict/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/strict/object" @@ -624,6 +624,13 @@ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/permissive/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/permissive/object" @@ -631,21 +638,14 @@ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/permissive/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/permissive/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/permissive/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/permissive/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/permissive/object" @@ -677,6 +677,13 @@ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/specification/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/specification/object" @@ -684,21 +691,14 @@ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/specification/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/specification/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/specification/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/specification/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/specification/object" @@ -730,6 +730,13 @@ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/strict/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/strict/object" @@ -737,21 +744,14 @@ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/strict/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/strict/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/strict/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/strict/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/strict/object" diff --git a/internal/project/platform/boardstxt/boardstxtschema_test.go b/internal/project/platform/boardstxt/boardstxtschema_test.go index 93c9709c..ac71f537 100644 --- a/internal/project/platform/boardstxt/boardstxtschema_test.go +++ b/internal/project/platform/boardstxt/boardstxtschema_test.go @@ -234,6 +234,16 @@ func TestEnum(t *testing.T) { {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "foo", compliancelevel.Specification, assert.True}, {"nano.upload.use_1200bps_touch", "nano/upload\\.use_1200bps_touch", "foo", compliancelevel.Strict, assert.True}, + {"nano.upload.serial.use_1200bps_touch", "nano/upload\\.serial\\.use_1200bps_touch", "true", compliancelevel.Permissive, assert.False}, + {"nano.upload.serial.use_1200bps_touch", "nano/upload\\.serial\\.use_1200bps_touch", "true", compliancelevel.Specification, assert.False}, + {"nano.upload.serial.use_1200bps_touch", "nano/upload\\.serial\\.use_1200bps_touch", "true", compliancelevel.Strict, assert.False}, + {"nano.upload.serial.use_1200bps_touch", "nano/upload\\.serial\\.use_1200bps_touch", "false", compliancelevel.Permissive, assert.False}, + {"nano.upload.serial.use_1200bps_touch", "nano/upload\\.serial\\.use_1200bps_touch", "false", compliancelevel.Specification, assert.False}, + {"nano.upload.serial.use_1200bps_touch", "nano/upload\\.serial\\.use_1200bps_touch", "false", compliancelevel.Strict, assert.False}, + {"nano.upload.serial.use_1200bps_touch", "nano/upload\\.serial\\.use_1200bps_touch", "foo", compliancelevel.Permissive, assert.True}, + {"nano.upload.serial.use_1200bps_touch", "nano/upload\\.serial\\.use_1200bps_touch", "foo", compliancelevel.Specification, assert.True}, + {"nano.upload.serial.use_1200bps_touch", "nano/upload\\.serial\\.use_1200bps_touch", "foo", compliancelevel.Strict, assert.True}, + {"nano.menu.bar.baz.upload.use_1200bps_touch", "nano/menu/bar/baz/upload\\.use_1200bps_touch", "true", compliancelevel.Permissive, assert.False}, {"nano.menu.bar.baz.upload.use_1200bps_touch", "nano/menu/bar/baz/upload\\.use_1200bps_touch", "true", compliancelevel.Specification, assert.False}, {"nano.menu.bar.baz.upload.use_1200bps_touch", "nano/menu/bar/baz/upload\\.use_1200bps_touch", "true", compliancelevel.Strict, assert.False}, @@ -244,6 +254,16 @@ func TestEnum(t *testing.T) { {"nano.menu.bar.baz.upload.use_1200bps_touch", "nano/menu/bar/baz/upload\\.use_1200bps_touch", "foo", compliancelevel.Specification, assert.True}, {"nano.menu.bar.baz.upload.use_1200bps_touch", "nano/menu/bar/baz/upload\\.use_1200bps_touch", "foo", compliancelevel.Strict, assert.True}, + {"nano.menu.bar.baz.upload.serial.use_1200bps_touch", "nano/menu/bar/baz/upload\\.serial\\.use_1200bps_touch", "true", compliancelevel.Permissive, assert.False}, + {"nano.menu.bar.baz.upload.serial.use_1200bps_touch", "nano/menu/bar/baz/upload\\.serial\\.use_1200bps_touch", "true", compliancelevel.Specification, assert.False}, + {"nano.menu.bar.baz.upload.serial.use_1200bps_touch", "nano/menu/bar/baz/upload\\.serial\\.use_1200bps_touch", "true", compliancelevel.Strict, assert.False}, + {"nano.menu.bar.baz.upload.serial.use_1200bps_touch", "nano/menu/bar/baz/upload\\.serial\\.use_1200bps_touch", "false", compliancelevel.Permissive, assert.False}, + {"nano.menu.bar.baz.upload.serial.use_1200bps_touch", "nano/menu/bar/baz/upload\\.serial\\.use_1200bps_touch", "false", compliancelevel.Specification, assert.False}, + {"nano.menu.bar.baz.upload.serial.use_1200bps_touch", "nano/menu/bar/baz/upload\\.serial\\.use_1200bps_touch", "false", compliancelevel.Strict, assert.False}, + {"nano.menu.bar.baz.upload.serial.use_1200bps_touch", "nano/menu/bar/baz/upload\\.serial\\.use_1200bps_touch", "foo", compliancelevel.Permissive, assert.True}, + {"nano.menu.bar.baz.upload.serial.use_1200bps_touch", "nano/menu/bar/baz/upload\\.serial\\.use_1200bps_touch", "foo", compliancelevel.Specification, assert.True}, + {"nano.menu.bar.baz.upload.serial.use_1200bps_touch", "nano/menu/bar/baz/upload\\.serial\\.use_1200bps_touch", "foo", compliancelevel.Strict, assert.True}, + {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "true", compliancelevel.Permissive, assert.False}, {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "true", compliancelevel.Specification, assert.False}, {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "true", compliancelevel.Strict, assert.False}, @@ -254,6 +274,16 @@ func TestEnum(t *testing.T) { {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "foo", compliancelevel.Specification, assert.True}, {"nano.upload.wait_for_upload_port", "nano/upload\\.wait_for_upload_port", "foo", compliancelevel.Strict, assert.True}, + {"nano.upload.serial.wait_for_upload_port", "nano/upload\\.serial\\.wait_for_upload_port", "true", compliancelevel.Permissive, assert.False}, + {"nano.upload.serial.wait_for_upload_port", "nano/upload\\.serial\\.wait_for_upload_port", "true", compliancelevel.Specification, assert.False}, + {"nano.upload.serial.wait_for_upload_port", "nano/upload\\.serial\\.wait_for_upload_port", "true", compliancelevel.Strict, assert.False}, + {"nano.upload.serial.wait_for_upload_port", "nano/upload\\.serial\\.wait_for_upload_port", "false", compliancelevel.Permissive, assert.False}, + {"nano.upload.serial.wait_for_upload_port", "nano/upload\\.serial\\.wait_for_upload_port", "false", compliancelevel.Specification, assert.False}, + {"nano.upload.serial.wait_for_upload_port", "nano/upload\\.serial\\.wait_for_upload_port", "false", compliancelevel.Strict, assert.False}, + {"nano.upload.serial.wait_for_upload_port", "nano/upload\\.serial\\.wait_for_upload_port", "foo", compliancelevel.Permissive, assert.True}, + {"nano.upload.serial.wait_for_upload_port", "nano/upload\\.serial\\.wait_for_upload_port", "foo", compliancelevel.Specification, assert.True}, + {"nano.upload.serial.wait_for_upload_port", "nano/upload\\.serial\\.wait_for_upload_port", "foo", compliancelevel.Strict, assert.True}, + {"nano.menu.bar.baz.upload.wait_for_upload_port", "nano/menu/bar/baz/upload\\.wait_for_upload_port", "true", compliancelevel.Permissive, assert.False}, {"nano.menu.bar.baz.upload.wait_for_upload_port", "nano/menu/bar/baz/upload\\.wait_for_upload_port", "true", compliancelevel.Specification, assert.False}, {"nano.menu.bar.baz.upload.wait_for_upload_port", "nano/menu/bar/baz/upload\\.wait_for_upload_port", "true", compliancelevel.Strict, assert.False}, @@ -263,6 +293,16 @@ func TestEnum(t *testing.T) { {"nano.menu.bar.baz.upload.wait_for_upload_port", "nano/menu/bar/baz/upload\\.wait_for_upload_port", "foo", compliancelevel.Permissive, assert.True}, {"nano.menu.bar.baz.upload.wait_for_upload_port", "nano/menu/bar/baz/upload\\.wait_for_upload_port", "foo", compliancelevel.Specification, assert.True}, {"nano.menu.bar.baz.upload.wait_for_upload_port", "nano/menu/bar/baz/upload\\.wait_for_upload_port", "foo", compliancelevel.Strict, assert.True}, + + {"nano.menu.bar.baz.upload.serial.wait_for_upload_port", "nano/menu/bar/baz/upload\\.serial\\.wait_for_upload_port", "true", compliancelevel.Permissive, assert.False}, + {"nano.menu.bar.baz.upload.serial.wait_for_upload_port", "nano/menu/bar/baz/upload\\.serial\\.wait_for_upload_port", "true", compliancelevel.Specification, assert.False}, + {"nano.menu.bar.baz.upload.serial.wait_for_upload_port", "nano/menu/bar/baz/upload\\.serial\\.wait_for_upload_port", "true", compliancelevel.Strict, assert.False}, + {"nano.menu.bar.baz.upload.serial.wait_for_upload_port", "nano/menu/bar/baz/upload\\.serial\\.wait_for_upload_port", "false", compliancelevel.Permissive, assert.False}, + {"nano.menu.bar.baz.upload.serial.wait_for_upload_port", "nano/menu/bar/baz/upload\\.serial\\.wait_for_upload_port", "false", compliancelevel.Specification, assert.False}, + {"nano.menu.bar.baz.upload.serial.wait_for_upload_port", "nano/menu/bar/baz/upload\\.serial\\.wait_for_upload_port", "false", compliancelevel.Strict, assert.False}, + {"nano.menu.bar.baz.upload.serial.wait_for_upload_port", "nano/menu/bar/baz/upload\\.serial\\.wait_for_upload_port", "foo", compliancelevel.Permissive, assert.True}, + {"nano.menu.bar.baz.upload.serial.wait_for_upload_port", "nano/menu/bar/baz/upload\\.serial\\.wait_for_upload_port", "foo", compliancelevel.Specification, assert.True}, + {"nano.menu.bar.baz.upload.serial.wait_for_upload_port", "nano/menu/bar/baz/upload\\.serial\\.wait_for_upload_port", "foo", compliancelevel.Strict, assert.True}, } for _, testTable := range testTables { diff --git a/internal/rule/rulefunction/platform.go b/internal/rule/rulefunction/platform.go index 357e18d8..bbdee73d 100644 --- a/internal/rule/rulefunction/platform.go +++ b/internal/rule/rulefunction/platform.go @@ -343,7 +343,7 @@ func BoardsTxtBoardIDUploadMaximumSizeInvalid() (result ruleresult.Type, output return ruleresult.Skip, "boards.txt has no boards" } - nonCompliantBoardIDs := boardIDValuePatternMismatch(projectdata.BoardsTxtBoardIds(), "upload\\.maximum_size", compliancelevel.Specification) + nonCompliantBoardIDs := boardIDValuePatternMismatch(projectdata.BoardsTxtBoardIds(), "upload(\\..+)?\\.maximum_size", compliancelevel.Specification) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") @@ -381,7 +381,7 @@ func BoardsTxtBoardIDUploadMaximumDataSizeInvalid() (result ruleresult.Type, out return ruleresult.Skip, "boards.txt has no boards" } - nonCompliantBoardIDs := boardIDValuePatternMismatch(projectdata.BoardsTxtBoardIds(), "upload\\.maximum_data_size", compliancelevel.Specification) + nonCompliantBoardIDs := boardIDValuePatternMismatch(projectdata.BoardsTxtBoardIds(), "upload(\\..+)?\\.maximum_data_size", compliancelevel.Specification) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") @@ -400,7 +400,7 @@ func BoardsTxtBoardIDUploadUse1200bpsTouchInvalid() (result ruleresult.Type, out return ruleresult.Skip, "boards.txt has no boards" } - nonCompliantBoardIDs := boardIDValueEnumMismatch(projectdata.BoardsTxtBoardIds(), "upload\\.use_1200bps_touch", compliancelevel.Specification) + nonCompliantBoardIDs := boardIDValueEnumMismatch(projectdata.BoardsTxtBoardIds(), "upload(\\..+)?\\.use_1200bps_touch", compliancelevel.Specification) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") @@ -419,7 +419,7 @@ func BoardsTxtBoardIDUploadWaitForUploadPortInvalid() (result ruleresult.Type, o return ruleresult.Skip, "boards.txt has no boards" } - nonCompliantBoardIDs := boardIDValueEnumMismatch(projectdata.BoardsTxtBoardIds(), "upload\\.wait_for_upload_port", compliancelevel.Specification) + nonCompliantBoardIDs := boardIDValueEnumMismatch(projectdata.BoardsTxtBoardIds(), "upload(\\..+)?\\.wait_for_upload_port", compliancelevel.Specification) if len(nonCompliantBoardIDs) > 0 { return ruleresult.Fail, strings.Join(nonCompliantBoardIDs, ", ") diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-use_1200bps_touch-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-use_1200bps_touch-invalid-boards.txt/boards.txt index 27b68f20..d6f1024e 100644 --- a/internal/rule/rulefunction/testdata/platforms/boardID-upload-use_1200bps_touch-invalid-boards.txt/boards.txt +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-use_1200bps_touch-invalid-boards.txt/boards.txt @@ -20,10 +20,10 @@ funo.name=Funo funo.build.board=FUNO funo.build.core=arduino funo.build.variant=standard -funo.upload.tool=avrdude +funo.upload.tool.serial=avrdude funo.upload.maximum_size=32256 funo.upload.maximum_data_size=2048 -funo.upload.use_1200bps_touch=foo +funo.upload.serial.use_1200bps_touch=foo zuno.name=Zuno zuno.build.board=ZUNO diff --git a/internal/rule/rulefunction/testdata/platforms/boardID-upload-wait_for_upload_port-invalid-boards.txt/boards.txt b/internal/rule/rulefunction/testdata/platforms/boardID-upload-wait_for_upload_port-invalid-boards.txt/boards.txt index 1100a9c8..db54b6c1 100644 --- a/internal/rule/rulefunction/testdata/platforms/boardID-upload-wait_for_upload_port-invalid-boards.txt/boards.txt +++ b/internal/rule/rulefunction/testdata/platforms/boardID-upload-wait_for_upload_port-invalid-boards.txt/boards.txt @@ -20,10 +20,10 @@ funo.name=Funo funo.build.board=FUNO funo.build.core=arduino funo.build.variant=standard -funo.upload.tool=avrdude +funo.upload.tool.serial=avrdude funo.upload.maximum_size=32256 funo.upload.maximum_data_size=2048 -funo.upload.wait_for_upload_port=foo +funo.upload.serial.wait_for_upload_port=foo zuno.name=Zuno zuno.build.board=ZUNO diff --git a/internal/rule/schema/schemadata/bindata.go b/internal/rule/schema/schemadata/bindata.go index 985e1062..d007d082 100644 --- a/internal/rule/schema/schemadata/bindata.go +++ b/internal/rule/schema/schemadata/bindata.go @@ -220,6 +220,13 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/permissive/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/permissive/object" @@ -227,21 +234,14 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/permissive/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/permissive/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/permissive/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/permissive/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/permissive/object" @@ -285,6 +285,13 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/specification/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/specification/object" @@ -292,21 +299,14 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/specification/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/specification/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/specification/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/specification/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/specification/object" @@ -350,6 +350,13 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/strict/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/strict/object" @@ -357,21 +364,14 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/strict/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/strict/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/strict/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/strict/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/strict/object" @@ -699,6 +699,13 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/permissive/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/permissive/object" @@ -706,21 +713,14 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/permissive/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/permissive/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/permissive/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/permissive/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/permissive/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/permissive/object" @@ -752,6 +752,13 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/specification/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/specification/object" @@ -759,21 +766,14 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/specification/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/specification/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/specification/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/specification/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/specification/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/specification/object" @@ -805,6 +805,13 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ }, "serial.disableRTS": { "$ref": "#/definitions/propertiesObjects/boardIDSerialDisableRTS/strict/object" + } + } + }, + { + "patternProperties": { + "^upload.tool(\\..+)?$": { + "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" }, "upload.maximum_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumSize/strict/object" @@ -812,21 +819,14 @@ var _arduinoBoardsTxtDefinitionsSchemaJson = []byte(`{ "upload.maximum_data_size": { "$ref": "#/definitions/propertiesObjects/boardIDUploadMaximumDataSize/strict/object" }, - "upload.protocol": { + "upload(\\..+)?\\.protocol": { "$ref": "#/definitions/propertiesObjects/boardIDUploadProtocol/strict/object" }, - "upload.use_1200bps_touch": { + "upload(\\..+)?\\.use_1200bps_touch": { "$ref": "#/definitions/propertiesObjects/boardIDUploadUse1200bpsTouch/strict/object" }, - "upload.wait_for_upload_port": { + "upload(\\..+)?\\.wait_for_upload_port": { "$ref": "#/definitions/propertiesObjects/boardIDUploadWaitForUploadPort/strict/object" - } - } - }, - { - "patternProperties": { - "^upload.tool(\\..+)?$": { - "$ref": "#/definitions/propertiesObjects/boardIDUploadTool/strict/object" }, "^[vp]id\\.[0-9]+$": { "$ref": "#/definitions/propertiesObjects/boardIDXidN/strict/object"