mirror of
https://github.com/go-task/task.git
synced 2024-12-16 10:59:23 +02:00
commit
d516b238b1
3
.gitignore
vendored
3
.gitignore
vendored
@ -18,3 +18,6 @@
|
|||||||
dist/
|
dist/
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# intellij idea/goland
|
||||||
|
.idea/
|
||||||
|
@ -17,7 +17,7 @@ var (
|
|||||||
version = "master"
|
version = "master"
|
||||||
)
|
)
|
||||||
|
|
||||||
const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--dry] [task...]
|
const usage = `Usage: task [-ilfwvsd] [--init] [--list] [--force] [--watch] [--verbose] [--silent] [--dir] [--dry] [--summary] [task...]
|
||||||
|
|
||||||
Runs the specified task(s). Falls back to the "default" task if no task name
|
Runs the specified task(s). Falls back to the "default" task if no task name
|
||||||
was specified, or lists all tasks if an unknown task name was specified.
|
was specified, or lists all tasks if an unknown task name was specified.
|
||||||
@ -56,6 +56,7 @@ func main() {
|
|||||||
verbose bool
|
verbose bool
|
||||||
silent bool
|
silent bool
|
||||||
dry bool
|
dry bool
|
||||||
|
summary bool
|
||||||
dir string
|
dir string
|
||||||
output string
|
output string
|
||||||
)
|
)
|
||||||
@ -69,6 +70,7 @@ func main() {
|
|||||||
pflag.BoolVarP(&verbose, "verbose", "v", false, "enables verbose mode")
|
pflag.BoolVarP(&verbose, "verbose", "v", false, "enables verbose mode")
|
||||||
pflag.BoolVarP(&silent, "silent", "s", false, "disables echoing")
|
pflag.BoolVarP(&silent, "silent", "s", false, "disables echoing")
|
||||||
pflag.BoolVar(&dry, "dry", false, "compiles and prints tasks in the order that they would be run, without executing them")
|
pflag.BoolVar(&dry, "dry", false, "compiles and prints tasks in the order that they would be run, without executing them")
|
||||||
|
pflag.BoolVar(&summary, "summary", false, "show summary about a task")
|
||||||
pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
|
pflag.StringVarP(&dir, "dir", "d", "", "sets directory of execution")
|
||||||
pflag.StringVarP(&output, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
|
pflag.StringVarP(&output, "output", "o", "", "sets output style: [interleaved|group|prefixed]")
|
||||||
pflag.Parse()
|
pflag.Parse()
|
||||||
@ -96,6 +98,7 @@ func main() {
|
|||||||
Silent: silent,
|
Silent: silent,
|
||||||
Dir: dir,
|
Dir: dir,
|
||||||
Dry: dry,
|
Dry: dry,
|
||||||
|
Summary: summary,
|
||||||
|
|
||||||
Stdin: os.Stdin,
|
Stdin: os.Stdin,
|
||||||
Stdout: os.Stdout,
|
Stdout: os.Stdout,
|
||||||
|
@ -542,6 +542,51 @@ would print the following output:
|
|||||||
* test: Run all the go tests.
|
* test: Run all the go tests.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Display summary of task
|
||||||
|
|
||||||
|
Running `task --summary task-name` will show a summary of a task
|
||||||
|
The following Taskfile:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
release:
|
||||||
|
deps: [build]
|
||||||
|
summary: |
|
||||||
|
Release your project to github
|
||||||
|
|
||||||
|
It will build your project before starting the release it.
|
||||||
|
Please make sure that you have set GITHUB_TOKEN before starting.
|
||||||
|
cmds:
|
||||||
|
- your-release-tool
|
||||||
|
|
||||||
|
build:
|
||||||
|
cmds:
|
||||||
|
- your-build-tool
|
||||||
|
```
|
||||||
|
|
||||||
|
with running ``task --summary release`` would print the following output:
|
||||||
|
|
||||||
|
```
|
||||||
|
task: release
|
||||||
|
|
||||||
|
Release your project to github
|
||||||
|
|
||||||
|
It will build your project before starting the release it.
|
||||||
|
Please make sure that you have set GITHUB_TOKEN before starting.
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- build
|
||||||
|
|
||||||
|
commands:
|
||||||
|
- your-release-tool
|
||||||
|
```
|
||||||
|
If a summary is missing, the description will be printed.
|
||||||
|
If the task does not have a summary or a description, a warning is printed.
|
||||||
|
|
||||||
|
Please note: *showing the summary will not execute the command*.
|
||||||
|
|
||||||
## Silent mode
|
## Silent mode
|
||||||
|
|
||||||
Silent mode disables echoing of commands before Task runs it.
|
Silent mode disables echoing of commands before Task runs it.
|
||||||
|
103
internal/summary/summary.go
Normal file
103
internal/summary/summary.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package summary
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v2/internal/logger"
|
||||||
|
"github.com/go-task/task/v2/internal/taskfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func PrintTasks(l *logger.Logger, t *taskfile.Taskfile, c []taskfile.Call) {
|
||||||
|
for i, call := range c {
|
||||||
|
printSpaceBetweenSummaries(l, i)
|
||||||
|
PrintTask(l, t.Tasks[call.Task])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printSpaceBetweenSummaries(l *logger.Logger, i int) {
|
||||||
|
spaceRequired := i > 0
|
||||||
|
if !spaceRequired {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Outf("")
|
||||||
|
l.Outf("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintTask(l *logger.Logger, t *taskfile.Task) {
|
||||||
|
printTaskName(l, t)
|
||||||
|
printTaskDescribingText(t, l)
|
||||||
|
printTaskDependencies(l, t)
|
||||||
|
printTaskCommands(l, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTaskDescribingText(t *taskfile.Task, l *logger.Logger) {
|
||||||
|
if hasSummary(t) {
|
||||||
|
printTaskSummary(l, t)
|
||||||
|
} else if hasDescription(t) {
|
||||||
|
printTaskDescription(l, t)
|
||||||
|
} else {
|
||||||
|
printNoDescriptionOrSummary(l)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasSummary(t *taskfile.Task) bool {
|
||||||
|
return t.Summary != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTaskSummary(l *logger.Logger, t *taskfile.Task) {
|
||||||
|
lines := strings.Split(t.Summary, "\n")
|
||||||
|
for i, line := range lines {
|
||||||
|
notLastLine := i+1 < len(lines)
|
||||||
|
if notLastLine || line != "" {
|
||||||
|
l.Outf(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTaskName(l *logger.Logger, t *taskfile.Task) {
|
||||||
|
l.Outf("task: %s", t.Task)
|
||||||
|
l.Outf("")
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasDescription(t *taskfile.Task) bool {
|
||||||
|
return t.Desc != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTaskDescription(l *logger.Logger, t *taskfile.Task) {
|
||||||
|
l.Outf(t.Desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printNoDescriptionOrSummary(l *logger.Logger) {
|
||||||
|
l.Outf("(task does not have description or summary)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTaskDependencies(l *logger.Logger, t *taskfile.Task) {
|
||||||
|
if len(t.Deps) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Outf("")
|
||||||
|
l.Outf("dependencies:")
|
||||||
|
|
||||||
|
for _, d := range t.Deps {
|
||||||
|
l.Outf(" - %s", d.Task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printTaskCommands(l *logger.Logger, t *taskfile.Task) {
|
||||||
|
if len(t.Cmds) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Outf("")
|
||||||
|
l.Outf("commands:")
|
||||||
|
for _, c := range t.Cmds {
|
||||||
|
isCommand := c.Cmd != ""
|
||||||
|
if isCommand {
|
||||||
|
l.Outf(" - %s", c.Cmd)
|
||||||
|
} else {
|
||||||
|
l.Outf(" - Task: %s", c.Task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
173
internal/summary/summary_test.go
Normal file
173
internal/summary/summary_test.go
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
package summary_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v2/internal/logger"
|
||||||
|
"github.com/go-task/task/v2/internal/summary"
|
||||||
|
"github.com/go-task/task/v2/internal/taskfile"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPrintsDependenciesIfPresent(t *testing.T) {
|
||||||
|
buffer, l := createDummyLogger()
|
||||||
|
task := &taskfile.Task{
|
||||||
|
Deps: []*taskfile.Dep{
|
||||||
|
{Task: "dep1"},
|
||||||
|
{Task: "dep2"},
|
||||||
|
{Task: "dep3"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.PrintTask(&l, task)
|
||||||
|
|
||||||
|
assert.Contains(t, buffer.String(), "\ndependencies:\n - dep1\n - dep2\n - dep3\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDummyLogger() (*bytes.Buffer, logger.Logger) {
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
l := logger.Logger{
|
||||||
|
Stderr: buffer,
|
||||||
|
Stdout: buffer,
|
||||||
|
Verbose: false,
|
||||||
|
}
|
||||||
|
return buffer, l
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDoesNotPrintDependenciesIfMissing(t *testing.T) {
|
||||||
|
buffer, l := createDummyLogger()
|
||||||
|
task := &taskfile.Task{
|
||||||
|
Deps: []*taskfile.Dep{},
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.PrintTask(&l, task)
|
||||||
|
|
||||||
|
assert.NotContains(t, buffer.String(), "dependencies:")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintTaskName(t *testing.T) {
|
||||||
|
buffer, l := createDummyLogger()
|
||||||
|
task := &taskfile.Task{
|
||||||
|
Task: "my-task-name",
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.PrintTask(&l, task)
|
||||||
|
|
||||||
|
assert.Contains(t, buffer.String(), "task: my-task-name\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintTaskCommandsIfPresent(t *testing.T) {
|
||||||
|
buffer, l := createDummyLogger()
|
||||||
|
task := &taskfile.Task{
|
||||||
|
Cmds: []*taskfile.Cmd{
|
||||||
|
{Cmd: "command-1"},
|
||||||
|
{Cmd: "command-2"},
|
||||||
|
{Task: "task-1"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.PrintTask(&l, task)
|
||||||
|
|
||||||
|
assert.Contains(t, buffer.String(), "\ncommands:\n")
|
||||||
|
assert.Contains(t, buffer.String(), "\n - command-1\n")
|
||||||
|
assert.Contains(t, buffer.String(), "\n - command-2\n")
|
||||||
|
assert.Contains(t, buffer.String(), "\n - Task: task-1\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDoesNotPrintCommandIfMissing(t *testing.T) {
|
||||||
|
buffer, l := createDummyLogger()
|
||||||
|
task := &taskfile.Task{
|
||||||
|
Cmds: []*taskfile.Cmd{},
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.PrintTask(&l, task)
|
||||||
|
|
||||||
|
assert.NotContains(t, buffer.String(), "commands")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLayout(t *testing.T) {
|
||||||
|
buffer, l := createDummyLogger()
|
||||||
|
task := &taskfile.Task{
|
||||||
|
Task: "sample-task",
|
||||||
|
Summary: "line1\nline2\nline3\n",
|
||||||
|
Deps: []*taskfile.Dep{
|
||||||
|
{Task: "dependency"},
|
||||||
|
},
|
||||||
|
Cmds: []*taskfile.Cmd{
|
||||||
|
{Cmd: "command"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
summary.PrintTask(&l, task)
|
||||||
|
|
||||||
|
assert.Equal(t, expectedOutput(), buffer.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectedOutput() string {
|
||||||
|
expected := `task: sample-task
|
||||||
|
|
||||||
|
line1
|
||||||
|
line2
|
||||||
|
line3
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- dependency
|
||||||
|
|
||||||
|
commands:
|
||||||
|
- command
|
||||||
|
`
|
||||||
|
return expected
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintDescriptionAsFallback(t *testing.T) {
|
||||||
|
buffer, l := createDummyLogger()
|
||||||
|
taskWithoutSummary := &taskfile.Task{
|
||||||
|
Desc: "description",
|
||||||
|
}
|
||||||
|
|
||||||
|
taskWithSummary := &taskfile.Task{
|
||||||
|
Desc: "description",
|
||||||
|
Summary: "summary",
|
||||||
|
}
|
||||||
|
taskWithoutSummaryOrDescription := &taskfile.Task{}
|
||||||
|
|
||||||
|
summary.PrintTask(&l, taskWithoutSummary)
|
||||||
|
|
||||||
|
assert.Contains(t, buffer.String(), "description")
|
||||||
|
|
||||||
|
buffer.Reset()
|
||||||
|
summary.PrintTask(&l, taskWithSummary)
|
||||||
|
|
||||||
|
assert.NotContains(t, buffer.String(), "description")
|
||||||
|
|
||||||
|
buffer.Reset()
|
||||||
|
summary.PrintTask(&l, taskWithoutSummaryOrDescription)
|
||||||
|
|
||||||
|
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrintAllWithSpaces(t *testing.T) {
|
||||||
|
buffer, l := createDummyLogger()
|
||||||
|
|
||||||
|
t1 := &taskfile.Task{Task: "t1"}
|
||||||
|
t2 := &taskfile.Task{Task: "t2"}
|
||||||
|
t3 := &taskfile.Task{Task: "t3"}
|
||||||
|
|
||||||
|
tasks := make(taskfile.Tasks, 3)
|
||||||
|
tasks["t1"] = t1
|
||||||
|
tasks["t2"] = t2
|
||||||
|
tasks["t3"] = t3
|
||||||
|
|
||||||
|
summary.PrintTasks(&l,
|
||||||
|
&taskfile.Taskfile{Tasks: tasks},
|
||||||
|
[]taskfile.Call{{Task: "t1"}, {Task: "t2"}, {Task: "t3"}})
|
||||||
|
|
||||||
|
assert.True(t, strings.HasPrefix(buffer.String(), "task: t1"))
|
||||||
|
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n\n\ntask: t2")
|
||||||
|
assert.Contains(t, buffer.String(), "\n(task does not have description or summary)\n\n\ntask: t3")
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package taskfile
|
package taskfile
|
||||||
|
|
||||||
// Tasks representas a group of tasks
|
// Tasks represents a group of tasks
|
||||||
type Tasks map[string]*Task
|
type Tasks map[string]*Task
|
||||||
|
|
||||||
// Task represents a task
|
// Task represents a task
|
||||||
@ -9,6 +9,7 @@ type Task struct {
|
|||||||
Cmds []*Cmd
|
Cmds []*Cmd
|
||||||
Deps []*Dep
|
Deps []*Dep
|
||||||
Desc string
|
Desc string
|
||||||
|
Summary string
|
||||||
Sources []string
|
Sources []string
|
||||||
Generates []string
|
Generates []string
|
||||||
Status []string
|
Status []string
|
||||||
|
7
task.go
7
task.go
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/go-task/task/v2/internal/execext"
|
"github.com/go-task/task/v2/internal/execext"
|
||||||
"github.com/go-task/task/v2/internal/logger"
|
"github.com/go-task/task/v2/internal/logger"
|
||||||
"github.com/go-task/task/v2/internal/output"
|
"github.com/go-task/task/v2/internal/output"
|
||||||
|
"github.com/go-task/task/v2/internal/summary"
|
||||||
"github.com/go-task/task/v2/internal/taskfile"
|
"github.com/go-task/task/v2/internal/taskfile"
|
||||||
"github.com/go-task/task/v2/internal/taskfile/read"
|
"github.com/go-task/task/v2/internal/taskfile/read"
|
||||||
"github.com/go-task/task/v2/internal/taskfile/version"
|
"github.com/go-task/task/v2/internal/taskfile/version"
|
||||||
@ -36,6 +37,7 @@ type Executor struct {
|
|||||||
Verbose bool
|
Verbose bool
|
||||||
Silent bool
|
Silent bool
|
||||||
Dry bool
|
Dry bool
|
||||||
|
Summary bool
|
||||||
|
|
||||||
Stdin io.Reader
|
Stdin io.Reader
|
||||||
Stdout io.Writer
|
Stdout io.Writer
|
||||||
@ -62,6 +64,11 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.Summary {
|
||||||
|
summary.PrintTasks(e.Logger, e.Taskfile, calls)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if e.Watch {
|
if e.Watch {
|
||||||
return e.watchTasks(calls...)
|
return e.watchTasks(calls...)
|
||||||
}
|
}
|
||||||
|
22
task_test.go
22
task_test.go
@ -553,3 +553,25 @@ func TestIncludesCallingRoot(t *testing.T) {
|
|||||||
}
|
}
|
||||||
tt.Run(t)
|
tt.Run(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSummary(t *testing.T) {
|
||||||
|
const dir = "testdata/summary"
|
||||||
|
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
Summary: true,
|
||||||
|
Silent: true,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "task-with-summary"}, taskfile.Call{Task: "other-task-with-summary"}))
|
||||||
|
assert.Equal(t, readTestFixture(t, dir, "task-with-summary.txt"), buff.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func readTestFixture(t *testing.T, dir string, file string) string {
|
||||||
|
b, err := ioutil.ReadFile(dir + "/" + file)
|
||||||
|
assert.NoError(t, err, "error reading text fixture")
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
26
testdata/summary/Taskfile.yml
vendored
Normal file
26
testdata/summary/Taskfile.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
task-with-summary:
|
||||||
|
deps: [dependend-task-1, dependend-task-2]
|
||||||
|
summary: |
|
||||||
|
summary of task-with-summary - line 1
|
||||||
|
line 2
|
||||||
|
line 3
|
||||||
|
cmds:
|
||||||
|
- echo 'task-with-summary was executed'
|
||||||
|
- echo 'another command'
|
||||||
|
- exit 0
|
||||||
|
|
||||||
|
other-task-with-summary:
|
||||||
|
summary: summary of other-task-with-summary
|
||||||
|
cmds:
|
||||||
|
- echo 'other-task-with-summary was executed'
|
||||||
|
|
||||||
|
dependend-task-1:
|
||||||
|
cmds:
|
||||||
|
- echo 'dependend-task-1 was executed'
|
||||||
|
|
||||||
|
dependend-task-2:
|
||||||
|
cmds:
|
||||||
|
- echo 'dependend-task-2 was executed'
|
22
testdata/summary/task-with-summary.txt
vendored
Normal file
22
testdata/summary/task-with-summary.txt
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
task: task-with-summary
|
||||||
|
|
||||||
|
summary of task-with-summary - line 1
|
||||||
|
line 2
|
||||||
|
line 3
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
- dependend-task-1
|
||||||
|
- dependend-task-2
|
||||||
|
|
||||||
|
commands:
|
||||||
|
- echo 'task-with-summary was executed'
|
||||||
|
- echo 'another command'
|
||||||
|
- exit 0
|
||||||
|
|
||||||
|
|
||||||
|
task: other-task-with-summary
|
||||||
|
|
||||||
|
summary of other-task-with-summary
|
||||||
|
|
||||||
|
commands:
|
||||||
|
- echo 'other-task-with-summary was executed'
|
Loading…
Reference in New Issue
Block a user