diff --git a/loader/loader.go b/loader/loader.go index 59f0ed9a..b5c86392 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -271,6 +271,12 @@ func InvalidProjectNameErr(v string) error { ) } +// projectName determines the canonical name to use for the project considering +// the loader Options as well as `name` fields in Compose YAML fields (which +// also support interpolation). +// +// TODO(milas): restructure loading so that we don't need to re-parse the YAML +// here, as it's both wasteful and makes this code error-prone. func projectName(details types.ConfigDetails, opts *Options) (string, error) { projectName, projectNameImperativelySet := opts.GetProjectName() @@ -281,10 +287,22 @@ func projectName(details types.ConfigDetails, opts *Options) (string, error) { for _, configFile := range details.ConfigFiles { yml, err := ParseYAML(configFile.Content) if err != nil { + // HACK: the way that loading is currently structured, this is + // a duplicative parse just for the `name`. if it fails, we + // give up but don't return the error, knowing that it'll get + // caught downstream for us return "", nil } if val, ok := yml["name"]; ok && val != "" { - pjNameFromConfigFile = yml["name"].(string) + sVal, ok := val.(string) + if !ok { + // HACK: see above - this is a temporary parsed version + // that hasn't been schema-validated, but we don't want + // to be the ones to actually report that, so give up, + // knowing that it'll get caught downstream for us + return "", nil + } + pjNameFromConfigFile = sVal } } if !opts.SkipInterpolation { diff --git a/loader/loader_test.go b/loader/loader_test.go index 59902af4..34766001 100644 --- a/loader/loader_test.go +++ b/loader/loader_test.go @@ -2337,3 +2337,9 @@ func TestDeviceWriteBps(t *testing.T) { }) } + +func TestInvalidProjectNameType(t *testing.T) { + p, err := loadYAML(`name: 123`) + assert.Error(t, err, "name must be a string") + assert.Assert(t, is.Nil(p)) +}