@@ -9,10 +9,12 @@ import (
9
9
"slices"
10
10
"strings"
11
11
12
+ "github.com/pkg/errors"
12
13
"github.com/samber/lo"
13
14
"github.com/spf13/cobra"
14
15
"github.com/spf13/pflag"
15
16
17
+ "go.jetify.com/devbox/internal/boxcli/multi"
16
18
"go.jetify.com/devbox/internal/boxcli/usererr"
17
19
"go.jetify.com/devbox/internal/devbox"
18
20
"go.jetify.com/devbox/internal/devbox/devopt"
@@ -27,6 +29,7 @@ type runCmdFlags struct {
27
29
pure bool
28
30
listScripts bool
29
31
recomputeEnv bool
32
+ allProjects bool
30
33
}
31
34
32
35
// runFlagDefaults are the flag default values that differ
@@ -65,24 +68,43 @@ func runCmd(defaults runFlagDefaults) *cobra.Command {
65
68
)
66
69
_ = command .Flags ().MarkHidden ("omit-nix-env" )
67
70
command .Flags ().BoolVar (& flags .recomputeEnv , "recompute" , true , "recompute environment if needed" )
71
+ command .Flags ().BoolVar (
72
+ & flags .allProjects ,
73
+ "all-projects" ,
74
+ false ,
75
+ "run command in all projects in the working directory, recursively. If command is not found in any project, it will be skipped." ,
76
+ )
68
77
69
78
command .ValidArgs = listScripts (command , flags )
70
79
71
80
return command
72
81
}
73
82
74
83
func listScripts (cmd * cobra.Command , flags runCmdFlags ) []string {
75
- box , err := devbox . Open ( & devopt.Opts {
84
+ devboxOpts := & devopt.Opts {
76
85
Dir : flags .config .path ,
77
86
Environment : flags .config .environment ,
78
87
Stderr : cmd .ErrOrStderr (),
79
88
IgnoreWarnings : true ,
80
- })
89
+ }
90
+
91
+ if flags .allProjects {
92
+ boxes , err := multi .Open (devboxOpts )
93
+ if err != nil {
94
+ slog .Error ("failed to open devbox" , "err" , err )
95
+ return nil
96
+ }
97
+ scripts := []string {}
98
+ for _ , box := range boxes {
99
+ scripts = append (scripts , box .ListScripts ()... )
100
+ }
101
+ return scripts
102
+ }
103
+ box , err := devbox .Open (devboxOpts )
81
104
if err != nil {
82
105
slog .Error ("failed to open devbox" , "err" , err )
83
106
return nil
84
107
}
85
-
86
108
return box .ListScripts ()
87
109
}
88
110
@@ -112,15 +134,25 @@ func runScriptCmd(cmd *cobra.Command, args []string, flags runCmdFlags) error {
112
134
return err
113
135
}
114
136
115
- // Check the directory exists.
116
- box , err := devbox . Open ( & devopt.Opts {
137
+ boxes := [] * devbox. Devbox {}
138
+ devboxOpts := & devopt.Opts {
117
139
Dir : path ,
118
140
Env : env ,
119
141
Environment : flags .config .environment ,
120
142
Stderr : cmd .ErrOrStderr (),
121
- })
122
- if err != nil {
123
- return redact .Errorf ("error reading devbox.json: %w" , err )
143
+ }
144
+
145
+ if flags .allProjects {
146
+ boxes , err = multi .Open (devboxOpts )
147
+ if err != nil {
148
+ return errors .WithStack (err )
149
+ }
150
+ } else {
151
+ box , err := devbox .Open (devboxOpts )
152
+ if err != nil {
153
+ return redact .Errorf ("error reading devbox.json: %w" , err )
154
+ }
155
+ boxes = append (boxes , box )
124
156
}
125
157
126
158
envOpts := devopt.EnvOptions {
@@ -140,8 +172,23 @@ func runScriptCmd(cmd *cobra.Command, args []string, flags runCmdFlags) error {
140
172
Pure : flags .pure ,
141
173
SkipRecompute : ! flags .recomputeEnv ,
142
174
}
143
- if err := box .RunScript (ctx , envOpts , script , scriptArgs ); err != nil {
144
- return redact .Errorf ("error running script %q in Devbox: %w" , script , err )
175
+
176
+ if flags .allProjects {
177
+ boxes = lo .Filter (boxes , func (box * devbox.Devbox , _ int ) bool {
178
+ return slices .Contains (box .ListScripts (), script )
179
+ })
180
+ }
181
+
182
+ for _ , box := range boxes {
183
+ ux .Finfof (
184
+ cmd .ErrOrStderr (),
185
+ "Running script %q on %s\n " ,
186
+ script ,
187
+ box .ProjectDir (),
188
+ )
189
+ if err := box .RunScript (ctx , envOpts , script , scriptArgs ); err != nil {
190
+ return redact .Errorf ("error running script %q in Devbox: %w" , script , err )
191
+ }
145
192
}
146
193
return nil
147
194
}
0 commit comments