diff --git a/taskfile/ast/task.go b/taskfile/ast/task.go index 9465c77770..d86ff0159d 100644 --- a/taskfile/ast/task.go +++ b/taskfile/ast/task.go @@ -87,7 +87,10 @@ func (t *Task) WildcardMatch(name string) (bool, []string) { names := append([]string{t.Task}, t.Aliases...) for _, taskName := range names { - regexStr := fmt.Sprintf("^%s$", strings.ReplaceAll(taskName, "*", "(.*)")) + // Escape regex metacharacters in the task name so a name like "c++" or + // "a.b" is matched literally (and doesn't panic in MustCompile); only + // "*" is a wildcard. + regexStr := fmt.Sprintf("^%s$", strings.ReplaceAll(regexp.QuoteMeta(taskName), `\*`, "(.*)")) regex := regexp.MustCompile(regexStr) wildcards := regex.FindStringSubmatch(name) diff --git a/taskfile/ast/task_test.go b/taskfile/ast/task_test.go new file mode 100644 index 0000000000..0083a537a9 --- /dev/null +++ b/taskfile/ast/task_test.go @@ -0,0 +1,41 @@ +package ast_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/go-task/task/v3/taskfile/ast" +) + +func TestTaskWildcardMatch(t *testing.T) { + t.Parallel() + + tests := []struct { + taskName string + call string + wantMatch bool + wantWildcards []string + }{ + {"build-*", "build-foo", true, []string{"foo"}}, + {"build-*-*", "build-foo-bar", true, []string{"foo", "bar"}}, + {"build-*", "test-foo", false, nil}, + // Regex metacharacters in the task name must be matched literally, not + // interpreted as regex (and must not panic in MustCompile). + {"c++", "c++", true, []string{}}, + {"c++", "cxx", false, nil}, + {"a.b", "axb", false, nil}, // '.' must not act as a wildcard + {"a.b", "a.b", true, []string{}}, + {"deploy.prod", "deploy-prod", false, nil}, + } + + for _, tt := range tests { + t.Run(tt.taskName+"/"+tt.call, func(t *testing.T) { + t.Parallel() + task := &ast.Task{Task: tt.taskName} + match, wildcards := task.WildcardMatch(tt.call) + assert.Equal(t, tt.wantMatch, match) + assert.Equal(t, tt.wantWildcards, wildcards) + }) + } +}