mirror of
https://github.com/go-task/task.git
synced 2025-04-25 12:25:07 +02:00
Add support for 'platforms' in both task and command (#980)
This commit is contained in:
parent
63c50d13ee
commit
aa6c7e4b94
@ -139,6 +139,7 @@ includes:
|
|||||||
| `prefix` | `string` | | Defines a string to prefix the output of tasks running in parallel. Only used when the output mode is `prefixed`. |
|
| `prefix` | `string` | | Defines a string to prefix the output of tasks running in parallel. Only used when the output mode is `prefixed`. |
|
||||||
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing commands. |
|
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing commands. |
|
||||||
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
|
| `run` | `string` | The one declared globally in the Taskfile or `always` | Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`. |
|
||||||
|
| `platforms` | `[]string` | All platforms | Specifies which platforms the task should be run on. |
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
@ -189,6 +190,7 @@ tasks:
|
|||||||
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the referenced task. Only relevant when setting `task` instead of `cmd`. |
|
| `vars` | [`map[string]Variable`](#variable) | | Optional additional variables to be passed to the referenced task. Only relevant when setting `task` instead of `cmd`. |
|
||||||
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
|
| `ignore_error` | `bool` | `false` | Continue execution if errors happen while executing the command. |
|
||||||
| `defer` | `string` | | Alternative to `cmd`, but schedules the command to be executed at the end of this task instead of immediately. This cannot be used together with `cmd`. |
|
| `defer` | `string` | | Alternative to `cmd`, but schedules the command to be executed at the end of this task instead of immediately. This cannot be used together with `cmd`. |
|
||||||
|
| `platforms` | `[]string` | All platforms | Specifies which platforms the command should be run on. |
|
||||||
|
|
||||||
:::info
|
:::info
|
||||||
|
|
||||||
|
@ -439,6 +439,73 @@ tasks:
|
|||||||
- echo {{.TEXT}}
|
- echo {{.TEXT}}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Platform specific tasks and commands
|
||||||
|
|
||||||
|
If you want to restrict the running of tasks to explicit platforms, this can be achieved
|
||||||
|
using the `platforms` key. Tasks can be restricted to a specific OS, architecture or a
|
||||||
|
combination of both.
|
||||||
|
|
||||||
|
The `build-windows` task below will run only on Windows, and on any architecture:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-windows:
|
||||||
|
platforms: [windows]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running command on windows'
|
||||||
|
```
|
||||||
|
|
||||||
|
This can be restricted to a specific architecture as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-windows-amd64:
|
||||||
|
platforms: [windows/amd64]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running command on windows (amd64)'
|
||||||
|
```
|
||||||
|
|
||||||
|
It is also possible to restrict the task to specific architectures:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-amd64:
|
||||||
|
platforms: [amd64]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running command on amd64'
|
||||||
|
```
|
||||||
|
|
||||||
|
Multiple platforms can be specified as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-windows:
|
||||||
|
platforms: [windows/amd64, darwin]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running command on windows (amd64) and darwin'
|
||||||
|
```
|
||||||
|
|
||||||
|
Individual commands can also be restricted to specific platforms:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-windows:
|
||||||
|
cmds:
|
||||||
|
- cmd: echo 'Running command on windows (amd64) and darwin'
|
||||||
|
platforms: [windows/amd64, darwin]
|
||||||
|
- cmd: echo 'Running on all platforms'
|
||||||
|
```
|
||||||
|
|
||||||
## Calling another task
|
## Calling another task
|
||||||
|
|
||||||
When a task has many dependencies, they are executed concurrently. This will
|
When a task has many dependencies, they are executed concurrently. This will
|
||||||
|
14
docs/static/schema.json
vendored
14
docs/static/schema.json
vendored
@ -155,6 +155,13 @@
|
|||||||
"run": {
|
"run": {
|
||||||
"description": "Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`.",
|
"description": "Specifies whether the task should run again or not if called more than once. Available options: `always`, `once` and `when_changed`.",
|
||||||
"$ref": "#/definitions/3/run"
|
"$ref": "#/definitions/3/run"
|
||||||
|
},
|
||||||
|
"platforms": {
|
||||||
|
"description": "Specifies which platforms the task should be run on.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -233,6 +240,13 @@
|
|||||||
"defer": {
|
"defer": {
|
||||||
"description": "",
|
"description": "",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"platforms": {
|
||||||
|
"description": "Specifies which platforms the command should be run on.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
24
task.go
24
task.go
@ -135,6 +135,13 @@ func (e *Executor) RunTask(ctx context.Context, call taskfile.Call) error {
|
|||||||
defer release()
|
defer release()
|
||||||
|
|
||||||
return e.startExecution(ctx, t, func(ctx context.Context) error {
|
return e.startExecution(ctx, t, func(ctx context.Context) error {
|
||||||
|
|
||||||
|
// Check platform
|
||||||
|
if !ShouldRunOnCurrentPlatform(t.Platforms) {
|
||||||
|
e.Logger.VerboseOutf(logger.Yellow, `task: "%s" not for current platform - ignored`, call.Task)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" started`, call.Task)
|
e.Logger.VerboseErrf(logger.Magenta, `task: "%s" started`, call.Task)
|
||||||
if err := e.runDeps(ctx, t); err != nil {
|
if err := e.runDeps(ctx, t); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -252,6 +259,11 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case cmd.Cmd != "":
|
case cmd.Cmd != "":
|
||||||
|
// Check platform
|
||||||
|
if !ShouldRunOnCurrentPlatform(cmd.Platforms) {
|
||||||
|
e.Logger.VerboseOutf(logger.Yellow, `task: [%s] %s not for current platform - ignored`, t.Name(), cmd.Cmd)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Taskfile.Silent && !e.Silent) {
|
if e.Verbose || (!cmd.Silent && !t.Silent && !e.Taskfile.Silent && !e.Silent) {
|
||||||
e.Logger.Errf(logger.Green, "task: [%s] %s", t.Name(), cmd.Cmd)
|
e.Logger.Errf(logger.Green, "task: [%s] %s", t.Name(), cmd.Cmd)
|
||||||
}
|
}
|
||||||
@ -455,3 +467,15 @@ func FilterOutInternal() FilterFunc {
|
|||||||
return task.Internal
|
return task.Internal
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ShouldRunOnCurrentPlatform(platforms []*taskfile.Platform) bool {
|
||||||
|
if len(platforms) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, platform := range platforms {
|
||||||
|
if platform.MatchesCurrentPlatform() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
11
task_test.go
11
task_test.go
@ -1696,3 +1696,14 @@ func TestUserWorkingDirectory(t *testing.T) {
|
|||||||
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "default"}))
|
||||||
assert.Equal(t, fmt.Sprintf("%s\n", wd), buff.String())
|
assert.Equal(t, fmt.Sprintf("%s\n", wd), buff.String())
|
||||||
}
|
}
|
||||||
|
func TestPlatforms(t *testing.T) {
|
||||||
|
var buff bytes.Buffer
|
||||||
|
e := task.Executor{
|
||||||
|
Dir: "testdata/platforms",
|
||||||
|
Stdout: &buff,
|
||||||
|
Stderr: &buff,
|
||||||
|
}
|
||||||
|
assert.NoError(t, e.Setup())
|
||||||
|
assert.NoError(t, e.Run(context.Background(), taskfile.Call{Task: "build-" + runtime.GOOS}))
|
||||||
|
assert.Equal(t, fmt.Sprintf("task: [build-%s] echo 'Running task on %s'\nRunning task on %s\n", runtime.GOOS, runtime.GOOS, runtime.GOOS), buff.String())
|
||||||
|
}
|
||||||
|
@ -14,6 +14,7 @@ type Cmd struct {
|
|||||||
Vars *Vars
|
Vars *Vars
|
||||||
IgnoreError bool
|
IgnoreError bool
|
||||||
Defer bool
|
Defer bool
|
||||||
|
Platforms []*Platform
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dep is a task dependency
|
// Dep is a task dependency
|
||||||
@ -40,11 +41,13 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
Cmd string
|
Cmd string
|
||||||
Silent bool
|
Silent bool
|
||||||
IgnoreError bool `yaml:"ignore_error"`
|
IgnoreError bool `yaml:"ignore_error"`
|
||||||
|
Platforms []*Platform
|
||||||
}
|
}
|
||||||
if err := node.Decode(&cmdStruct); err == nil && cmdStruct.Cmd != "" {
|
if err := node.Decode(&cmdStruct); err == nil && cmdStruct.Cmd != "" {
|
||||||
c.Cmd = cmdStruct.Cmd
|
c.Cmd = cmdStruct.Cmd
|
||||||
c.Silent = cmdStruct.Silent
|
c.Silent = cmdStruct.Silent
|
||||||
c.IgnoreError = cmdStruct.IgnoreError
|
c.IgnoreError = cmdStruct.IgnoreError
|
||||||
|
c.Platforms = cmdStruct.Platforms
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
113
taskfile/platforms.go
Normal file
113
taskfile/platforms.go
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
package taskfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Platform represents GOOS and GOARCH values
|
||||||
|
type Platform struct {
|
||||||
|
OS string
|
||||||
|
Arch string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParsePlatform takes a string representing an OS/Arch combination (or either on their own)
|
||||||
|
// and parses it into the Platform struct. It returns an error if the input string is invalid.
|
||||||
|
// Valid combinations for input: OS, Arch, OS/Arch
|
||||||
|
func (p *Platform) ParsePlatform(input string) error {
|
||||||
|
// tidy up input
|
||||||
|
platformString := strings.ToLower(strings.TrimSpace(input))
|
||||||
|
splitValues := strings.Split(platformString, "/")
|
||||||
|
if len(splitValues) > 2 {
|
||||||
|
return fmt.Errorf("task: Invalid OS/Arch provided: %s", input)
|
||||||
|
}
|
||||||
|
err := p.parseOsOrArch(splitValues[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(splitValues) == 2 {
|
||||||
|
return p.parseArch(splitValues[1])
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// supportedOSes is a list of supported OSes
|
||||||
|
var supportedOSes = map[string]struct{}{
|
||||||
|
"windows": {},
|
||||||
|
"darwin": {},
|
||||||
|
"linux": {},
|
||||||
|
"freebsd": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSupportedOS(input string) bool {
|
||||||
|
_, exists := supportedOSes[input]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// supportedArchs is a list of supported architectures
|
||||||
|
var supportedArchs = map[string]struct{}{
|
||||||
|
"amd64": {},
|
||||||
|
"arm64": {},
|
||||||
|
"386": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSupportedArch(input string) bool {
|
||||||
|
_, exists := supportedArchs[input]
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesCurrentPlatform returns true if the platform matches the current platform
|
||||||
|
func (p *Platform) MatchesCurrentPlatform() bool {
|
||||||
|
return (p.OS == "" || p.OS == runtime.GOOS) &&
|
||||||
|
(p.Arch == "" || p.Arch == runtime.GOARCH)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalYAML implements yaml.Unmarshaler interface.
|
||||||
|
func (p *Platform) UnmarshalYAML(node *yaml.Node) error {
|
||||||
|
switch node.Kind {
|
||||||
|
|
||||||
|
case yaml.ScalarNode:
|
||||||
|
var platform string
|
||||||
|
if err := node.Decode(&platform); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.ParsePlatform(platform); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into platform", node.Line, node.ShortTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseOsOrArch will check if the given input is a valid OS or Arch value.
|
||||||
|
// If so, it will store it. If not, an error is returned
|
||||||
|
func (p *Platform) parseOsOrArch(osOrArch string) error {
|
||||||
|
if osOrArch == "" {
|
||||||
|
return fmt.Errorf("task: Blank OS/Arch value provided")
|
||||||
|
}
|
||||||
|
if isSupportedOS(osOrArch) {
|
||||||
|
p.OS = osOrArch
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if isSupportedArch(osOrArch) {
|
||||||
|
p.Arch = osOrArch
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("task: Invalid OS/Arch value provided (%s)", osOrArch)
|
||||||
|
}
|
||||||
|
func (p *Platform) parseArch(arch string) error {
|
||||||
|
if arch == "" {
|
||||||
|
return fmt.Errorf("task: Blank Arch value provided")
|
||||||
|
}
|
||||||
|
if p.Arch != "" {
|
||||||
|
return fmt.Errorf("task: Multiple Arch values provided")
|
||||||
|
}
|
||||||
|
if isSupportedArch(arch) {
|
||||||
|
p.Arch = arch
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("task: Invalid Arch value provided (%s)", arch)
|
||||||
|
}
|
@ -36,6 +36,7 @@ type Task struct {
|
|||||||
IncludeVars *Vars
|
IncludeVars *Vars
|
||||||
IncludedTaskfileVars *Vars
|
IncludedTaskfileVars *Vars
|
||||||
IncludedTaskfile *IncludedTaskfile
|
IncludedTaskfile *IncludedTaskfile
|
||||||
|
Platforms []*Platform
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Task) Name() string {
|
func (t *Task) Name() string {
|
||||||
@ -90,6 +91,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
Prefix string
|
Prefix string
|
||||||
IgnoreError bool `yaml:"ignore_error"`
|
IgnoreError bool `yaml:"ignore_error"`
|
||||||
Run string
|
Run string
|
||||||
|
Platforms []*Platform
|
||||||
}
|
}
|
||||||
if err := node.Decode(&task); err != nil {
|
if err := node.Decode(&task); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -115,6 +117,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||||||
t.Prefix = task.Prefix
|
t.Prefix = task.Prefix
|
||||||
t.IgnoreError = task.IgnoreError
|
t.IgnoreError = task.IgnoreError
|
||||||
t.Run = task.Run
|
t.Run = task.Run
|
||||||
|
t.Platforms = task.Platforms
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +153,7 @@ func (t *Task) DeepCopy() *Task {
|
|||||||
IncludeVars: t.IncludeVars.DeepCopy(),
|
IncludeVars: t.IncludeVars.DeepCopy(),
|
||||||
IncludedTaskfileVars: t.IncludedTaskfileVars.DeepCopy(),
|
IncludedTaskfileVars: t.IncludedTaskfileVars.DeepCopy(),
|
||||||
IncludedTaskfile: t.IncludedTaskfile.DeepCopy(),
|
IncludedTaskfile: t.IncludedTaskfile.DeepCopy(),
|
||||||
|
Platforms: deepCopySlice(t.Platforms),
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
55
testdata/platforms/Taskfile.yml
vendored
Normal file
55
testdata/platforms/Taskfile.yml
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
build-windows:
|
||||||
|
platforms: [windows]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running task on windows'
|
||||||
|
|
||||||
|
build-darwin:
|
||||||
|
platforms: [darwin]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running task on darwin'
|
||||||
|
|
||||||
|
build-linux:
|
||||||
|
platforms: [linux]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running task on linux'
|
||||||
|
|
||||||
|
build-freebsd:
|
||||||
|
platforms: [freebsd]
|
||||||
|
cmds:
|
||||||
|
- echo 'Running task on freebsd'
|
||||||
|
|
||||||
|
build-blank-os:
|
||||||
|
platforms: []
|
||||||
|
cmds:
|
||||||
|
- echo 'Running command'
|
||||||
|
|
||||||
|
build-multiple:
|
||||||
|
platforms: []
|
||||||
|
cmds:
|
||||||
|
- cmd: echo 'Running command'
|
||||||
|
- cmd: echo 'Running on Windows'
|
||||||
|
platforms: [windows]
|
||||||
|
- cmd: echo 'Running on Darwin'
|
||||||
|
platforms: [darwin]
|
||||||
|
|
||||||
|
build-amd64:
|
||||||
|
platforms: [amd64]
|
||||||
|
cmds:
|
||||||
|
- echo "Running command on amd64"
|
||||||
|
|
||||||
|
build-arm64:
|
||||||
|
platforms: [arm64]
|
||||||
|
cmds:
|
||||||
|
- echo "Running command on arm64"
|
||||||
|
|
||||||
|
build-mixed:
|
||||||
|
cmds:
|
||||||
|
- cmd: echo 'building on windows/arm64'
|
||||||
|
platforms: [windows/arm64]
|
||||||
|
- cmd: echo 'building on linux/amd64'
|
||||||
|
platforms: [linux/amd64]
|
||||||
|
- cmd: echo 'building on darwin'
|
||||||
|
platforms: [darwin]
|
@ -68,6 +68,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
Run: r.Replace(origTask.Run),
|
Run: r.Replace(origTask.Run),
|
||||||
IncludeVars: origTask.IncludeVars,
|
IncludeVars: origTask.IncludeVars,
|
||||||
IncludedTaskfileVars: origTask.IncludedTaskfileVars,
|
IncludedTaskfileVars: origTask.IncludedTaskfileVars,
|
||||||
|
Platforms: origTask.Platforms,
|
||||||
}
|
}
|
||||||
new.Dir, err = execext.Expand(new.Dir)
|
new.Dir, err = execext.Expand(new.Dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -130,6 +131,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
|||||||
Vars: r.ReplaceVars(cmd.Vars),
|
Vars: r.ReplaceVars(cmd.Vars),
|
||||||
IgnoreError: cmd.IgnoreError,
|
IgnoreError: cmd.IgnoreError,
|
||||||
Defer: cmd.Defer,
|
Defer: cmd.Defer,
|
||||||
|
Platforms: cmd.Platforms,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user