1
0
mirror of https://github.com/go-task/task.git synced 2025-01-24 05:17:21 +02:00

feat: enable any variables without maps (#1547)

* feat: enable any variable experiment (without maps)

* chore: rename any_variables experiment to map_variables

* docs: create map variables experiment docs and update usage

* blog: any variables

* fix: links

* fix: warn about broken links instead of failing
This commit is contained in:
Pete Davison 2024-04-09 12:14:14 +01:00 committed by GitHub
parent eb2783fcce
commit 1ff618cc17
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 322 additions and 132 deletions

View File

@ -27,14 +27,14 @@ type Experiment struct {
var (
GentleForce Experiment
RemoteTaskfiles Experiment
AnyVariables Experiment
MapVariables Experiment
)
func init() {
readDotEnv()
GentleForce = New("GENTLE_FORCE")
RemoteTaskfiles = New("REMOTE_TASKFILES")
AnyVariables = New("ANY_VARIABLES", "1", "2")
MapVariables = New("MAP_VARIABLES", "1", "2")
}
func New(xName string, enabledValues ...string) Experiment {
@ -101,6 +101,6 @@ func List(l *logger.Logger) error {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 0, ' ', 0)
printExperiment(w, l, GentleForce)
printExperiment(w, l, RemoteTaskfiles)
printExperiment(w, l, AnyVariables)
printExperiment(w, l, MapVariables)
return w.Flush()
}

View File

@ -83,10 +83,10 @@ type Var struct {
}
func (v *Var) UnmarshalYAML(node *yaml.Node) error {
if experiments.AnyVariables.Enabled {
if experiments.MapVariables.Enabled {
// This implementation is not backwards-compatible and replaces the 'sh' key with map variables
if experiments.AnyVariables.Value == "1" {
if experiments.MapVariables.Value == "1" {
var value any
if err := node.Decode(&value); err != nil {
return err
@ -103,7 +103,7 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
}
// This implementation IS backwards-compatible and keeps the 'sh' key and allows map variables to be added under the `map` key
if experiments.AnyVariables.Value == "2" {
if experiments.MapVariables.Value == "2" {
switch node.Kind {
case yaml.MappingNode:
key := node.Content[0].Value
@ -141,15 +141,10 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
switch node.Kind {
case yaml.ScalarNode:
var str string
if err := node.Decode(&str); err != nil {
return err
}
v.Value = str
return nil
case yaml.MappingNode:
if len(node.Content) > 2 || node.Content[0].Value != "sh" {
return fmt.Errorf(`task: line %d: maps cannot be assigned to variables`, node.Line)
}
var sh struct {
Sh string
}
@ -158,7 +153,13 @@ func (v *Var) UnmarshalYAML(node *yaml.Node) error {
}
v.Sh = sh.Sh
return nil
}
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into variable", node.Line, node.ShortTag())
default:
var value any
if err := node.Decode(&value); err != nil {
return err
}
v.Value = value
return nil
}
}

View File

@ -16,7 +16,7 @@ communicate these kinds of thoughts to the community. So, with that in mind,
this is the first (hopefully of many) blog posts talking about Task and what
we're up to.
<!--truncate-->
{/* truncate */}
## :calendar: So, what have we been up to?
@ -122,7 +122,7 @@ I plan to write more of these blog posts in the future on a variety of
Task-related topics, so make sure to check in occasionally and see what we're up
to!
<!-- prettier-ignore-start -->
{/* prettier-ignore-start */}
[vscode-task]: https://github.com/go-task/vscode-task
[crowdin]: https://crowdin.com
[contributors]: https://github.com/go-task/task/graphs/contributors
@ -139,4 +139,4 @@ to!
[experiments-project]: https://github.com/orgs/go-task/projects/1
[gentle-force-experiment]: https://github.com/go-task/task/issues/1200
[remote-taskfiles-experiment]: https://github.com/go-task/task/issues/1317
<!-- prettier-ignore-end -->
{/* prettier-ignore-end */}

View File

@ -0,0 +1,182 @@
---
title: Any Variables
description: Task variables are no longer limited to strings!
slug: any-variables
authors: [pd93]
tags: [experiments, variables]
image: https://i.imgur.com/mErPwqL.png
hide_table_of_contents: false
draft: true
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
Task has always had variables, but even though you were able to define them
using different YAML types, they would always be converted to strings by Task.
This limited users to string manipulation and encouraged messy workarounds for
simple problems. Starting from [v3.36.0][v3.36.0], this is no longer the case!
Task now supports most variable types, including **booleans**, **integers**,
**floats** and **arrays**!
{/* truncate */}
## What's the big deal?
These changes allow you to use variables in a much more natural way and opens up
a wide variety of sprig functions that were previously useless. Take a look at
some of the examples below for some inspiration.
### Evaluating booleans
No more comparing strings to "true" or "false". Now you can use actual boolean
values in your templates:
<Tabs defaultValue="2"
values={[
{label: 'Before', value: '1'},
{label: 'After', value: '2'}
]}>
<TabItem value="1">
```yaml
version: 3
tasks:
foo:
vars:
BOOL: true # <-- Parsed as a string even though its a YAML boolean
cmds:
- '{{if eq .BOOL "true"}}echo foo{{end}}'
```
</TabItem>
<TabItem value="2">
```yaml
version: 3
tasks:
foo:
vars:
BOOL: true # <-- Parsed as a boolean
cmds:
- '{{if .BOOL}}echo foo{{end}}' # <-- No need to compare to "true"
```
</TabItem></Tabs>
### Arithmetic
You can now perform basic arithmetic operations on integer and float variables:
```yaml
version: 3
tasks:
foo:
vars:
INT: 10
FLOAT: 3.14159
cmds:
- 'echo {{add .INT .FLOAT}}'
```
You can use any of the following arithmetic functions: `add`, `sub`, `mul`,
`div`, `mod`, `max`, `min`, `floor`, `ceil`, `round` and `randInt`. Check out
the [slim-sprig math documentation][slim-sprig-math] for more information.
### Arrays
You can now range over arrays inside templates and use list-based functions:
```yaml
version: 3
tasks:
foo:
vars:
ARRAY: [1, 2, 3]
cmds:
- 'echo {{range .ARRAY}}{{.}}{{end}}'
```
You can use any of the following list-based functions: `first`, `rest`, `last`,
`initial`, `append`, `prepend`, `concat`, `reverse`, `uniq`, `without`, `has`,
`compact`, `slice` and `chunk`. Check out the [slim-sprg lists
documentation][slim-sprig-list] for more information.
### Looping over variables using `for`
Previously, you would have to use a delimiter separated string to loop over an
arbitrary list of items in a variable and split them by using the `split` subkey
to specify the delimiter. However, we have now added support for looping over
"collection-type" variables using the `for` keyword, so now you are able to loop
over list variables directly:
<Tabs defaultValue="2"
values={[
{label: 'Before', value: '1'},
{label: 'After', value: '2'}
]}>
<TabItem value="1">
```yaml
version: 3
tasks:
foo:
vars:
LIST: 'foo,bar,baz'
cmds:
- for:
var: LIST
split: ','
cmd: echo {{.ITEM}}
```
</TabItem>
<TabItem value="2">
```yaml
version: 3
tasks:
foo:
vars:
LIST: ['foo', 'bar', 'baz']
cmds:
- for:
var: LIST
cmd: echo {{.ITEM}}
```
</TabItem></Tabs>
## What about maps?
Maps were originally included in the Any Variables experiment. However, they
weren't quite ready yet. Instead of making you wait for everything to be ready
at once, we have released support for all other variable types and we will
continue working on map support in the new "[Map Variables][map-variables]"
experiment.
:::note
If you were previously using maps with the Any Variables experiment and wish to
continue using them, you will need to enable the new [Map Variables
experiment][map-variables] instead.
:::
We're looking for feedback on a couple of different proposals, so please give
them a go and let us know what you think. :pray:
{/* prettier-ignore-start */}
[v3.36.0]: https://github.com/go-task/task/releases/tag/v3.36.0
[slim-sprig-math]: https://go-task.github.io/slim-sprig/math.html
[slim-sprig-list]: https://go-task.github.io/slim-sprig/lists.html
[map-variables]: /experiments/map-variables
{/* prettier-ignore-end */}

View File

@ -45,5 +45,5 @@ if you want to adopt the new behavior, you can continue to use the `--force`
flag as you do now!
{/* prettier-ignore-start */}
[enabling-experiments]: /experiments/#enabling-experiments
[enabling-experiments]: ./experiments.mdx#enabling-experiments
{/* prettier-ignore-end */}

View File

@ -1,11 +1,11 @@
---
slug: /experiments/any-variables/
slug: /experiments/map-variables/
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
# Any Variables (#1415)
# Map Variables (#1585)
:::caution
@ -15,19 +15,9 @@ environment. They are intended for testing and feedback only.
:::
Currently, Task only supports string variables. This experiment allows you to
specify and use the following variable types:
- `string`
- `bool`
- `int`
- `float`
- `array`
- `map`
This allows you to have a lot more flexibility in how you use variables in
Task's templating engine. There are two active proposals for this experiment.
Click on the tabs below to switch between them.
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={[
@ -48,13 +38,11 @@ This experiment proposal breaks the following functionality:
:::info
To enable this experiment, set the environment variable:
`TASK_X_ANY_VARIABLES=1`. Check out [our guide to enabling experiments
`TASK_X_MAP_VARIABLES=1`. Check out [our guide to enabling experiments
][enabling-experiments] for more information.
:::
## Maps
This proposal removes support for the `sh` keyword in favour of a new syntax for
dynamically defined variables, This allows you to define a map directly as you
would for any other type:
@ -111,19 +99,16 @@ will now need to escape the `$` with a backslash (`\`) to stop Task from
executing it as a command.
</TabItem>
<TabItem value="2">
:::info
To enable this experiment, set the environment variable:
`TASK_X_ANY_VARIABLES=2`. Check out [our guide to enabling experiments
`TASK_X_MAP_VARIABLES=2`. Check out [our guide to enabling experiments
][enabling-experiments] for more information.
:::
## Maps
This proposal maintains backwards-compatibility and the `sh` subkey and adds
another new `map` subkey for defining map variables:
@ -150,7 +135,13 @@ objects/arrays. This is similar to the `fromJSON` template function, but means
that you only have to parse the JSON/YAML once when you declare the variable,
instead of every time you want to access a value.
Before:
<Tabs defaultValue="2"
values={[
{label: 'Before', value: '1'},
{label: 'After', value: '2'}
]}>
<TabItem value="1">
```yaml
version: 3
@ -164,7 +155,8 @@ tasks:
- 'echo {{(fromJSON .FOO).b}}'
```
After:
</TabItem>
<TabItem value="2">
```yaml
version: 3
@ -179,12 +171,26 @@ tasks:
- 'echo {{.FOO.b}}'
```
</TabItem></Tabs>
## Variables by reference
Lastly, this proposal adds support for defining and passing variables by
reference. This is really important now that variables can be types other than a
string. Previously, to send a variable from one task to another, you would have
to use the templating system to pass it:
string.
Previously, to send a variable from one task to another, you would have to use
the templating system. Unfortunately, the templater _always_ outputs a string
and operations on the passed variable may not have behaved as expected. With
this proposal, you can now pass variables by reference using the `ref` subkey:
<Tabs defaultValue="2"
values={[
{label: 'Before', value: '1'},
{label: 'After', value: '2'}
]}>
<TabItem value="1">
```yaml
version: 3
@ -202,10 +208,8 @@ tasks:
- 'echo {{index .FOO 0}}' # <-- FOO is a string so the task outputs '91' which is the ASCII code for '[' instead of the expected 'A'
```
Unfortunately, this results in the value always being passed as a string as this
is the output type of the templater and operations on the passed variable may
not behave as expected. With this proposal, you can now pass variables by
reference using the `ref` subkey:
</TabItem>
<TabItem value="2">
```yaml
version: 3
@ -224,6 +228,8 @@ tasks:
- 'echo {{index .FOO 0}}' # <-- FOO is still a map so the task outputs 'A' as expected
```
</TabItem></Tabs>
This means that the type of the variable is maintained when it is passed to
another Task. This also works the same way when calling `deps` and when defining
a variable and can be used in any combination:
@ -249,14 +255,20 @@ tasks:
</TabItem></Tabs>
---
## Looping over maps
## Common to both proposals
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.
Both proposals add support for all other variable types by directly defining
them in the Taskfile. For example:
<Tabs defaultValue="1" queryString="proposal"
values={[
{label: 'Proposal 1', value: '1'},
{label: 'Proposal 2', value: '2'}
]}>
### Evaluating booleans
<TabItem value="1">
```yaml
version: 3
@ -264,64 +276,15 @@ version: 3
tasks:
foo:
vars:
BOOL: false
cmds:
- '{{if .BOOL}}echo foo{{end}}'
```
### Arithmetic
```yaml
version: 3
tasks:
foo:
vars:
INT: 10
FLOAT: 3.14159
cmds:
- 'echo {{add .INT .FLOAT}}'
```
### Ranging
```yaml
version: 3
tasks:
foo:
vars:
ARRAY: [1, 2, 3]
cmds:
- 'echo {{range .ARRAY}}{{.}}{{end}}'
```
There are many more templating functions which can be used with the new types of
variables. For a full list, see the [slim-sprig][slim-sprig] documentation.
## Looping over variables
Previously, you would have to use a delimiter separated string to loop over an
arbitrary list of items in a variable and split them by using the `split` subkey
to specify the delimiter:
```yaml
version: 3
tasks:
foo:
vars:
LIST: 'foo,bar,baz'
MAP: {a: 1, b: 2, c: 3}
cmds:
- for:
var: LIST
split: ','
cmd: echo {{.ITEM}}
var: MAP
cmd: 'echo "{{.KEY}}: {{.ITEM}}"'
```
Both of these proposals add support for looping over "collection-type" variables
using the `for` keyword, so now you are able to loop over a map/array variable
directly:
</TabItem>
<TabItem value="2">
```yaml
version: 3
@ -329,18 +292,23 @@ version: 3
tasks:
foo:
vars:
LIST: [foo, bar, baz]
map:
MAP: {a: 1, b: 2, c: 3}
cmds:
- for:
var: LIST
cmd: echo {{.ITEM}}
var: MAP
cmd: 'echo "{{.KEY}}: {{.ITEM}}"'
```
When looping over a map we also make an additional `{{.KEY}}` variable availabe
that holds the string value of the map key. Remember that maps are unordered, so
:::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/#enabling-experiments
[slim-sprig]: https://go-task.github.io/slim-sprig/
[enabling-experiments]: ./experiments.mdx#enabling-experiments
{/* prettier-ignore-end */}

View File

@ -99,6 +99,6 @@ the `--timeout` flag and specifying a duration. For example, `--timeout 5s` will
set the timeout to 5 seconds.
{/* prettier-ignore-start */}
[enabling-experiments]: /experiments/#enabling-experiments
[enabling-experiments]: ./experiments.mdx#enabling-experiments
[man-in-the-middle-attacks]: https://en.wikipedia.org/wiki/Man-in-the-middle_attack
{/* prettier-ignore-end */}

View File

@ -38,5 +38,5 @@ information.
\{Short explanation of how users should migrate to the new behavior\}
{/* prettier-ignore-start */}
[enabling-experiments]: /experiments/#enabling-experiments
[enabling-experiments]: ./experiments.mdx#enabling-experiments
{/* prettier-ignore-end */}

View File

@ -31,7 +31,7 @@ brew install go-task
### pkgx
If you're on macOS or Linux and have [pkgx](https://pkgx.sh/) installed, getting Task is as
If you're on macOS or Linux and have [pkgx][pkgx] installed, getting Task is as
simple as running:
```shell
@ -299,5 +299,5 @@ Invoke-Expression -Command path/to/task.ps1
[godownloader]: https://github.com/goreleaser/godownloader
[choco]: https://chocolatey.org/
[scoop]: https://scoop.sh/
[tea]: https://tea.xyz/
[pkgx]: https://pkgx.sh/
{/* prettier-ignore-end */}

View File

@ -256,8 +256,8 @@ The variable priority order was also different:
4. `Taskvars.yml` variables
{/* prettier-ignore-start */}
[deprecate-version-2-schema]: /deprecations/version-2-schema/
[output]: /usage#output-syntax
[ignore_errors]: /usage#ignore-errors
[includes]: /usage#including-other-taskfiles
[deprecate-version-2-schema]: ./deprecations/version_2_schema.mdx
[output]: ./usage.mdx#output-syntax
[ignore_errors]: ./usage.mdx#ignore-errors
[includes]: ./usage.mdx#including-other-taskfiles
{/* prettier-ignore-end */}

View File

@ -947,8 +947,26 @@ tasks:
## Variables
When doing interpolation of variables, Task will look for the below. They are
listed below in order of importance (i.e. most important first):
Task allows you to set variables using the `vars` keyword. The following
variable types are supported:
- `string`
- `bool`
- `int`
- `float`
- `array`
:::note
Maps are not supported by default, but there is an
[experiment][map-variables] that can be enabled to add support. If
you're interested in this functionality, we would appreciate your feedback.
:pray:
:::
Variables can be set in many places in a Taskfile. When executing templates,
Task will look for variables in the order listed below (most important first):
- Variables declared in the task definition
- Variables given while calling a task from another (See
@ -1093,8 +1111,8 @@ tasks:
### Looping over variables
To loop over the contents of a variable, you simply need to specify the variable
you want to loop over. By default, variables will be split on any whitespace
characters.
you want to loop over. By default, string variables will be split on any
whitespace characters.
```yaml
version: '3'
@ -1108,8 +1126,8 @@ tasks:
cmd: cat {{.ITEM}}
```
If you need to split on a different character, you can do this by specifying the
`split` property:
If you need to split a string on a different character, you can do this by
specifying the `split` property:
```yaml
version: '3'
@ -1123,6 +1141,26 @@ tasks:
cmd: cat {{.ITEM}}
```
You can also loop over arrays directly (and maps if you have the
[maps experiment](/experiments/map-variables) enabled):
```yaml
version: 3
tasks:
foo:
vars:
LIST: [foo, bar, baz]
cmds:
- for:
var: LIST
cmd: echo {{.ITEM}}
```
When looping over a map we also make an additional `{{.KEY}}` variable available
that holds the string value of the map key. Remember that maps are unordered, so
the order in which the items are looped over is random.
All of this also works with dynamic variables!
```yaml
@ -1956,4 +1994,5 @@ if called by another task, either directly or as a dependency.
{/* prettier-ignore-start */}
[gotemplate]: https://golang.org/pkg/text/template/
[map-variables]: ./experiments/map_variables.mdx
{/* prettier-ignore-end */}

View File

@ -21,8 +21,8 @@ const config: Config = {
tagline: 'A task runner / simpler Make alternative written in Go ',
url: 'https://taskfile.dev',
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'throw',
onBrokenLinks: 'warn',
onBrokenMarkdownLinks: 'warn',
favicon: 'img/favicon.ico',
organizationName: 'go-task',