diff options
| author | 2023-04-28 01:36:55 +0800 | |
|---|---|---|
| committer | 2023-04-28 01:36:55 +0800 | |
| commit | fc8c5fdce62fb229202659408798a7b6c98f6e8b (patch) | |
| tree | 7554f80e50de4af6fd255afa7c21bcdd58a7af34 /cli/internal/graph | |
| parent | dd84b9d64fb98746a230cd24233ff50a562c39c9 (diff) | |
| download | HydroRoll-fc8c5fdce62fb229202659408798a7b6c98f6e8b.tar.gz HydroRoll-fc8c5fdce62fb229202659408798a7b6c98f6e8b.zip | |
Diffstat (limited to 'cli/internal/graph')
| -rw-r--r-- | cli/internal/graph/graph.go | 274 | ||||
| -rw-r--r-- | cli/internal/graph/graph_test.go | 50 |
2 files changed, 0 insertions, 324 deletions
diff --git a/cli/internal/graph/graph.go b/cli/internal/graph/graph.go deleted file mode 100644 index 480dec9..0000000 --- a/cli/internal/graph/graph.go +++ /dev/null @@ -1,274 +0,0 @@ -// Package graph contains the CompleteGraph struct and some methods around it -package graph - -import ( - gocontext "context" - "fmt" - "path/filepath" - "regexp" - "sort" - "strings" - - "github.com/hashicorp/go-hclog" - "github.com/pyr-sh/dag" - "github.com/vercel/turbo/cli/internal/env" - "github.com/vercel/turbo/cli/internal/fs" - "github.com/vercel/turbo/cli/internal/nodes" - "github.com/vercel/turbo/cli/internal/runsummary" - "github.com/vercel/turbo/cli/internal/taskhash" - "github.com/vercel/turbo/cli/internal/turbopath" - "github.com/vercel/turbo/cli/internal/util" - "github.com/vercel/turbo/cli/internal/workspace" -) - -// CompleteGraph represents the common state inferred from the filesystem and pipeline. -// It is not intended to include information specific to a particular run. -type CompleteGraph struct { - // WorkspaceGraph expresses the dependencies between packages - WorkspaceGraph dag.AcyclicGraph - - // Pipeline is config from turbo.json - Pipeline fs.Pipeline - - // WorkspaceInfos stores the package.json contents by package name - WorkspaceInfos workspace.Catalog - - // GlobalHash is the hash of all global dependencies - GlobalHash string - - RootNode string - - // Map of TaskDefinitions by taskID - TaskDefinitions map[string]*fs.TaskDefinition - RepoRoot turbopath.AbsoluteSystemPath - - TaskHashTracker *taskhash.Tracker -} - -// GetPackageTaskVisitor wraps a `visitor` function that is used for walking the TaskGraph -// during execution (or dry-runs). The function returned here does not execute any tasks itself, -// but it helps curry some data from the Complete Graph and pass it into the visitor function. -func (g *CompleteGraph) GetPackageTaskVisitor( - ctx gocontext.Context, - taskGraph *dag.AcyclicGraph, - globalEnvMode util.EnvMode, - getArgs func(taskID string) []string, - logger hclog.Logger, - execFunc func(ctx gocontext.Context, packageTask *nodes.PackageTask, taskSummary *runsummary.TaskSummary) error, -) func(taskID string) error { - return func(taskID string) error { - packageName, taskName := util.GetPackageTaskFromId(taskID) - pkg, ok := g.WorkspaceInfos.PackageJSONs[packageName] - if !ok { - return fmt.Errorf("cannot find package %v for task %v", packageName, taskID) - } - - // Check for root task - var command string - if cmd, ok := pkg.Scripts[taskName]; ok { - command = cmd - } - - if packageName == util.RootPkgName && commandLooksLikeTurbo(command) { - return fmt.Errorf("root task %v (%v) looks like it invokes turbo and might cause a loop", taskName, command) - } - - taskDefinition, ok := g.TaskDefinitions[taskID] - if !ok { - return fmt.Errorf("Could not find definition for task") - } - - // Task env mode is only independent when global env mode is `infer`. - taskEnvMode := globalEnvMode - useOldTaskHashable := false - if taskEnvMode == util.Infer { - if taskDefinition.PassthroughEnv != nil { - taskEnvMode = util.Strict - } else { - // If we're in infer mode we have just detected non-usage of strict env vars. - // Since we haven't stabilized this we don't want to break their cache. - useOldTaskHashable = true - - // But our old behavior's actual meaning of this state is `loose`. - taskEnvMode = util.Loose - } - } - - // TODO: maybe we can remove this PackageTask struct at some point - packageTask := &nodes.PackageTask{ - TaskID: taskID, - Task: taskName, - PackageName: packageName, - Pkg: pkg, - EnvMode: taskEnvMode, - Dir: pkg.Dir.ToString(), - TaskDefinition: taskDefinition, - Outputs: taskDefinition.Outputs.Inclusions, - ExcludedOutputs: taskDefinition.Outputs.Exclusions, - } - - passThruArgs := getArgs(taskName) - hash, err := g.TaskHashTracker.CalculateTaskHash( - packageTask, - taskGraph.DownEdges(taskID), - logger, - passThruArgs, - useOldTaskHashable, - ) - - // Not being able to construct the task hash is a hard error - if err != nil { - return fmt.Errorf("Hashing error: %v", err) - } - - pkgDir := pkg.Dir - packageTask.Hash = hash - envVars := g.TaskHashTracker.GetEnvVars(taskID) - expandedInputs := g.TaskHashTracker.GetExpandedInputs(packageTask) - framework := g.TaskHashTracker.GetFramework(taskID) - - logFile := repoRelativeLogFile(pkgDir, taskName) - packageTask.LogFile = logFile - packageTask.Command = command - - var envVarPassthroughMap env.EnvironmentVariableMap - if taskDefinition.PassthroughEnv != nil { - if envVarPassthroughDetailedMap, err := env.GetHashableEnvVars(taskDefinition.PassthroughEnv, nil, ""); err == nil { - envVarPassthroughMap = envVarPassthroughDetailedMap.BySource.Explicit - } - } - - summary := &runsummary.TaskSummary{ - TaskID: taskID, - Task: taskName, - Hash: hash, - Package: packageName, - Dir: pkgDir.ToString(), - Outputs: taskDefinition.Outputs.Inclusions, - ExcludedOutputs: taskDefinition.Outputs.Exclusions, - LogFile: logFile, - ResolvedTaskDefinition: taskDefinition, - ExpandedInputs: expandedInputs, - ExpandedOutputs: []turbopath.AnchoredSystemPath{}, - Command: command, - CommandArguments: passThruArgs, - Framework: framework, - EnvMode: taskEnvMode, - EnvVars: runsummary.TaskEnvVarSummary{ - Configured: envVars.BySource.Explicit.ToSecretHashable(), - Inferred: envVars.BySource.Matching.ToSecretHashable(), - Passthrough: envVarPassthroughMap.ToSecretHashable(), - }, - ExternalDepsHash: pkg.ExternalDepsHash, - } - - if ancestors, err := g.getTaskGraphAncestors(taskGraph, packageTask.TaskID); err == nil { - summary.Dependencies = ancestors - } - if descendents, err := g.getTaskGraphDescendants(taskGraph, packageTask.TaskID); err == nil { - summary.Dependents = descendents - } - - return execFunc(ctx, packageTask, summary) - } -} - -// GetPipelineFromWorkspace returns the Unmarshaled fs.Pipeline struct from turbo.json in the given workspace. -func (g *CompleteGraph) GetPipelineFromWorkspace(workspaceName string, isSinglePackage bool) (fs.Pipeline, error) { - turboConfig, err := g.GetTurboConfigFromWorkspace(workspaceName, isSinglePackage) - - if err != nil { - return nil, err - } - - return turboConfig.Pipeline, nil -} - -// GetTurboConfigFromWorkspace returns the Unmarshaled fs.TurboJSON from turbo.json in the given workspace. -func (g *CompleteGraph) GetTurboConfigFromWorkspace(workspaceName string, isSinglePackage bool) (*fs.TurboJSON, error) { - cachedTurboConfig, ok := g.WorkspaceInfos.TurboConfigs[workspaceName] - - if ok { - return cachedTurboConfig, nil - } - - var workspacePackageJSON *fs.PackageJSON - if pkgJSON, err := g.GetPackageJSONFromWorkspace(workspaceName); err == nil { - workspacePackageJSON = pkgJSON - } else { - return nil, err - } - - // Note: pkgJSON.Dir for the root workspace will be an empty string, and for - // other workspaces, it will be a relative path. - workspaceAbsolutePath := workspacePackageJSON.Dir.RestoreAnchor(g.RepoRoot) - turboConfig, err := fs.LoadTurboConfig(workspaceAbsolutePath, workspacePackageJSON, isSinglePackage) - - // If we failed to load a TurboConfig, bubble up the error - if err != nil { - return nil, err - } - - // add to cache - g.WorkspaceInfos.TurboConfigs[workspaceName] = turboConfig - - return g.WorkspaceInfos.TurboConfigs[workspaceName], nil -} - -// GetPackageJSONFromWorkspace returns an Unmarshaled struct of the package.json in the given workspace -func (g *CompleteGraph) GetPackageJSONFromWorkspace(workspaceName string) (*fs.PackageJSON, error) { - if pkgJSON, ok := g.WorkspaceInfos.PackageJSONs[workspaceName]; ok { - return pkgJSON, nil - } - - return nil, fmt.Errorf("No package.json for %s", workspaceName) -} - -// repoRelativeLogFile returns the path to the log file for this task execution as a -// relative path from the root of the monorepo. -func repoRelativeLogFile(dir turbopath.AnchoredSystemPath, taskName string) string { - return filepath.Join(dir.ToStringDuringMigration(), ".turbo", fmt.Sprintf("turbo-%v.log", taskName)) -} - -// getTaskGraphAncestors gets all the ancestors for a given task in the graph. -// "ancestors" are all tasks that the given task depends on. -func (g *CompleteGraph) getTaskGraphAncestors(taskGraph *dag.AcyclicGraph, taskID string) ([]string, error) { - ancestors, err := taskGraph.Ancestors(taskID) - if err != nil { - return nil, err - } - stringAncestors := []string{} - for _, dep := range ancestors { - // Don't leak out internal root node name, which are just placeholders - if !strings.Contains(dep.(string), g.RootNode) { - stringAncestors = append(stringAncestors, dep.(string)) - } - } - - sort.Strings(stringAncestors) - return stringAncestors, nil -} - -// getTaskGraphDescendants gets all the descendants for a given task in the graph. -// "descendants" are all tasks that depend on the given taskID. -func (g *CompleteGraph) getTaskGraphDescendants(taskGraph *dag.AcyclicGraph, taskID string) ([]string, error) { - descendents, err := taskGraph.Descendents(taskID) - if err != nil { - return nil, err - } - stringDescendents := []string{} - for _, dep := range descendents { - // Don't leak out internal root node name, which are just placeholders - if !strings.Contains(dep.(string), g.RootNode) { - stringDescendents = append(stringDescendents, dep.(string)) - } - } - sort.Strings(stringDescendents) - return stringDescendents, nil -} - -var _isTurbo = regexp.MustCompile(`(?:^|\s)turbo(?:$|\s)`) - -func commandLooksLikeTurbo(command string) bool { - return _isTurbo.MatchString(command) -} diff --git a/cli/internal/graph/graph_test.go b/cli/internal/graph/graph_test.go deleted file mode 100644 index 9323e19..0000000 --- a/cli/internal/graph/graph_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package graph - -import ( - "testing" - - "gotest.tools/v3/assert" -) - -func Test_CommandsInvokingTurbo(t *testing.T) { - type testCase struct { - command string - match bool - } - testCases := []testCase{ - { - "turbo run foo", - true, - }, - { - "rm -rf ~/Library/Caches/pnpm && turbo run foo && rm -rf ~/.npm", - true, - }, - { - "FLAG=true turbo run foo", - true, - }, - { - "npx turbo run foo", - true, - }, - { - "echo starting; turbo foo; echo done", - true, - }, - // We don't catch this as if people are going to try to invoke the turbo - // binary directly, they'll always be able to work around us. - { - "./node_modules/.bin/turbo foo", - false, - }, - { - "rm -rf ~/Library/Caches/pnpm && rm -rf ~/Library/Caches/turbo && rm -rf ~/.npm && rm -rf ~/.pnpm-store && rm -rf ~/.turbo", - false, - }, - } - - for _, tc := range testCases { - assert.Equal(t, commandLooksLikeTurbo(tc.command), tc.match, tc.command) - } -} |
