Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for --only flag that matches lerna/pnpm run behavior #174

Merged
merged 2 commits into from
Dec 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions cli/internal/core/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ type SchedulerExecutionOptions struct {
Concurrency int
// Parallel is whether to run tasks in parallel
Parallel bool
// Restrict execution to only the listed task names
TasksOnly bool
}

// Execute executes the pipeline, constructing an internal task graph and walking it accordlingly.
Expand All @@ -78,7 +80,7 @@ func (p *scheduler) Prepare(options *SchedulerExecutionOptions) error {

p.Parallel = options.Parallel

if err := p.generateTaskGraph(pkgs, tasks, true); err != nil {
if err := p.generateTaskGraph(pkgs, tasks, options.TasksOnly); err != nil {
return err
}

Expand Down Expand Up @@ -110,7 +112,7 @@ func (p *scheduler) Execute() []error {
})
}

func (p *scheduler) generateTaskGraph(scope []string, targets []string, targetsOnly bool) error {
func (p *scheduler) generateTaskGraph(scope []string, taskNames []string, tasksOnly bool) error {
if p.PackageTaskDeps == nil {
p.PackageTaskDeps = [][]string{}
}
Expand All @@ -122,7 +124,7 @@ func (p *scheduler) generateTaskGraph(scope []string, targets []string, targetsO
traversalQueue := []string{}

for _, pkg := range scope {
for _, target := range targets {
for _, target := range taskNames {
traversalQueue = append(traversalQueue, GetTaskId(pkg, target))
}
}
Expand All @@ -141,12 +143,16 @@ func (p *scheduler) generateTaskGraph(scope []string, targets []string, targetsO
visited.Add(taskId)
deps := task.Deps

if targetsOnly {
if tasksOnly {
deps = deps.Filter(func(d interface{}) bool {
for _, target := range targets {
if dag.VertexName(d) == target {
return true
}
for _, target := range taskNames {
return fmt.Sprintf("%v", d) == target
}
return false
})
task.TopoDeps = task.TopoDeps.Filter(func(d interface{}) bool {
for _, target := range taskNames {
return fmt.Sprintf("%v", d) == target
}
return false
})
Expand Down
98 changes: 94 additions & 4 deletions cli/internal/core/scheduler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/pyr-sh/dag"
)

func TestSchedulerAddTask(t *testing.T) {
func TestSchedulerDefault(t *testing.T) {
var g dag.AcyclicGraph
g.Add("a")
g.Add("b")
Expand Down Expand Up @@ -61,6 +61,7 @@ func TestSchedulerAddTask(t *testing.T) {
TaskNames: []string{"test"},
Concurrency: 10,
Parallel: false,
TasksOnly: false,
})

if err != nil {
Expand All @@ -74,23 +75,112 @@ func TestSchedulerAddTask(t *testing.T) {
}

actual := strings.TrimSpace(p.TaskGraph.String())
expected := strings.TrimSpace(leafString)
expected := strings.TrimSpace(leafStringAll)
if actual != expected {
t.Fatalf("bad: \n\nactual---\n%s\n\n expected---\n%s", actual, expected)
}
}

const leafString = `
func TestSchedulerTasksOnly(t *testing.T) {
var g dag.AcyclicGraph
g.Add("a")
g.Add("b")
g.Add("c")
g.Connect(dag.BasicEdge("c", "b"))
g.Connect(dag.BasicEdge("c", "a"))

p := NewScheduler(&g)
topoDeps := make(util.Set)
topoDeps.Add("build")
deps := make(util.Set)
deps.Add("prepare")
p.AddTask(&Task{
Name: "build",
TopoDeps: topoDeps,
Deps: deps,
Run: func(cwd string) error {
fmt.Println(cwd)
return nil
},
})
p.AddTask(&Task{
Name: "test",
TopoDeps: topoDeps,
Deps: deps,
Run: func(cwd string) error {
fmt.Println(cwd)
return nil
},
})
p.AddTask(&Task{
Name: "prepare",
Run: func(cwd string) error {
fmt.Println(cwd)
return nil
},
})

if _, ok := p.Tasks["build"]; !ok {
t.Fatal("AddTask is not adding tasks (build)")
}

if _, ok := p.Tasks["test"]; !ok {
t.Fatal("AddTask is not adding tasks (test)")
}

err := p.Prepare(&SchedulerExecutionOptions{
Packages: nil,
TaskNames: []string{"test"},
Concurrency: 10,
Parallel: false,
TasksOnly: true,
})

if err != nil {
t.Fatalf("%v", err)
}

errs := p.Execute()

for _, err := range errs {
t.Fatalf("%v", err)
}

actual := strings.TrimSpace(p.TaskGraph.String())
expected := strings.TrimSpace(leafStringOnly)
if actual != expected {
t.Fatalf("bad: \n\nactual---\n%s\n\n expected---\n%s", actual, expected)
}
}

const leafStringAll = `
___ROOT___
a#build
a#prepare
a#prepare
___ROOT___
a#test
___ROOT___
a#prepare
b#build
b#prepare
b#prepare
___ROOT___
b#test
b#prepare
c#prepare
___ROOT___
c#test
a#build
b#build
c#prepare
`

const leafStringOnly = `
___ROOT___
a#test
___ROOT___
b#test
___ROOT___
c#test
___ROOT___
`
6 changes: 6 additions & 0 deletions cli/internal/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ func (c *RunCommand) Run(args []string) int {
TaskNames: ctx.Targets.UnsafeListOfStrings(),
Concurrency: runOptions.concurrency,
Parallel: runOptions.parallel,
TasksOnly: runOptions.only,
}); err != nil {
c.Ui.Error(fmt.Sprintf("Error preparing engine: %s", err))
return 1
Expand Down Expand Up @@ -692,6 +693,8 @@ type RunOptions struct {
// Immediately exit on task failure
bail bool
passThroughArgs []string
// Restrict execution to only the listed task names. Default false
only bool
}

func getDefaultRunOptions() *RunOptions {
Expand All @@ -706,6 +709,7 @@ func getDefaultRunOptions() *RunOptions {
profile: "", // empty string does no tracing
forceExecution: false,
stream: true,
only: false,
}
}

Expand Down Expand Up @@ -789,6 +793,8 @@ func parseRunArgs(args []string, cwd string) (*RunOptions, error) {
}
case strings.HasPrefix(arg, "--includeDependencies"):
runOptions.ancestors = true
case strings.HasPrefix(arg, "--only"):
runOptions.only = true
case strings.HasPrefix(arg, "--team"):
case strings.HasPrefix(arg, "--token"):
default:
Expand Down
31 changes: 31 additions & 0 deletions docs/pages/docs/reference/command-line-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,37 @@ turbo run build --no-cache
turbo run dev --parallel --no-cache
```

#### `--only`

Default false. Restricts execution to only include specified tasks. This is very similar to how how `lerna` or `pnpm` run tasks by default.

Given this pipeline:

```json
{
"turbo": {
"pipeline": {
"build": {
"dependsOn": [
"^build"
]
},
"test": {
"dependsOn": [
"^build"
]
}
}
}
}
```

```shell
turbo run test --only
```

Will execute _only_ the `test` tasks in each package. It will not `build`.

#### `--parallel`

Default `false`. Run commands in parallel across packages and apps and ignore the dependency graph. This is useful for developing with live reloading.
Expand Down