mirror of
https://github.com/go-task/task.git
synced 2024-12-04 10:24:45 +02:00
parent
aa6c7e4b94
commit
2efb3533ec
@ -1,5 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Add new `platforms:` attribute to `task` and `cmd`, so it's now possible to
|
||||
choose in which platforms that given task or command will be run on. Possible
|
||||
values are operating system (GOOS), architecture (GOARCH) or a combination of
|
||||
the two. Example: `platforms: [linux]`, `platforms: [amd64]` or
|
||||
`platforms: [linux/amd64]`. Other platforms will be skipped
|
||||
([#978](https://github.com/go-task/task/issues/978), [#980](https://github.com/go-task/task/pull/980) by @leaanthony).
|
||||
|
||||
## v3.19.1 - 2022-12-31
|
||||
|
||||
- Small bug fix: closing `Taskfile.yml` once we're done reading it
|
||||
|
@ -139,7 +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. |
|
||||
| `platforms` | `[]string` | All platforms | Specifies which platforms the task should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/master/src/go/build/syslist.go). Task will be skipped otherwise. |
|
||||
|
||||
:::info
|
||||
|
||||
@ -190,7 +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. |
|
||||
| `platforms` | `[]string` | All platforms | Specifies which platforms the command should be run on. [Valid GOOS and GOARCH values allowed](https://github.com/golang/go/blob/master/src/go/build/syslist.go). Command will be skipped otherwise. |
|
||||
|
||||
:::info
|
||||
|
||||
|
@ -442,8 +442,13 @@ tasks:
|
||||
## 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
|
||||
using the `platforms:` key. Tasks can be restricted to a specific OS, architecture or a
|
||||
combination of both.
|
||||
On a mismatch, the task or command will be skipped, and no error will be thrown.
|
||||
|
||||
The values allowed as OS or Arch are valid `GOOS` and `GOARCH` values, as
|
||||
defined by the Go language
|
||||
[here](https://github.com/golang/go/blob/master/src/go/build/syslist.go).
|
||||
|
||||
The `build-windows` task below will run only on Windows, and on any architecture:
|
||||
|
||||
@ -454,7 +459,7 @@ tasks:
|
||||
build-windows:
|
||||
platforms: [windows]
|
||||
cmds:
|
||||
- echo 'Running command on windows'
|
||||
- echo 'Running command on Windows'
|
||||
```
|
||||
|
||||
This can be restricted to a specific architecture as follows:
|
||||
@ -466,7 +471,7 @@ tasks:
|
||||
build-windows-amd64:
|
||||
platforms: [windows/amd64]
|
||||
cmds:
|
||||
- echo 'Running command on windows (amd64)'
|
||||
- echo 'Running command on Windows (amd64)'
|
||||
```
|
||||
|
||||
It is also possible to restrict the task to specific architectures:
|
||||
@ -487,10 +492,10 @@ Multiple platforms can be specified as follows:
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
build-windows:
|
||||
build:
|
||||
platforms: [windows/amd64, darwin]
|
||||
cmds:
|
||||
- echo 'Running command on windows (amd64) and darwin'
|
||||
- echo 'Running command on Windows (amd64) and macOS'
|
||||
```
|
||||
|
||||
Individual commands can also be restricted to specific platforms:
|
||||
@ -499,9 +504,9 @@ Individual commands can also be restricted to specific platforms:
|
||||
version: '3'
|
||||
|
||||
tasks:
|
||||
build-windows:
|
||||
build:
|
||||
cmds:
|
||||
- cmd: echo 'Running command on windows (amd64) and darwin'
|
||||
- cmd: echo 'Running command on Windows (amd64) and macOS'
|
||||
platforms: [windows/amd64, darwin]
|
||||
- cmd: echo 'Running on all platforms'
|
||||
```
|
||||
|
62
internal/goext/meta.go
Normal file
62
internal/goext/meta.go
Normal file
@ -0,0 +1,62 @@
|
||||
package goext
|
||||
|
||||
// NOTE(@andreynering): The lists in this file were copied from:
|
||||
//
|
||||
// https://github.com/golang/go/blob/master/src/go/build/syslist.go
|
||||
|
||||
func IsKnownOS(str string) bool {
|
||||
_, known := knownOS[str]
|
||||
return known
|
||||
}
|
||||
|
||||
func IsKnownArch(str string) bool {
|
||||
_, known := knownArch[str]
|
||||
return known
|
||||
}
|
||||
|
||||
var knownOS = map[string]struct{}{
|
||||
"aix": {},
|
||||
"android": {},
|
||||
"darwin": {},
|
||||
"dragonfly": {},
|
||||
"freebsd": {},
|
||||
"hurd": {},
|
||||
"illumos": {},
|
||||
"ios": {},
|
||||
"js": {},
|
||||
"linux": {},
|
||||
"nacl": {},
|
||||
"netbsd": {},
|
||||
"openbsd": {},
|
||||
"plan9": {},
|
||||
"solaris": {},
|
||||
"windows": {},
|
||||
"zos": {},
|
||||
}
|
||||
|
||||
var knownArch = map[string]struct{}{
|
||||
"386": {},
|
||||
"amd64": {},
|
||||
"amd64p32": {},
|
||||
"arm": {},
|
||||
"armbe": {},
|
||||
"arm64": {},
|
||||
"arm64be": {},
|
||||
"loong64": {},
|
||||
"mips": {},
|
||||
"mipsle": {},
|
||||
"mips64": {},
|
||||
"mips64le": {},
|
||||
"mips64p32": {},
|
||||
"mips64p32le": {},
|
||||
"ppc": {},
|
||||
"ppc64": {},
|
||||
"ppc64le": {},
|
||||
"riscv": {},
|
||||
"riscv64": {},
|
||||
"s390": {},
|
||||
"s390x": {},
|
||||
"sparc": {},
|
||||
"sparc64": {},
|
||||
"wasm": {},
|
||||
}
|
15
task.go
15
task.go
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -135,9 +136,7 @@ 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) {
|
||||
if !shouldRunOnCurrentPlatform(t.Platforms) {
|
||||
e.Logger.VerboseOutf(logger.Yellow, `task: "%s" not for current platform - ignored`, call.Task)
|
||||
return nil
|
||||
}
|
||||
@ -259,11 +258,11 @@ func (e *Executor) runCommand(ctx context.Context, t *taskfile.Task, call taskfi
|
||||
}
|
||||
return nil
|
||||
case cmd.Cmd != "":
|
||||
// Check platform
|
||||
if !ShouldRunOnCurrentPlatform(cmd.Platforms) {
|
||||
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)
|
||||
}
|
||||
@ -468,12 +467,12 @@ func FilterOutInternal() FilterFunc {
|
||||
})
|
||||
}
|
||||
|
||||
func ShouldRunOnCurrentPlatform(platforms []*taskfile.Platform) bool {
|
||||
func shouldRunOnCurrentPlatform(platforms []*taskfile.Platform) bool {
|
||||
if len(platforms) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, platform := range platforms {
|
||||
if platform.MatchesCurrentPlatform() {
|
||||
for _, p := range platforms {
|
||||
if (p.OS == "" || p.OS == runtime.GOOS) && (p.Arch == "" || p.Arch == runtime.GOARCH) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -2,10 +2,11 @@ package taskfile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/go-task/task/v3/internal/goext"
|
||||
)
|
||||
|
||||
// Platform represents GOOS and GOARCH values
|
||||
@ -14,67 +15,23 @@ type Platform struct {
|
||||
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
|
||||
type ErrInvalidPlatform struct {
|
||||
Platform string
|
||||
}
|
||||
|
||||
// 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)
|
||||
func (err *ErrInvalidPlatform) Error() string {
|
||||
return fmt.Sprintf(`task: Invalid platform "%s"`, err.Platform)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if err := p.parsePlatform(platform); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -82,22 +39,42 @@ func (p *Platform) UnmarshalYAML(node *yaml.Node) error {
|
||||
return fmt.Errorf("yaml: line %d: cannot unmarshal %s into platform", node.Line, node.ShortTag())
|
||||
}
|
||||
|
||||
// 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 {
|
||||
splitValues := strings.Split(input, "/")
|
||||
if len(splitValues) > 2 {
|
||||
return &ErrInvalidPlatform{Platform: input}
|
||||
}
|
||||
if err := p.parseOsOrArch(splitValues[0]); err != nil {
|
||||
return &ErrInvalidPlatform{Platform: input}
|
||||
}
|
||||
if len(splitValues) == 2 {
|
||||
if err := p.parseArch(splitValues[1]); err != nil {
|
||||
return &ErrInvalidPlatform{Platform: input}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if goext.IsKnownOS(osOrArch) {
|
||||
p.OS = osOrArch
|
||||
return nil
|
||||
}
|
||||
if isSupportedArch(osOrArch) {
|
||||
if goext.IsKnownArch(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")
|
||||
@ -105,7 +82,7 @@ func (p *Platform) parseArch(arch string) error {
|
||||
if p.Arch != "" {
|
||||
return fmt.Errorf("task: Multiple Arch values provided")
|
||||
}
|
||||
if isSupportedArch(arch) {
|
||||
if goext.IsKnownArch(arch) {
|
||||
p.Arch = arch
|
||||
return nil
|
||||
}
|
||||
|
49
taskfile/platforms_test.go
Normal file
49
taskfile/platforms_test.go
Normal file
@ -0,0 +1,49 @@
|
||||
package taskfile
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestPlatformParsing(t *testing.T) {
|
||||
tests := []struct {
|
||||
Input string
|
||||
ExpectedOS string
|
||||
ExpectedArch string
|
||||
Error string
|
||||
}{
|
||||
{Input: "windows", ExpectedOS: "windows", ExpectedArch: ""},
|
||||
{Input: "linux", ExpectedOS: "linux", ExpectedArch: ""},
|
||||
{Input: "darwin", ExpectedOS: "darwin", ExpectedArch: ""},
|
||||
|
||||
{Input: "386", ExpectedOS: "", ExpectedArch: "386"},
|
||||
{Input: "amd64", ExpectedOS: "", ExpectedArch: "amd64"},
|
||||
{Input: "arm64", ExpectedOS: "", ExpectedArch: "arm64"},
|
||||
|
||||
{Input: "windows/386", ExpectedOS: "windows", ExpectedArch: "386"},
|
||||
{Input: "windows/amd64", ExpectedOS: "windows", ExpectedArch: "amd64"},
|
||||
{Input: "windows/arm64", ExpectedOS: "windows", ExpectedArch: "arm64"},
|
||||
|
||||
{Input: "invalid", Error: `task: Invalid platform "invalid"`},
|
||||
{Input: "invalid/invalid", Error: `task: Invalid platform "invalid/invalid"`},
|
||||
{Input: "windows/invalid", Error: `task: Invalid platform "windows/invalid"`},
|
||||
{Input: "invalid/amd64", Error: `task: Invalid platform "invalid/amd64"`},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Input, func(t *testing.T) {
|
||||
var p Platform
|
||||
err := p.parsePlatform(test.Input)
|
||||
|
||||
if test.Error != "" {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, test.Error, err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, test.ExpectedOS, p.OS)
|
||||
assert.Equal(t, test.ExpectedArch, p.Arch)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user