From f19c520f23c19df7ebcd27f293b5c59709f04edd Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Sun, 21 Apr 2024 15:28:02 +0000 Subject: [PATCH] feat: add support for multiple includes on a graph edge --- errors/errors.go | 2 +- errors/errors_taskfile.go | 17 ----------------- task_test.go | 4 ++-- taskfile/ast/graph.go | 16 +++++++++------- taskfile/reader.go | 26 +++++++++++++++----------- testdata/includes/Taskfile.yml | 10 +++++----- testdata/includes/module1/Taskfile.yml | 4 ++++ testdata/includes/module2/Taskfile.yml | 6 +++++- testdata/includes/module3/Taskfile.yml | 6 ------ testdata/includes/module4/Taskfile.yml | 6 ------ 10 files changed, 41 insertions(+), 56 deletions(-) delete mode 100644 testdata/includes/module3/Taskfile.yml delete mode 100644 testdata/includes/module4/Taskfile.yml diff --git a/errors/errors.go b/errors/errors.go index 400c0f16..24e6a7a2 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -19,7 +19,7 @@ const ( CodeTaskfileCacheNotFound CodeTaskfileVersionCheckError CodeTaskfileNetworkTimeout - CodeTaskfileDuplicateInclude + _ // CodeTaskfileDuplicateInclude CodeTaskfileCycle ) diff --git a/errors/errors_taskfile.go b/errors/errors_taskfile.go index ba5579c2..ad6d1898 100644 --- a/errors/errors_taskfile.go +++ b/errors/errors_taskfile.go @@ -3,7 +3,6 @@ package errors import ( "fmt" "net/http" - "strings" "time" "github.com/Masterminds/semver/v3" @@ -176,22 +175,6 @@ func (err *TaskfileNetworkTimeoutError) Code() int { return CodeTaskfileNetworkTimeout } -type TaskfileDuplicateIncludeError struct { - URI string - IncludedURI string - Namespaces []string -} - -func (err *TaskfileDuplicateIncludeError) Error() string { - return fmt.Sprintf( - `task: Taskfile %q attempted to include %q multiple times with namespaces: %s`, err.URI, err.IncludedURI, strings.Join(err.Namespaces, ", "), - ) -} - -func (err *TaskfileDuplicateIncludeError) Code() int { - return CodeTaskfileDuplicateInclude -} - // TaskfileCycleError is returned when we detect that a Taskfile includes a // set of Taskfiles that include each other in a cycle. type TaskfileCycleError struct { diff --git a/task_test.go b/task_test.go index fe6e7ec7..662f0b67 100644 --- a/task_test.go +++ b/task_test.go @@ -981,8 +981,8 @@ func TestIncludes(t *testing.T) { "included_directory.txt": "included_directory", "included_directory_without_dir.txt": "included_directory_without_dir", "included_taskfile_without_dir.txt": "included_taskfile_without_dir", - "./module3/included_taskfile_with_dir.txt": "included_taskfile_with_dir", - "./module4/included_directory_with_dir.txt": "included_directory_with_dir", + "./module2/included_directory_with_dir.txt": "included_directory_with_dir", + "./module2/included_taskfile_with_dir.txt": "included_taskfile_with_dir", "os_include.txt": "os", }, } diff --git a/taskfile/ast/graph.go b/taskfile/ast/graph.go index 03e5c672..ca2d1c53 100644 --- a/taskfile/ast/graph.go +++ b/taskfile/ast/graph.go @@ -79,17 +79,19 @@ func (tfg *TaskfileGraph) Merge() (*Taskfile, error) { } // Get the merge options - include, ok := edge.Properties.Data.(*Include) + includes, ok := edge.Properties.Data.([]*Include) if !ok { return fmt.Errorf("task: Failed to get merge options") } - // Merge the included Taskfile into the parent Taskfile - if err := vertex.Taskfile.Merge( - includedVertex.Taskfile, - include, - ); err != nil { - return err + // Merge the included Taskfiles into the parent Taskfile + for _, include := range includes { + if err := vertex.Taskfile.Merge( + includedVertex.Taskfile, + include, + ); err != nil { + return err + } } return nil diff --git a/taskfile/reader.go b/taskfile/reader.go index bb00590d..82138dc9 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -140,17 +140,21 @@ func (r *Reader) include(node Node) error { } // Create an edge between the Taskfiles - err = r.graph.AddEdge(node.Location(), includeNode.Location(), graph.EdgeData(include)) - if errors.Is(err, graph.ErrEdgeAlreadyExists) { - edge, err := r.graph.Edge(node.Location(), includeNode.Location()) - if err != nil { - return err - } - return &errors.TaskfileDuplicateIncludeError{ - URI: node.Location(), - IncludedURI: includeNode.Location(), - Namespaces: []string{namespace, edge.Properties.Data.(*ast.Include).Namespace}, - } + edge, err := r.graph.Edge(node.Location(), includeNode.Location()) + if err == graph.ErrEdgeNotFound { + // If the edge doesn't exist, create it + err = r.graph.AddEdge( + node.Location(), + includeNode.Location(), + graph.EdgeData([]*ast.Include{include}), + ) + } else { + // If the edge already exists + err = r.graph.UpdateEdge( + node.Location(), + includeNode.Location(), + graph.EdgeData(append(edge.Properties.Data.([]*ast.Include), include)), + ) } if errors.Is(err, graph.ErrEdgeCreatesCycle) { return errors.TaskfileCycleError{ diff --git a/testdata/includes/Taskfile.yml b/testdata/includes/Taskfile.yml index 0d1efec9..8ed9e416 100644 --- a/testdata/includes/Taskfile.yml +++ b/testdata/includes/Taskfile.yml @@ -6,13 +6,13 @@ includes: included_without_dir: taskfile: ./module1 included_taskfile_without_dir: - taskfile: ./module2/Taskfile.yml + taskfile: ./module1/Taskfile.yml included_with_dir: - taskfile: ./module3 - dir: ./module3 + taskfile: ./module2 + dir: ./module2 included_taskfile_with_dir: - taskfile: ./module4/Taskfile.yml - dir: ./module4 + taskfile: ./module2/Taskfile.yml + dir: ./module2 included_os: ./Taskfile_{{OS}}.yml tasks: diff --git a/testdata/includes/module1/Taskfile.yml b/testdata/includes/module1/Taskfile.yml index 34596e52..3659073e 100644 --- a/testdata/includes/module1/Taskfile.yml +++ b/testdata/includes/module1/Taskfile.yml @@ -1,6 +1,10 @@ version: '3' tasks: + gen_dir: + cmds: + - echo included_directory_without_dir > included_directory_without_dir.txt + gen_file: cmds: - echo included_taskfile_without_dir > included_taskfile_without_dir.txt diff --git a/testdata/includes/module2/Taskfile.yml b/testdata/includes/module2/Taskfile.yml index 26e749bf..09bbdb60 100644 --- a/testdata/includes/module2/Taskfile.yml +++ b/testdata/includes/module2/Taskfile.yml @@ -3,4 +3,8 @@ version: '3' tasks: gen_dir: cmds: - - echo included_directory_without_dir > included_directory_without_dir.txt + - echo included_directory_with_dir > included_directory_with_dir.txt + + gen_file: + cmds: + - echo included_taskfile_with_dir > included_taskfile_with_dir.txt diff --git a/testdata/includes/module3/Taskfile.yml b/testdata/includes/module3/Taskfile.yml deleted file mode 100644 index 619f1986..00000000 --- a/testdata/includes/module3/Taskfile.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: '3' - -tasks: - gen_file: - cmds: - - echo included_taskfile_with_dir > included_taskfile_with_dir.txt diff --git a/testdata/includes/module4/Taskfile.yml b/testdata/includes/module4/Taskfile.yml deleted file mode 100644 index 25cb62cd..00000000 --- a/testdata/includes/module4/Taskfile.yml +++ /dev/null @@ -1,6 +0,0 @@ -version: '3' - -tasks: - gen_dir: - cmds: - - echo included_directory_with_dir > included_directory_with_dir.txt