refactor(interactive): move interactive setting to taskrc

This commit is contained in:
Valentin Maerten
2025-12-13 12:01:36 +01:00
parent 2f01add607
commit 419442bba6
12 changed files with 86 additions and 67 deletions

View File

@@ -44,6 +44,7 @@ type (
AssumeYes bool
AssumeTerm bool // Used for testing
NoTTY bool
Interactive bool
Dry bool
Summary bool
Parallel bool
@@ -367,6 +368,19 @@ func (o *noTTYOption) ApplyToExecutor(e *Executor) {
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
// actually running them.
func WithDry(dry bool) ExecutorOption {

View File

@@ -80,6 +80,7 @@ var (
Timeout time.Duration
CacheExpiryDuration time.Duration
NoTTY bool
Interactive bool
)
func init() {
@@ -145,6 +146,7 @@ func init() {
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(&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.")
// 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.WithAssumeYes(AssumeYes),
task.WithNoTTY(NoTTY),
task.WithInteractive(Interactive),
task.WithDry(Dry || Status),
task.WithSummary(Summary),
task.WithParallel(Parallel),

View File

@@ -9,7 +9,7 @@ import (
"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
// prompted for (meaning the task needs to be recompiled).
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
}
// 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
if e.NoTTY || (!e.AssumeTerm && !term.IsTerminal()) {
return false, nil
@@ -26,11 +31,6 @@ func (e *Executor) promptForInteractiveVars(t *ast.Task, call *Call) (bool, erro
var prompted bool
for _, requiredVar := range t.Requires.Vars {
// Skip non-interactive vars
if !requiredVar.Interactive {
continue
}
// Skip if already set
if _, ok := t.Vars.Get(requiredVar.Name); ok {
continue

View File

@@ -25,7 +25,6 @@ func (r *Requires) DeepCopy() *Requires {
type VarsWithValidation struct {
Name string
Enum []string
Interactive bool
}
func (v *VarsWithValidation) DeepCopy() *VarsWithValidation {
@@ -35,7 +34,6 @@ func (v *VarsWithValidation) DeepCopy() *VarsWithValidation {
return &VarsWithValidation{
Name: v.Name,
Enum: v.Enum,
Interactive: v.Interactive,
}
}
@@ -56,14 +54,12 @@ func (v *VarsWithValidation) UnmarshalYAML(node *yaml.Node) error {
var vv struct {
Name string
Enum []string
Interactive bool
}
if err := node.Decode(&vv); err != nil {
return errors.NewTaskfileDecodeError(err, node)
}
v.Name = vv.Name
v.Enum = vv.Enum
v.Interactive = vv.Interactive
return nil
}

View File

@@ -14,6 +14,7 @@ type TaskRC struct {
Verbose *bool `yaml:"verbose"`
DisableFuzzy *bool `yaml:"disable-fuzzy"`
Concurrency *int `yaml:"concurrency"`
Interactive *bool `yaml:"interactive"`
Remote Remote `yaml:"remote"`
Failfast bool `yaml:"failfast"`
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.DisableFuzzy = cmp.Or(other.DisableFuzzy, t.DisableFuzzy)
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
t.Interactive = cmp.Or(other.Interactive, t.Interactive)
t.Failfast = cmp.Or(other.Failfast, t.Failfast)
}

View File

@@ -4,8 +4,7 @@ tasks:
simple:
requires:
vars:
- name: MY_VAR
interactive: true
- MY_VAR
cmds:
- echo "{{.MY_VAR}}"
@@ -13,23 +12,14 @@ tasks:
requires:
vars:
- name: ENV
interactive: true
enum: [dev, staging, prod]
cmds:
- echo "{{.ENV}}"
non-interactive:
multiple:
requires:
vars:
- NON_INTERACTIVE_VAR
- VAR1
- VAR2
cmds:
- echo "{{.NON_INTERACTIVE_VAR}}"
mixed:
requires:
vars:
- name: INTERACTIVE_VAR
interactive: true
- NON_INTERACTIVE_VAR
cmds:
- echo "{{.INTERACTIVE_VAR}} {{.NON_INTERACTIVE_VAR}}"
- echo "{{.VAR1}} {{.VAR2}}"

View File

@@ -1129,14 +1129,20 @@ This is supported only for string variables.
### Prompting for missing variables interactively
If you want Task to prompt users for missing variables instead of failing, you
can mark a variable as `interactive: true`. When a variable is missing and has
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.
If you want Task to prompt users for missing required variables instead of
failing, you can enable interactive mode in your `.taskrc.yml`:
```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'
tasks:
@@ -1144,10 +1150,8 @@ tasks:
requires:
vars:
- name: ENVIRONMENT
interactive: true
enum: [dev, staging, prod]
- name: VERSION
interactive: true
- VERSION
cmds:
- echo "Deploying {{.VERSION}} to {{.ENVIRONMENT}}"
```
@@ -1158,6 +1162,8 @@ $ task deploy
dev
staging
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
@@ -1168,11 +1174,14 @@ $ task deploy ENVIRONMENT=prod VERSION=1.0.0
Deploying 1.0.0 to prod
```
::: warning
::: info
Interactive prompts require a TTY. In non-interactive environments like CI
pipelines, use `--no-tty` to disable prompts (missing variables will cause an
error as usual), or provide all required variables explicitly.
Interactive prompts require a TTY (terminal). Task automatically detects
non-interactive environments like GitHub Actions, GitLab CI, and other CI
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.
:::

View File

@@ -305,7 +305,11 @@ task deploy --yes
Disable interactive prompts for missing variables. When a variable is marked
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
task deploy --no-tty

View File

@@ -124,6 +124,20 @@ concurrency: 4
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
Here's a complete example of a `.taskrc.yml` file with all available options:

View File

@@ -632,7 +632,7 @@ tasks:
#### `requires`
- **Type**: `Requires`
- **Description**: Required variables with optional enums and interactive prompting
- **Description**: Required variables with optional enum validation
```yaml
tasks:
@@ -655,23 +655,10 @@ tasks:
cmds:
- echo "Deploying to {{.ENVIRONMENT}} with log level {{.LOG_LEVEL}}"
- ./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
if it is not already defined. For variables with `enum`, a selection menu is shown.
Use `--no-tty` to disable interactive prompts (useful for CI environments).
See [Prompting for missing variables interactively](/docs/guide#prompting-for-missing-variables-interactively)
for information on enabling interactive prompts for missing required variables.
#### `watch`

View File

@@ -70,6 +70,11 @@
"description": "When running tasks in parallel, stop all tasks if one fails.",
"type": "boolean",
"default": false
},
"interactive": {
"description": "Prompt for missing required variables instead of failing. Requires a TTY.",
"type": "boolean",
"default": false
}
},
"additionalProperties": false

View File

@@ -598,12 +598,7 @@
"type": "object",
"properties": {
"name": { "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
}
"enum": { "type": "array", "items": { "type": "string" } }
},
"required": ["name"],
"additionalProperties": false