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`. |
|
||||
| `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`. |
|
||||
| `platforms` | `[]string` | All platforms | Specifies which platforms the task should be run on. |
|
||||
|
||||
:::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`. |
|
||||
| `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`. |
|
||||
| `platforms` | `[]string` | All platforms | Specifies which platforms the command should be run on. |
|
||||
|
||||
:::info
|
||||
|
||||
|
@ -439,6 +439,73 @@ tasks:
|
||||
- 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
|
||||
|
||||
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": {
|
||||
"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"
|
||||
},
|
||||
"platforms": {
|
||||
"description": "Specifies which platforms the task should be run on.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -233,6 +240,13 @@
|
||||
"defer": {
|
||||
"description": "",
|
||||
"type": "boolean"
|
||||
},
|
||||
"platforms": {
|
||||
"description": "Specifies which platforms the command should be run on.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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()
|
||||
|
||||
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)
|
||||
if err := e.runDeps(ctx, t); err != nil {
|
||||
return err
|
||||
@ -252,6 +259,11 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
|
||||
}
|
||||
return nil
|
||||
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) {
|
||||
e.Logger.Errf(logger.Green, "task: [%s] %s", t.Name(), cmd.Cmd)
|
||||
}
|
||||
@ -455,3 +467,15 @@ func FilterOutInternal() FilterFunc {
|
||||
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.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
|
||||
IgnoreError bool
|
||||
Defer bool
|
||||
Platforms []*Platform
|
||||
}
|
||||
|
||||
// Dep is a task dependency
|
||||
@ -40,11 +41,13 @@ func (c *Cmd) UnmarshalYAML(node *yaml.Node) error {
|
||||
Cmd string
|
||||
Silent bool
|
||||
IgnoreError bool `yaml:"ignore_error"`
|
||||
Platforms []*Platform
|
||||
}
|
||||
if err := node.Decode(&cmdStruct); err == nil && cmdStruct.Cmd != "" {
|
||||
c.Cmd = cmdStruct.Cmd
|
||||
c.Silent = cmdStruct.Silent
|
||||
c.IgnoreError = cmdStruct.IgnoreError
|
||||
c.Platforms = cmdStruct.Platforms
|
||||
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
|
||||
IncludedTaskfileVars *Vars
|
||||
IncludedTaskfile *IncludedTaskfile
|
||||
Platforms []*Platform
|
||||
}
|
||||
|
||||
func (t *Task) Name() string {
|
||||
@ -90,6 +91,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
||||
Prefix string
|
||||
IgnoreError bool `yaml:"ignore_error"`
|
||||
Run string
|
||||
Platforms []*Platform
|
||||
}
|
||||
if err := node.Decode(&task); err != nil {
|
||||
return err
|
||||
@ -115,6 +117,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
||||
t.Prefix = task.Prefix
|
||||
t.IgnoreError = task.IgnoreError
|
||||
t.Run = task.Run
|
||||
t.Platforms = task.Platforms
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -150,6 +153,7 @@ func (t *Task) DeepCopy() *Task {
|
||||
IncludeVars: t.IncludeVars.DeepCopy(),
|
||||
IncludedTaskfileVars: t.IncludedTaskfileVars.DeepCopy(),
|
||||
IncludedTaskfile: t.IncludedTaskfile.DeepCopy(),
|
||||
Platforms: deepCopySlice(t.Platforms),
|
||||
}
|
||||
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),
|
||||
IncludeVars: origTask.IncludeVars,
|
||||
IncludedTaskfileVars: origTask.IncludedTaskfileVars,
|
||||
Platforms: origTask.Platforms,
|
||||
}
|
||||
new.Dir, err = execext.Expand(new.Dir)
|
||||
if err != nil {
|
||||
@ -130,6 +131,7 @@ func (e *Executor) compiledTask(call taskfile.Call, evaluateShVars bool) (*taskf
|
||||
Vars: r.ReplaceVars(cmd.Vars),
|
||||
IgnoreError: cmd.IgnoreError,
|
||||
Defer: cmd.Defer,
|
||||
Platforms: cmd.Platforms,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user