diff --git a/task_test.go b/task_test.go index 540e7ba8de..c3ceb86769 100644 --- a/task_test.go +++ b/task_test.go @@ -3417,6 +3417,16 @@ func TestWildcard(t *testing.T) { call: "wildcard-foo-bar", expectedOutput: "Hello foo-bar\n", }, + { + name: "regex metacharacters are matched literally", + call: "c++", + expectedOutput: "Building c++\n", + }, + { + name: "a dot is not a wildcard", + call: "deploy-prod", + wantErr: true, + }, } for _, test := range tests { diff --git a/taskfile/ast/task.go b/taskfile/ast/task.go index 9465c77770..e3bb0cf08b 100644 --- a/taskfile/ast/task.go +++ b/taskfile/ast/task.go @@ -1,7 +1,6 @@ package ast import ( - "fmt" "regexp" "strings" @@ -87,23 +86,24 @@ 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, "*", "(.*)")) - regex := regexp.MustCompile(regexStr) - wildcards := regex.FindStringSubmatch(name) - - if len(wildcards) == 0 { + // Without a wildcard the name is a plain string, so skip building a regex + if !strings.Contains(taskName, "*") { + if taskName == name { + return true, nil + } continue } - // Remove the first match, which is the full string - wildcards = wildcards[1:] - wildcardCount := strings.Count(taskName, "*") + // Escape the task name so a name like "c++" or "a.b" is matched literally + // and does not panic in MustCompile, then turn the escaped "*" back into + // the wildcard group + pattern := strings.ReplaceAll(regexp.QuoteMeta(taskName), `\*`, "(.*)") + regex := regexp.MustCompile("^" + pattern + "$") + wildcards := regex.FindStringSubmatch(name) - if len(wildcards) != wildcardCount { - continue + if len(wildcards) > 1 { + return true, wildcards[1:] } - - return true, wildcards } return false, nil diff --git a/testdata/wildcards/Taskfile.yml b/testdata/wildcards/Taskfile.yml index 0ec2ae2895..e152876c01 100644 --- a/testdata/wildcards/Taskfile.yml +++ b/testdata/wildcards/Taskfile.yml @@ -25,3 +25,12 @@ tasks: SERVICE: "{{index .MATCH 0}}" cmds: - echo "Starting {{.SERVICE}}" + + # Regex metacharacters in a task name must be matched literally + c++: + cmds: + - echo "Building c++" + + deploy.prod: + cmds: + - echo "Deploying prod"