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