This is an automated email from the ASF dual-hosted git repository. sjanc pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mynewt-newt.git
commit 0d173ab42bef38bea76d269c35d6c79b87fd8684 Author: Szymon Janc <[email protected]> AuthorDate: Mon Oct 19 17:54:35 2026 +0200 newt/ycfg: Warn about ignored non-map values Values of keys that must be maps (e.g., syscfg.defs, syscfg.vals, pkg.init) were silently ignored if they were not maps. Common mistakes are "syscfg.vals: [FOO]" lists and flattened keys like "syscfg.vals.FOO: 1", where FOO is a condition, not a setting name. Warn about such values, including values under conditions which are false. Assisted-by: Claude:claude-opus-5-5 --- newt/error_reporting_test.go | 47 ++++++++++++++++++++++++++++++++++++++++++++ newt/ycfg/ycfg.go | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/newt/error_reporting_test.go b/newt/error_reporting_test.go index 299f1fba..9d7ab1c1 100644 --- a/newt/error_reporting_test.go +++ b/newt/error_reporting_test.go @@ -171,3 +171,50 @@ func TestErrorReportingInfoUnknownRepo(t *testing.T) { } } } + +// Values that must be maps but are not (e.g., a setting value written as a +// flattened "syscfg.vals.FOO" key) are ignored; newt must warn about them. +func TestErrorReportingNonMapSyscfg(t *testing.T) { + tests := []struct { + name string + syscfg string + msg string + }{ + { + name: "flattened-val", + syscfg: "syscfg.vals.FOO: 1\n", + msg: `ignoring "syscfg.vals.FOO"; "FOO" is a condition ` + + "and value must be a map", + }, + { + name: "vals-list", + syscfg: "syscfg.vals:\n - FOO\n", + msg: `ignoring "syscfg.vals"; value must be a map`, + }, + { + name: "defs-list", + syscfg: "syscfg.defs:\n - BAR\n", + msg: `ignoring "syscfg.defs"; value must be a map`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dir := copyToTemp(t, errorReportingProjDir) + errorReportingWriteFiles(t, dir, map[string]string{ + "targets/app/syscfg.yml": tt.syscfg, + }) + + out, err := newtCombinedOutput(dir, nil, "target", "config", + "show", "app") + if err != nil { + t.Fatalf("newt target config show failed: %s\n%s", err, out) + } + if !strings.Contains(out, "WARNING: ") || + !strings.Contains(out, tt.msg) { + + t.Errorf("missing warning %q\n%s", tt.msg, out) + } + }) + } +} diff --git a/newt/ycfg/ycfg.go b/newt/ycfg/ycfg.go index 53571175..b588b235 100644 --- a/newt/ycfg/ycfg.go +++ b/newt/ycfg/ycfg.go @@ -453,9 +453,46 @@ func (yc *YCfg) GetValStringSliceNonempty( // values to type map[string]interface{}. The returned map[string]YCfgEntry is // formed from the union of all these maps. The returned error is a set of // warnings just as in `Get`. +// warnNonMapValues warns about values of the specified key, and of its +// conditional variants, that are not maps. Such values are ignored when a map +// is expected. For example, "syscfg.vals.FOO: 1" is "syscfg.vals" conditional +// on FOO, not a value of setting FOO, and "syscfg.vals: [FOO]" is a list. +func (yc *YCfg) warnNonMapValues(key string) { + node := yc.find(key) + if node == nil { + return + } + + isMapOrEmpty := func(val interface{}) bool { + if val == nil { + return true + } + if s, ok := val.(string); ok && s == "" { + return true + } + _, err := cast.ToStringMapE(val) + return err == nil + } + + if !isMapOrEmpty(node.Value) { + util.OneTimeWarning("%s: ignoring \"%s\"; value must be a map, "+ + "not %T", yc.name, key, node.Value) + } + + for _, child := range node.Children { + if !isMapOrEmpty(child.Value) { + util.OneTimeWarning("%s: ignoring \"%s\"; \"%s\" is a condition "+ + "and value must be a map, not %T", yc.name, + child.FullName(), child.Name, child.Value) + } + } +} + func (yc *YCfg) GetStringMap( key string, settings *cfgv.Settings) (map[string]YCfgEntry, error) { + yc.warnNonMapValues(key) + mapEntries, getErr := yc.Get(key, settings) if len(mapEntries) == 0 { return nil, getErr
