mirror of
				https://github.com/go-task/task.git
				synced 2025-10-30 23:58:01 +02:00 
			
		
		
		
	fix: run once in shared dependencies (#1655)
* fix: run once in shared dependencies * feat: add test
This commit is contained in:
		
							
								
								
									
										7
									
								
								hash.go
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								hash.go
									
									
									
									
									
								
							| @@ -4,15 +4,12 @@ import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/go-task/task/v3/internal/hash" | ||||
| 	"github.com/go-task/task/v3/internal/slicesext" | ||||
| 	"github.com/go-task/task/v3/taskfile/ast" | ||||
| ) | ||||
|  | ||||
| func (e *Executor) GetHash(t *ast.Task) (string, error) { | ||||
| 	r := t.Run | ||||
| 	if r == "" { | ||||
| 		r = e.Taskfile.Run | ||||
| 	} | ||||
|  | ||||
| 	r := slicesext.FirstNonZero(t.Run, e.Taskfile.Run) | ||||
| 	var h hash.HashFunc | ||||
| 	switch r { | ||||
| 	case "always": | ||||
|   | ||||
| @@ -15,7 +15,7 @@ func Empty(*ast.Task) (string, error) { | ||||
| } | ||||
|  | ||||
| func Name(t *ast.Task) (string, error) { | ||||
| 	return t.Task, nil | ||||
| 	return fmt.Sprintf("%s:%s", t.Location.Taskfile, t.LocalName()), nil | ||||
| } | ||||
|  | ||||
| func Hash(t *ast.Task) (string, error) { | ||||
|   | ||||
| @@ -18,3 +18,13 @@ func UniqueJoin[T cmp.Ordered](ss ...[]T) []T { | ||||
| 	slices.Sort(r) | ||||
| 	return slices.Compact(r) | ||||
| } | ||||
|  | ||||
| func FirstNonZero[T comparable](values ...T) T { | ||||
| 	var zero T | ||||
| 	for _, v := range values { | ||||
| 		if v != zero { | ||||
| 			return v | ||||
| 		} | ||||
| 	} | ||||
| 	return zero | ||||
| } | ||||
|   | ||||
							
								
								
									
										20
									
								
								task_test.go
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								task_test.go
									
									
									
									
									
								
							| @@ -1664,6 +1664,26 @@ func TestRunOnlyRunsJobsHashOnce(t *testing.T) { | ||||
| 	tt.Run(t) | ||||
| } | ||||
|  | ||||
| func TestRunOnceSharedDeps(t *testing.T) { | ||||
| 	const dir = "testdata/run_once_shared_deps" | ||||
|  | ||||
| 	var buff bytes.Buffer | ||||
| 	e := task.Executor{ | ||||
| 		Dir:      dir, | ||||
| 		Stdout:   &buff, | ||||
| 		Stderr:   &buff, | ||||
| 		ForceAll: true, | ||||
| 	} | ||||
| 	require.NoError(t, e.Setup()) | ||||
| 	require.NoError(t, e.Run(context.Background(), &ast.Call{Task: "build"})) | ||||
|  | ||||
| 	rx := regexp.MustCompile(`task: \[service-[a,b]:library:build\] echo "build library"`) | ||||
| 	matches := rx.FindAllStringSubmatch(buff.String(), -1) | ||||
| 	assert.Len(t, matches, 1) | ||||
| 	assert.Contains(t, buff.String(), `task: [service-a:build] echo "build a"`) | ||||
| 	assert.Contains(t, buff.String(), `task: [service-b:build] echo "build b"`) | ||||
| } | ||||
|  | ||||
| func TestDeferredCmds(t *testing.T) { | ||||
| 	const dir = "testdata/deferred" | ||||
| 	var buff bytes.Buffer | ||||
|   | ||||
| @@ -13,37 +13,39 @@ import ( | ||||
|  | ||||
| // Task represents a task | ||||
| type Task struct { | ||||
| 	Task                 string | ||||
| 	Cmds                 []*Cmd | ||||
| 	Deps                 []*Dep | ||||
| 	Label                string | ||||
| 	Desc                 string | ||||
| 	Prompt               string | ||||
| 	Summary              string | ||||
| 	Requires             *Requires | ||||
| 	Aliases              []string | ||||
| 	Sources              []*Glob | ||||
| 	Generates            []*Glob | ||||
| 	Status               []string | ||||
| 	Preconditions        []*Precondition | ||||
| 	Dir                  string | ||||
| 	Set                  []string | ||||
| 	Shopt                []string | ||||
| 	Vars                 *Vars | ||||
| 	Env                  *Vars | ||||
| 	Dotenv               []string | ||||
| 	Silent               bool | ||||
| 	Interactive          bool | ||||
| 	Internal             bool | ||||
| 	Method               string | ||||
| 	Prefix               string | ||||
| 	IgnoreError          bool | ||||
| 	Run                  string | ||||
| 	Task          string | ||||
| 	Cmds          []*Cmd | ||||
| 	Deps          []*Dep | ||||
| 	Label         string | ||||
| 	Desc          string | ||||
| 	Prompt        string | ||||
| 	Summary       string | ||||
| 	Requires      *Requires | ||||
| 	Aliases       []string | ||||
| 	Sources       []*Glob | ||||
| 	Generates     []*Glob | ||||
| 	Status        []string | ||||
| 	Preconditions []*Precondition | ||||
| 	Dir           string | ||||
| 	Set           []string | ||||
| 	Shopt         []string | ||||
| 	Vars          *Vars | ||||
| 	Env           *Vars | ||||
| 	Dotenv        []string | ||||
| 	Silent        bool | ||||
| 	Interactive   bool | ||||
| 	Internal      bool | ||||
| 	Method        string | ||||
| 	Prefix        string | ||||
| 	IgnoreError   bool | ||||
| 	Run           string | ||||
| 	Platforms     []*Platform | ||||
| 	Watch         bool | ||||
| 	Location      *Location | ||||
| 	// Populated during merging | ||||
| 	Namespace            string | ||||
| 	IncludeVars          *Vars | ||||
| 	IncludedTaskfileVars *Vars | ||||
| 	Platforms            []*Platform | ||||
| 	Location             *Location | ||||
| 	Watch                bool | ||||
| } | ||||
|  | ||||
| func (t *Task) Name() string { | ||||
| @@ -53,6 +55,13 @@ func (t *Task) Name() string { | ||||
| 	return t.Task | ||||
| } | ||||
|  | ||||
| func (t *Task) LocalName() string { | ||||
| 	name := t.Task | ||||
| 	name = strings.TrimPrefix(name, t.Namespace) | ||||
| 	name = strings.TrimPrefix(name, ":") | ||||
| 	return name | ||||
| } | ||||
|  | ||||
| // WildcardMatch will check if the given string matches the name of the Task and returns any wildcard values. | ||||
| func (t *Task) WildcardMatch(name string) (bool, []string) { | ||||
| 	// Convert the name into a regex string | ||||
| @@ -210,6 +219,7 @@ func (t *Task) DeepCopy() *Task { | ||||
| 		Platforms:            deepcopy.Slice(t.Platforms), | ||||
| 		Location:             t.Location.DeepCopy(), | ||||
| 		Requires:             t.Requires.DeepCopy(), | ||||
| 		Namespace:            t.Namespace, | ||||
| 	} | ||||
| 	return c | ||||
| } | ||||
|   | ||||
| @@ -47,7 +47,7 @@ func (t *Tasks) FindMatchingTasks(call *Call) []*MatchingTask { | ||||
| } | ||||
|  | ||||
| func (t1 *Tasks) Merge(t2 Tasks, include *Include, includedTaskfileVars *Vars) { | ||||
| 	_ = t2.Range(func(k string, v *Task) error { | ||||
| 	_ = t2.Range(func(name string, v *Task) error { | ||||
| 		// We do a deep copy of the task struct here to ensure that no data can | ||||
| 		// be changed elsewhere once the taskfile is merged. | ||||
| 		task := v.DeepCopy() | ||||
| @@ -95,7 +95,8 @@ func (t1 *Tasks) Merge(t2 Tasks, include *Include, includedTaskfileVars *Vars) { | ||||
| 		} | ||||
|  | ||||
| 		// Add the task to the merged taskfile | ||||
| 		taskNameWithNamespace := taskNameWithNamespace(k, include.Namespace) | ||||
| 		taskNameWithNamespace := taskNameWithNamespace(name, include.Namespace) | ||||
| 		task.Namespace = include.Namespace | ||||
| 		task.Task = taskNameWithNamespace | ||||
| 		t1.Set(taskNameWithNamespace, task) | ||||
|  | ||||
|   | ||||
							
								
								
									
										11
									
								
								testdata/run_once_shared_deps/Taskfile.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								testdata/run_once_shared_deps/Taskfile.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| version: '3' | ||||
|  | ||||
| includes: | ||||
|   service-a: ./service-a | ||||
|   service-b: ./service-b | ||||
|  | ||||
| tasks: | ||||
|   build: | ||||
|     deps: | ||||
|       - service-a:build | ||||
|       - service-b:build | ||||
							
								
								
									
										9
									
								
								testdata/run_once_shared_deps/library/Taskfile.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								testdata/run_once_shared_deps/library/Taskfile.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| version: '3' | ||||
|  | ||||
| tasks: | ||||
|   build: | ||||
|     run: once | ||||
|     cmds: | ||||
|       - echo "build library" | ||||
|     sources: | ||||
|       - src/**/* | ||||
							
								
								
									
										15
									
								
								testdata/run_once_shared_deps/service-a/Taskfile.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								testdata/run_once_shared_deps/service-a/Taskfile.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| version: '3' | ||||
|  | ||||
| includes: | ||||
|   library: | ||||
|     taskfile: ../library/Taskfile.yml | ||||
|     dir: ../library | ||||
|  | ||||
| tasks: | ||||
|   build: | ||||
|     run: once | ||||
|     deps: [library:build] | ||||
|     cmds: | ||||
|       - echo "build a" | ||||
|     sources: | ||||
|       - src/**/* | ||||
							
								
								
									
										1
									
								
								testdata/run_once_shared_deps/service-a/src/imasource.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								testdata/run_once_shared_deps/service-a/src/imasource.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| package main | ||||
							
								
								
									
										15
									
								
								testdata/run_once_shared_deps/service-b/Taskfile.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								testdata/run_once_shared_deps/service-b/Taskfile.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| version: '3' | ||||
|  | ||||
| includes: | ||||
|   library: | ||||
|     taskfile: ../library/Taskfile.yml | ||||
|     dir: ../library | ||||
|  | ||||
| tasks: | ||||
|   build: | ||||
|     run: once | ||||
|     deps: [library:build] | ||||
|     cmds: | ||||
|       - echo "build b" | ||||
|     sources: | ||||
|       - src/**/* | ||||
							
								
								
									
										1
									
								
								testdata/run_once_shared_deps/service-b/src/imasource.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								testdata/run_once_shared_deps/service-b/src/imasource.go
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| package main | ||||
| @@ -72,6 +72,7 @@ func (e *Executor) compiledTask(call *ast.Call, evaluateShVars bool) (*ast.Task, | ||||
| 		Location:             origTask.Location, | ||||
| 		Requires:             origTask.Requires, | ||||
| 		Watch:                origTask.Watch, | ||||
| 		Namespace:            origTask.Namespace, | ||||
| 	} | ||||
| 	new.Dir, err = execext.Expand(new.Dir) | ||||
| 	if err != nil { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user