Repository: compose-spec/compose-go
Pull request #382 (2023-04-04 06:42:35)

Support legacy boolean `yes|no|on|off` but warn user about incompatibility


Patch:
diff --git a/interpolation/interpolation.go b/interpolation/interpolation.go
index befc6f0..f77c19c 100644
--- a/interpolation/interpolation.go
+++ b/interpolation/interpolation.go
@@ -72,7 +72,7 @@ func recursiveInterpolate(value interface{}, path Path, opts Options) (interface
 	switch value := value.(type) {
 	case string:
 		newValue, err := opts.Substitute(value, template.Mapping(opts.LookupValue))
-		if err != nil || newValue == value {
+		if err != nil {
 			return value, newPathError(path, err)
 		}
 		caster, ok := opts.getCasterForPath(path)
diff --git a/loader/interpolate.go b/loader/interpolate.go
index 97a19f5..42ffe64 100644
--- a/loader/interpolate.go
+++ b/loader/interpolate.go
@@ -22,6 +22,7 @@ import (
 
 	interp "github.com/compose-spec/compose-go/interpolation"
 	"github.com/pkg/errors"
+	"github.com/sirupsen/logrus"
 )
 
 var interpolateTypeCastMapping = map[interp.Path]interp.Cast{
@@ -114,9 +115,15 @@ func toFloat32(value string) (interface{}, error) {
 // should match http://yaml.org/type/bool.html
 func toBoolean(value string) (interface{}, error) {
 	switch strings.ToLower(value) {
-	case "y", "yes", "true", "on":
+	case "true":
 		return true, nil
-	case "n", "no", "false", "off":
+	case "false":
+		return false, nil
+	case "y", "yes", "on":
+		logrus.Warnf("%q for boolean is not supported by YAML 1.2, please use `true`", value)
+		return true, nil
+	case "n", "no", "off":
+		logrus.Warnf("%q for boolean is not supported by YAML 1.2, please use `false`", value)
 		return false, nil
 	default:
 		return nil, errors.Errorf("invalid boolean: %s", value)
diff --git a/loader/normalize.go b/loader/normalize.go
index d915604..3c9e1ad 100644
--- a/loader/normalize.go
+++ b/loader/normalize.go
@@ -117,7 +117,7 @@ func Normalize(project *types.Project, resolvePaths bool) error {
 		}
 
 		for _, vol := range s.VolumesFrom {
-			if !strings.HasPrefix(vol, types.ContainerPrefix) {
+			if !strings.HasPrefix(s.Pid, types.ContainerPrefix) {
 				spec := strings.Split(vol, ":")
 				s.DependsOn = setIfMissing(s.DependsOn, spec[0], types.ServiceDependency{
 					Condition: types.ServiceConditionStarted,
diff --git a/types/types.go b/types/types.go
index 226ddc8..c59b540 100644
--- a/types/types.go
+++ b/types/types.go
@@ -296,7 +296,7 @@ func (s set) toSlice() []string {
 type BuildConfig struct {
 	Context            string                `yaml:",omitempty" json:"context,omitempty"`
 	Dockerfile         string                `yaml:",omitempty" json:"dockerfile,omitempty"`
-	DockerfileInline   string                `yaml:"dockerfile_inline,omitempty" json:"dockerfile_inline,omitempty"`
+	DockerfileInline   string                `yaml:",omitempty" json:"dockerfile_inline,omitempty"`
 	Args               MappingWithEquals     `yaml:",omitempty" json:"args,omitempty"`
 	SSH                SSHConfig             `yaml:"ssh,omitempty" json:"ssh,omitempty"`
 	Labels             Labels                `yaml:",omitempty" json:"labels,omitempty"`


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 59902af..b8dfe7e 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -2126,6 +2126,17 @@ services:
 	assert.ErrorContains(t, err, "services.test.build.ssh must be a mapping")
 }
 
+func TestLoadLegacyBoolean(t *testing.T) {
+	actual, err := loadYAML(`
+name: load-legacy-boolean
+services:
+  test:
+    init: yes # used to be a valid YAML bool, removed in YAML 1.2 
+`)
+	assert.NilError(t, err)
+	assert.Check(t, *actual.Services[0].Init)
+}
+
 func TestLoadSSHWithDefaultValueInBuildConfig(t *testing.T) {
 	actual, err := loadYAML(`
 name: load-ssh-with-default-value-in-build-config
diff --git a/loader/normalize_test.go b/loader/normalize_test.go
index 807e286..1066a53 100644
--- a/loader/normalize_test.go
+++ b/loader/normalize_test.go
@@ -201,11 +201,8 @@ func TestNormalizeDependsOn(t *testing.T) {
 				NetworkMode: "service:zot",
 			},
 			{
-				Name: "bar",
-				VolumesFrom: []string{
-					"zot",
-					"container:xxx",
-				},
+				Name:        "bar",
+				VolumesFrom: []string{"zot"},
 			},
 			{
 				Name: "zot",
@@ -223,7 +220,6 @@ services:
       default: null
     volumes_from:
       - zot
-      - container:xxx
   foo:
     depends_on:
       bar:
diff --git a/types/types_test.go b/types/types_test.go
index e4e7a13..2ec5476 100644
--- a/types/types_test.go
+++ b/types/types_test.go
@@ -17,9 +17,7 @@
 package types
 
 import (
-	"bytes"
 	"encoding/json"
-	"fmt"
 	"strings"
 	"testing"
 
@@ -331,40 +329,3 @@ func TestMarshalServiceEntrypoint(t *testing.T) {
 	}
 
 }
-
-func TestMarshalBuild_DockerfileInline(t *testing.T) {
-	b := BuildConfig{
-		DockerfileInline: "FROM alpine\n\n# echo the env\nRUN env\n\nENTRYPOINT /bin/echo\n",
-	}
-	out, err := yaml.Marshal(b)
-	assert.NilError(t, err)
-
-	const expected = `
-dockerfile_inline: |
-    FROM alpine
-
-    # echo the env
-    RUN env
-
-    ENTRYPOINT /bin/echo
-`
-	assert.Check(t, equalTrimSpace(out, expected))
-
-	// round-trip
-	var b2 BuildConfig
-	assert.NilError(t, yaml.Unmarshal(out, &b2))
-	assert.Check(t, equalTrimSpace(b.DockerfileInline, b2.DockerfileInline))
-}
-
-func equalTrimSpace(x interface{}, y interface{}) is.Comparison {
-	trim := func(v interface{}) interface{} {
-		switch vv := v.(type) {
-		case string:
-			return strings.TrimSpace(vv)
-		case []byte:
-			return string(bytes.TrimSpace(vv))
-		}
-		panic(fmt.Errorf("invalid type %T (value: %+v)", v, v))
-	}
-	return is.DeepEqual(trim(x), trim(y))
-}