mirror of
https://github.com/go-task/task.git
synced 2025-12-16 11:47:44 +01:00
chore: improvements on #1163 + changelog entry
This commit is contained in:
@@ -8,6 +8,9 @@
|
|||||||
website (#1198 by @pd93).
|
website (#1198 by @pd93).
|
||||||
- Deprecated `version: 2` schema. This will be removed in the next major release
|
- Deprecated `version: 2` schema. This will be removed in the next major release
|
||||||
(#1197, #1198, #1199 by @pd93).
|
(#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
|
## v3.25.0 - 2023-05-22
|
||||||
|
|
||||||
|
|||||||
@@ -1216,7 +1216,9 @@ tasks:
|
|||||||
|
|
||||||
Warning Prompts to prompt a user for confirmation before a task is executed.
|
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
|
```yaml
|
||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
@@ -1229,46 +1231,47 @@ tasks:
|
|||||||
|
|
||||||
not-dangerous:
|
not-dangerous:
|
||||||
cmds:
|
cmds:
|
||||||
- echo 'not dangerous command.'
|
- echo 'not dangerous command'
|
||||||
|
|
||||||
another-not-dangerous:
|
another-not-dangerous:
|
||||||
cmds:
|
cmds:
|
||||||
- echo 'another not dangerous command.'
|
- echo 'another not dangerous command'
|
||||||
|
|
||||||
dangerous:
|
dangerous:
|
||||||
prompt: This is a dangerous command.. Do you want to continue?
|
prompt: This is a dangerous command... Do you want to continue?
|
||||||
cmds:
|
cmds:
|
||||||
- echo 'dangerous command.'
|
- echo 'dangerous command'
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
❯ task dangerous
|
❯ 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
|
||||||
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.
|
will continue as normal.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
❯ taskd --dir ./testdata/prompt example
|
❯ task example
|
||||||
task: [not-dangerous] echo 'not dangerous command.'
|
not dangerous command
|
||||||
not dangerous command.
|
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]
|
|
||||||
y
|
y
|
||||||
task: [dangerous] echo 'dangerous command.'
|
dangerous command
|
||||||
dangerous command.
|
another not 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 `--yes` (alias `-y`)
|
||||||
|
option when calling the task. By including this option, all warnings, will be
|
||||||
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.
|
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
|
## Silent mode
|
||||||
|
|
||||||
|
|||||||
@@ -99,18 +99,34 @@ func (err *TaskCalledTooManyTimesError) Code() int {
|
|||||||
return CodeTaskCalledTooManyTimes
|
return CodeTaskCalledTooManyTimes
|
||||||
}
|
}
|
||||||
|
|
||||||
// TaskCancelledError is returned when the user does not accept an optional prompt to continue.
|
// TaskCancelledByUserError is returned when the user does not accept an optional prompt to continue.
|
||||||
type TaskCancelledError struct {
|
type TaskCancelledByUserError struct {
|
||||||
TaskName string
|
TaskName string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (err *TaskCancelledError) Error() string {
|
func (err *TaskCancelledByUserError) Error() string {
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
`task: %q Cancelled by user`,
|
`task: Task "%q" cancelled by user`,
|
||||||
err.TaskName,
|
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
|
return CodeTaskCancelled
|
||||||
}
|
}
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -15,6 +15,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
golang.org/x/exp v0.0.0-20230212135524-a684f29349b6
|
golang.org/x/exp v0.0.0-20230212135524-a684f29349b6
|
||||||
golang.org/x/sync v0.2.0
|
golang.org/x/sync v0.2.0
|
||||||
|
golang.org/x/term v0.3.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
mvdan.cc/sh/v3 v3.6.0
|
mvdan.cc/sh/v3 v3.6.0
|
||||||
)
|
)
|
||||||
@@ -26,6 +27,5 @@ require (
|
|||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.0 // indirect
|
github.com/stretchr/objx v0.5.0 // indirect
|
||||||
golang.org/x/sys v0.6.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
|
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/sort"
|
||||||
"github.com/go-task/task/v3/internal/summary"
|
"github.com/go-task/task/v3/internal/summary"
|
||||||
"github.com/go-task/task/v3/internal/templater"
|
"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/go-task/task/v3/taskfile"
|
||||||
|
|
||||||
"github.com/sajari/fuzzy"
|
"github.com/sajari/fuzzy"
|
||||||
@@ -59,6 +60,7 @@ type Executor struct {
|
|||||||
Color bool
|
Color bool
|
||||||
Concurrency int
|
Concurrency int
|
||||||
Interval time.Duration
|
Interval time.Duration
|
||||||
|
AssumesTerm bool
|
||||||
|
|
||||||
Stdin io.Reader
|
Stdin io.Reader
|
||||||
Stdout io.Writer
|
Stdout io.Writer
|
||||||
@@ -102,7 +104,6 @@ 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 {
|
||||||
@@ -148,10 +149,13 @@ 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 {
|
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)
|
e.Logger.Outf(logger.Yellow, "task: %q [y/N]\n", t.Prompt)
|
||||||
|
|
||||||
reader := bufio.NewReader(e.Stdin)
|
reader := bufio.NewReader(e.Stdin)
|
||||||
userInput, err := reader.ReadString('\n')
|
userInput, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -160,7 +164,7 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
|
|
||||||
userInput = strings.ToLower(strings.TrimSpace(userInput))
|
userInput = strings.ToLower(strings.TrimSpace(userInput))
|
||||||
if !shouldPromptContinue(userInput) {
|
if !shouldPromptContinue(userInput) {
|
||||||
return &errors.TaskCancelledError{TaskName: call.Task}
|
return &errors.TaskCancelledByUserError{TaskName: call.Task}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -683,6 +683,7 @@ func TestPromptInSummary(t *testing.T) {
|
|||||||
Dir: dir,
|
Dir: dir,
|
||||||
Stdin: &inBuff,
|
Stdin: &inBuff,
|
||||||
Stdout: &outBuff,
|
Stdout: &outBuff,
|
||||||
|
AssumesTerm: true,
|
||||||
}
|
}
|
||||||
require.NoError(t, e.Setup())
|
require.NoError(t, e.Setup())
|
||||||
|
|
||||||
@@ -690,9 +691,9 @@ func TestPromptInSummary(t *testing.T) {
|
|||||||
|
|
||||||
if test.wantError {
|
if test.wantError {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
return
|
} else {
|
||||||
}
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -708,6 +709,7 @@ func TestPromptWithIndirectTask(t *testing.T) {
|
|||||||
Dir: dir,
|
Dir: dir,
|
||||||
Stdin: &inBuff,
|
Stdin: &inBuff,
|
||||||
Stdout: &outBuff,
|
Stdout: &outBuff,
|
||||||
|
AssumesTerm: true,
|
||||||
}
|
}
|
||||||
require.NoError(t, e.Setup())
|
require.NoError(t, e.Setup())
|
||||||
|
|
||||||
|
|||||||
2
testdata/prompt/Taskfile.yml
vendored
2
testdata/prompt/Taskfile.yml
vendored
@@ -1,4 +1,5 @@
|
|||||||
version: 3
|
version: 3
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
foo:
|
foo:
|
||||||
prompt: Do you want to continue?
|
prompt: Do you want to continue?
|
||||||
@@ -10,7 +11,6 @@ tasks:
|
|||||||
- task: show-prompt
|
- task: show-prompt
|
||||||
|
|
||||||
show-prompt:
|
show-prompt:
|
||||||
summary: some text for the summary
|
|
||||||
prompt: Do you want to continue?
|
prompt: Do you want to continue?
|
||||||
cmds:
|
cmds:
|
||||||
- echo 'show-prompt'
|
- echo 'show-prompt'
|
||||||
|
|||||||
Reference in New Issue
Block a user