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 6cefa62ae..f51f6e8e2 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 b22b92737..a44f23a45 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 63aec64fb..6c575671f 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 8c13640b9..d9a6ed44c 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 f51f6e8e2..c2e28446f 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 bb4c5ba14..58226e79c 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 2fbc4b33e..50140cc8b 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 50e85c5d2..af541a7ac 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 d66b9c52b..fc299a138 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 871d14539..05f71286c 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 d6d9b5137..fd3b6976e 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 a55168fcc..221d1a6e7 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 d9a6ed44c..513afa572 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 3adfb9b67..ddbab5ea1 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 a44f23a45..e7728cfb2 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 8effadebf..bb2fac0d2 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 14781796d..27fe7d292 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 bed0553cd..fa21761d3 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 6c575671f..2c79dd934 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 e72779772..3e0fa7053 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 000000000..1866d3c44 --- /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 000000000..a5cb97302 --- /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 000000000..1866d3c44 --- /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 000000000..a4ca8bb89 --- /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 000000000..1866d3c44 --- /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 000000000..dec708ad5 --- /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 000000000..1866d3c44 --- /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 000000000..d25b65ff5 --- /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 dec708ad5..c672acdba 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 000000000..1866d3c44 --- /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 000000000..fdc29f4c9 --- /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 96acd7b8c..7cfdec8d8 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 2c79dd934..347fcafd7 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 0468b2545..9f746fa01 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 c4e09df52..93c9709c8 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 fa21761d3..e40988c51 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 347fcafd7..fb5c21eae 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 098c8075b..2cf60e7c5 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 1866d3c44..8f48a3050 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 7cfdec8d8..d3f18ea7f 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 9f746fa01..76d197ccc 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 5ecff6d94..7e0ab5c2e 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 d3f18ea7f..11898e0c5 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 50140cc8b..51e86c9d7 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 fd3b6976e..13cb7fcd1 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 513afa572..2b9b3e49e 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 fb5c21eae..adc7c1e5d 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 11898e0c5..85b12cf7b 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 51e86c9d7..57ce303e3 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 13cb7fcd1..aa5f4541e 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 221d1a6e7..7a108ff58 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 2b9b3e49e..c687949c9 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 e7728cfb2..aa4da58a8 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 bb2fac0d2..5f8e0ae61 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 27fe7d292..2a217d811 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 e40988c51..89721672b 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 adc7c1e5d..357e18d8b 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 3e0fa7053..da7aca93b 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 000000000..1866d3c44 --- /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 000000000..c672acdba --- /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 000000000..1866d3c44 --- /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 000000000..1eb51ea86 --- /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 000000000..1866d3c44 --- /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 000000000..bb9e43884 --- /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 000000000..1866d3c44 --- /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 000000000..746491dda --- /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 c672acdba..ff72f1567 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 85b12cf7b..985e1062e 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 76d197ccc..e3144e51a 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 93c9709c8..ac71f5375 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 357e18d8b..bbdee73d3 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 27b68f20d..d6f1024e7 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 1100a9c81..db54b6c1a 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 985e1062e..d007d082b 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"