mirror of
https://github.com/go-task/task.git
synced 2025-12-16 19:57:43 +01:00
refactor(interactive): move interactive setting to taskrc
This commit is contained in:
14
executor.go
14
executor.go
@@ -44,6 +44,7 @@ type (
|
|||||||
AssumeYes bool
|
AssumeYes bool
|
||||||
AssumeTerm bool // Used for testing
|
AssumeTerm bool // Used for testing
|
||||||
NoTTY bool
|
NoTTY bool
|
||||||
|
Interactive bool
|
||||||
Dry bool
|
Dry bool
|
||||||
Summary bool
|
Summary bool
|
||||||
Parallel bool
|
Parallel bool
|
||||||
@@ -367,6 +368,19 @@ func (o *noTTYOption) ApplyToExecutor(e *Executor) {
|
|||||||
e.NoTTY = o.noTTY
|
e.NoTTY = o.noTTY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithInteractive tells the [Executor] to prompt for missing required variables.
|
||||||
|
func WithInteractive(interactive bool) ExecutorOption {
|
||||||
|
return &interactiveOption{interactive}
|
||||||
|
}
|
||||||
|
|
||||||
|
type interactiveOption struct {
|
||||||
|
interactive bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *interactiveOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.Interactive = o.interactive
|
||||||
|
}
|
||||||
|
|
||||||
// WithDry tells the [Executor] to output the commands that would be run without
|
// WithDry tells the [Executor] to output the commands that would be run without
|
||||||
// actually running them.
|
// actually running them.
|
||||||
func WithDry(dry bool) ExecutorOption {
|
func WithDry(dry bool) ExecutorOption {
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ var (
|
|||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
CacheExpiryDuration time.Duration
|
CacheExpiryDuration time.Duration
|
||||||
NoTTY bool
|
NoTTY bool
|
||||||
|
Interactive bool
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -145,6 +146,7 @@ func init() {
|
|||||||
pflag.DurationVarP(&Interval, "interval", "I", 0, "Interval to watch for changes.")
|
pflag.DurationVarP(&Interval, "interval", "I", 0, "Interval to watch for changes.")
|
||||||
pflag.BoolVarP(&Failfast, "failfast", "F", getConfig(config, func() *bool { return &config.Failfast }, false), "When running tasks in parallel, stop all tasks if one fails.")
|
pflag.BoolVarP(&Failfast, "failfast", "F", getConfig(config, func() *bool { return &config.Failfast }, false), "When running tasks in parallel, stop all tasks if one fails.")
|
||||||
pflag.BoolVarP(&Global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.")
|
pflag.BoolVarP(&Global, "global", "g", false, "Runs global Taskfile, from $HOME/{T,t}askfile.{yml,yaml}.")
|
||||||
|
Interactive = getConfig(config, func() *bool { return config.Interactive }, false)
|
||||||
pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.")
|
pflag.BoolVar(&Experiments, "experiments", false, "Lists all the available experiments and whether or not they are enabled.")
|
||||||
|
|
||||||
// Gentle force experiment will override the force flag and add a new force-all flag
|
// Gentle force experiment will override the force flag and add a new force-all flag
|
||||||
@@ -255,6 +257,7 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
task.WithDisableFuzzy(DisableFuzzy),
|
task.WithDisableFuzzy(DisableFuzzy),
|
||||||
task.WithAssumeYes(AssumeYes),
|
task.WithAssumeYes(AssumeYes),
|
||||||
task.WithNoTTY(NoTTY),
|
task.WithNoTTY(NoTTY),
|
||||||
|
task.WithInteractive(Interactive),
|
||||||
task.WithDry(Dry || Status),
|
task.WithDry(Dry || Status),
|
||||||
task.WithSummary(Summary),
|
task.WithSummary(Summary),
|
||||||
task.WithParallel(Parallel),
|
task.WithParallel(Parallel),
|
||||||
|
|||||||
12
requires.go
12
requires.go
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/go-task/task/v3/taskfile/ast"
|
"github.com/go-task/task/v3/taskfile/ast"
|
||||||
)
|
)
|
||||||
|
|
||||||
// promptForInteractiveVars prompts the user for any missing interactive variables
|
// promptForInteractiveVars prompts the user for any missing required variables
|
||||||
// and injects them into the call's Vars. It returns true if any variables were
|
// and injects them into the call's Vars. It returns true if any variables were
|
||||||
// prompted for (meaning the task needs to be recompiled).
|
// prompted for (meaning the task needs to be recompiled).
|
||||||
func (e *Executor) promptForInteractiveVars(t *ast.Task, call *Call) (bool, error) {
|
func (e *Executor) promptForInteractiveVars(t *ast.Task, call *Call) (bool, error) {
|
||||||
@@ -17,6 +17,11 @@ func (e *Executor) promptForInteractiveVars(t *ast.Task, call *Call) (bool, erro
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't prompt if interactive mode is disabled
|
||||||
|
if !e.Interactive {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Don't prompt if NoTTY is set or we're not in a terminal
|
// Don't prompt if NoTTY is set or we're not in a terminal
|
||||||
if e.NoTTY || (!e.AssumeTerm && !term.IsTerminal()) {
|
if e.NoTTY || (!e.AssumeTerm && !term.IsTerminal()) {
|
||||||
return false, nil
|
return false, nil
|
||||||
@@ -26,11 +31,6 @@ func (e *Executor) promptForInteractiveVars(t *ast.Task, call *Call) (bool, erro
|
|||||||
var prompted bool
|
var prompted bool
|
||||||
|
|
||||||
for _, requiredVar := range t.Requires.Vars {
|
for _, requiredVar := range t.Requires.Vars {
|
||||||
// Skip non-interactive vars
|
|
||||||
if !requiredVar.Interactive {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip if already set
|
// Skip if already set
|
||||||
if _, ok := t.Vars.Get(requiredVar.Name); ok {
|
if _, ok := t.Vars.Get(requiredVar.Name); ok {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -23,9 +23,8 @@ func (r *Requires) DeepCopy() *Requires {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type VarsWithValidation struct {
|
type VarsWithValidation struct {
|
||||||
Name string
|
Name string
|
||||||
Enum []string
|
Enum []string
|
||||||
Interactive bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VarsWithValidation) DeepCopy() *VarsWithValidation {
|
func (v *VarsWithValidation) DeepCopy() *VarsWithValidation {
|
||||||
@@ -33,9 +32,8 @@ func (v *VarsWithValidation) DeepCopy() *VarsWithValidation {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &VarsWithValidation{
|
return &VarsWithValidation{
|
||||||
Name: v.Name,
|
Name: v.Name,
|
||||||
Enum: v.Enum,
|
Enum: v.Enum,
|
||||||
Interactive: v.Interactive,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,16 +52,14 @@ func (v *VarsWithValidation) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
|
|
||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
var vv struct {
|
var vv struct {
|
||||||
Name string
|
Name string
|
||||||
Enum []string
|
Enum []string
|
||||||
Interactive bool
|
|
||||||
}
|
}
|
||||||
if err := node.Decode(&vv); err != nil {
|
if err := node.Decode(&vv); err != nil {
|
||||||
return errors.NewTaskfileDecodeError(err, node)
|
return errors.NewTaskfileDecodeError(err, node)
|
||||||
}
|
}
|
||||||
v.Name = vv.Name
|
v.Name = vv.Name
|
||||||
v.Enum = vv.Enum
|
v.Enum = vv.Enum
|
||||||
v.Interactive = vv.Interactive
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type TaskRC struct {
|
|||||||
Verbose *bool `yaml:"verbose"`
|
Verbose *bool `yaml:"verbose"`
|
||||||
DisableFuzzy *bool `yaml:"disable-fuzzy"`
|
DisableFuzzy *bool `yaml:"disable-fuzzy"`
|
||||||
Concurrency *int `yaml:"concurrency"`
|
Concurrency *int `yaml:"concurrency"`
|
||||||
|
Interactive *bool `yaml:"interactive"`
|
||||||
Remote Remote `yaml:"remote"`
|
Remote Remote `yaml:"remote"`
|
||||||
Failfast bool `yaml:"failfast"`
|
Failfast bool `yaml:"failfast"`
|
||||||
Experiments map[string]int `yaml:"experiments"`
|
Experiments map[string]int `yaml:"experiments"`
|
||||||
@@ -56,5 +57,6 @@ func (t *TaskRC) Merge(other *TaskRC) {
|
|||||||
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
|
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
|
||||||
t.DisableFuzzy = cmp.Or(other.DisableFuzzy, t.DisableFuzzy)
|
t.DisableFuzzy = cmp.Or(other.DisableFuzzy, t.DisableFuzzy)
|
||||||
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
|
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
|
||||||
|
t.Interactive = cmp.Or(other.Interactive, t.Interactive)
|
||||||
t.Failfast = cmp.Or(other.Failfast, t.Failfast)
|
t.Failfast = cmp.Or(other.Failfast, t.Failfast)
|
||||||
}
|
}
|
||||||
|
|||||||
20
testdata/interactive_vars/Taskfile.yml
vendored
20
testdata/interactive_vars/Taskfile.yml
vendored
@@ -4,8 +4,7 @@ tasks:
|
|||||||
simple:
|
simple:
|
||||||
requires:
|
requires:
|
||||||
vars:
|
vars:
|
||||||
- name: MY_VAR
|
- MY_VAR
|
||||||
interactive: true
|
|
||||||
cmds:
|
cmds:
|
||||||
- echo "{{.MY_VAR}}"
|
- echo "{{.MY_VAR}}"
|
||||||
|
|
||||||
@@ -13,23 +12,14 @@ tasks:
|
|||||||
requires:
|
requires:
|
||||||
vars:
|
vars:
|
||||||
- name: ENV
|
- name: ENV
|
||||||
interactive: true
|
|
||||||
enum: [dev, staging, prod]
|
enum: [dev, staging, prod]
|
||||||
cmds:
|
cmds:
|
||||||
- echo "{{.ENV}}"
|
- echo "{{.ENV}}"
|
||||||
|
|
||||||
non-interactive:
|
multiple:
|
||||||
requires:
|
requires:
|
||||||
vars:
|
vars:
|
||||||
- NON_INTERACTIVE_VAR
|
- VAR1
|
||||||
|
- VAR2
|
||||||
cmds:
|
cmds:
|
||||||
- echo "{{.NON_INTERACTIVE_VAR}}"
|
- echo "{{.VAR1}} {{.VAR2}}"
|
||||||
|
|
||||||
mixed:
|
|
||||||
requires:
|
|
||||||
vars:
|
|
||||||
- name: INTERACTIVE_VAR
|
|
||||||
interactive: true
|
|
||||||
- NON_INTERACTIVE_VAR
|
|
||||||
cmds:
|
|
||||||
- echo "{{.INTERACTIVE_VAR}} {{.NON_INTERACTIVE_VAR}}"
|
|
||||||
|
|||||||
@@ -1129,14 +1129,20 @@ This is supported only for string variables.
|
|||||||
|
|
||||||
### Prompting for missing variables interactively
|
### Prompting for missing variables interactively
|
||||||
|
|
||||||
If you want Task to prompt users for missing variables instead of failing, you
|
If you want Task to prompt users for missing required variables instead of
|
||||||
can mark a variable as `interactive: true`. When a variable is missing and has
|
failing, you can enable interactive mode in your `.taskrc.yml`:
|
||||||
this flag, Task will display an interactive prompt to collect the value.
|
|
||||||
|
|
||||||
For variables with an `enum`, a selection menu is shown. For variables without
|
|
||||||
an enum, a text input is displayed.
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
# ~/.taskrc.yml
|
||||||
|
interactive: true
|
||||||
|
```
|
||||||
|
|
||||||
|
When enabled, Task will display an interactive prompt for any missing required
|
||||||
|
variable. For variables with an `enum`, a selection menu is shown. For variables
|
||||||
|
without an enum, a text input is displayed.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Taskfile.yml
|
||||||
version: '3'
|
version: '3'
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
@@ -1144,10 +1150,8 @@ tasks:
|
|||||||
requires:
|
requires:
|
||||||
vars:
|
vars:
|
||||||
- name: ENVIRONMENT
|
- name: ENVIRONMENT
|
||||||
interactive: true
|
|
||||||
enum: [dev, staging, prod]
|
enum: [dev, staging, prod]
|
||||||
- name: VERSION
|
- VERSION
|
||||||
interactive: true
|
|
||||||
cmds:
|
cmds:
|
||||||
- echo "Deploying {{.VERSION}} to {{.ENVIRONMENT}}"
|
- echo "Deploying {{.VERSION}} to {{.ENVIRONMENT}}"
|
||||||
```
|
```
|
||||||
@@ -1158,6 +1162,8 @@ $ task deploy
|
|||||||
❯ dev
|
❯ dev
|
||||||
staging
|
staging
|
||||||
prod
|
prod
|
||||||
|
? Enter value for VERSION: 1.0.0
|
||||||
|
Deploying 1.0.0 to prod
|
||||||
```
|
```
|
||||||
|
|
||||||
If the variable is already set (via CLI, environment, or Taskfile), no prompt
|
If the variable is already set (via CLI, environment, or Taskfile), no prompt
|
||||||
@@ -1168,11 +1174,14 @@ $ task deploy ENVIRONMENT=prod VERSION=1.0.0
|
|||||||
Deploying 1.0.0 to prod
|
Deploying 1.0.0 to prod
|
||||||
```
|
```
|
||||||
|
|
||||||
::: warning
|
::: info
|
||||||
|
|
||||||
Interactive prompts require a TTY. In non-interactive environments like CI
|
Interactive prompts require a TTY (terminal). Task automatically detects
|
||||||
pipelines, use `--no-tty` to disable prompts (missing variables will cause an
|
non-interactive environments like GitHub Actions, GitLab CI, and other CI
|
||||||
error as usual), or provide all required variables explicitly.
|
pipelines where stdin/stdout are not connected to a terminal. In these cases,
|
||||||
|
prompts are skipped and missing variables will cause an error as usual.
|
||||||
|
|
||||||
|
You can also explicitly disable prompts with `--no-tty` if needed.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|||||||
@@ -305,7 +305,11 @@ task deploy --yes
|
|||||||
|
|
||||||
Disable interactive prompts for missing variables. When a variable is marked
|
Disable interactive prompts for missing variables. When a variable is marked
|
||||||
with `interactive: true` in the Taskfile and is not provided, Task will error
|
with `interactive: true` in the Taskfile and is not provided, Task will error
|
||||||
instead of prompting for input. Useful in CI environments.
|
instead of prompting for input.
|
||||||
|
|
||||||
|
Note: Task automatically detects non-TTY environments (like CI pipelines) and
|
||||||
|
disables prompts. This flag is useful when you want to explicitly disable
|
||||||
|
prompts even when a TTY is available.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
task deploy --no-tty
|
task deploy --no-tty
|
||||||
|
|||||||
@@ -124,6 +124,20 @@ concurrency: 4
|
|||||||
failfast: true
|
failfast: true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `interactive`
|
||||||
|
|
||||||
|
- **Type**: `boolean`
|
||||||
|
- **Default**: `false`
|
||||||
|
- **Description**: Prompt for missing required variables instead of failing.
|
||||||
|
When enabled, Task will display an interactive prompt for any missing required
|
||||||
|
variable. Requires a TTY. Task automatically detects non-TTY environments
|
||||||
|
(CI pipelines, etc.) and skips prompts.
|
||||||
|
- **CLI override**: [`--no-tty`](./cli.md#--no-tty) to disable prompts
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
interactive: true
|
||||||
|
```
|
||||||
|
|
||||||
## Example Configuration
|
## Example Configuration
|
||||||
|
|
||||||
Here's a complete example of a `.taskrc.yml` file with all available options:
|
Here's a complete example of a `.taskrc.yml` file with all available options:
|
||||||
|
|||||||
@@ -632,7 +632,7 @@ tasks:
|
|||||||
#### `requires`
|
#### `requires`
|
||||||
|
|
||||||
- **Type**: `Requires`
|
- **Type**: `Requires`
|
||||||
- **Description**: Required variables with optional enums and interactive prompting
|
- **Description**: Required variables with optional enum validation
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
tasks:
|
tasks:
|
||||||
@@ -655,23 +655,10 @@ tasks:
|
|||||||
cmds:
|
cmds:
|
||||||
- echo "Deploying to {{.ENVIRONMENT}} with log level {{.LOG_LEVEL}}"
|
- echo "Deploying to {{.ENVIRONMENT}} with log level {{.LOG_LEVEL}}"
|
||||||
- ./deploy.sh
|
- ./deploy.sh
|
||||||
|
|
||||||
# Interactive prompts for missing variables
|
|
||||||
interactive-deploy:
|
|
||||||
requires:
|
|
||||||
vars:
|
|
||||||
- name: ENVIRONMENT
|
|
||||||
interactive: true
|
|
||||||
enum: [development, staging, production]
|
|
||||||
- name: API_KEY
|
|
||||||
interactive: true
|
|
||||||
cmds:
|
|
||||||
- ./deploy.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
When `interactive: true` is set, Task will prompt the user for the variable value
|
See [Prompting for missing variables interactively](/docs/guide#prompting-for-missing-variables-interactively)
|
||||||
if it is not already defined. For variables with `enum`, a selection menu is shown.
|
for information on enabling interactive prompts for missing required variables.
|
||||||
Use `--no-tty` to disable interactive prompts (useful for CI environments).
|
|
||||||
|
|
||||||
#### `watch`
|
#### `watch`
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,11 @@
|
|||||||
"description": "When running tasks in parallel, stop all tasks if one fails.",
|
"description": "When running tasks in parallel, stop all tasks if one fails.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"interactive": {
|
||||||
|
"description": "Prompt for missing required variables instead of failing. Requires a TTY.",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|||||||
@@ -598,12 +598,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": { "type": "string" },
|
"name": { "type": "string" },
|
||||||
"enum": { "type": "array", "items": { "type": "string" } },
|
"enum": { "type": "array", "items": { "type": "string" } }
|
||||||
"interactive": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "If true, prompt the user for this variable if it is not set",
|
|
||||||
"default": false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"required": ["name"],
|
"required": ["name"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|||||||
Reference in New Issue
Block a user