Repository: compose-spec/compose-go
Pull request #197 (2021-10-24 12:46:52)

don't truncate project name computed by parent folder name with dots
when project name includes some unsupported characters (like `my.project`) we must ignore them, not truncate the computed project name => `myproject`

fix https://github.com/docker/compose/issues/8822

Also introduce a dedicated `normalizeName` func to manage project name computation, vs regexp which tends to become complex to maintain :P 

rfay: Just a note: This isn't just a problem for projects computed from project name, it's also a problem for project names provided by `docker-compose -p` or `COMPOSE_PROJECT_NAME`. If any of those ways have '.' in them, the project name is truncated.

Patch:
diff --git a/cli/options.go b/cli/options.go
index 4498aaf..e7c277c 100644
--- a/cli/options.go
+++ b/cli/options.go
@@ -335,7 +335,7 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
 		} else {
 			opts.Name = filepath.Base(absWorkingDir)
 		}
-		opts.Name = regexp.MustCompile(`(?m)[a-z0-9]+[-_a-z0-9]*`).FindString(strings.ToLower(opts.Name))
+		opts.Name = normalizeName(opts.Name)
 	}
 	options.loadOptions = append(options.loadOptions, nameLoadOpt)
 
@@ -352,6 +352,13 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
 	return project, nil
 }
 
+func normalizeName(s string) string {
+	r := regexp.MustCompile("[a-z0-9_-]")
+	s = strings.ToLower(s)
+	s = strings.Join(r.FindAllString(s, -1), "")
+	return strings.TrimLeft(s, "_-")
+}
+
 // getConfigPathsFromOptions retrieves the config files for project based on project options
 func getConfigPathsFromOptions(options *ProjectOptions) ([]string, error) {
 	if len(options.ConfigPaths) != 0 {
diff --git a/types/types.go b/types/types.go
index 4d68f86..7e0de83 100644
--- a/types/types.go
+++ b/types/types.go
@@ -89,7 +89,7 @@ type ServiceConfig struct {
 	Profiles []string `mapstructure:"profiles" yaml:"profiles,omitempty" json:"profiles,omitempty"`
 
 	Build           *BuildConfig                     `yaml:",omitempty" json:"build,omitempty"`
-	BlkioConfig     *BlkioConfig                     `mapstructure:"blkio_config" yaml:",omitempty" json:"blkio_config,omitempty"`
+	BlkioConfig     *BlkioConfig                     `yaml:",omitempty" json:"blkio_config,omitempty"`
 	CapAdd          []string                         `mapstructure:"cap_add" yaml:"cap_add,omitempty" json:"cap_add,omitempty"`
 	CapDrop         []string                         `mapstructure:"cap_drop" yaml:"cap_drop,omitempty" json:"cap_drop,omitempty"`
 	CgroupParent    string                           `mapstructure:"cgroup_parent" yaml:"cgroup_parent,omitempty" json:"cgroup_parent,omitempty"`


Test patch:
diff --git a/cli/options_test.go b/cli/options_test.go
index 80b0fcf..8dfebee 100644
--- a/cli/options_test.go
+++ b/cli/options_test.go
@@ -82,6 +82,22 @@ func TestProjectName(t *testing.T) {
 		assert.Equal(t, p.Name, "my_project")
 	})
 
+	t.Run("by name contains dots", func(t *testing.T) {
+		opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithName("www.my.project"))
+		assert.NilError(t, err)
+		p, err := ProjectFromOptions(opts)
+		assert.NilError(t, err)
+		assert.Equal(t, p.Name, "wwwmyproject")
+	})
+
+	t.Run("by name uppercase", func(t *testing.T) {
+		opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithName("MY_PROJECT"))
+		assert.NilError(t, err)
+		p, err := ProjectFromOptions(opts)
+		assert.NilError(t, err)
+		assert.Equal(t, p.Name, "my_project")
+	})
+
 	t.Run("by working dir", func(t *testing.T) {
 		opts, err := NewProjectOptions([]string{"testdata/simple/compose.yaml"}, WithWorkingDirectory("."))
 		assert.NilError(t, err)