mirror of
https://github.com/go-task/task.git
synced 2025-06-23 00:38:19 +02:00
feat(prompts): add ability for tasks to prompt user pre execution (#1163)
This commit is contained in:
@ -56,6 +56,7 @@ var flags struct {
|
|||||||
watch bool
|
watch bool
|
||||||
verbose bool
|
verbose bool
|
||||||
silent bool
|
silent bool
|
||||||
|
assumeYes bool
|
||||||
dry bool
|
dry bool
|
||||||
summary bool
|
summary bool
|
||||||
exitCode bool
|
exitCode bool
|
||||||
@ -113,6 +114,7 @@ func run() error {
|
|||||||
pflag.BoolVarP(&flags.watch, "watch", "w", false, "Enables watch of the given task.")
|
pflag.BoolVarP(&flags.watch, "watch", "w", false, "Enables watch of the given task.")
|
||||||
pflag.BoolVarP(&flags.verbose, "verbose", "v", false, "Enables verbose mode.")
|
pflag.BoolVarP(&flags.verbose, "verbose", "v", false, "Enables verbose mode.")
|
||||||
pflag.BoolVarP(&flags.silent, "silent", "s", false, "Disables echoing.")
|
pflag.BoolVarP(&flags.silent, "silent", "s", false, "Disables echoing.")
|
||||||
|
pflag.BoolVarP(&flags.assumeYes, "yes", "y", false, "Assume \"yes\" as answer to all prompts.")
|
||||||
pflag.BoolVarP(&flags.parallel, "parallel", "p", false, "Executes tasks provided on command line in parallel.")
|
pflag.BoolVarP(&flags.parallel, "parallel", "p", false, "Executes tasks provided on command line in parallel.")
|
||||||
pflag.BoolVarP(&flags.dry, "dry", "n", false, "Compiles and prints tasks in the order that they would be run, without executing them.")
|
pflag.BoolVarP(&flags.dry, "dry", "n", false, "Compiles and prints tasks in the order that they would be run, without executing them.")
|
||||||
pflag.BoolVar(&flags.summary, "summary", false, "Show summary about a task.")
|
pflag.BoolVar(&flags.summary, "summary", false, "Show summary about a task.")
|
||||||
@ -195,6 +197,7 @@ func run() error {
|
|||||||
Watch: flags.watch,
|
Watch: flags.watch,
|
||||||
Verbose: flags.verbose,
|
Verbose: flags.verbose,
|
||||||
Silent: flags.silent,
|
Silent: flags.silent,
|
||||||
|
AssumeYes: flags.assumeYes,
|
||||||
Dir: flags.dir,
|
Dir: flags.dir,
|
||||||
Dry: flags.dry,
|
Dry: flags.dry,
|
||||||
Entrypoint: flags.entrypoint,
|
Entrypoint: flags.entrypoint,
|
||||||
|
@ -44,6 +44,7 @@ If `--` is given, all remaning arguments will be assigned to a special
|
|||||||
| | `--output-group-error-only` | `bool` | `false` | Swallow command output on zero exit code. |
|
| | `--output-group-error-only` | `bool` | `false` | Swallow command output on zero exit code. |
|
||||||
| `-p` | `--parallel` | `bool` | `false` | Executes tasks provided on command line in parallel. |
|
| `-p` | `--parallel` | `bool` | `false` | Executes tasks provided on command line in parallel. |
|
||||||
| `-s` | `--silent` | `bool` | `false` | Disables echoing. |
|
| `-s` | `--silent` | `bool` | `false` | Disables echoing. |
|
||||||
|
| `-y` | `--yes` | `bool` | `false` | Assume "yes" as answer to all prompts. |
|
||||||
| | `--status` | `bool` | `false` | Exits with non-zero exit code if any of the given tasks is not up-to-date. |
|
| | `--status` | `bool` | `false` | Exits with non-zero exit code if any of the given tasks is not up-to-date. |
|
||||||
| | `--summary` | `bool` | `false` | Show summary about a task. |
|
| | `--summary` | `bool` | `false` | Show summary about a task. |
|
||||||
| `-t` | `--taskfile` | `string` | `Taskfile.yml` or `Taskfile.yaml` | |
|
| `-t` | `--taskfile` | `string` | `Taskfile.yml` or `Taskfile.yaml` | |
|
||||||
@ -53,7 +54,8 @@ If `--` is given, all remaning arguments will be assigned to a special
|
|||||||
|
|
||||||
## Exit Codes
|
## Exit Codes
|
||||||
|
|
||||||
Task will sometimes exit with specific exit codes. These codes are split into three groups with the following ranges:
|
Task will sometimes exit with specific exit codes. These codes are split into
|
||||||
|
three groups with the following ranges:
|
||||||
|
|
||||||
- General errors (0-99)
|
- General errors (0-99)
|
||||||
- Taskfile errors (100-199)
|
- Taskfile errors (100-199)
|
||||||
@ -73,12 +75,13 @@ A full list of the exit codes and their descriptions can be found below:
|
|||||||
| 202 | The user tried to invoke a task that is internal |
|
| 202 | The user tried to invoke a task that is internal |
|
||||||
| 203 | There a multiple tasks with the same name or alias |
|
| 203 | There a multiple tasks with the same name or alias |
|
||||||
| 204 | A task was called too many times |
|
| 204 | A task was called too many times |
|
||||||
|
| 205 | A task was cancelled by the user |
|
||||||
|
|
||||||
These codes can also be found in the repository in [`errors/errors.go`](https://github.com/go-task/task/blob/main/errors/errors.go).
|
These codes can also be found in the repository in
|
||||||
|
[`errors/errors.go`](https://github.com/go-task/task/blob/main/errors/errors.go).
|
||||||
|
|
||||||
:::info
|
:::info When Task is run with the `-x`/`--exit-code` flag, the exit code of any
|
||||||
When Task is run with the `-x`/`--exit-code` flag, the exit code of any failed commands will be passed through to the user instead.
|
failed commands will be passed through to the user instead. :::
|
||||||
:::
|
|
||||||
|
|
||||||
## JSON Output
|
## JSON Output
|
||||||
|
|
||||||
@ -206,6 +209,7 @@ vars:
|
|||||||
| `deps` | [`[]Dependency`](#dependency) | | A list of dependencies of this task. Tasks defined here will run in parallel before this task. |
|
| `deps` | [`[]Dependency`](#dependency) | | A list of dependencies of this task. Tasks defined here will run in parallel before this task. |
|
||||||
| `label` | `string` | | Overrides the name of the task in the output when a task is run. Supports variables. |
|
| `label` | `string` | | Overrides the name of the task in the output when a task is run. Supports variables. |
|
||||||
| `desc` | `string` | | A short description of the task. This is displayed when calling `task --list`. |
|
| `desc` | `string` | | A short description of the task. This is displayed when calling `task --list`. |
|
||||||
|
| `prompt` | `string` | | A prompt that will be presented before a task is run. Declining will cancel running the current and any subsequent tasks. |
|
||||||
| `summary` | `string` | | A longer description of the task. This is displayed when calling `task --summary [task]`. |
|
| `summary` | `string` | | A longer description of the task. This is displayed when calling `task --summary [task]`. |
|
||||||
| `aliases` | `[]string` | | A list of alternative names by which the task can be called. |
|
| `aliases` | `[]string` | | A list of alternative names by which the task can be called. |
|
||||||
| `sources` | `[]string` | | A list of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs. |
|
| `sources` | `[]string` | | A list of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs. |
|
||||||
@ -223,7 +227,7 @@ vars:
|
|||||||
| `prefix` | `string` | | Defines a string to prefix the output of tasks running in parallel. Only used when the output mode is `prefixed`. |
|
| `prefix` | `string` | | Defines a string to prefix the output of tasks running in parallel. Only used when the output mode is `prefixed`. |
|
||||||
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing commands. |
|
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing commands. |
|
||||||
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
|
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
|
||||||
| `platforms` | `[]string` | All platforms | Specifies which platforms the task should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/main/src/go/build/syslist.go). Task will be skipped otherwise. |
|
| `platforms` | `[]string` | All platforms | Specifies which platforms the task should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/main/src/go/build/syslist.go). Task will be skipped otherwise. |
|
||||||
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
|
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
|
||||||
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
|
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
|
||||||
|
|
||||||
@ -248,17 +252,17 @@ tasks:
|
|||||||
|
|
||||||
#### Command
|
#### Command
|
||||||
|
|
||||||
| Attribute | Type | Default | Description |
|
| Attribute | Type | Default | Description |
|
||||||
| -------------- | ---------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| -------------- | ---------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `cmd` | `string` | | The shell command to be executed. |
|
| `cmd` | `string` | | The shell command to be executed. |
|
||||||
| `silent` | `bool` | `false` | Skips some output for this command. Note that STDOUT and STDERR of the commands will still be redirected. |
|
| `silent` | `bool` | `false` | Skips some output for this command. Note that STDOUT and STDERR of the commands will still be redirected. |
|
||||||
| `task` | `string` | | Set this to trigger execution of another task instead of running a command. This cannot be set together with `cmd`. |
|
| `task` | `string` | | Set this to trigger execution of another task instead of running a command. This cannot be set together with `cmd`. |
|
||||||
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the referenced task. Only relevant when setting `task` instead of `cmd`. |
|
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the referenced task. Only relevant when setting `task` instead of `cmd`. |
|
||||||
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
|
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
|
||||||
| `defer` | `string` | | Alternative to `cmd`, but schedules the command to be executed at the end of this task instead of immediately. This cannot be used together with `cmd`. |
|
| `defer` | `string` | | Alternative to `cmd`, but schedules the command to be executed at the end of this task instead of immediately. This cannot be used together with `cmd`. |
|
||||||
| `platforms` | `[]string` | All platforms | Specifies which platforms the command should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/main/src/go/build/syslist.go). Command will be skipped otherwise. |
|
| `platforms` | `[]string` | All platforms | Specifies which platforms the command should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/main/src/go/build/syslist.go). Command will be skipped otherwise. |
|
||||||
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
|
| `set` | `[]string` | | Specify options for the [`set` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Set-Builtin.html). |
|
||||||
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
|
| `shopt` | `[]string` | | Specify option for the [`shopt` builtin](https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html). |
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
|
@ -1212,6 +1212,64 @@ tasks:
|
|||||||
- echo "{{.MESSAGE}}"
|
- echo "{{.MESSAGE}}"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Warning Prompts
|
||||||
|
|
||||||
|
Warning Prompts to prompt a user for confirmation before a task is executed.
|
||||||
|
|
||||||
|
Below is an example using `prompt` with a dangerous command, that is called between two safe commands
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
example:
|
||||||
|
cmds:
|
||||||
|
- task: not-dangerous
|
||||||
|
- task: dangerous
|
||||||
|
- task: another-not-dangerous
|
||||||
|
|
||||||
|
not-dangerous:
|
||||||
|
cmds:
|
||||||
|
- echo 'not dangerous command.'
|
||||||
|
|
||||||
|
another-not-dangerous:
|
||||||
|
cmds:
|
||||||
|
- echo 'another not dangerous command.'
|
||||||
|
|
||||||
|
dangerous:
|
||||||
|
prompt: This is a dangerous command.. Do you want to continue?
|
||||||
|
cmds:
|
||||||
|
- echo 'dangerous command.'
|
||||||
|
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
❯ task dangerous
|
||||||
|
task: "This is a dangerous command.. Do you want to continue?" [y/N]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Prompt behaviour
|
||||||
|
|
||||||
|
Warning prompts are called before executing a task. If a prompt is denied Task will exit with [Exit code](api_reference.md#exit-codes) 205. If approved, Task will continue as normal.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
❯ taskd --dir ./testdata/prompt example
|
||||||
|
task: [not-dangerous] echo 'not dangerous command.'
|
||||||
|
not dangerous command.
|
||||||
|
task: "This is a dangerous command.. Do you want to continue?" [y/N]
|
||||||
|
y
|
||||||
|
task: [dangerous] echo 'dangerous command.'
|
||||||
|
dangerous command.
|
||||||
|
task: [another-not-dangerous] echo 'another not dangerous command.'
|
||||||
|
another not dangerous command.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Skipping Warning Prompts
|
||||||
|
|
||||||
|
To skip warning prompts automatically, you can use the [-y | --yes] option when calling the task. By including this option, all warnings, will be automatically confirmed, and no prompts will be shows.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Silent mode
|
## Silent mode
|
||||||
|
|
||||||
Silent mode disables the echoing of commands before Task runs it. For the
|
Silent mode disables the echoing of commands before Task runs it. For the
|
||||||
|
4
docs/static/schema.json
vendored
4
docs/static/schema.json
vendored
@ -65,6 +65,10 @@
|
|||||||
"description": "A short description of the task. This is displayed when calling `task --list`.",
|
"description": "A short description of the task. This is displayed when calling `task --list`.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"prompt": {
|
||||||
|
"description": "A prompt that will be presented before a task is run. Declining will cancel running the current and any subsequent tasks.",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"summary": {
|
"summary": {
|
||||||
"description": "A longer description of the task. This is displayed when calling `task --summary [task]`.",
|
"description": "A longer description of the task. This is displayed when calling `task --summary [task]`.",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
@ -22,6 +22,7 @@ const (
|
|||||||
CodeTaskInternal
|
CodeTaskInternal
|
||||||
CodeTaskNameConflict
|
CodeTaskNameConflict
|
||||||
CodeTaskCalledTooManyTimes
|
CodeTaskCalledTooManyTimes
|
||||||
|
CodeTaskCancelled
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskError extends the standard error interface with a Code method. This code will
|
// TaskError extends the standard error interface with a Code method. This code will
|
||||||
|
@ -89,7 +89,7 @@ type TaskCalledTooManyTimesError struct {
|
|||||||
|
|
||||||
func (err *TaskCalledTooManyTimesError) Error() string {
|
func (err *TaskCalledTooManyTimesError) Error() string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
`task: maximum task call exceeded (%d) for task %q: probably an cyclic dep or infinite loop`,
|
`task: Maximum task call exceeded (%d) for task %q: probably an cyclic dep or infinite loop`,
|
||||||
err.MaximumTaskCall,
|
err.MaximumTaskCall,
|
||||||
err.TaskName,
|
err.TaskName,
|
||||||
)
|
)
|
||||||
@ -98,3 +98,19 @@ func (err *TaskCalledTooManyTimesError) Error() string {
|
|||||||
func (err *TaskCalledTooManyTimesError) Code() int {
|
func (err *TaskCalledTooManyTimesError) Code() int {
|
||||||
return CodeTaskCalledTooManyTimes
|
return CodeTaskCalledTooManyTimes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TaskCancelledError is returned when the user does not accept an optional prompt to continue.
|
||||||
|
type TaskCancelledError struct {
|
||||||
|
TaskName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *TaskCancelledError) Error() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`task: %q Cancelled by user`,
|
||||||
|
err.TaskName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *TaskCancelledError) Code() int {
|
||||||
|
return CodeTaskCancelled
|
||||||
|
}
|
||||||
|
25
task.go
25
task.go
@ -1,11 +1,13 @@
|
|||||||
package task
|
package task
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@ -34,6 +36,11 @@ const (
|
|||||||
MaximumTaskCall = 100
|
MaximumTaskCall = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func shouldPromptContinue(input string) bool {
|
||||||
|
input = strings.ToLower(strings.TrimSpace(input))
|
||||||
|
return slices.Contains([]string{"y", "yes"}, input)
|
||||||
|
}
|
||||||
|
|
||||||
// Executor executes a Taskfile
|
// Executor executes a Taskfile
|
||||||
type Executor struct {
|
type Executor struct {
|
||||||
Taskfile *taskfile.Taskfile
|
Taskfile *taskfile.Taskfile
|
||||||
@ -45,6 +52,7 @@ type Executor struct {
|
|||||||
Watch bool
|
Watch bool
|
||||||
Verbose bool
|
Verbose bool
|
||||||
Silent bool
|
Silent bool
|
||||||
|
AssumeYes bool
|
||||||
Dry bool
|
Dry bool
|
||||||
Summary bool
|
Summary bool
|
||||||
Parallel bool
|
Parallel bool
|
||||||
@ -94,6 +102,7 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
|||||||
}
|
}
|
||||||
return &errors.TaskInternalError{TaskName: call.Task}
|
return &errors.TaskInternalError{TaskName: call.Task}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Summary {
|
if e.Summary {
|
||||||
@ -139,6 +148,22 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
release := e.acquireConcurrencyLimit()
|
release := e.acquireConcurrencyLimit()
|
||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
|
// check if the given task has a warning prompt
|
||||||
|
if t.Prompt != "" && !e.AssumeYes {
|
||||||
|
|
||||||
|
e.Logger.Outf(logger.Yellow, "task: %q [y/N]\n", t.Prompt)
|
||||||
|
reader := bufio.NewReader(e.Stdin)
|
||||||
|
userInput, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userInput = strings.ToLower(strings.TrimSpace(userInput))
|
||||||
|
if !shouldPromptContinue(userInput) {
|
||||||
|
return &errors.TaskCancelledError{TaskName: call.Task}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return e.startExecution(ctx, t, func(ctx context.Context) error {
|
return e.startExecution(ctx, t, func(ctx context.Context) error {
|
||||||
if !shouldRunOnCurrentPlatform(t.Platforms) {
|
if !shouldRunOnCurrentPlatform(t.Platforms) {
|
||||||
e.Logger.VerboseOutf(logger.Yellow, `task: %q not for current platform - ignored\n`, call.Task)
|
e.Logger.VerboseOutf(logger.Yellow, `task: %q not for current platform - ignored\n`, call.Task)
|
||||||
|
95
task_test.go
95
task_test.go
@ -657,6 +657,101 @@ func TestLabelInSummary(t *testing.T) {
|
|||||||
assert.Contains(t, buff.String(), "foobar")
|
assert.Contains(t, buff.String(), "foobar")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPromptInSummary(t *testing.T) {
|
||||||
|
const dir = "testdata/prompt"
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
wantError bool
|
||||||
|
}{
|
||||||
|
{"test short approval", "y\n", false},
|
||||||
|
{"test long approval", "yes\n", false},
|
||||||
|
{"test uppercase approval", "Y\n", false},
|
||||||
|
{"test stops task", "n\n", true},
|
||||||
|
{"test junk value stops task", "foobar\n", true},
|
||||||
|
{"test Enter stops task", "\n", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
var inBuff bytes.Buffer
|
||||||
|
var outBuff bytes.Buffer
|
||||||
|
|
||||||
|
inBuff.Write([]byte(test.input))
|
||||||
|
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdin: &inBuff,
|
||||||
|
Stdout: &outBuff,
|
||||||
|
}
|
||||||
|
require.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "foo"})
|
||||||
|
|
||||||
|
if test.wantError {
|
||||||
|
require.Error(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPromptWithIndirectTask(t *testing.T) {
|
||||||
|
const dir = "testdata/prompt"
|
||||||
|
var inBuff bytes.Buffer
|
||||||
|
var outBuff bytes.Buffer
|
||||||
|
|
||||||
|
inBuff.Write([]byte("y\n"))
|
||||||
|
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdin: &inBuff,
|
||||||
|
Stdout: &outBuff,
|
||||||
|
}
|
||||||
|
require.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "bar"})
|
||||||
|
assert.Contains(t, outBuff.String(), "show-prompt")
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPromptAssumeYes(t *testing.T) {
|
||||||
|
const dir = "testdata/prompt"
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
assumeYes bool
|
||||||
|
}{
|
||||||
|
{"--yes flag should skip prompt", true},
|
||||||
|
{"task should raise errors.TaskCancelledError", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
var inBuff bytes.Buffer
|
||||||
|
var outBuff bytes.Buffer
|
||||||
|
|
||||||
|
// always cancel the prompt so we can require.Error
|
||||||
|
inBuff.Write([]byte("\n"))
|
||||||
|
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: dir,
|
||||||
|
Stdin: &inBuff,
|
||||||
|
Stdout: &outBuff,
|
||||||
|
AssumeYes: test.assumeYes,
|
||||||
|
}
|
||||||
|
require.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
err := e.Run(context.Background(), taskfile.Call{Task: "foo"})
|
||||||
|
|
||||||
|
if !test.assumeYes {
|
||||||
|
require.Error(t, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNoLabelInList(t *testing.T) {
|
func TestNoLabelInList(t *testing.T) {
|
||||||
const dir = "testdata/label_list"
|
const dir = "testdata/label_list"
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ type Task struct {
|
|||||||
Deps []*Dep
|
Deps []*Dep
|
||||||
Label string
|
Label string
|
||||||
Desc string
|
Desc string
|
||||||
|
Prompt string
|
||||||
Summary string
|
Summary string
|
||||||
Aliases []string
|
Aliases []string
|
||||||
Sources []string
|
Sources []string
|
||||||
@ -76,6 +77,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
Deps []*Dep
|
Deps []*Dep
|
||||||
Label string
|
Label string
|
||||||
Desc string
|
Desc string
|
||||||
|
Prompt string
|
||||||
Summary string
|
Summary string
|
||||||
Aliases []string
|
Aliases []string
|
||||||
Sources []string
|
Sources []string
|
||||||
@ -104,6 +106,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
t.Deps = task.Deps
|
t.Deps = task.Deps
|
||||||
t.Label = task.Label
|
t.Label = task.Label
|
||||||
t.Desc = task.Desc
|
t.Desc = task.Desc
|
||||||
|
t.Prompt = task.Prompt
|
||||||
t.Summary = task.Summary
|
t.Summary = task.Summary
|
||||||
t.Aliases = task.Aliases
|
t.Aliases = task.Aliases
|
||||||
t.Sources = task.Sources
|
t.Sources = task.Sources
|
||||||
@ -142,6 +145,7 @@ func (t *Task) DeepCopy() *Task {
|
|||||||
Deps: deepcopy.Slice(t.Deps),
|
Deps: deepcopy.Slice(t.Deps),
|
||||||
Label: t.Label,
|
Label: t.Label,
|
||||||
Desc: t.Desc,
|
Desc: t.Desc,
|
||||||
|
Prompt: t.Prompt,
|
||||||
Summary: t.Summary,
|
Summary: t.Summary,
|
||||||
Aliases: deepcopy.Slice(t.Aliases),
|
Aliases: deepcopy.Slice(t.Aliases),
|
||||||
Sources: deepcopy.Slice(t.Sources),
|
Sources: deepcopy.Slice(t.Sources),
|
||||||
|
16
testdata/prompt/Taskfile.yml
vendored
Normal file
16
testdata/prompt/Taskfile.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
version: 3
|
||||||
|
tasks:
|
||||||
|
foo:
|
||||||
|
prompt: Do you want to continue?
|
||||||
|
cmds:
|
||||||
|
- echo 'foo'
|
||||||
|
|
||||||
|
bar:
|
||||||
|
cmds:
|
||||||
|
- task: show-prompt
|
||||||
|
|
||||||
|
show-prompt:
|
||||||
|
summary: some text for the summary
|
||||||
|
prompt: Do you want to continue?
|
||||||
|
cmds:
|
||||||
|
- echo 'show-prompt'
|
@ -46,6 +46,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
Task: origTask.Task,
|
Task: origTask.Task,
|
||||||
Label: r.Replace(origTask.Label),
|
Label: r.Replace(origTask.Label),
|
||||||
Desc: r.Replace(origTask.Desc),
|
Desc: r.Replace(origTask.Desc),
|
||||||
|
Prompt: r.Replace(origTask.Prompt),
|
||||||
Summary: r.Replace(origTask.Summary),
|
Summary: r.Replace(origTask.Summary),
|
||||||
Aliases: origTask.Aliases,
|
Aliases: origTask.Aliases,
|
||||||
Sources: r.ReplaceSlice(origTask.Sources),
|
Sources: r.ReplaceSlice(origTask.Sources),
|
||||||
|
Reference in New Issue
Block a user