mirror of
https://github.com/go-task/task.git
synced 2025-04-25 12:25:07 +02:00
feat: add ability to specify which vars are required (#1204)
This commit is contained in:
parent
f346015d8c
commit
307f39cee3
@ -76,6 +76,7 @@ A full list of the exit codes and their descriptions can be found below:
|
|||||||
| 203 | There a multiple tasks with the same name or alias |
|
| 203 | There a multiple tasks with the same name or alias |
|
||||||
| 204 | A task was called too many times |
|
| 204 | A task was called too many times |
|
||||||
| 205 | A task was cancelled by the user |
|
| 205 | A task was cancelled by the user |
|
||||||
|
| 206 | A task was not executed due to missing required variables |
|
||||||
|
|
||||||
These codes can also be found in the repository in
|
These codes can also be found in the repository in
|
||||||
[`errors/errors.go`](https://github.com/go-task/task/blob/main/errors/errors.go).
|
[`errors/errors.go`](https://github.com/go-task/task/blob/main/errors/errors.go).
|
||||||
@ -219,7 +220,9 @@ vars:
|
|||||||
| `sources` | `[]string` | | A list of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs. |
|
| `sources` | `[]string` | | A list of sources to check before running this task. Relevant for `checksum` and `timestamp` methods. Can be file paths or star globs. |
|
||||||
| `generates` | `[]string` | | A list of files meant to be generated by this task. Relevant for `timestamp` method. Can be file paths or star globs. |
|
| `generates` | `[]string` | | A list of files meant to be generated by this task. Relevant for `timestamp` method. Can be file paths or star globs. |
|
||||||
| `status` | `[]string` | | A list of commands to check if this task should run. The task is skipped otherwise. This overrides `method`, `sources` and `generates`. |
|
| `status` | `[]string` | | A list of commands to check if this task should run. The task is skipped otherwise. This overrides `method`, `sources` and `generates`. |
|
||||||
|
| `requires` | `[]string` | | A list of variables which should be set if this task is to run, if any of these variables are unset the task will error and not run. |
|
||||||
| `preconditions` | [`[]Precondition`](#precondition) | | A list of commands to check if this task should run. If a condition is not met, the task will error. |
|
| `preconditions` | [`[]Precondition`](#precondition) | | A list of commands to check if this task should run. If a condition is not met, the task will error. |
|
||||||
|
| `requires` | [`Requires`](#requires) | | A list of required variables which should be set if this task is to run, if any variables listed are unset the task will error and not run. |
|
||||||
| `dir` | `string` | | The directory in which this task should run. Defaults to the current working directory. |
|
| `dir` | `string` | | The directory in which this task should run. Defaults to the current working directory. |
|
||||||
| `vars` | [`map[string]Variable`](#variable) | | A set of variables that can be used in the task. |
|
| `vars` | [`map[string]Variable`](#variable) | | A set of variables that can be used in the task. |
|
||||||
| `env` | [`map[string]Variable`](#variable) | | A set of environment variables that will be made available to shell commands. |
|
| `env` | [`map[string]Variable`](#variable) | | A set of environment variables that will be made available to shell commands. |
|
||||||
@ -322,3 +325,9 @@ tasks:
|
|||||||
```
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
#### Requires
|
||||||
|
|
||||||
|
| Attribute | Type | Default | Description |
|
||||||
|
| --------- | ---------- | ------- | -------------------------------------------------------------------------------------------------- |
|
||||||
|
| `vars` | `[]string` | | List of variable or environment variable names that must be set if this task is to execute and run |
|
||||||
|
@ -876,6 +876,48 @@ tasks:
|
|||||||
- sleep 5 # long operation like installing packages
|
- sleep 5 # long operation like installing packages
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Ensuring required variables are set
|
||||||
|
|
||||||
|
If you want to check that certain variables are set before running a task then
|
||||||
|
you can use `requires`. This is useful when might not be clear to users which
|
||||||
|
variables are needed, or if you want clear message about what is required. Also
|
||||||
|
some tasks could have dangerous side effects if run with un-set variables.
|
||||||
|
|
||||||
|
Using `requires` you specify an array of strings in the `vars` sub-section
|
||||||
|
under `requires`, these strings are variable names which are checked prior to
|
||||||
|
running the task. If any variables are un-set the the task will error and not
|
||||||
|
run.
|
||||||
|
|
||||||
|
Environmental variables are also checked.
|
||||||
|
|
||||||
|
Syntax:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
requires:
|
||||||
|
vars: [] # Array of strings
|
||||||
|
```
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
Variables set to empty zero length strings, will pass the `requires` check.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
Example of using `requires`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
docker-build:
|
||||||
|
cmds:
|
||||||
|
- 'docker build . -t {{.IMAGE_NAME}}:{{.IMAGE_TAG}}'
|
||||||
|
|
||||||
|
# Make sure these variables are set before running
|
||||||
|
requires:
|
||||||
|
vars: [IMAGE_NAME, IMAGE_TAG]
|
||||||
|
```
|
||||||
|
|
||||||
## Variables
|
## Variables
|
||||||
|
|
||||||
When doing interpolation of variables, Task will look for the below. They are
|
When doing interpolation of variables, Task will look for the below. They are
|
||||||
|
32
docs/static/schema.json
vendored
32
docs/static/schema.json
vendored
@ -184,6 +184,10 @@
|
|||||||
"items": {
|
"items": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"requires": {
|
||||||
|
"description": "A list of variables which should be set if this task is to run, if any of these variables are unset the task will error and not run",
|
||||||
|
"$ref": "#/definitions/3/requires_obj"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -208,7 +212,21 @@
|
|||||||
},
|
},
|
||||||
"set": {
|
"set": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["allexport", "a", "errexit", "e", "noexec", "n", "noglob", "f", "nounset", "u", "xtrace", "x", "pipefail"]
|
"enum": [
|
||||||
|
"allexport",
|
||||||
|
"a",
|
||||||
|
"errexit",
|
||||||
|
"e",
|
||||||
|
"noexec",
|
||||||
|
"n",
|
||||||
|
"noglob",
|
||||||
|
"f",
|
||||||
|
"nounset",
|
||||||
|
"u",
|
||||||
|
"xtrace",
|
||||||
|
"x",
|
||||||
|
"pipefail"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"shopt": {
|
"shopt": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -352,6 +370,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"requires_obj": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"vars": {
|
||||||
|
"description": "List of variables that must be defined for the task to run",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -23,6 +23,7 @@ const (
|
|||||||
CodeTaskNameConflict
|
CodeTaskNameConflict
|
||||||
CodeTaskCalledTooManyTimes
|
CodeTaskCalledTooManyTimes
|
||||||
CodeTaskCancelled
|
CodeTaskCancelled
|
||||||
|
CodeTaskMissingRequiredVars
|
||||||
)
|
)
|
||||||
|
|
||||||
// TaskError extends the standard error interface with a Code method. This code will
|
// TaskError extends the standard error interface with a Code method. This code will
|
||||||
|
@ -130,3 +130,21 @@ func (err *TaskCancelledNoTerminalError) Error() string {
|
|||||||
func (err *TaskCancelledNoTerminalError) Code() int {
|
func (err *TaskCancelledNoTerminalError) Code() int {
|
||||||
return CodeTaskCancelled
|
return CodeTaskCancelled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TaskMissingRequiredVars is returned when a task is missing required variables.
|
||||||
|
type TaskMissingRequiredVars struct {
|
||||||
|
TaskName string
|
||||||
|
MissingVars []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *TaskMissingRequiredVars) Error() string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`task: Task %q cancelled because it is missing required variables: %s`,
|
||||||
|
err.TaskName,
|
||||||
|
strings.Join(err.MissingVars, ", "),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *TaskMissingRequiredVars) Code() int {
|
||||||
|
return CodeTaskMissingRequiredVars
|
||||||
|
}
|
||||||
|
35
requires.go
Normal file
35
requires.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/go-task/task/v3/errors"
|
||||||
|
"github.com/go-task/task/v3/taskfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (e *Executor) areTaskRequiredVarsSet(ctx context.Context, t *taskfile.Task, call taskfile.Call) error {
|
||||||
|
if t.Requires == nil || len(t.Requires.Vars) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
vars, err := e.Compiler.GetVariables(t, call)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var missingVars []string
|
||||||
|
for _, requiredVar := range t.Requires.Vars {
|
||||||
|
if !vars.Exists(requiredVar) {
|
||||||
|
missingVars = append(missingVars, requiredVar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(missingVars) > 0 {
|
||||||
|
return &errors.TaskMissingRequiredVars{
|
||||||
|
TaskName: t.Name(),
|
||||||
|
MissingVars: missingVars,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
4
task.go
4
task.go
@ -186,6 +186,10 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := e.areTaskRequiredVarsSet(ctx, t, call); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
|
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
18
taskfile/requires.go
Normal file
18
taskfile/requires.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package taskfile
|
||||||
|
|
||||||
|
import "github.com/go-task/task/v3/internal/deepcopy"
|
||||||
|
|
||||||
|
// Requires represents a set of required variables necessary for a task to run
|
||||||
|
type Requires struct {
|
||||||
|
Vars []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Requires) DeepCopy() *Requires {
|
||||||
|
if r == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Requires{
|
||||||
|
Vars: deepcopy.Slice(r.Vars),
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@ type Task struct {
|
|||||||
Desc string
|
Desc string
|
||||||
Prompt string
|
Prompt string
|
||||||
Summary string
|
Summary string
|
||||||
|
Requires *Requires
|
||||||
Aliases []string
|
Aliases []string
|
||||||
Sources []string
|
Sources []string
|
||||||
Generates []string
|
Generates []string
|
||||||
@ -99,6 +100,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
IgnoreError bool `yaml:"ignore_error"`
|
IgnoreError bool `yaml:"ignore_error"`
|
||||||
Run string
|
Run string
|
||||||
Platforms []*Platform
|
Platforms []*Platform
|
||||||
|
Requires *Requires
|
||||||
}
|
}
|
||||||
if err := node.Decode(&task); err != nil {
|
if err := node.Decode(&task); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -135,6 +137,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
t.IgnoreError = task.IgnoreError
|
t.IgnoreError = task.IgnoreError
|
||||||
t.Run = task.Run
|
t.Run = task.Run
|
||||||
t.Platforms = task.Platforms
|
t.Platforms = task.Platforms
|
||||||
|
t.Requires = task.Requires
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,6 +181,7 @@ func (t *Task) DeepCopy() *Task {
|
|||||||
IncludedTaskfile: t.IncludedTaskfile.DeepCopy(),
|
IncludedTaskfile: t.IncludedTaskfile.DeepCopy(),
|
||||||
Platforms: deepcopy.Slice(t.Platforms),
|
Platforms: deepcopy.Slice(t.Platforms),
|
||||||
Location: t.Location.DeepCopy(),
|
Location: t.Location.DeepCopy(),
|
||||||
|
Requires: t.Requires.DeepCopy(),
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
IncludedTaskfileVars: origTask.IncludedTaskfileVars,
|
IncludedTaskfileVars: origTask.IncludedTaskfileVars,
|
||||||
Platforms: origTask.Platforms,
|
Platforms: origTask.Platforms,
|
||||||
Location: origTask.Location,
|
Location: origTask.Location,
|
||||||
|
Requires: origTask.Requires,
|
||||||
}
|
}
|
||||||
new.Dir, err = execext.Expand(new.Dir)
|
new.Dir, err = execext.Expand(new.Dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user