diff --git a/task.go b/task.go index 2709ecdb..3a83fc16 100644 --- a/task.go +++ b/task.go @@ -176,7 +176,7 @@ func (e *Executor) Setup() error { if v < 2.1 && e.Taskfile.Output != "" { return fmt.Errorf(`task: Taskfile option "output" is only available starting on Taskfile version v2.1`) } - if v < 2.2 && len(e.Taskfile.Includes) > 0 { + if v < 2.2 && e.Taskfile.Includes.Len() > 0 { return fmt.Errorf(`task: Including Taskfiles is only available starting on Taskfile version v2.2`) } if v >= 3.0 && e.Taskfile.Expansions > 2 { @@ -229,10 +229,14 @@ func (e *Executor) Setup() error { } if v < 3 { - for _, taskfile := range e.Taskfile.Includes { + err := e.Taskfile.Includes.Range(func(_ string, taskfile taskfile.IncludedTaskfile) error { if taskfile.AdvancedImport { return errors.New(`task: Import with additional parameters is only available starting on Taskfile version v3`) } + return nil + }) + if err != nil { + return err } } diff --git a/taskfile/included_taskfile.go b/taskfile/included_taskfile.go index 346744f9..67d7891d 100644 --- a/taskfile/included_taskfile.go +++ b/taskfile/included_taskfile.go @@ -1,6 +1,10 @@ package taskfile -import "errors" +import ( + "errors" + + "gopkg.in/yaml.v3" +) var ( // ErrCantUnmarshalIncludedTaskfile is returned for invalid var YAML. @@ -15,7 +19,72 @@ type IncludedTaskfile struct { } // IncludedTaskfiles represents information about included tasksfiles -type IncludedTaskfiles = map[string]IncludedTaskfile +type IncludedTaskfiles struct { + Keys []string + Mapping map[string]IncludedTaskfile +} + +// UnmarshalYAML implements the yaml.Unmarshaler interface. +func (tfs *IncludedTaskfiles) UnmarshalYAML(node *yaml.Node) error { + if node.Kind != yaml.MappingNode { + return errors.New("task: includes is not a map") + } + + // NOTE(@andreynering): on this style of custom unmarsheling, + // even number contains the keys, while odd numbers contains + // the values. + for i := 0; i < len(node.Content); i += 2 { + keyNode := node.Content[i] + valueNode := node.Content[i+1] + + var v IncludedTaskfile + if err := valueNode.Decode(&v); err != nil { + return err + } + tfs.Set(keyNode.Value, v) + } + return nil +} + +// Len returns the length of the map +func (tfs *IncludedTaskfiles) Len() int { + if tfs == nil { + return 0 + } + return len(tfs.Keys) +} + +// Merge merges the given IncludedTaskfiles into the caller one +func (tfs *IncludedTaskfiles) Merge(other *IncludedTaskfiles) { + other.Range(func(key string, value IncludedTaskfile) error { + tfs.Set(key, value) + return nil + }) +} + +// Set sets a value to a given key +func (tfs *IncludedTaskfiles) Set(key string, includedTaskfile IncludedTaskfile) { + if tfs.Mapping == nil { + tfs.Mapping = make(map[string]IncludedTaskfile, 1) + } + if !stringSliceContains(tfs.Keys, key) { + tfs.Keys = append(tfs.Keys, key) + } + tfs.Mapping[key] = includedTaskfile +} + +// Range allows you to loop into the included taskfiles in its right order +func (tfs *IncludedTaskfiles) Range(yield func(key string, includedTaskfile IncludedTaskfile) error) error { + if tfs == nil { + return nil + } + for _, k := range tfs.Keys { + if err := yield(k, tfs.Mapping[k]); err != nil { + return err + } + } + return nil +} // UnmarshalYAML implements yaml.Unmarshaler interface func (it *IncludedTaskfile) UnmarshalYAML(unmarshal func(interface{}) error) error { diff --git a/taskfile/merge.go b/taskfile/merge.go index 24b3ebb0..93466008 100644 --- a/taskfile/merge.go +++ b/taskfile/merge.go @@ -22,11 +22,9 @@ func Merge(t1, t2 *Taskfile, namespaces ...string) error { } if t1.Includes == nil { - t1.Includes = make(IncludedTaskfiles) - } - for k, v := range t2.Includes { - t1.Includes[k] = v + t1.Includes = &IncludedTaskfiles{} } + t1.Includes.Merge(t2.Includes) if t1.Vars == nil { t1.Vars = &Vars{} diff --git a/taskfile/read/taskfile.go b/taskfile/read/taskfile.go index a16c4422..47fb1ee8 100644 --- a/taskfile/read/taskfile.go +++ b/taskfile/read/taskfile.go @@ -58,7 +58,7 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) { } } - for namespace, includedTask := range t.Includes { + err = t.Includes.Range(func(namespace string, includedTask taskfile.IncludedTaskfile) error { if v >= 3.0 { tr := templater.Templater{Vars: &taskfile.Vars{}, RemoveNoValue: true} includedTask = taskfile.IncludedTaskfile{ @@ -67,7 +67,7 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) { AdvancedImport: includedTask.AdvancedImport, } if err := tr.Err(); err != nil { - return nil, err + return err } } @@ -79,21 +79,21 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) { info, err := os.Stat(path) if err != nil { - return nil, err + return err } if info.IsDir() { path = filepath.Join(path, "Taskfile.yml") } includedTaskfile, err := readTaskfile(path) if err != nil { - return nil, err + return err } - if len(includedTaskfile.Includes) > 0 { - return nil, ErrIncludedTaskfilesCantHaveIncludes + if includedTaskfile.Includes.Len() > 0 { + return ErrIncludedTaskfilesCantHaveIncludes } if v >= 3.0 && len(includedTaskfile.Dotenv) > 0 { - return nil, ErrIncludedTaskfilesCantHaveDotenvs + return ErrIncludedTaskfilesCantHaveDotenvs } if includedTask.AdvancedImport { @@ -105,8 +105,12 @@ func Taskfile(dir string, entrypoint string) (*taskfile.Taskfile, error) { } if err = taskfile.Merge(t, includedTaskfile, namespace); err != nil { - return nil, err + return err } + return nil + }) + if err != nil { + return nil, err } if v < 3.0 { diff --git a/taskfile/slice.go b/taskfile/slice.go new file mode 100644 index 00000000..9cc50105 --- /dev/null +++ b/taskfile/slice.go @@ -0,0 +1,10 @@ +package taskfile + +func stringSliceContains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + return false +} diff --git a/taskfile/taskfile.go b/taskfile/taskfile.go index 4c53c296..b3b8a556 100644 --- a/taskfile/taskfile.go +++ b/taskfile/taskfile.go @@ -11,7 +11,7 @@ type Taskfile struct { Expansions int Output string Method string - Includes IncludedTaskfiles + Includes *IncludedTaskfiles Vars *Vars Env *Vars Tasks Tasks @@ -26,7 +26,7 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error { Expansions int Output string Method string - Includes IncludedTaskfiles + Includes *IncludedTaskfiles Vars *Vars Env *Vars Tasks Tasks diff --git a/taskfile/var.go b/taskfile/var.go index 2466e93c..e552c94a 100644 --- a/taskfile/var.go +++ b/taskfile/var.go @@ -53,21 +53,12 @@ func (vs *Vars) Set(key string, value Var) { if vs.Mapping == nil { vs.Mapping = make(map[string]Var, 1) } - if !strSliceContains(vs.Keys, key) { + if !stringSliceContains(vs.Keys, key) { vs.Keys = append(vs.Keys, key) } vs.Mapping[key] = value } -func strSliceContains(s []string, str string) bool { - for _, v := range s { - if v == str { - return true - } - } - return false -} - // Range allows you to loop into the vars in its right order func (vs *Vars) Range(yield func(key string, value Var) error) error { if vs == nil {