mirror of
https://github.com/go-task/task.git
synced 2025-05-15 22:26:28 +02:00
feat: make map variables experiment (prop 2) generally available (#2081)
* feat: make map variables experiment (prop 2) generally available * docs: remove map variables experiment page and update usage to include map variable info
This commit is contained in:
parent
cb14a4f3a1
commit
c6f1b3ae4f
@ -44,7 +44,7 @@ func init() {
|
|||||||
GentleForce = New("GENTLE_FORCE", 1)
|
GentleForce = New("GENTLE_FORCE", 1)
|
||||||
RemoteTaskfiles = New("REMOTE_TASKFILES", 1)
|
RemoteTaskfiles = New("REMOTE_TASKFILES", 1)
|
||||||
AnyVariables = New("ANY_VARIABLES")
|
AnyVariables = New("ANY_VARIABLES")
|
||||||
MapVariables = New("MAP_VARIABLES", 1, 2)
|
MapVariables = New("MAP_VARIABLES")
|
||||||
EnvPrecedence = New("ENV_PRECEDENCE", 1)
|
EnvPrecedence = New("ENV_PRECEDENCE", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/go-task/task/v3/errors"
|
"github.com/go-task/task/v3/errors"
|
||||||
"github.com/go-task/task/v3/internal/experiments"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Var represents either a static or dynamic variable.
|
// Var represents either a static or dynamic variable.
|
||||||
@ -19,31 +16,6 @@ type Var struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
||||||
if experiments.MapVariables.Enabled() {
|
|
||||||
|
|
||||||
// This implementation is not backwards-compatible and replaces the 'sh' key with map variables
|
|
||||||
if experiments.MapVariables.Value == 1 {
|
|
||||||
var value any
|
|
||||||
if err := node.Decode(&value); err != nil {
|
|
||||||
return errors.NewTaskfileDecodeError(err, node)
|
|
||||||
}
|
|
||||||
// If the value is a string and it starts with $, then it's a shell command
|
|
||||||
if str, ok := value.(string); ok {
|
|
||||||
if str, ok = strings.CutPrefix(str, "$"); ok {
|
|
||||||
v.Sh = &str
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if str, ok = strings.CutPrefix(str, "#"); ok {
|
|
||||||
v.Ref = str
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
v.Value = value
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// This implementation IS backwards-compatible and keeps the 'sh' key and allows map variables to be added under the `map` key
|
|
||||||
if experiments.MapVariables.Value == 2 {
|
|
||||||
switch node.Kind {
|
switch node.Kind {
|
||||||
case yaml.MappingNode:
|
case yaml.MappingNode:
|
||||||
key := node.Content[0].Value
|
key := node.Content[0].Value
|
||||||
@ -72,35 +44,4 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
v.Value = value
|
v.Value = value
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch node.Kind {
|
|
||||||
|
|
||||||
case yaml.MappingNode:
|
|
||||||
key := node.Content[0].Value
|
|
||||||
switch key {
|
|
||||||
case "sh", "ref":
|
|
||||||
var m struct {
|
|
||||||
Sh *string
|
|
||||||
Ref string
|
|
||||||
}
|
|
||||||
if err := node.Decode(&m); err != nil {
|
|
||||||
return errors.NewTaskfileDecodeError(err, node)
|
|
||||||
}
|
|
||||||
v.Sh = m.Sh
|
|
||||||
v.Ref = m.Ref
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
return errors.NewTaskfileDecodeError(nil, node).WithMessage("maps cannot be assigned to variables")
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
var value any
|
|
||||||
if err := node.Decode(&value); err != nil {
|
|
||||||
return errors.NewTaskfileDecodeError(err, node)
|
|
||||||
}
|
|
||||||
v.Value = value
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,245 +0,0 @@
|
|||||||
---
|
|
||||||
slug: /experiments/map-variables/
|
|
||||||
---
|
|
||||||
|
|
||||||
import Tabs from '@theme/Tabs';
|
|
||||||
import TabItem from '@theme/TabItem';
|
|
||||||
|
|
||||||
# Map Variables (#1585)
|
|
||||||
|
|
||||||
:::caution
|
|
||||||
|
|
||||||
All experimental features are subject to breaking changes and/or removal _at any
|
|
||||||
time_. We strongly recommend that you do not use these features in a production
|
|
||||||
environment. They are intended for testing and feedback only.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
Currently, Task supports all variable types except for maps. This experiment
|
|
||||||
adds two different proposals for map variables. Click on the tabs below to
|
|
||||||
switch between them.
|
|
||||||
|
|
||||||
<Tabs defaultValue="1" queryString="proposal"
|
|
||||||
values={[
|
|
||||||
{label: 'Proposal 1', value: '1'},
|
|
||||||
{label: 'Proposal 2', value: '2'}
|
|
||||||
]}>
|
|
||||||
|
|
||||||
<TabItem value="1">
|
|
||||||
|
|
||||||
:::warning
|
|
||||||
|
|
||||||
This experiment proposal breaks the following functionality:
|
|
||||||
|
|
||||||
- Dynamically defined variables (using the `sh` keyword)
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
:::info
|
|
||||||
|
|
||||||
To enable this experiment, set the environment variable:
|
|
||||||
`TASK_X_MAP_VARIABLES=1`. Check out [our guide to enabling experiments
|
|
||||||
][enabling-experiments] for more information.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
This proposal removes support for the `sh` and `ref` keywords in favour of a new
|
|
||||||
syntax for dynamically defined variables and references. This allows you to
|
|
||||||
define a map directly as you would for any other type:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: 3
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
foo:
|
|
||||||
vars:
|
|
||||||
FOO: {a: 1, b: 2, c: 3} # <-- Directly defined map on the `FOO` key
|
|
||||||
cmds:
|
|
||||||
- 'echo {{.FOO.a}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
## Migration
|
|
||||||
|
|
||||||
Taskfiles with dynamically defined variables via the `sh` subkey or references
|
|
||||||
defined with `ref` will no longer work with this experiment enabled. In order to
|
|
||||||
keep using these features, you will need to migrate your Taskfile to use the new
|
|
||||||
syntax.
|
|
||||||
|
|
||||||
### Dynamic Variables
|
|
||||||
|
|
||||||
Previously, you had to define dynamic variables using the `sh` subkey. With this
|
|
||||||
experiment enabled, you will need to remove the `sh` subkey and define your
|
|
||||||
command as a string that begins with a `$`. This will instruct Task to interpret
|
|
||||||
the string as a command instead of a literal value and the variable will be
|
|
||||||
populated with the output of the command. For example:
|
|
||||||
|
|
||||||
<Tabs defaultValue="2"
|
|
||||||
values={[
|
|
||||||
{label: 'Before', value: '1'},
|
|
||||||
{label: 'After', value: '2'}
|
|
||||||
]}>
|
|
||||||
|
|
||||||
<TabItem value="1">
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: 3
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
foo:
|
|
||||||
vars:
|
|
||||||
CALCULATED_VAR:
|
|
||||||
sh: 'echo hello'
|
|
||||||
cmds:
|
|
||||||
- 'echo {{.CALCULATED_VAR}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
<TabItem value="2">
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: 3
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
foo:
|
|
||||||
vars:
|
|
||||||
CALCULATED_VAR: '$echo hello' # <-- Prefix dynamic variable with a `$`
|
|
||||||
cmds:
|
|
||||||
- 'echo {{.CALCULATED_VAR}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem></Tabs>
|
|
||||||
|
|
||||||
### References
|
|
||||||
|
|
||||||
<Tabs defaultValue="2"
|
|
||||||
values={[
|
|
||||||
{label: 'Before', value: '1'},
|
|
||||||
{label: 'After', value: '2'}
|
|
||||||
]}>
|
|
||||||
|
|
||||||
<TabItem value="1">
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: 3
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
foo:
|
|
||||||
vars:
|
|
||||||
VAR: 42
|
|
||||||
VAR_REF:
|
|
||||||
ref: '.FOO'
|
|
||||||
cmds:
|
|
||||||
- 'echo {{.VAR_REF}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
<TabItem value="2">
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: 3
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
foo:
|
|
||||||
vars:
|
|
||||||
VAR: 42
|
|
||||||
VAR_REF: '#.FOO' # <-- Prefix reference with a `#`
|
|
||||||
cmds:
|
|
||||||
- 'echo {{.VAR_REF}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem></Tabs>
|
|
||||||
|
|
||||||
If your current Taskfile contains a string variable that begins with a `$` or a
|
|
||||||
`#`, you will now need to escape it with a backslash (`\`) to stop Task from
|
|
||||||
interpreting it as a command or reference.
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
<TabItem value="2">
|
|
||||||
|
|
||||||
:::info
|
|
||||||
|
|
||||||
To enable this experiment, set the environment variable:
|
|
||||||
`TASK_X_MAP_VARIABLES=2`. Check out [our guide to enabling experiments
|
|
||||||
][enabling-experiments] for more information.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
This proposal maintains backwards-compatibility and the `sh` subkey and adds
|
|
||||||
another new `map` subkey for defining map variables:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: 3
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
foo:
|
|
||||||
vars:
|
|
||||||
FOO:
|
|
||||||
map: {a: 1, b: 2, c: 3} # <-- Defined using the `map' subkey instead of directly on 'FOO'
|
|
||||||
BAR: true # <-- Other types of variables are still defined directly on the key
|
|
||||||
BAZ:
|
|
||||||
sh: 'echo Hello Task' # <-- The `sh` subkey is still supported
|
|
||||||
QUX:
|
|
||||||
ref: '.BAZ' # <-- The `ref` subkey is still supported
|
|
||||||
cmds:
|
|
||||||
- 'echo {{.FOO.a}}'
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem></Tabs>
|
|
||||||
|
|
||||||
## Looping over maps
|
|
||||||
|
|
||||||
This experiment also adds support for looping over maps using the `for` keyword,
|
|
||||||
just like arrays. In addition to the `{{.ITEM}}` variable being populated when
|
|
||||||
looping over a map, we also make an additional `{{.KEY}}` variable available
|
|
||||||
that holds the string value of the map key.
|
|
||||||
|
|
||||||
<Tabs defaultValue="1" queryString="proposal"
|
|
||||||
values={[
|
|
||||||
{label: 'Proposal 1', value: '1'},
|
|
||||||
{label: 'Proposal 2', value: '2'}
|
|
||||||
]}>
|
|
||||||
|
|
||||||
<TabItem value="1">
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: 3
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
foo:
|
|
||||||
vars:
|
|
||||||
MAP: {a: 1, b: 2, c: 3}
|
|
||||||
cmds:
|
|
||||||
- for:
|
|
||||||
var: MAP
|
|
||||||
cmd: 'echo "{{.KEY}}: {{.ITEM}}"'
|
|
||||||
```
|
|
||||||
|
|
||||||
</TabItem>
|
|
||||||
<TabItem value="2">
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: 3
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
foo:
|
|
||||||
vars:
|
|
||||||
map:
|
|
||||||
MAP: {a: 1, b: 2, c: 3}
|
|
||||||
cmds:
|
|
||||||
- for:
|
|
||||||
var: MAP
|
|
||||||
cmd: 'echo "{{.KEY}}: {{.ITEM}}"'
|
|
||||||
```
|
|
||||||
|
|
||||||
:::note
|
|
||||||
|
|
||||||
Remember that maps are unordered, so
|
|
||||||
the order in which the items are looped over is random.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
</TabItem></Tabs>
|
|
||||||
|
|
||||||
{/* prettier-ignore-start */}
|
|
||||||
[enabling-experiments]: ./experiments.mdx#enabling-experiments
|
|
||||||
{/* prettier-ignore-end */}
|
|
@ -1113,53 +1113,38 @@ variable types are supported:
|
|||||||
- `int`
|
- `int`
|
||||||
- `float`
|
- `float`
|
||||||
- `array`
|
- `array`
|
||||||
|
- `map`
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
|
|
||||||
Maps are not supported by default, but there is an
|
Defining a map requires that you use a special `map` subkey (see example below).
|
||||||
[experiment][map-variables] that can be enabled to add support. If
|
|
||||||
you're interested in this functionality, we would appreciate your feedback.
|
|
||||||
:pray:
|
|
||||||
|
|
||||||
In the meantime, it is technically possible to define a map using a `ref` resolver and a templating function. For example:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: '3'
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
task-with-map:
|
|
||||||
vars:
|
|
||||||
FOO:
|
|
||||||
ref: dict "a" "1" "b" "2" "c" "3"
|
|
||||||
cmds:
|
|
||||||
- echo {{.FOO}}
|
|
||||||
```
|
|
||||||
|
|
||||||
```txt
|
|
||||||
map[a:1 b:2 c:3]
|
|
||||||
```
|
|
||||||
|
|
||||||
OR by using the same technique with JSON:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
version: '3'
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
task-with-map:
|
|
||||||
vars:
|
|
||||||
JSON: '{"a": 1, "b": 2, "c": 3}'
|
|
||||||
FOO:
|
|
||||||
ref: "fromJson .JSON"
|
|
||||||
cmds:
|
|
||||||
- echo {{.FOO}}
|
|
||||||
```
|
|
||||||
|
|
||||||
```txt
|
|
||||||
map[a:1 b:2 c:3]
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: 3
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
foo:
|
||||||
|
vars:
|
||||||
|
STRING: 'Hello, World!'
|
||||||
|
BOOL: true
|
||||||
|
INT: 42
|
||||||
|
FLOAT: 3.14
|
||||||
|
ARRAY: [1, 2, 3]
|
||||||
|
MAP:
|
||||||
|
map: {A: 1, B: 2, C: 3}
|
||||||
|
cmds:
|
||||||
|
- 'echo {{.STRING}}' # Hello, World!
|
||||||
|
- 'echo {{.BOOL}}' # true
|
||||||
|
- 'echo {{.INT}}' # 42
|
||||||
|
- 'echo {{.FLOAT}}' # 3.14
|
||||||
|
- 'echo {{.ARRAY}}' # [1 2 3]
|
||||||
|
- 'echo {{.ARRAY.0}}' # 1
|
||||||
|
- 'echo {{.MAP}}' # map[A:1 B:2 C:3]
|
||||||
|
- 'echo {{.MAP.A}}' # 1
|
||||||
|
```
|
||||||
|
|
||||||
Variables can be set in many places in a Taskfile. When executing
|
Variables can be set in many places in a Taskfile. When executing
|
||||||
[templates][templating-reference], Task will look for variables in the order
|
[templates][templating-reference], Task will look for variables in the order
|
||||||
listed below (most important first):
|
listed below (most important first):
|
||||||
@ -1360,6 +1345,29 @@ tasks:
|
|||||||
- 'echo {{.FOO}}' # <-- FOO is just the letter 'A'
|
- 'echo {{.FOO}}' # <-- FOO is just the letter 'A'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Parsing JSON/YAML into map variables
|
||||||
|
|
||||||
|
If you have a raw JSON or YAML string that you want to process in Task, you can
|
||||||
|
use a combination of the `ref` keyword and the `fromJson` or `fromYaml`
|
||||||
|
templating functions to parse the string into a map variable. For example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
task-with-map:
|
||||||
|
vars:
|
||||||
|
JSON: '{"a": 1, "b": 2, "c": 3}'
|
||||||
|
FOO:
|
||||||
|
ref: "fromJson .JSON"
|
||||||
|
cmds:
|
||||||
|
- echo {{.FOO}}
|
||||||
|
```
|
||||||
|
|
||||||
|
```txt
|
||||||
|
map[a:1 b:2 c:3]
|
||||||
|
```
|
||||||
|
|
||||||
## Looping over values
|
## Looping over values
|
||||||
|
|
||||||
Task allows you to loop over certain values and execute a command for each.
|
Task allows you to loop over certain values and execute a command for each.
|
||||||
@ -1508,7 +1516,7 @@ tasks:
|
|||||||
cmd: cat {{.ITEM}}
|
cmd: cat {{.ITEM}}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also loop over arrays directly and maps:
|
You can also loop over arrays and maps directly:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: 3
|
version: 3
|
||||||
@ -2319,6 +2327,5 @@ if called by another task, either directly or as a dependency.
|
|||||||
|
|
||||||
{/* prettier-ignore-start */}
|
{/* prettier-ignore-start */}
|
||||||
[gotemplate]: https://golang.org/pkg/text/template/
|
[gotemplate]: https://golang.org/pkg/text/template/
|
||||||
[map-variables]: ./experiments/map_variables.mdx
|
|
||||||
[templating-reference]: ./reference/templating.mdx
|
[templating-reference]: ./reference/templating.mdx
|
||||||
{/* prettier-ignore-end */}
|
{/* prettier-ignore-end */}
|
||||||
|
@ -276,7 +276,7 @@
|
|||||||
"^.*$": {
|
"^.*$": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"type": ["boolean", "integer", "null", "number", "string", "object", "array"]
|
"type": ["boolean", "integer", "null", "number", "string", "array"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/definitions/var_subkey"
|
"$ref": "#/definitions/var_subkey"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user