From b537359c280ada92ae39d00652b09bbe3fb6cfd1 Mon Sep 17 00:00:00 2001 From: nikitulb Date: Tue, 11 Jan 2022 16:03:40 +0300 Subject: [PATCH] add exclude scope option for cli --- cli/internal/run/run.go | 35 +++++++++++++++++ cli/internal/run/run_test.go | 73 +++++++++++++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 2 deletions(-) diff --git a/cli/internal/run/run.go b/cli/internal/run/run.go index d403c2ad71575..a4962fbb9d1ff 100644 --- a/cli/internal/run/run.go +++ b/cli/internal/run/run.go @@ -66,6 +66,7 @@ Options: --help Show this message. --scope Specify package(s) to act as entry points for task execution. Supports globs. + --exclude-scope Specify package(s) to remove from scope. --cache-dir Specify local filesystem cache directory. (default "./node_modules/.cache/turbo") --concurrency Limit the concurrency of task execution. Use 1 for @@ -195,6 +196,16 @@ func (c *RunCommand) Run(args []string) int { } } + //Exclude packages + if len(runOptions.excludeScope) > 0 { + err = removePackages(ctx, runOptions.excludeScope) + + if err != nil { + c.logError(c.Config.Logger, "", fmt.Errorf("Invalid exclude scope: %w", err)) + return 1 + } + } + // Scoped packages // Unwind scope globs scopePkgs, err := getScopedPackages(ctx, runOptions.scope) @@ -668,6 +679,8 @@ type RunOptions struct { globalDeps []string // Filtered list of package entrypoints scope []string + // Exclude from scope + excludeScope []string // Force execution to be serially one-at-a-time concurrency int // Whether to execute in parallel (defaults to false) @@ -730,6 +743,10 @@ func parseRunArgs(args []string, cwd string) (*RunOptions, error) { if len(arg[len("--scope="):]) > 0 { runOptions.scope = append(runOptions.scope, arg[len("--scope="):]) } + case strings.HasPrefix(arg, "--exclude-scope="): + if len(arg[len("--exclude-scope="):]) > 0 { + runOptions.excludeScope = append(runOptions.excludeScope, arg[len("--exclude-scope="):]) + } case strings.HasPrefix(arg, "--ignore="): if len(arg[len("--ignore="):]) > 0 { runOptions.ignore = append(runOptions.ignore, arg[len("--ignore="):]) @@ -842,6 +859,24 @@ func getScopedPackages(ctx *context.Context, scopePatterns []string) (scopePkgs return scopedPkgs, nil } +func removePackages(ctx *context.Context, excludePatterns []string) (err error) { + var packages []string + + glob, err := filter.Compile(excludePatterns) + if err != nil { + return err + } + for _, f := range ctx.PackageNames { + if !glob.Match(f) { + packages = append(packages, f) + } + } + + ctx.PackageNames = packages + + return nil +} + // logError logs an error and outputs it to the UI. func (c *RunCommand) logError(log hclog.Logger, prefix string, err error) { log.Error(prefix, "error", err) diff --git a/cli/internal/run/run_test.go b/cli/internal/run/run_test.go index ffa03a8226d9e..9c529e7fde387 100644 --- a/cli/internal/run/run_test.go +++ b/cli/internal/run/run_test.go @@ -66,6 +66,23 @@ func TestParseConfig(t *testing.T) { cacheFolder: filepath.FromSlash("node_modules/.cache/turbo"), }, }, + { + "scope", + []string{"foo", "--exclude-scope=foo", "--exclude-scope=blah"}, + &RunOptions{ + includeDependents: true, + stream: true, + bail: true, + dotGraph: "", + concurrency: 10, + includeDependencies: false, + cache: true, + forceExecution: false, + profile: "", + excludeScope: []string{"foo", "blah"}, + cacheFolder: filepath.FromSlash("node_modules/.cache/turbo"), + }, + }, { "concurrency", []string{"foo", "--concurrency=12"}, @@ -150,7 +167,7 @@ func TestScopedPackages(t *testing.T) { cases := []struct { Name string Ctx *context.Context - Patttern []string + Pattern []string Expected util.Set }{ { @@ -189,7 +206,7 @@ func TestScopedPackages(t *testing.T) { for i, tc := range cases { t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { - actual, err := getScopedPackages(tc.Ctx, tc.Patttern) + actual, err := getScopedPackages(tc.Ctx, tc.Pattern) if err != nil { t.Fatalf("invalid scope parse: %#v", err) } @@ -197,3 +214,55 @@ func TestScopedPackages(t *testing.T) { }) } } + +func TestExcludePackages(t *testing.T) { + cases := []struct { + Name string + Ctx *context.Context + Pattern []string + Expected []string + }{ + { + "starts with @", + &context.Context{ + PackageNames: []string{"@sample/app", "sample-app", "jared"}, + }, + []string{"@sample/*"}, + []string{"sample-app", "jared"}, + }, + { + "return an array of matches", + &context.Context{ + PackageNames: []string{"foo", "bar", "baz"}, + }, + []string{"f*"}, + []string{"bar", "baz"}, + }, + { + "return an array of matches", + &context.Context{ + PackageNames: []string{"foo", "bar", "baz"}, + }, + []string{"f*", "bar"}, + []string{"baz"}, + }, + { + "return matches in the order the list were defined", + &context.Context{ + PackageNames: []string{"foo", "bar", "baz"}, + }, + []string{"*a*", "!f*"}, + []string{"foo"}, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { + err := removePackages(tc.Ctx, tc.Pattern) + if err != nil { + t.Fatalf("invalid scope parse: %#v", err) + } + assert.EqualValues(t, tc.Expected, tc.Ctx.PackageNames) + }) + } +}