From 98da31e05b1a9fc90f1893d1bf8f30dc03c4a641 Mon Sep 17 00:00:00 2001 From: Mehul Kar Date: Tue, 11 Apr 2023 14:54:53 -0700 Subject: [PATCH] Update data sent to vercel for runs (#4495) - Add context and command when starting a Run - Update data sent for Tasks - Fix PATCH endpoint to mark Run as done - Improve error propagation --- .../single_package/run-summary.t | 2 + cli/internal/run/run.go | 6 +- cli/internal/run/run_spec.go | 32 +++++ cli/internal/run/run_spec_test.go | 107 ++++++++++++++++ cli/internal/runsummary/execution_summary.go | 39 ++++-- cli/internal/runsummary/run_summary.go | 83 ++++++++----- cli/internal/runsummary/spaces.go | 117 ++++++++++++++++++ cli/internal/runsummary/vercel.go | 49 -------- cli/internal/scope/scope.go | 38 ++++-- cli/internal/scope/scope_test.go | 6 +- 10 files changed, 373 insertions(+), 106 deletions(-) create mode 100644 cli/internal/run/run_spec_test.go create mode 100644 cli/internal/runsummary/spaces.go delete mode 100644 cli/internal/runsummary/vercel.go diff --git a/cli/integration_tests/single_package/run-summary.t b/cli/integration_tests/single_package/run-summary.t index 37554fce4b613..036becb4261ef 100644 --- a/cli/integration_tests/single_package/run-summary.t +++ b/cli/integration_tests/single_package/run-summary.t @@ -20,9 +20,11 @@ Check [ "attempted", "cached", + "command", "endTime", "exitCode", "failed", + "repoPath", "startTime", "success" ] diff --git a/cli/internal/run/run.go b/cli/internal/run/run.go index ee002829ed00b..cc6f812f8515e 100644 --- a/cli/internal/run/run.go +++ b/cli/internal/run/run.go @@ -61,7 +61,9 @@ func optsFromArgs(args *turbostate.ParsedArgsFromRust) (*Opts, error) { opts := getDefaultOptions() // aliases := make(map[string]string) - scope.OptsFromArgs(&opts.scopeOpts, args) + if err := scope.OptsFromArgs(&opts.scopeOpts, args); err != nil { + return nil, err + } // Cache flags opts.clientOpts.Timeout = args.RemoteCacheTimeout @@ -350,6 +352,7 @@ func (r *run) run(ctx gocontext.Context, targets []string) error { startAt, r.base.UI, r.base.RepoRoot, + rs.Opts.scopeOpts.PackageInferenceRoot, r.base.TurboVersion, r.base.APIClient, rs.Opts.runOpts, @@ -361,6 +364,7 @@ func (r *run) run(ctx gocontext.Context, targets []string) error { globalHashable.globalCacheKey, globalHashable.pipeline, ), + rs.Opts.SynthesizeCommand(rs.Targets), ) // Dry Run diff --git a/cli/internal/run/run_spec.go b/cli/internal/run/run_spec.go index 0b37303607d18..14402d39b5c83 100644 --- a/cli/internal/run/run_spec.go +++ b/cli/internal/run/run_spec.go @@ -3,6 +3,8 @@ package run import ( + "strings" + "github.com/vercel/turbo/cli/internal/cache" "github.com/vercel/turbo/cli/internal/client" "github.com/vercel/turbo/cli/internal/runcache" @@ -45,6 +47,36 @@ type Opts struct { scopeOpts scope.Opts } +// SynthesizeCommand produces a command that produces an equivalent set of packages, tasks, +// and task arguments to what the current set of opts selects. +func (o *Opts) SynthesizeCommand(tasks []string) string { + cmd := "turbo run" + cmd += " " + strings.Join(tasks, " ") + for _, filterPattern := range o.scopeOpts.FilterPatterns { + cmd += " --filter=" + filterPattern + } + for _, filterPattern := range o.scopeOpts.LegacyFilter.AsFilterPatterns() { + cmd += " --filter=" + filterPattern + } + if o.runOpts.Parallel { + cmd += " --parallel" + } + if o.runOpts.ContinueOnError { + cmd += " --continue" + } + if o.runOpts.DryRun { + if o.runOpts.DryRunJSON { + cmd += " --dry=json" + } else { + cmd += " --dry" + } + } + if len(o.runOpts.PassThroughArgs) > 0 { + cmd += " -- " + strings.Join(o.runOpts.PassThroughArgs, " ") + } + return cmd +} + // getDefaultOptions returns the default set of Opts for every run func getDefaultOptions() *Opts { return &Opts{ diff --git a/cli/internal/run/run_spec_test.go b/cli/internal/run/run_spec_test.go new file mode 100644 index 0000000000000..2bcfe2b2475d2 --- /dev/null +++ b/cli/internal/run/run_spec_test.go @@ -0,0 +1,107 @@ +package run + +import ( + "testing" + + "github.com/vercel/turbo/cli/internal/scope" + "github.com/vercel/turbo/cli/internal/util" +) + +func TestSynthesizeCommand(t *testing.T) { + testCases := []struct { + filterPatterns []string + legacyFilter scope.LegacyFilter + passThroughArgs []string + parallel bool + continueOnError bool + dryRun bool + dryRunJSON bool + tasks []string + expected string + }{ + { + filterPatterns: []string{"my-app"}, + tasks: []string{"build"}, + expected: "turbo run build --filter=my-app", + }, + { + filterPatterns: []string{"my-app"}, + tasks: []string{"build"}, + passThroughArgs: []string{"-v", "--foo=bar"}, + expected: "turbo run build --filter=my-app -- -v --foo=bar", + }, + { + legacyFilter: scope.LegacyFilter{ + Entrypoints: []string{"my-app"}, + SkipDependents: true, + }, + tasks: []string{"build"}, + passThroughArgs: []string{"-v", "--foo=bar"}, + expected: "turbo run build --filter=my-app -- -v --foo=bar", + }, + { + legacyFilter: scope.LegacyFilter{ + Entrypoints: []string{"my-app"}, + SkipDependents: true, + }, + filterPatterns: []string{"other-app"}, + tasks: []string{"build"}, + passThroughArgs: []string{"-v", "--foo=bar"}, + expected: "turbo run build --filter=other-app --filter=my-app -- -v --foo=bar", + }, + { + legacyFilter: scope.LegacyFilter{ + Entrypoints: []string{"my-app"}, + IncludeDependencies: true, + Since: "some-ref", + }, + filterPatterns: []string{"other-app"}, + tasks: []string{"build"}, + expected: "turbo run build --filter=other-app --filter=...my-app...[some-ref]...", + }, + { + filterPatterns: []string{"my-app"}, + tasks: []string{"build"}, + parallel: true, + continueOnError: true, + expected: "turbo run build --filter=my-app --parallel --continue", + }, + { + filterPatterns: []string{"my-app"}, + tasks: []string{"build"}, + dryRun: true, + expected: "turbo run build --filter=my-app --dry", + }, + { + filterPatterns: []string{"my-app"}, + tasks: []string{"build"}, + dryRun: true, + dryRunJSON: true, + expected: "turbo run build --filter=my-app --dry=json", + }, + } + + for _, testCase := range testCases { + testCase := testCase + t.Run(testCase.expected, func(t *testing.T) { + o := Opts{ + scopeOpts: scope.Opts{ + FilterPatterns: testCase.filterPatterns, + LegacyFilter: testCase.legacyFilter, + }, + runOpts: util.RunOpts{ + PassThroughArgs: testCase.passThroughArgs, + Parallel: testCase.parallel, + ContinueOnError: testCase.continueOnError, + DryRun: testCase.dryRun, + DryRunJSON: testCase.dryRunJSON, + }, + } + cmd := o.SynthesizeCommand(testCase.tasks) + if cmd != testCase.expected { + t.Errorf("SynthesizeCommand() got %v, want %v", cmd, testCase.expected) + } + }) + } + +} diff --git a/cli/internal/runsummary/execution_summary.go b/cli/internal/runsummary/execution_summary.go index 083dbf6769f28..77a2c1325f0a2 100644 --- a/cli/internal/runsummary/execution_summary.go +++ b/cli/internal/runsummary/execution_summary.go @@ -9,6 +9,7 @@ import ( "github.com/vercel/turbo/cli/internal/chrometracing" "github.com/vercel/turbo/cli/internal/fs" + "github.com/vercel/turbo/cli/internal/turbopath" "github.com/mitchellh/cli" ) @@ -72,6 +73,10 @@ type TaskExecutionSummary struct { exitCode *int // pointer so we can distinguish between 0 and unknown. } +func (ts *TaskExecutionSummary) endTime() time.Time { + return ts.startAt.Add(ts.Duration) +} + // MarshalJSON munges the TaskExecutionSummary into a format we want // We'll use an anonmyous, private struct for this, so it's not confusingly duplicated func (ts *TaskExecutionSummary) MarshalJSON() ([]byte, error) { @@ -82,7 +87,7 @@ func (ts *TaskExecutionSummary) MarshalJSON() ([]byte, error) { ExitCode *int `json:"exitCode"` }{ Start: ts.startAt.UnixMilli(), - End: ts.startAt.Add(ts.Duration).UnixMilli(), + End: ts.endTime().UnixMilli(), Err: ts.err, ExitCode: ts.exitCode, } @@ -108,10 +113,12 @@ type executionSummary struct { profileFilename string // These get serialized to JSON - success int // number of tasks that exited successfully (does not include cache hits) - failure int // number of tasks that exited with failure - cached int // number of tasks that had a cache hit - attempted int // number of tasks that started + command string // a synthesized turbo command to produce this invocation + repoPath turbopath.RelativeSystemPath // the (possibly empty) path from the turborepo root to where the command was run + success int // number of tasks that exited successfully (does not include cache hits) + failure int // number of tasks that exited with failure + cached int // number of tasks that had a cache hit + attempted int // number of tasks that started startedAt time.Time endedAt time.Time exitCode int @@ -121,14 +128,18 @@ type executionSummary struct { // We'll use an anonmyous, private struct for this, so it's not confusingly duplicated. func (es *executionSummary) MarshalJSON() ([]byte, error) { serializable := struct { - Success int `json:"success"` - Failure int `json:"failed"` - Cached int `json:"cached"` - Attempted int `json:"attempted"` - StartTime int64 `json:"startTime"` - EndTime int64 `json:"endTime"` - ExitCode int `json:"exitCode"` + Command string `json:"command"` + RepoPath string `json:"repoPath"` + Success int `json:"success"` + Failure int `json:"failed"` + Cached int `json:"cached"` + Attempted int `json:"attempted"` + StartTime int64 `json:"startTime"` + EndTime int64 `json:"endTime"` + ExitCode int `json:"exitCode"` }{ + Command: es.command, + RepoPath: es.repoPath.ToString(), StartTime: es.startedAt.UnixMilli(), EndTime: es.endedAt.UnixMilli(), Success: es.success, @@ -142,12 +153,14 @@ func (es *executionSummary) MarshalJSON() ([]byte, error) { } // newExecutionSummary creates a executionSummary instance to track events in a `turbo run`.` -func newExecutionSummary(start time.Time, tracingProfile string) *executionSummary { +func newExecutionSummary(command string, repoPath turbopath.RelativeSystemPath, start time.Time, tracingProfile string) *executionSummary { if tracingProfile != "" { chrometracing.EnableTracing() } return &executionSummary{ + command: command, + repoPath: repoPath, success: 0, failure: 0, cached: 0, diff --git a/cli/internal/runsummary/run_summary.go b/cli/internal/runsummary/run_summary.go index e5c5fc28cc539..1c6062a3c7c56 100644 --- a/cli/internal/runsummary/run_summary.go +++ b/cli/internal/runsummary/run_summary.go @@ -26,6 +26,7 @@ const MissingFrameworkLabel = "" const runSummarySchemaVersion = "0" const runsEndpoint = "/v0/spaces/%s/runs" +const runsPatchEndpoint = "/v0/spaces/%s/runs/%s" const tasksEndpoint = "/v0/spaces/%s/runs/%s/tasks" type runType int @@ -39,14 +40,16 @@ const ( // Meta is a wrapper around the serializable RunSummary, with some extra information // about the Run and references to other things that we need. type Meta struct { - RunSummary *RunSummary - ui cli.Ui - repoRoot turbopath.AbsoluteSystemPath // used to write run summary - singlePackage bool - shouldSave bool - apiClient *client.APIClient - spaceID string - runType runType + RunSummary *RunSummary + ui cli.Ui + repoRoot turbopath.AbsoluteSystemPath // used to write run summary + repoPath turbopath.RelativeSystemPath + singlePackage bool + shouldSave bool + apiClient *client.APIClient + spaceID string + runType runType + synthesizedCommand string } // RunSummary contains a summary of what happens in the `turbo run` command and why. @@ -65,11 +68,13 @@ func NewRunSummary( startAt time.Time, ui cli.Ui, repoRoot turbopath.AbsoluteSystemPath, + repoPath turbopath.RelativeSystemPath, turboVersion string, apiClient *client.APIClient, runOpts util.RunOpts, packages []string, globalHashSummary *GlobalHashSummary, + synthesizedCommand string, ) Meta { singlePackage := runOpts.SinglePackage profile := runOpts.Profile @@ -84,7 +89,7 @@ func NewRunSummary( } } - executionSummary := newExecutionSummary(startAt, profile) + executionSummary := newExecutionSummary(synthesizedCommand, repoPath, startAt, profile) return Meta{ RunSummary: &RunSummary{ @@ -96,13 +101,14 @@ func NewRunSummary( Tasks: []*TaskSummary{}, GlobalHashSummary: globalHashSummary, }, - ui: ui, - runType: runType, - repoRoot: repoRoot, - singlePackage: singlePackage, - shouldSave: shouldSave, - apiClient: apiClient, - spaceID: spaceID, + ui: ui, + runType: runType, + repoRoot: repoRoot, + singlePackage: singlePackage, + shouldSave: shouldSave, + apiClient: apiClient, + spaceID: spaceID, + synthesizedCommand: synthesizedCommand, } } @@ -143,9 +149,16 @@ func (rsm *Meta) Close(exitCode int, workspaceInfos workspace.Catalog) error { rsm.printExecutionSummary() if rsm.shouldSave { - if rsm.spaceID != "" && rsm.apiClient.IsLinked() { - if err := rsm.record(); err != nil { - rsm.ui.Warn(fmt.Sprintf("Error recording Run to Vercel: %v", err)) + if rsm.spaceID != "" { + if rsm.apiClient.IsLinked() { + if errs := rsm.record(); len(errs) > 0 { + rsm.ui.Warn("Errors recording run to Spaces") + for _, err := range errs { + rsm.ui.Warn(fmt.Sprintf("%v", err)) + } + } + } else { + rsm.ui.Warn("Failed to post to space because repo is not linked to a Space. Run `turbo link` first.") } } } @@ -200,20 +213,21 @@ func (rsm *Meta) record() []error { errs := []error{} // Right now we'll send the POST to create the Run and the subsequent task payloads - // when everything after all execution is done, but in the future, this first POST request - // can happen when the Run actually starts, so we can send updates to Vercel as the tasks progress. + // after all execution is done, but in the future, this first POST request + // can happen when the Run actually starts, so we can send updates to the associated Space + // as tasks complete. runsURL := fmt.Sprintf(runsEndpoint, rsm.spaceID) var runID string - payload := newVercelRunCreatePayload(rsm.RunSummary) + payload := rsm.newSpacesRunCreatePayload() if startPayload, err := json.Marshal(payload); err == nil { if resp, err := rsm.apiClient.JSONPost(runsURL, startPayload); err != nil { errs = append(errs, err) } else { - vercelRunResponse := &vercelRunResponse{} - if err := json.Unmarshal(resp, vercelRunResponse); err != nil { + spacesRunResponse := &spacesRunResponse{} + if err := json.Unmarshal(resp, spacesRunResponse); err != nil { errs = append(errs, err) } else { - runID = vercelRunResponse.ID + runID = spacesRunResponse.ID } } } @@ -221,8 +235,9 @@ func (rsm *Meta) record() []error { if runID != "" { rsm.postTaskSummaries(runID) - if donePayload, err := json.Marshal(newVercelDonePayload(rsm.RunSummary)); err == nil { - if _, err := rsm.apiClient.JSONPatch(runsURL, donePayload); err != nil { + if donePayload, err := json.Marshal(newSpacesDonePayload(rsm.RunSummary)); err == nil { + patchURL := fmt.Sprintf(runsPatchEndpoint, rsm.spaceID, runID) + if _, err := rsm.apiClient.JSONPatch(patchURL, donePayload); err != nil { errs = append(errs, err) } } @@ -235,7 +250,8 @@ func (rsm *Meta) record() []error { return nil } -func (rsm *Meta) postTaskSummaries(runID string) { +func (rsm *Meta) postTaskSummaries(runID string) []error { + errs := []error{} // We make at most 8 requests at a time. maxParallelRequests := 8 taskSummaries := rsm.RunSummary.Tasks @@ -256,9 +272,10 @@ func (rsm *Meta) postTaskSummaries(runID string) { defer wg.Done() for index := range queue { task := taskSummaries[index] - if taskPayload, err := json.Marshal(task); err == nil { + payload := newSpacesTaskPayload(task) + if taskPayload, err := json.Marshal(payload); err == nil { if _, err := rsm.apiClient.JSONPost(taskURL, taskPayload); err != nil { - rsm.ui.Warn(fmt.Sprintf("Eror uploading summary of %s", task.TaskID)) + errs = append(errs, fmt.Errorf("Eror uploading summary of %s", task.TaskID)) } } } @@ -270,4 +287,10 @@ func (rsm *Meta) postTaskSummaries(runID string) { } close(queue) wg.Wait() + + if len(errs) > 0 { + return errs + } + + return nil } diff --git a/cli/internal/runsummary/spaces.go b/cli/internal/runsummary/spaces.go new file mode 100644 index 0000000000000..65c4957985150 --- /dev/null +++ b/cli/internal/runsummary/spaces.go @@ -0,0 +1,117 @@ +package runsummary + +import ( + "github.com/vercel/turbo/cli/internal/ci" +) + +type spacesRunResponse struct { + ID string +} + +type spacesRunPayload struct { + // StartTime is when this run was started + StartTime int64 `json:"startTime,omitempty"` + + // EndTime is when this run ended. We will never be submitting start and endtime at the same time. + EndTime int64 `json:"endTime,omitempty"` + + // Status is + Status string `json:"status,omitempty"` + + // Type should be hardcoded to TURBO + Type string `json:"type,omitempty"` + + // ExitCode is the exit code for the full run + ExitCode int `json:"exitCode,omitempty"` + + // The command that kicked off the turbo run + Command string `json:"command,omitempty"` + + // RepositoryPath is the relative directory from the turborepo root to where + // the command was invoked. + RepositoryPath string `json:"repositoryPath,omitempty"` + + // Context is the host on which this Run was executed (e.g. Github Action, Vercel, etc) + Context string `json:"context,omitempty"` + + // TODO: we need to add these in + // originationUser string + // gitBranch string + // gitSha string +} + +type spacesCacheStatus struct { + Status string `json:"status,omitempty"` + Source string `json:"source,omitempty"` +} + +type spacesTask struct { + Key string `json:"key,omitempty"` + Name string `json:"name,omitempty"` + Workspace string `json:"workspace,omitempty"` + Hash string `json:"hash,omitempty"` + StartTime int64 `json:"startTime,omitempty"` + EndTime int64 `json:"endTime,omitempty"` + Cache spacesCacheStatus `json:"cache,omitempty"` + ExitCode int `json:"exitCode,omitempty"` + Dependencies []string `json:"dependencies,omitempty"` + Dependents []string `json:"dependents,omitempty"` +} + +func (rsm *Meta) newSpacesRunCreatePayload() *spacesRunPayload { + startTime := rsm.RunSummary.ExecutionSummary.startedAt.UnixMilli() + context := "LOCAL" + if name := ci.Constant(); name != "" { + context = name + } + return &spacesRunPayload{ + StartTime: startTime, + Status: "running", + Command: rsm.synthesizedCommand, + RepositoryPath: rsm.repoPath.ToString(), + Type: "TURBO", + Context: context, + } +} + +func newSpacesDonePayload(runsummary *RunSummary) *spacesRunPayload { + endTime := runsummary.ExecutionSummary.endedAt.UnixMilli() + return &spacesRunPayload{ + Status: "completed", + EndTime: endTime, + ExitCode: runsummary.ExecutionSummary.exitCode, + } +} + +func newSpacesTaskPayload(taskSummary *TaskSummary) *spacesTask { + // Set the cache source. Local and Remote shouldn't _both_ be true. + var source string + if taskSummary.CacheState.Local { + source = "LOCAL" + } else if taskSummary.CacheState.Remote { + source = "REMOTE" + } + status := "MISS" + if source != "" { + status = "HIT" + } + + startTime := taskSummary.Execution.startAt.UnixMilli() + endTime := taskSummary.Execution.endTime().UnixMilli() + + return &spacesTask{ + Key: taskSummary.TaskID, + Name: taskSummary.Task, + Workspace: taskSummary.Package, + Hash: taskSummary.Hash, + StartTime: startTime, + EndTime: endTime, + Cache: spacesCacheStatus{ + Status: status, + Source: source, + }, + ExitCode: *taskSummary.Execution.exitCode, + Dependencies: taskSummary.Dependencies, + Dependents: taskSummary.Dependents, + } +} diff --git a/cli/internal/runsummary/vercel.go b/cli/internal/runsummary/vercel.go deleted file mode 100644 index d0533600e7d40..0000000000000 --- a/cli/internal/runsummary/vercel.go +++ /dev/null @@ -1,49 +0,0 @@ -package runsummary - -type vercelRunResponse struct { - ID string -} - -type vercelRunPayload struct { - // ID is set by the backend, including it here for completeness, but we never fill this in. - ID string `json:"vercelId,omitempty"` - - // StartTime is when this run was started - StartTime int `json:"startTime,omitempty"` - - // EndTime is when this run ended. We will never be submitting start and endtime at the same time. - EndTime int `json:"endTime,omitempty"` - - // Status is - Status string `json:"status,omitempty"` - - // Type should be hardcoded to TURBO - Type string `json:"type,omitempty"` - - // ExitCode is the exit code for the full run - ExitCode int `json:"exitCode,omitempty"` - // TODO: we need to add these in - // originationUser string - // gitBranch string - // gitSha string - // context string - // command string -} - -func newVercelRunCreatePayload(runsummary *RunSummary) *vercelRunPayload { - startTime := int(runsummary.ExecutionSummary.startedAt.UnixMilli()) - return &vercelRunPayload{ - StartTime: startTime, - Status: "started", - Type: "TURBO", - } -} - -func newVercelDonePayload(runsummary *RunSummary) *vercelRunPayload { - endTime := int(runsummary.ExecutionSummary.endedAt.UnixMilli()) - return &vercelRunPayload{ - Status: "completed", - EndTime: endTime, - ExitCode: runsummary.ExecutionSummary.exitCode, - } -} diff --git a/cli/internal/scope/scope.go b/cli/internal/scope/scope.go index df40b7366f0ac..b5ed4e7074949 100644 --- a/cli/internal/scope/scope.go +++ b/cli/internal/scope/scope.go @@ -55,7 +55,7 @@ type Opts struct { // Patterns are the filter patterns supplied to --filter on the commandline FilterPatterns []string - PackageInferenceRoot string + PackageInferenceRoot turbopath.RelativeSystemPath } var ( @@ -70,17 +70,35 @@ match any filter will be included.` in the root directory. Includes turbo.json, root package.json, and the root lockfile by default.` ) +// normalize package inference path. We compare against "" in several places, so maintain +// that behavior. In a post-rust-port world, this should more properly be an Option +func resolvePackageInferencePath(raw string) (turbopath.RelativeSystemPath, error) { + pkgInferenceRoot, err := turbopath.CheckedToRelativeSystemPath(raw) + if err != nil { + return "", errors.Wrapf(err, "invalid package inference root %v", raw) + } + if pkgInferenceRoot == "." { + return "", nil + } + return pkgInferenceRoot, nil +} + // OptsFromArgs adds the settings relevant to this package to the given Opts -func OptsFromArgs(opts *Opts, args *turbostate.ParsedArgsFromRust) { +func OptsFromArgs(opts *Opts, args *turbostate.ParsedArgsFromRust) error { opts.FilterPatterns = args.Command.Run.Filter opts.IgnorePatterns = args.Command.Run.Ignore opts.GlobalDepPatterns = args.Command.Run.GlobalDeps - opts.PackageInferenceRoot = args.Command.Run.PkgInferenceRoot + pkgInferenceRoot, err := resolvePackageInferencePath(args.Command.Run.PkgInferenceRoot) + if err != nil { + return err + } + opts.PackageInferenceRoot = pkgInferenceRoot addLegacyFlagsFromArgs(&opts.LegacyFilter, args) + return nil } -// asFilterPatterns normalizes legacy selectors to filter syntax -func (l *LegacyFilter) asFilterPatterns() []string { +// AsFilterPatterns normalizes legacy selectors to filter syntax +func (l *LegacyFilter) AsFilterPatterns() []string { var patterns []string prefix := "" if !l.SkipDependents { @@ -131,7 +149,7 @@ func ResolvePackages(opts *Opts, repoRoot turbopath.AbsoluteSystemPath, scm scm. PackagesChangedInRange: opts.getPackageChangeFunc(scm, repoRoot, ctx), } filterPatterns := opts.FilterPatterns - legacyFilterPatterns := opts.LegacyFilter.asFilterPatterns() + legacyFilterPatterns := opts.LegacyFilter.AsFilterPatterns() filterPatterns = append(filterPatterns, legacyFilterPatterns...) isAllPackages := len(filterPatterns) == 0 && opts.PackageInferenceRoot == "" filteredPkgs, err := filterResolver.GetPackagesFromPatterns(filterPatterns) @@ -149,15 +167,11 @@ func ResolvePackages(opts *Opts, repoRoot turbopath.AbsoluteSystemPath, scm scm. return filteredPkgs, isAllPackages, nil } -func calculateInference(repoRoot turbopath.AbsoluteSystemPath, rawPkgInferenceDir string, packageInfos workspace.Catalog, logger hclog.Logger) (*scope_filter.PackageInference, error) { - if rawPkgInferenceDir == "" { +func calculateInference(repoRoot turbopath.AbsoluteSystemPath, pkgInferencePath turbopath.RelativeSystemPath, packageInfos workspace.Catalog, logger hclog.Logger) (*scope_filter.PackageInference, error) { + if pkgInferencePath == "" { // No inference specified, no need to calculate anything return nil, nil } - pkgInferencePath, err := turbopath.CheckedToRelativeSystemPath(rawPkgInferenceDir) - if err != nil { - return nil, err - } logger.Debug(fmt.Sprintf("Using %v as a basis for selecting packages", pkgInferencePath)) fullInferencePath := repoRoot.Join(pkgInferencePath) for _, pkgInfo := range packageInfos.PackageJSONs { diff --git a/cli/internal/scope/scope_test.go b/cli/internal/scope/scope_test.go index 0bfe1ac198e8f..216984dca3152 100644 --- a/cli/internal/scope/scope_test.go +++ b/cli/internal/scope/scope_test.go @@ -510,6 +510,10 @@ func TestResolvePackages(t *testing.T) { readLockfile := func(_rootPackageJSON *fs.PackageJSON, content []byte) (lockfile.Lockfile, error) { return tc.prevLockfile, nil } + pkgInferenceRoot, err := resolvePackageInferencePath(tc.inferPkgPath) + if err != nil { + t.Errorf("bad inference path (%v): %v", tc.inferPkgPath, err) + } pkgs, isAllPackages, err := ResolvePackages(&Opts{ LegacyFilter: LegacyFilter{ Entrypoints: tc.scope, @@ -519,7 +523,7 @@ func TestResolvePackages(t *testing.T) { }, IgnorePatterns: []string{tc.ignore}, GlobalDepPatterns: tc.globalDeps, - PackageInferenceRoot: tc.inferPkgPath, + PackageInferenceRoot: pkgInferenceRoot, }, root, scm, &context.Context{ WorkspaceInfos: workspaceInfos, WorkspaceNames: packageNames,