Repository: compose-spec/compose-go
Pull request #346 (2023-01-17 04:53:55)

fix build context path to allow url to a git repository
Related to: https://github.com/docker/compose/issues/10174


Patch:
diff --git a/loader/loader.go b/loader/loader.go
index 6afa79f..43c4b1d 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -554,9 +554,7 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
 			// absolute path.
 			baseFileParent := filepath.Dir(*file)
 			if baseService.Build != nil {
-				// Note that the Dockerfile is always defined relative to the
-				// build context, so there's no need to update the Dockerfile field.
-				baseService.Build.Context = absPath(baseFileParent, baseService.Build.Context)
+				baseService.Build.Context = resolveBuildContextPath(baseFileParent, baseService.Build.Context)
 			}
 
 			for i, vol := range baseService.Volumes {
@@ -576,6 +574,19 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
 	return serviceConfig, nil
 }
 
+func resolveBuildContextPath(baseFileParent string, context string) string {
+	// Checks if the context is an HTTP(S) URL or a remote git repository URL
+	for _, prefix := range []string{"https://", "http://", "git://", "github.com/", "git@"} {
+		if strings.HasPrefix(context, prefix) {
+			return context
+		}
+	}
+
+	// Note that the Dockerfile is always defined relative to the
+	// build context, so there's no need to update the Dockerfile field.
+	return absPath(baseFileParent, context)
+}
+
 // LoadService produces a single ServiceConfig from a compose file Dict
 // the serviceDict is not validated if directly used. Use Load() to enable validation
 func LoadService(name string, serviceDict map[string]interface{}, workingDir string, lookupEnv template.Mapping, resolvePaths bool, convertPaths bool) (*types.ServiceConfig, error) {


Test patch:
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 66a9252..e97713f 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -1873,6 +1873,39 @@ func TestLoadWithExtends(t *testing.T) {
 	assert.Check(t, is.DeepEqual(expServices, actual.Services))
 }
 
+func TestLoadWithExtendsWithContextUrl(t *testing.T) {
+	b, err := os.ReadFile("testdata/compose-test-extends-with-context-url.yaml")
+	assert.NilError(t, err)
+
+	configDetails := types.ConfigDetails{
+		WorkingDir: "testdata",
+		ConfigFiles: []types.ConfigFile{
+			{Filename: "testdata/compose-test-extends-with-context-url.yaml", Content: b},
+		},
+	}
+
+	actual, err := Load(configDetails)
+	assert.NilError(t, err)
+
+	expServices := types.Services{
+		{
+			Name: "importer-with-https-url",
+			Build: &types.BuildConfig{
+				Context:    "https://github.com/docker/compose.git",
+				Dockerfile: "Dockerfile",
+			},
+			Extends: types.ExtendsConfig{
+				"file":    strPtr("compose-test-extends-with-context-url-imported.yaml"),
+				"service": strPtr("imported-with-https-url"),
+			},
+			Environment: types.MappingWithEquals{},
+			Networks:    map[string]*types.ServiceNetworkConfig{"default": nil},
+			Scale:       1,
+		},
+	}
+	assert.Check(t, is.DeepEqual(expServices, actual.Services))
+}
+
 func TestServiceDeviceRequestCount(t *testing.T) {
 	_, err := loadYAML(`
 services:
diff --git a/loader/testdata/compose-test-extends-with-context-url-imported.yaml b/loader/testdata/compose-test-extends-with-context-url-imported.yaml
new file mode 100644
index 0000000..3fa1b5b
--- /dev/null
+++ b/loader/testdata/compose-test-extends-with-context-url-imported.yaml
@@ -0,0 +1,4 @@
+services:
+  imported-with-https-url:
+    build:
+      context: https://github.com/docker/compose.git
diff --git a/loader/testdata/compose-test-extends-with-context-url.yaml b/loader/testdata/compose-test-extends-with-context-url.yaml
new file mode 100644
index 0000000..963c875
--- /dev/null
+++ b/loader/testdata/compose-test-extends-with-context-url.yaml
@@ -0,0 +1,5 @@
+services:
+  importer-with-https-url:
+    extends:
+      file: compose-test-extends-with-context-url-imported.yaml
+      service: imported-with-https-url