mirror of
https://github.com/go-task/task.git
synced 2024-12-14 10:52:43 +02:00
Merge pull request #134 from go-task/feature/include
Support including Taskfiles
This commit is contained in:
commit
a567f7ed20
@ -23,7 +23,7 @@ tasks:
|
||||
desc: Downloads cli dependencies
|
||||
cmds:
|
||||
- task: go-get
|
||||
vars: {REPO: github.com/golang/lint/golint}
|
||||
vars: {REPO: golang.org/x/lint/golint}
|
||||
- task: go-get
|
||||
vars: {REPO: github.com/golang/dep/cmd/dep}
|
||||
- task: go-get
|
||||
@ -68,7 +68,7 @@ tasks:
|
||||
ci:
|
||||
cmds:
|
||||
- task: go-get
|
||||
vars: {REPO: github.com/golang/lint/golint}
|
||||
vars: {REPO: golang.org/x/lint/golint}
|
||||
- task: lint
|
||||
- task: test
|
||||
|
||||
|
@ -128,5 +128,21 @@ tasks:
|
||||
ignore_error: true
|
||||
```
|
||||
|
||||
[output]: https://github.com/go-task/task#output-syntax
|
||||
[ignore_errors]: https://github.com/go-task/task#ignore-errors
|
||||
## Version 2.2
|
||||
|
||||
Version 2.2 comes with a global `includes` options to include other
|
||||
Taskfiles:
|
||||
|
||||
```yaml
|
||||
version: '2'
|
||||
|
||||
includes:
|
||||
docs: ./documentation # will look for ./documentation/Taskfile.yml
|
||||
docker: ./DockerTasks.yml
|
||||
```
|
||||
|
||||
Please check the [documentation][includes]
|
||||
|
||||
[output]: usage#output-syntax
|
||||
[ignore_errors]: usage#ignore-errors
|
||||
[includes]: usage#including-other-taskfiles
|
||||
|
@ -48,7 +48,7 @@ tasks:
|
||||
hallo: welt
|
||||
```
|
||||
|
||||
## OS specific task
|
||||
## Operating System specific tasks
|
||||
|
||||
If you add a `Taskfile_{{GOOS}}.yml` you can override or amend your Taskfile
|
||||
based on the operating system.
|
||||
@ -86,6 +86,31 @@ It's also possible to have an OS specific `Taskvars.yml` file, like
|
||||
`Taskvars_windows.yml`, `Taskfile_linux.yml`, or `Taskvars_darwin.yml`. See the
|
||||
[variables section](#variables) below.
|
||||
|
||||
## Including other Taskfiles
|
||||
|
||||
If you want to share tasks between different projects (Taskfiles), you can use
|
||||
the importing mechanism to include other Taskfiles using the `includes` keyword:
|
||||
|
||||
```yaml
|
||||
version: '2'
|
||||
|
||||
includes:
|
||||
docs: ./documentation # will look for ./documentation/Taskfile.yml
|
||||
docker: ./DockerTasks.yml
|
||||
```
|
||||
|
||||
The tasks described in the given Taskfiles will be available with the informed
|
||||
namespace. So, you'd call `task docs:serve` to run the `serve` task from
|
||||
`documentation/Taskfile.yml` or `task docker:build` to run the `build` task
|
||||
from the `DockerTasks.yml` file.
|
||||
|
||||
> The included Taskfiles must be using the same schema version the main
|
||||
> Taskfile uses.
|
||||
|
||||
> Also, for now included Taskfiles can't include other Taskfiles.
|
||||
> This was a deliberate decision to keep use and implementation simple.
|
||||
> If you disagree, open an GitHub issue and explain your use case. =)
|
||||
|
||||
## Task directory
|
||||
|
||||
By default, tasks will be executed in the directory where the Taskfile is
|
||||
|
@ -2,10 +2,14 @@ package taskfile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NamespaceSeparator contains the character that separates namescapes
|
||||
const NamespaceSeparator = ":"
|
||||
|
||||
// Merge merges the second Taskfile into the first
|
||||
func Merge(t1, t2 *Taskfile) error {
|
||||
func Merge(t1, t2 *Taskfile, namespaces ...string) error {
|
||||
if t1.Version != t2.Version {
|
||||
return fmt.Errorf(`Taskfiles versions should match. First is "%s" but second is "%s"`, t1.Version, t2.Version)
|
||||
}
|
||||
@ -16,12 +20,19 @@ func Merge(t1, t2 *Taskfile) error {
|
||||
if t2.Output != "" {
|
||||
t1.Output = t2.Output
|
||||
}
|
||||
for k, v := range t2.Includes {
|
||||
t1.Includes[k] = v
|
||||
}
|
||||
for k, v := range t2.Vars {
|
||||
t1.Vars[k] = v
|
||||
}
|
||||
for k, v := range t2.Tasks {
|
||||
t1.Tasks[k] = v
|
||||
t1.Tasks[taskNameWithNamespace(k, namespaces...)] = v
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func taskNameWithNamespace(taskName string, namespaces ...string) string {
|
||||
return strings.Join(append(namespaces, taskName), NamespaceSeparator)
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package read
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -11,6 +12,9 @@ import (
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// ErrIncludedTaskfilesCantHaveIncludes is returned when a included Taskfile contains includes
|
||||
var ErrIncludedTaskfilesCantHaveIncludes = errors.New("task: Included Taskfiles can't have includes. Please, move the include to the main Taskfile")
|
||||
|
||||
// Taskfile reads a Taskfile for a given directory
|
||||
func Taskfile(dir string) (*taskfile.Taskfile, error) {
|
||||
path := filepath.Join(dir, "Taskfile.yml")
|
||||
@ -22,6 +26,27 @@ func Taskfile(dir string) (*taskfile.Taskfile, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for namespace, path := range t.Includes {
|
||||
path = filepath.Join(dir, path)
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.IsDir() {
|
||||
path = filepath.Join(path, "Taskfile.yml")
|
||||
}
|
||||
includedTaskfile, err := readTaskfile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(includedTaskfile.Includes) > 0 {
|
||||
return nil, ErrIncludedTaskfilesCantHaveIncludes
|
||||
}
|
||||
if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
path = filepath.Join(dir, fmt.Sprintf("Taskfile_%s.yml", runtime.GOOS))
|
||||
if _, err = os.Stat(path); err == nil {
|
||||
osTaskfile, err := readTaskfile(path)
|
||||
|
@ -5,6 +5,7 @@ type Taskfile struct {
|
||||
Version string
|
||||
Expansions int
|
||||
Output string
|
||||
Includes map[string]string
|
||||
Vars Vars
|
||||
Tasks Tasks
|
||||
}
|
||||
@ -20,6 +21,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
Version string
|
||||
Expansions int
|
||||
Output string
|
||||
Includes map[string]string
|
||||
Vars Vars
|
||||
Tasks Tasks
|
||||
}
|
||||
@ -29,6 +31,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
tf.Version = taskfile.Version
|
||||
tf.Expansions = taskfile.Expansions
|
||||
tf.Output = taskfile.Output
|
||||
tf.Includes = taskfile.Includes
|
||||
tf.Vars = taskfile.Vars
|
||||
tf.Tasks = taskfile.Tasks
|
||||
if tf.Expansions <= 0 {
|
||||
|
@ -9,6 +9,7 @@ var (
|
||||
v2 = mustVersion("2")
|
||||
v21 = mustVersion("2.1")
|
||||
v22 = mustVersion("2.2")
|
||||
v23 = mustVersion("2.3")
|
||||
)
|
||||
|
||||
// IsV1 returns if is a given Taskfile version is version 1
|
||||
@ -31,6 +32,11 @@ func IsV22(v *semver.Constraints) bool {
|
||||
return v.Check(v22)
|
||||
}
|
||||
|
||||
// IsV23 returns if is a given Taskfile version is at least version 2.3
|
||||
func IsV23(v *semver.Constraints) bool {
|
||||
return v.Check(v23)
|
||||
}
|
||||
|
||||
func mustVersion(s string) *semver.Version {
|
||||
v, err := semver.NewVersion(s)
|
||||
if err != nil {
|
||||
|
9
task.go
9
task.go
@ -116,7 +116,7 @@ func (e *Executor) Setup() error {
|
||||
Vars: e.taskvars,
|
||||
Logger: e.Logger,
|
||||
}
|
||||
case version.IsV2(v), version.IsV21(v):
|
||||
case version.IsV2(v), version.IsV21(v), version.IsV22(v):
|
||||
e.Compiler = &compilerv2.CompilerV2{
|
||||
Dir: e.Dir,
|
||||
Taskvars: e.taskvars,
|
||||
@ -124,13 +124,16 @@ func (e *Executor) Setup() error {
|
||||
Expansions: e.Taskfile.Expansions,
|
||||
Logger: e.Logger,
|
||||
}
|
||||
case version.IsV22(v):
|
||||
return fmt.Errorf(`task: Taskfile versions greater than v2.1 not implemented in the version of Task`)
|
||||
case version.IsV23(v):
|
||||
return fmt.Errorf(`task: Taskfile versions greater than v2.3 not implemented in the version of Task`)
|
||||
}
|
||||
|
||||
if !version.IsV21(v) && e.Taskfile.Output != "" {
|
||||
return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`)
|
||||
}
|
||||
if !version.IsV22(v) && len(e.Taskfile.Includes) > 0 {
|
||||
return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`)
|
||||
}
|
||||
switch e.Taskfile.Output {
|
||||
case "", "interleaved":
|
||||
e.Output = output.Interleaved{}
|
||||
|
14
task_test.go
14
task_test.go
@ -470,3 +470,17 @@ func TestDry(t *testing.T) {
|
||||
t.Errorf("File should not exist %s", file)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncludes(t *testing.T) {
|
||||
tt := fileContentTest{
|
||||
Dir: "testdata/includes",
|
||||
Target: "default",
|
||||
TrimSpace: true,
|
||||
Files: map[string]string{
|
||||
"main.txt": "main",
|
||||
"included_directory.txt": "included_directory",
|
||||
"included_taskfile.txt": "included_taskfile",
|
||||
},
|
||||
}
|
||||
tt.Run(t)
|
||||
}
|
||||
|
1
testdata/includes/.gitignore
vendored
Normal file
1
testdata/includes/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.txt
|
16
testdata/includes/Taskfile.yml
vendored
Normal file
16
testdata/includes/Taskfile.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
version: '2'
|
||||
|
||||
includes:
|
||||
included: ./included
|
||||
included_taskfile: ./Taskfile2.yml
|
||||
|
||||
tasks:
|
||||
default:
|
||||
cmds:
|
||||
- task: gen
|
||||
- task: included:gen
|
||||
- task: included_taskfile:gen
|
||||
|
||||
gen:
|
||||
cmds:
|
||||
- echo main > main.txt
|
6
testdata/includes/Taskfile2.yml
vendored
Normal file
6
testdata/includes/Taskfile2.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
version: '2'
|
||||
|
||||
tasks:
|
||||
gen:
|
||||
cmds:
|
||||
- echo included_taskfile > included_taskfile.txt
|
6
testdata/includes/included/Taskfile.yml
vendored
Normal file
6
testdata/includes/included/Taskfile.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
version: '2'
|
||||
|
||||
tasks:
|
||||
gen:
|
||||
cmds:
|
||||
- echo included_directory > included_directory.txt
|
Loading…
Reference in New Issue
Block a user