Repository: compose-spec/compose-go
Pull request #607 (2024-03-20 11:24:37)

fix WithProfiles to return a project wih Profiles set
fix `WithProfiles` to return a project with `Profiles` attribute set when `--profiles=*` is used. Otherwise later call to `WithServicesEnabled` as an explicit service is passed as command argument won't get those services enabled

closes https://github.com/docker/compose/issues/11608


Patch:
diff --git a/template/template.go b/template/template.go
index 63ae61c..0d9c17e 100644
--- a/template/template.go
+++ b/template/template.go
@@ -44,7 +44,7 @@ var patternString = fmt.Sprintf(
 	groupInvalid,
 )
 
-var defaultPattern = regexp.MustCompile(patternString)
+var DefaultPattern = regexp.MustCompile(patternString)
 
 // InvalidTemplateError is returned when a variable template is not in a valid
 // format
@@ -121,7 +121,7 @@ func SubstituteWithOptions(template string, mapping Mapping, options ...Option)
 	var returnErr error
 
 	cfg := &Config{
-		pattern:         defaultPattern,
+		pattern:         DefaultPattern,
 		replacementFunc: DefaultReplacementFunc,
 		logging:         true,
 	}
@@ -268,14 +268,14 @@ func getFirstBraceClosingIndex(s string) int {
 
 // Substitute variables in the string with their values
 func Substitute(template string, mapping Mapping) (string, error) {
-	return SubstituteWith(template, mapping, defaultPattern)
+	return SubstituteWith(template, mapping, DefaultPattern)
 }
 
 // ExtractVariables returns a map of all the variables defined in the specified
 // composefile (dict representation) and their default value if any.
 func ExtractVariables(configDict map[string]interface{}, pattern *regexp.Regexp) map[string]Variable {
 	if pattern == nil {
-		pattern = defaultPattern
+		pattern = DefaultPattern
 	}
 	return recurseExtract(configDict, pattern)
 }
diff --git a/types/project.go b/types/project.go
index c12e966..b9d18eb 100644
--- a/types/project.go
+++ b/types/project.go
@@ -332,6 +332,9 @@ func (s ServiceConfig) HasProfile(profiles []string) bool {
 		return true
 	}
 	for _, p := range profiles {
+		if p == "*" {
+			return true
+		}
 		for _, sp := range s.Profiles {
 			if sp == p {
 				return true
@@ -345,11 +348,6 @@ func (s ServiceConfig) HasProfile(profiles []string) bool {
 // It returns a new Project instance with the changes and keep the original Project unchanged
 func (p *Project) WithProfiles(profiles []string) (*Project, error) {
 	newProject := p.deepCopy()
-	for _, p := range profiles {
-		if p == "*" {
-			return newProject, nil
-		}
-	}
 	enabled := Services{}
 	disabled := Services{}
 	for name, service := range newProject.AllServices() {


Test patch:
diff --git a/template/template_test.go b/template/template_test.go
index 66fa3c5..b1afa21 100644
--- a/template/template_test.go
+++ b/template/template_test.go
@@ -366,15 +366,15 @@ func TestSubstituteWithCustomFunc(t *testing.T) {
 		return value, true, nil
 	}
 
-	result, err := SubstituteWith("ok ${FOO}", defaultMapping, defaultPattern, errIsMissing)
+	result, err := SubstituteWith("ok ${FOO}", defaultMapping, DefaultPattern, errIsMissing)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal("ok first", result))
 
-	result, err = SubstituteWith("ok ${BAR}", defaultMapping, defaultPattern, errIsMissing)
+	result, err = SubstituteWith("ok ${BAR}", defaultMapping, DefaultPattern, errIsMissing)
 	assert.NilError(t, err)
 	assert.Check(t, is.Equal("ok ", result))
 
-	_, err = SubstituteWith("ok ${NOTHERE}", defaultMapping, defaultPattern, errIsMissing)
+	_, err = SubstituteWith("ok ${NOTHERE}", defaultMapping, DefaultPattern, errIsMissing)
 	assert.Check(t, is.ErrorContains(err, "required variable"))
 }
 
@@ -594,7 +594,7 @@ func TestExtractVariables(t *testing.T) {
 	for _, tc := range testCases {
 		tc := tc
 		t.Run(tc.name, func(t *testing.T) {
-			actual := ExtractVariables(tc.dict, defaultPattern)
+			actual := ExtractVariables(tc.dict, DefaultPattern)
 			assert.Check(t, is.DeepEqual(actual, tc.expected))
 		})
 	}