-
Notifications
You must be signed in to change notification settings - Fork 33
/
run_cmd.go
123 lines (105 loc) · 3.43 KB
/
run_cmd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package beta
import (
"context"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"go.uber.org/zap"
"github.com/stateful/runme/v3/internal/command"
"github.com/stateful/runme/v3/internal/config/autoconfig"
"github.com/stateful/runme/v3/internal/project"
"github.com/stateful/runme/v3/pkg/document"
)
func runCmd(*commonFlags) *cobra.Command {
cmd := cobra.Command{
Use: "run [command1 command2 ...]",
Aliases: []string{"exec"},
Short: "Run one or more commands.",
Long: `Run commands by providing their names delimited by space.
The names are interpreted as glob patterns.
In the case of multiple commands, they are executed one-by-one in the order they appear in the document.
The --category option additionally filters the list of tasks to execute by category.`,
Example: `Run all blocks starting with the "generate-" prefix:
runme beta run "generate-*"
Run all blocks from the "setup" and "teardown" categories:
runme beta run --category=setup,teardown
`,
RunE: func(cmd *cobra.Command, args []string) error {
return autoconfig.InvokeForCommand(
func(
cmdFactory command.Factory,
filters []project.Filter,
logger *zap.Logger,
proj *project.Project,
) error {
defer logger.Sync()
tasks, err := project.LoadTasks(cmd.Context(), proj)
if err != nil {
return err
}
logger.Info("found tasks", zap.Int("count", len(tasks)))
argsFilter, err := createProjectFilterFromPatterns(args)
if err != nil {
return err
}
filters = append(filters, argsFilter)
tasks, err = project.FilterTasksByFn(tasks, filters...)
if err != nil {
return err
}
logger.Info("filtered tasks by filters", zap.Int("count", len(tasks)))
if len(tasks) == 0 {
_, err := cmd.ErrOrStderr().Write([]byte("no tasks to run\n"))
return errors.WithStack(err)
}
session := command.NewSession()
options := getCommandOptions(cmd, session)
for _, t := range tasks {
err := runCodeBlock(cmd.Context(), t.CodeBlock, cmdFactory, options)
if err != nil {
return err
}
}
return nil
},
)
},
}
return &cmd
}
func getCommandOptions(
cmd *cobra.Command,
sess *command.Session,
) command.CommandOptions {
return command.CommandOptions{
Session: sess,
Stdin: cmd.InOrStdin(),
Stdout: cmd.OutOrStdout(),
Stderr: cmd.ErrOrStderr(),
}
}
func runCodeBlock(
ctx context.Context,
block *document.CodeBlock,
factory command.Factory,
options command.CommandOptions,
) error {
// TODO(adamb): [command.Config] is generated exclusively from the [document.CodeBlock].
// As we introduce some document- and block-related configs in runme.yaml (root but also nested),
// this [Command.Config] should be further extended.
//
// The way to do it is to use [config.Loader] and calling [config.Loader.FindConfigChain] with
// task's document path. It will produce all the configs that are relevant to the document.
// Next, they should be merged into a single [config.Config] in a correct order, starting from
// the last element of the returned config chain. Finally, [command.Config] should be updated.
// This algorithm should be likely encapsulated in the [internal/config] and [internal/command]
// packages.
cfg, err := command.NewProgramConfigFromCodeBlock(block)
if err != nil {
return err
}
cmd := factory.Build(cfg, options)
if err := cmd.Start(ctx); err != nil {
return err
}
return cmd.Wait()
}