1
0
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:
Andrey Nering 2018-10-13 18:31:19 -03:00 committed by GitHub
commit a567f7ed20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 142 additions and 10 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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)

View File

@ -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 {

View File

@ -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 {

View File

@ -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{}

View File

@ -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
View File

@ -0,0 +1 @@
*.txt

16
testdata/includes/Taskfile.yml vendored Normal file
View 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
View File

@ -0,0 +1,6 @@
version: '2'
tasks:
gen:
cmds:
- echo included_taskfile > included_taskfile.txt

View File

@ -0,0 +1,6 @@
version: '2'
tasks:
gen:
cmds:
- echo included_directory > included_directory.txt