mirror of
https://github.com/go-task/task.git
synced 2025-08-10 22:42:19 +02:00
chore: improvements on #1163 + changelog entry
This commit is contained in:
@@ -8,6 +8,9 @@
|
||||
website (#1198 by @pd93).
|
||||
- Deprecated `version: 2` schema. This will be removed in the next major release
|
||||
(#1197, #1198, #1199 by @pd93).
|
||||
- Added a new `prompt:` prop to set a warning prompt to be shown before running
|
||||
a potential dangurous task (#100, #1163 by @MaxCheetham,
|
||||
[Documentation](https://taskfile.dev/usage/#warning-prompts))
|
||||
|
||||
## v3.25.0 - 2023-05-22
|
||||
|
||||
|
@@ -44,7 +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. |
|
||||
| `-p` | `--parallel` | `bool` | `false` | Executes tasks provided on command line in parallel. |
|
||||
| `-s` | `--silent` | `bool` | `false` | Disables echoing. |
|
||||
| `-y` | `--yes` | `bool` | `false` | Assume "yes" as answer to all prompts. |
|
||||
| `-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. |
|
||||
| | `--summary` | `bool` | `false` | Show summary about a task. |
|
||||
| `-t` | `--taskfile` | `string` | `Taskfile.yml` or `Taskfile.yaml` | |
|
||||
|
@@ -1216,7 +1216,9 @@ tasks:
|
||||
|
||||
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
|
||||
Below is an example using `prompt` with a dangerous command, that is called
|
||||
between two safe commands:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
@@ -1229,46 +1231,47 @@ tasks:
|
||||
|
||||
not-dangerous:
|
||||
cmds:
|
||||
- echo 'not dangerous command.'
|
||||
- echo 'not dangerous command'
|
||||
|
||||
another-not-dangerous:
|
||||
cmds:
|
||||
- echo 'another not dangerous command.'
|
||||
- echo 'another not dangerous command'
|
||||
|
||||
dangerous:
|
||||
prompt: This is a dangerous command.. Do you want to continue?
|
||||
prompt: This is a dangerous command... Do you want to continue?
|
||||
cmds:
|
||||
- echo 'dangerous command.'
|
||||
|
||||
- echo 'dangerous command'
|
||||
```
|
||||
|
||||
```bash
|
||||
❯ task dangerous
|
||||
task: "This is a dangerous command.. Do you want to continue?" [y/N]
|
||||
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.
|
||||
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]
|
||||
❯ task example
|
||||
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.
|
||||
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.
|
||||
|
||||
To skip warning prompts automatically, you can use the `--yes` (alias `-y`)
|
||||
option when calling the task. By including this option, all warnings, will be
|
||||
automatically confirmed, and no prompts will be shown.
|
||||
|
||||
:::caution
|
||||
|
||||
Tasks with prompts always fail by default on non-terminal environments, like a
|
||||
CI, where an `stdin` won't be available for the user to answer. In cases like,
|
||||
use `--yes` (`-y`) to force all tasks with a prompt to run.
|
||||
|
||||
:::
|
||||
|
||||
## Silent mode
|
||||
|
||||
|
@@ -99,18 +99,34 @@ func (err *TaskCalledTooManyTimesError) Code() int {
|
||||
return CodeTaskCalledTooManyTimes
|
||||
}
|
||||
|
||||
// TaskCancelledError is returned when the user does not accept an optional prompt to continue.
|
||||
type TaskCancelledError struct {
|
||||
// TaskCancelledByUserError is returned when the user does not accept an optional prompt to continue.
|
||||
type TaskCancelledByUserError struct {
|
||||
TaskName string
|
||||
}
|
||||
|
||||
func (err *TaskCancelledError) Error() string {
|
||||
func (err *TaskCancelledByUserError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
`task: %q Cancelled by user`,
|
||||
`task: Task "%q" cancelled by user`,
|
||||
err.TaskName,
|
||||
)
|
||||
}
|
||||
|
||||
func (err *TaskCancelledError) Code() int {
|
||||
func (err *TaskCancelledByUserError) Code() int {
|
||||
return CodeTaskCancelled
|
||||
}
|
||||
|
||||
// TaskCancelledNoTerminalError is returned when trying to run a task with a prompt in a non-terminal environment.
|
||||
type TaskCancelledNoTerminalError struct {
|
||||
TaskName string
|
||||
}
|
||||
|
||||
func (err *TaskCancelledNoTerminalError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
`task: Task "%q" cancelled because it has a prompt and the environment is not a terminal. Use --yes (-y) to run anyway.`,
|
||||
err.TaskName,
|
||||
)
|
||||
}
|
||||
|
||||
func (err *TaskCancelledNoTerminalError) Code() int {
|
||||
return CodeTaskCancelled
|
||||
}
|
||||
|
2
go.mod
2
go.mod
@@ -15,6 +15,7 @@ require (
|
||||
github.com/stretchr/testify v1.8.4
|
||||
golang.org/x/exp v0.0.0-20230212135524-a684f29349b6
|
||||
golang.org/x/sync v0.2.0
|
||||
golang.org/x/term v0.3.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/sh/v3 v3.6.0
|
||||
)
|
||||
@@ -26,6 +27,5 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/term v0.3.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
|
11
internal/term/term.go
Normal file
11
internal/term/term.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/term"
|
||||
)
|
||||
|
||||
func IsTerminal() bool {
|
||||
return term.IsTerminal(int(os.Stdin.Fd())) && term.IsTerminal(int(os.Stdout.Fd()))
|
||||
}
|
10
task.go
10
task.go
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/go-task/task/v3/internal/sort"
|
||||
"github.com/go-task/task/v3/internal/summary"
|
||||
"github.com/go-task/task/v3/internal/templater"
|
||||
"github.com/go-task/task/v3/internal/term"
|
||||
"github.com/go-task/task/v3/taskfile"
|
||||
|
||||
"github.com/sajari/fuzzy"
|
||||
@@ -59,6 +60,7 @@ type Executor struct {
|
||||
Color bool
|
||||
Concurrency int
|
||||
Interval time.Duration
|
||||
AssumesTerm bool
|
||||
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
@@ -102,7 +104,6 @@ func (e *Executor) Run(ctx context.Context, calls ...taskfile.Call) error {
|
||||
}
|
||||
return &errors.TaskInternalError{TaskName: call.Task}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if e.Summary {
|
||||
@@ -148,10 +149,13 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
||||
release := e.acquireConcurrencyLimit()
|
||||
defer release()
|
||||
|
||||
// check if the given task has a warning prompt
|
||||
if t.Prompt != "" && !e.AssumeYes {
|
||||
if !e.AssumesTerm && !term.IsTerminal() {
|
||||
return &errors.TaskCancelledNoTerminalError{TaskName: call.Task}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -160,7 +164,7 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
||||
|
||||
userInput = strings.ToLower(strings.TrimSpace(userInput))
|
||||
if !shouldPromptContinue(userInput) {
|
||||
return &errors.TaskCancelledError{TaskName: call.Task}
|
||||
return &errors.TaskCancelledByUserError{TaskName: call.Task}
|
||||
}
|
||||
}
|
||||
|
||||
|
18
task_test.go
18
task_test.go
@@ -680,9 +680,10 @@ func TestPromptInSummary(t *testing.T) {
|
||||
inBuff.Write([]byte(test.input))
|
||||
|
||||
e := task.Executor{
|
||||
Dir: dir,
|
||||
Stdin: &inBuff,
|
||||
Stdout: &outBuff,
|
||||
Dir: dir,
|
||||
Stdin: &inBuff,
|
||||
Stdout: &outBuff,
|
||||
AssumesTerm: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
@@ -690,9 +691,9 @@ func TestPromptInSummary(t *testing.T) {
|
||||
|
||||
if test.wantError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -705,9 +706,10 @@ func TestPromptWithIndirectTask(t *testing.T) {
|
||||
inBuff.Write([]byte("y\n"))
|
||||
|
||||
e := task.Executor{
|
||||
Dir: dir,
|
||||
Stdin: &inBuff,
|
||||
Stdout: &outBuff,
|
||||
Dir: dir,
|
||||
Stdin: &inBuff,
|
||||
Stdout: &outBuff,
|
||||
AssumesTerm: true,
|
||||
}
|
||||
require.NoError(t, e.Setup())
|
||||
|
||||
|
2
testdata/prompt/Taskfile.yml
vendored
2
testdata/prompt/Taskfile.yml
vendored
@@ -1,4 +1,5 @@
|
||||
version: 3
|
||||
|
||||
tasks:
|
||||
foo:
|
||||
prompt: Do you want to continue?
|
||||
@@ -10,7 +11,6 @@ tasks:
|
||||
- task: show-prompt
|
||||
|
||||
show-prompt:
|
||||
summary: some text for the summary
|
||||
prompt: Do you want to continue?
|
||||
cmds:
|
||||
- echo 'show-prompt'
|
||||
|
Reference in New Issue
Block a user