mirror of
https://github.com/go-task/task.git
synced 2025-06-23 00:38:19 +02:00
feat: error when multiple wildcard matches are found
This commit is contained in:
@ -1249,8 +1249,8 @@ $ task run-foo-bar
|
|||||||
foo bar
|
foo bar
|
||||||
```
|
```
|
||||||
|
|
||||||
If multiple matching tasks are found, the first one listed in the Taskfile will
|
If multiple matching tasks are found, an error occurs. If you are using included
|
||||||
be used. If you are using included Taskfiles,
|
Taskfiles, tasks in parent files will be considered first.
|
||||||
|
|
||||||
## Doing task cleanup with `defer`
|
## Doing task cleanup with `defer`
|
||||||
|
|
||||||
|
@ -65,15 +65,15 @@ func (err *TaskInternalError) Code() int {
|
|||||||
return CodeTaskInternal
|
return CodeTaskInternal
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskNameConflictError is returned when multiple tasks with the same name or
|
// TaskNameConflictError is returned when multiple tasks with a matching name or
|
||||||
// alias are found.
|
// alias are found.
|
||||||
type TaskNameConflictError struct {
|
type TaskNameConflictError struct {
|
||||||
AliasName string
|
Call string
|
||||||
TaskNames []string
|
TaskNames []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *TaskNameConflictError) Error() string {
|
func (err *TaskNameConflictError) Error() string {
|
||||||
return fmt.Sprintf(`task: Multiple tasks (%s) with alias %q found`, strings.Join(err.TaskNames, ", "), err.AliasName)
|
return fmt.Sprintf(`task: Found multiple tasks (%s) that match %q`, strings.Join(err.TaskNames, ", "), err.Call)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *TaskNameConflictError) Code() int {
|
func (err *TaskNameConflictError) Code() int {
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
func PrintTasks(l *logger.Logger, t *ast.Taskfile, c []*ast.Call) {
|
func PrintTasks(l *logger.Logger, t *ast.Taskfile, c []*ast.Call) {
|
||||||
for i, call := range c {
|
for i, call := range c {
|
||||||
PrintSpaceBetweenSummaries(l, i)
|
PrintSpaceBetweenSummaries(l, i)
|
||||||
PrintTask(l, t.Tasks.Get(call))
|
PrintTask(l, t.Tasks.Get(call.Task))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
task.go
24
task.go
@ -415,12 +415,28 @@ func (e *Executor) startExecution(ctx context.Context, t *ast.Task, execute func
|
|||||||
// If multiple tasks contain the same alias or no matches are found an error is returned.
|
// If multiple tasks contain the same alias or no matches are found an error is returned.
|
||||||
func (e *Executor) GetTask(call *ast.Call) (*ast.Task, error) {
|
func (e *Executor) GetTask(call *ast.Call) (*ast.Task, error) {
|
||||||
// Search for a matching task
|
// Search for a matching task
|
||||||
matchingTask := e.Taskfile.Tasks.Get(call)
|
matchingTasks := e.Taskfile.Tasks.FindMatchingTasks(call)
|
||||||
if matchingTask != nil {
|
switch len(matchingTasks) {
|
||||||
return matchingTask, nil
|
case 0: // Carry on
|
||||||
|
case 1:
|
||||||
|
if call.Vars == nil {
|
||||||
|
call.Vars = &ast.Vars{}
|
||||||
|
}
|
||||||
|
call.Vars.Set("MATCH", ast.Var{Value: matchingTasks[0].Wildcards})
|
||||||
|
return matchingTasks[0].Task, nil
|
||||||
|
default:
|
||||||
|
taskNames := make([]string, len(matchingTasks))
|
||||||
|
for i, matchingTask := range matchingTasks {
|
||||||
|
taskNames[i] = matchingTask.Task.Task
|
||||||
|
}
|
||||||
|
return nil, &errors.TaskNameConflictError{
|
||||||
|
Call: call.Task,
|
||||||
|
TaskNames: taskNames,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If didn't find one, search for a task with a matching alias
|
// If didn't find one, search for a task with a matching alias
|
||||||
|
var matchingTask *ast.Task
|
||||||
var aliasedTasks []string
|
var aliasedTasks []string
|
||||||
for _, task := range e.Taskfile.Tasks.Values() {
|
for _, task := range e.Taskfile.Tasks.Values() {
|
||||||
if slices.Contains(task.Aliases, call.Task) {
|
if slices.Contains(task.Aliases, call.Task) {
|
||||||
@ -431,7 +447,7 @@ func (e *Executor) GetTask(call *ast.Call) (*ast.Task, error) {
|
|||||||
// If we found multiple tasks
|
// If we found multiple tasks
|
||||||
if len(aliasedTasks) > 1 {
|
if len(aliasedTasks) > 1 {
|
||||||
return nil, &errors.TaskNameConflictError{
|
return nil, &errors.TaskNameConflictError{
|
||||||
AliasName: call.Task,
|
Call: call.Task,
|
||||||
TaskNames: aliasedTasks,
|
TaskNames: aliasedTasks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
29
task_test.go
29
task_test.go
@ -2253,33 +2253,44 @@ func TestFor(t *testing.T) {
|
|||||||
func TestWildcard(t *testing.T) {
|
func TestWildcard(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
call string
|
||||||
expectedOutput string
|
expectedOutput string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "wildcard-foo",
|
name: "basic wildcard",
|
||||||
|
call: "wildcard-foo",
|
||||||
expectedOutput: "Hello foo\n",
|
expectedOutput: "Hello foo\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "foo-wildcard-bar",
|
name: "double wildcard",
|
||||||
|
call: "foo-wildcard-bar",
|
||||||
expectedOutput: "Hello foo bar\n",
|
expectedOutput: "Hello foo bar\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "start-foo",
|
name: "store wildcard",
|
||||||
|
call: "start-foo",
|
||||||
expectedOutput: "Starting foo\n",
|
expectedOutput: "Starting foo\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "matches-exactly-*",
|
name: "matches exactly",
|
||||||
expectedOutput: "I don't consume matches: \n",
|
call: "matches-exactly-*",
|
||||||
|
expectedOutput: "I don't consume matches: []\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no-match",
|
name: "no matches",
|
||||||
|
call: "no-match",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple matches",
|
||||||
|
call: "wildcard-foo-bar",
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.call, func(t *testing.T) {
|
||||||
var buff bytes.Buffer
|
var buff bytes.Buffer
|
||||||
e := task.Executor{
|
e := task.Executor{
|
||||||
Dir: "testdata/wildcards",
|
Dir: "testdata/wildcards",
|
||||||
@ -2290,10 +2301,10 @@ func TestWildcard(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.NoError(t, e.Setup())
|
require.NoError(t, e.Setup())
|
||||||
if test.wantErr {
|
if test.wantErr {
|
||||||
require.Error(t, e.Run(context.Background(), &ast.Call{Task: test.name}))
|
require.Error(t, e.Run(context.Background(), &ast.Call{Task: test.call}))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: test.name}))
|
require.NoError(t, e.Run(context.Background(), &ast.Call{Task: test.call}))
|
||||||
assert.Equal(t, test.expectedOutput, buff.String())
|
assert.Equal(t, test.expectedOutput, buff.String())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -14,29 +14,34 @@ type Tasks struct {
|
|||||||
omap.OrderedMap[string, *Task]
|
omap.OrderedMap[string, *Task]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Tasks) Get(call *Call) *Task {
|
type MatchingTask struct {
|
||||||
|
Task *Task
|
||||||
|
Wildcards []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Tasks) FindMatchingTasks(call *Call) []*MatchingTask {
|
||||||
if call == nil {
|
if call == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var task *Task
|
var task *Task
|
||||||
|
var matchingTasks []*MatchingTask
|
||||||
// If there is a direct match, return it
|
// If there is a direct match, return it
|
||||||
if task = t.OrderedMap.Get(call.Task); task != nil {
|
if task = t.OrderedMap.Get(call.Task); task != nil {
|
||||||
return task
|
matchingTasks = append(matchingTasks, &MatchingTask{Task: task, Wildcards: nil})
|
||||||
}
|
return matchingTasks
|
||||||
if call.Vars == nil {
|
|
||||||
call.Vars = &Vars{}
|
|
||||||
}
|
}
|
||||||
// Attempt a wildcard match
|
// Attempt a wildcard match
|
||||||
// TODO: We need to add a yield func to the Range method so that we can stop looping when we find a match
|
|
||||||
// For now, we can just nil check the task before each loop
|
// For now, we can just nil check the task before each loop
|
||||||
_ = t.Range(func(key string, value *Task) error {
|
_ = t.Range(func(key string, value *Task) error {
|
||||||
if match, wildcards := value.WildcardMatch(call.Task); match && task == nil {
|
if match, wildcards := value.WildcardMatch(call.Task); match {
|
||||||
task = value
|
matchingTasks = append(matchingTasks, &MatchingTask{
|
||||||
call.Vars.Set("MATCH", Var{Value: wildcards})
|
Task: value,
|
||||||
|
Wildcards: wildcards,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return task
|
return matchingTasks
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t1 *Tasks) Merge(t2 Tasks, include *Include) {
|
func (t1 *Tasks) Merge(t2 Tasks, include *Include) {
|
||||||
@ -86,11 +91,10 @@ func (t1 *Tasks) Merge(t2 Tasks, include *Include) {
|
|||||||
// run the included Taskfile's default task without specifying its full
|
// run the included Taskfile's default task without specifying its full
|
||||||
// name. If the parent namespace has aliases, we add another alias for each
|
// name. If the parent namespace has aliases, we add another alias for each
|
||||||
// of them.
|
// of them.
|
||||||
if t2.Get(&Call{Task: "default"}) != nil && t1.Get(&Call{Task: include.Namespace}) == nil {
|
if t2.Get("default") != nil && t1.Get(include.Namespace) == nil {
|
||||||
defaultTaskName := fmt.Sprintf("%s:default", include.Namespace)
|
defaultTaskName := fmt.Sprintf("%s:default", include.Namespace)
|
||||||
defaultTaskCall := &Call{Task: defaultTaskName}
|
t1.Get(defaultTaskName).Aliases = append(t1.Get(defaultTaskName).Aliases, include.Namespace)
|
||||||
t1.Get(defaultTaskCall).Aliases = append(t1.Get(defaultTaskCall).Aliases, include.Namespace)
|
t1.Get(defaultTaskName).Aliases = append(t1.Get(defaultTaskName).Aliases, include.Aliases...)
|
||||||
t1.Get(defaultTaskCall).Aliases = append(t1.Get(defaultTaskCall).Aliases, include.Aliases...)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
testdata/wildcards/Taskfile.yml
vendored
4
testdata/wildcards/Taskfile.yml
vendored
@ -5,6 +5,10 @@ tasks:
|
|||||||
cmds:
|
cmds:
|
||||||
- echo "Hello {{index .MATCH 0}}"
|
- echo "Hello {{index .MATCH 0}}"
|
||||||
|
|
||||||
|
wildcard-*-*:
|
||||||
|
cmds:
|
||||||
|
- echo "Hello {{index .MATCH 0}}"
|
||||||
|
|
||||||
'*-wildcard-*':
|
'*-wildcard-*':
|
||||||
cmds:
|
cmds:
|
||||||
- echo "Hello {{index .MATCH 0}} {{index .MATCH 1}}"
|
- echo "Hello {{index .MATCH 0}} {{index .MATCH 1}}"
|
||||||
|
Reference in New Issue
Block a user