mirror of
https://github.com/go-task/task.git
synced 2025-01-20 04:59:37 +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 |
|
||||
| 204 | A task was called too many times |
|
||||
| 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
|
||||
[`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. |
|
||||
| `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`. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
@ -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
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
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": {
|
||||
"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": {
|
||||
"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": {
|
||||
"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
|
||||
CodeTaskCalledTooManyTimes
|
||||
CodeTaskCancelled
|
||||
CodeTaskMissingRequiredVars
|
||||
)
|
||||
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
|
||||
if err := e.areTaskRequiredVarsSet(ctx, t, call); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
preCondMet, err := e.areTaskPreconditionsMet(ctx, t)
|
||||
if err != nil {
|
||||
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
|
||||
Prompt string
|
||||
Summary string
|
||||
Requires *Requires
|
||||
Aliases []string
|
||||
Sources []string
|
||||
Generates []string
|
||||
@ -99,6 +100,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
||||
IgnoreError bool `yaml:"ignore_error"`
|
||||
Run string
|
||||
Platforms []*Platform
|
||||
Requires *Requires
|
||||
}
|
||||
if err := node.Decode(&task); err != nil {
|
||||
return err
|
||||
@ -135,6 +137,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
||||
t.IgnoreError = task.IgnoreError
|
||||
t.Run = task.Run
|
||||
t.Platforms = task.Platforms
|
||||
t.Requires = task.Requires
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -178,6 +181,7 @@ func (t *Task) DeepCopy() *Task {
|
||||
IncludedTaskfile: t.IncludedTaskfile.DeepCopy(),
|
||||
Platforms: deepcopy.Slice(t.Platforms),
|
||||
Location: t.Location.DeepCopy(),
|
||||
Requires: t.Requires.DeepCopy(),
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
||||
IncludedTaskfileVars: origTask.IncludedTaskfileVars,
|
||||
Platforms: origTask.Platforms,
|
||||
Location: origTask.Location,
|
||||
Requires: origTask.Requires,
|
||||
}
|
||||
new.Dir, err = execext.Expand(new.Dir)
|
||||
if err != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user