1
0
mirror of https://github.com/go-task/task.git synced 2025-01-20 04:59:37 +02:00

feat: aliases

feat: add aliases to --list and --list-all flags

feat: add aliases to --summary

feat: enable aliases for included tasks

tests: added alias unit tests
This commit is contained in:
Pete Davison 2022-10-01 22:39:44 +00:00
parent d2061ec898
commit 376a6182eb
15 changed files with 183 additions and 21 deletions

View File

@ -3,6 +3,7 @@ package task
import (
"errors"
"fmt"
"strings"
"mvdan.cc/sh/v3/interp"
)
@ -20,6 +21,15 @@ func (err *taskNotFoundError) Error() string {
return fmt.Sprintf(`task: Task %q not found`, err.taskName)
}
type multipleTasksWithAliasError struct {
aliasName string
taskNames []string
}
func (err *multipleTasksWithAliasError) Error() string {
return fmt.Sprintf(`task: Multiple tasks (%s) with alias %q found`, strings.Join(err.taskNames, ", "), err.aliasName)
}
type taskInternalError struct {
taskName string
}

3
go.mod
View File

@ -9,6 +9,7 @@ require (
github.com/radovskyb/watcher v1.0.7
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
gopkg.in/yaml.v3 v3.0.1
mvdan.cc/sh/v3 v3.6.0-0.dev.0.20220704111049-a6e3029cd899
@ -19,7 +20,7 @@ require (
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)

9
go.sum
View File

@ -7,7 +7,7 @@ github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYF
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@ -34,16 +34,17 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4=
golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -44,8 +44,9 @@ func (e *Executor) printTasks(listAll bool) {
// Format in tab-separated columns with a tab stop of 8.
w := tabwriter.NewWriter(e.Stdout, 0, 8, 6, ' ', 0)
for _, task := range tasks {
taskNames := append([]string{task.Task}, task.Aliases...)
e.Logger.FOutf(w, logger.Yellow, "* ")
e.Logger.FOutf(w, logger.Green, task.Task)
e.Logger.FOutf(w, logger.Green, strings.Join(taskNames, "|"))
e.Logger.FOutf(w, logger.Default, ": \t%s", task.Desc)
fmt.Fprint(w, "\n")
}

View File

@ -28,6 +28,7 @@ func PrintTask(l *logger.Logger, t *taskfile.Task) {
printTaskName(l, t)
printTaskDescribingText(t, l)
printTaskDependencies(l, t)
printTaskAliases(l, t)
printTaskCommands(l, t)
}
@ -61,6 +62,18 @@ func printTaskName(l *logger.Logger, t *taskfile.Task) {
l.Outf(logger.Default, "")
}
func printTaskAliases(l *logger.Logger, t *taskfile.Task) {
if len(t.Aliases) == 0 {
return
}
l.Outf(logger.Default, "")
l.Outf(logger.Default, "aliases:")
for _, alias := range t.Aliases {
l.FOutf(l.Stdout, logger.Default, " - ")
l.Outf(logger.Cyan, alias)
}
}
func hasDescription(t *taskfile.Task) bool {
return t.Desc != ""
}

53
task.go
View File

@ -16,6 +16,7 @@ import (
"github.com/go-task/task/v3/internal/templater"
"github.com/go-task/task/v3/taskfile"
"golang.org/x/exp/slices"
"golang.org/x/sync/errgroup"
)
@ -63,16 +64,15 @@ type Executor struct {
// Run runs Task
func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
// check if given tasks exist
for _, c := range calls {
t, ok := e.Taskfile.Tasks[c.Task]
if !ok {
// FIXME: move to the main package
for _, call := range calls {
task, err := e.GetTask(call)
if err != nil {
e.ListTasksWithDesc()
return &taskNotFoundError{taskName: c.Task}
return err
}
if t.Internal {
if task.Internal {
e.ListTasksWithDesc()
return &taskInternalError{taskName: c.Task}
return &taskInternalError{taskName: call.Task}
}
}
@ -112,8 +112,8 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
if err != nil {
return err
}
if !e.Watch && atomic.AddInt32(e.taskCallCount[call.Task], 1) >= MaximumTaskCall {
return &MaximumTaskCallExceededError{task: call.Task}
if !e.Watch && atomic.AddInt32(e.taskCallCount[t.Task], 1) >= MaximumTaskCall {
return &MaximumTaskCallExceededError{task: t.Task}
}
release := e.acquireConcurrencyLimit()
@ -330,3 +330,38 @@ func (e *Executor) startExecution(ctx context.Context, t *taskfile.Task, execute
return execute(ctx)
}
// GetTask will return the task with the name matching the given call from the taskfile.
// If no task is found, it will search for tasks with a matching alias.
// If multiple tasks contain the same alias or no matches are found an error is returned.
func (e *Executor) GetTask(call taskfile.Call) (*taskfile.Task, error) {
// Search for a matching task
matchingTask, ok := e.Taskfile.Tasks[call.Task]
if ok {
return matchingTask, nil
}
// If didn't find one, search for a task with a matching alias
var aliasedTasks []string
for _, task := range e.Taskfile.Tasks {
if slices.Contains(task.Aliases, call.Task) {
aliasedTasks = append(aliasedTasks, task.Task)
matchingTask = task
}
}
// If we found multiple tasks
if len(aliasedTasks) > 1 {
return nil, &multipleTasksWithAliasError{
aliasName: call.Task,
taskNames: aliasedTasks,
}
}
// If we found no tasks
if len(aliasedTasks) == 0 {
return nil, &taskNotFoundError{
taskName: call.Task,
}
}
return matchingTask, nil
}

View File

@ -476,6 +476,58 @@ func TestStatusChecksum(t *testing.T) {
assert.Equal(t, `task: Task "build" is up to date`+"\n", buff.String())
}
func TestAlias(t *testing.T) {
const dir = "testdata/alias"
data, err := os.ReadFile(filepathext.SmartJoin(dir, "alias.txt"))
assert.NoError(t, err)
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "f"}))
assert.Equal(t, string(data), buff.String())
}
func TestDuplicateAlias(t *testing.T) {
const dir = "testdata/alias"
data, err := os.ReadFile(filepathext.SmartJoin(dir, "alias-duplicate.txt"))
assert.NoError(t, err)
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.Error(t, e.Run(context.Background(), taskfile.Call{Task: "x"}))
assert.Equal(t, string(data), buff.String())
}
func TestAliasSummary(t *testing.T) {
const dir = "testdata/alias"
data, err := os.ReadFile(filepathext.SmartJoin(dir, "alias-summary.txt"))
assert.NoError(t, err)
var buff bytes.Buffer
e := task.Executor{
Dir: dir,
Summary: true,
Stdout: &buff,
Stderr: &buff,
}
assert.NoError(t, e.Setup())
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "f"}))
assert.Equal(t, string(data), buff.String())
}
func TestLabelUpToDate(t *testing.T) {
const dir = "testdata/label_uptodate"
@ -989,7 +1041,7 @@ func TestIncludesInternal(t *testing.T) {
err := e.Run(context.Background(), taskfile.Call{Task: test.task})
if test.expectedErr {
assert.Error(t, err, test.expectedErr)
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
@ -1029,7 +1081,7 @@ func TestInternalTask(t *testing.T) {
err := e.Run(context.Background(), taskfile.Call{Task: test.task})
if test.expectedErr {
assert.Error(t, err, test.expectedErr)
assert.Error(t, err)
} else {
assert.NoError(t, err)
}

View File

@ -55,6 +55,9 @@ func Merge(t1, t2 *Taskfile, internal bool, namespaces ...string) error {
cmd.Task = taskNameWithNamespace(cmd.Task, namespaces...)
}
}
for i, alias := range v.Aliases {
v.Aliases[i] = taskNameWithNamespace(alias, namespaces...)
}
}
return nil

View File

@ -11,6 +11,7 @@ type Task struct {
Label string
Desc string
Summary string
Aliases []string
Sources []string
Generates []string
Status []string
@ -56,6 +57,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
Label string
Desc string
Summary string
Aliases []string
Sources []string
Generates []string
Status []string
@ -78,6 +80,7 @@ func (t *Task) UnmarshalYAML(unmarshal func(interface{}) error) error {
t.Deps = task.Deps
t.Label = task.Label
t.Desc = task.Desc
t.Aliases = task.Aliases
t.Summary = task.Summary
t.Sources = task.Sources
t.Generates = task.Generates

18
testdata/alias/Taskfile.yml vendored Normal file
View File

@ -0,0 +1,18 @@
version: '3'
includes:
included:
taskfile: Taskfile2.yml
tasks:
foo:
aliases: [f, x]
cmds:
- echo "foo"
- task: b
bar:
aliases: [b, x]
cmds:
- echo "bar"
- task: included:q

7
testdata/alias/Taskfile2.yml vendored Normal file
View File

@ -0,0 +1,7 @@
version: '3'
tasks:
qux:
aliases: [q, x]
cmds:
- echo "qux"

1
testdata/alias/alias-duplicate.txt vendored Normal file
View File

@ -0,0 +1 @@
task: No tasks with description available. Try --list-all to list all tasks

11
testdata/alias/alias-summary.txt vendored Normal file
View File

@ -0,0 +1,11 @@
task: foo
(task does not have description or summary)
aliases:
- f
- x
commands:
- echo "foo"
- Task: b

6
testdata/alias/alias.txt vendored Normal file
View File

@ -0,0 +1,6 @@
task: [foo] echo "foo"
foo
task: [bar] echo "bar"
bar
task: [included:qux] echo "qux"
qux

View File

@ -22,13 +22,12 @@ func (e *Executor) FastCompiledTask(call taskfile.Call) (*taskfile.Task, error)
}
func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskfile.Task, error) {
origTask, ok := e.Taskfile.Tasks[call.Task]
if !ok {
return nil, &taskNotFoundError{call.Task}
origTask, err := e.GetTask(call)
if err != nil {
return nil, err
}
var vars *taskfile.Vars
var err error
if evaluateShVars {
vars, err = e.Compiler.GetVariables(origTask, call)
} else {
@ -50,6 +49,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
Label: r.Replace(origTask.Label),
Desc: r.Replace(origTask.Desc),
Summary: r.Replace(origTask.Summary),
Aliases: origTask.Aliases,
Sources: r.ReplaceSlice(origTask.Sources),
Generates: r.ReplaceSlice(origTask.Generates),
Dir: r.Replace(origTask.Dir),