mirror of
				https://github.com/go-task/task.git
				synced 2025-10-30 23:58:01 +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