Repository: compose-spec/compose-go
Pull request #472 (2023-10-13 21:58:52)

loader: fix a panic
fixes https://github.com/compose-spec/compose-go/issues/471

nicks: (originally was going to only file the issue, but it turned out to be easy enough to fix)

Patch:
diff --git a/loader/loader.go b/loader/loader.go
index 1799075..a700046 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -238,6 +238,15 @@ func LoadWithContext(ctx context.Context, configDetails types.ConfigDetails, opt
 		return nil, err
 	}
 	opts.projectName = projectName
+
+	// TODO(milas): this should probably ALWAYS set (overriding any existing)
+	if _, ok := configDetails.Environment[consts.ComposeProjectName]; !ok && projectName != "" {
+		if configDetails.Environment == nil {
+			configDetails.Environment = map[string]string{}
+		}
+		configDetails.Environment[consts.ComposeProjectName] = projectName
+	}
+
 	return load(ctx, configDetails, opts, nil)
 }
 
@@ -461,10 +470,6 @@ func projectName(details types.ConfigDetails, opts *Options) (string, error) {
 		return "", InvalidProjectNameErr(projectName)
 	}
 
-	// TODO(milas): this should probably ALWAYS set (overriding any existing)
-	if _, ok := details.Environment[consts.ComposeProjectName]; !ok && projectName != "" {
-		details.Environment[consts.ComposeProjectName] = projectName
-	}
 	return projectName, nil
 }
 


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index a3f4a17..8d7c668 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -2246,7 +2246,7 @@ func TestLoadLegacyBoolean(t *testing.T) {
 name: load-legacy-boolean
 services:
   test:
-    init: yes # used to be a valid YAML bool, removed in YAML 1.2 
+    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)
@@ -2276,7 +2276,7 @@ services:
   test:
     build:
       context: .
-      ssh: 
+      ssh:
         key1: value1
 `)
 	assert.NilError(t, err)
@@ -2294,7 +2294,7 @@ services:
   test:
     build:
       context: .
-      ssh: 
+      ssh:
         - key1=value1
         - key2=value2
 `)
@@ -2642,7 +2642,7 @@ include:
     env_file: ./testdata/subdir/extra.env
   - path: ./testdata/compose-include.yaml
     env_file: ./testdata/subdir/extra.env
-    
+
 
 services:
   bar:
@@ -2767,7 +2767,7 @@ services:
 func TestLoadWithNestedResources(t *testing.T) {
 	config := buildConfigDetails(`
 name: test-nested-resources
-include: 
+include:
   - remote:nested/compose.yaml
 `, nil)
 	_, err := LoadWithContext(context.Background(), config, func(options *Options) {
@@ -2808,7 +2808,7 @@ name: load-multi-docs
 services:
   test:
     image: nginx:latest
---- 
+---
 services:
   test:
     image: nginx:override
@@ -2826,7 +2826,7 @@ services:
     image: example/webapp
     build: ./webapp
     develop:
-      watch: 
+      watch:
         # sync static content
         - path: ./webapp/html
           action: sync
@@ -2838,7 +2838,7 @@ services:
     image: example/backend
     build: ./backend
     develop:
-      watch: 
+      watch:
         # rebuild image and recreate service
         - path: ./backend/src
           action: rebuild
@@ -2869,3 +2869,25 @@ services:
 		},
 	})
 }
+
+func TestBadServiceConfig(t *testing.T) {
+	yaml := `name: scratch
+services:
+  redis:
+    image: redis:6.2.6-alpine
+    network_mode: bridge
+    networks:
+      gratheon: null
+networks:
+  gratheon:
+    name: scratch_gratheon
+`
+	_, err := LoadWithContext(context.Background(), types.ConfigDetails{
+		ConfigFiles: []types.ConfigFile{
+			{
+				Content: []byte(yaml),
+			},
+		},
+	})
+	assert.ErrorContains(t, err, "service redis declares mutually exclusive `network_mode` and `networks`")
+}