diff --git a/examples/development/elixir/elixir_hello/devbox.lock b/examples/development/elixir/elixir_hello/devbox.lock index 0a47a5dd787..cf8992c4229 100644 --- a/examples/development/elixir/elixir_hello/devbox.lock +++ b/examples/development/elixir/elixir_hello/devbox.lock @@ -1,54 +1,71 @@ { "lockfile_version": "1", "packages": { + "darwin.apple_sdk.frameworks.CoreServices": { + "resolved": "github:NixOS/nixpkgs/3a05eebede89661660945da1f151959900903b6a?narHash=sha256-Ly2fBL1LscV%2BKyCqPRufUBuiw%2BzmWrlJzpWOWbahplg%3D#darwin.apple_sdk.frameworks.CoreServices", + "source": "nixpkg", + "systems": { + "aarch64-darwin": { + "outputs": [ + { + "path": "/nix/store/mpq140x7nsx9gz73h4nfp9kpp297mshh-CoreServices-11.0", + "default": true + } + ] + } + } + }, "elixir@latest": { - "last_modified": "2024-11-28T07:51:56Z", + "last_modified": "2024-12-27T03:08:00Z", "plugin_version": "0.0.1", - "resolved": "github:NixOS/nixpkgs/226216574ada4c3ecefcbbec41f39ce4655f78ef#elixir", + "resolved": "github:NixOS/nixpkgs/7cc0bff31a3a705d3ac4fdceb030a17239412210#elixir", "source": "devbox-search", - "version": "1.17.3", + "version": "1.18.1", "systems": { "aarch64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/91w79z55qsjkhnbs3a21l3h27va98mf6-elixir-1.17.3", + "path": "/nix/store/hdd9x34p0gplcl1bramq80lqi76xrd87-elixir-1.18.1", "default": true } ], - "store_path": "/nix/store/91w79z55qsjkhnbs3a21l3h27va98mf6-elixir-1.17.3" + "store_path": "/nix/store/hdd9x34p0gplcl1bramq80lqi76xrd87-elixir-1.18.1" }, "aarch64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/pz4hk0sp4zj76waaprlfdmvc4xdblz55-elixir-1.17.3", + "path": "/nix/store/a4g29icpil9b1hsniqspz5k110h4df8v-elixir-1.18.1", "default": true } ], - "store_path": "/nix/store/pz4hk0sp4zj76waaprlfdmvc4xdblz55-elixir-1.17.3" + "store_path": "/nix/store/a4g29icpil9b1hsniqspz5k110h4df8v-elixir-1.18.1" }, "x86_64-darwin": { "outputs": [ { "name": "out", - "path": "/nix/store/gnjg57wv71svvqw7s3rxyjc6lkps2r95-elixir-1.17.3", + "path": "/nix/store/b3h6f36zd4gjivb76lvvs6a9bi1mq9q8-elixir-1.18.1", "default": true } ], - "store_path": "/nix/store/gnjg57wv71svvqw7s3rxyjc6lkps2r95-elixir-1.17.3" + "store_path": "/nix/store/b3h6f36zd4gjivb76lvvs6a9bi1mq9q8-elixir-1.18.1" }, "x86_64-linux": { "outputs": [ { "name": "out", - "path": "/nix/store/rx7qr8bar4qldx6yg3njvm8hn84d3yyk-elixir-1.17.3", + "path": "/nix/store/3zd200bq28mv3pdbii7rijzmb0gmhhs3-elixir-1.18.1", "default": true } ], - "store_path": "/nix/store/rx7qr8bar4qldx6yg3njvm8hn84d3yyk-elixir-1.17.3" + "store_path": "/nix/store/3zd200bq28mv3pdbii7rijzmb0gmhhs3-elixir-1.18.1" } } + }, + "github:NixOS/nixpkgs/nixpkgs-unstable": { + "resolved": "github:NixOS/nixpkgs/3a05eebede89661660945da1f151959900903b6a?lastModified=1740547748&narHash=sha256-Ly2fBL1LscV%2BKyCqPRufUBuiw%2BzmWrlJzpWOWbahplg%3D" } } } diff --git a/internal/boxcli/run.go b/internal/boxcli/run.go index 6be62594bd7..9391338f46e 100644 --- a/internal/boxcli/run.go +++ b/internal/boxcli/run.go @@ -7,12 +7,15 @@ import ( "fmt" "log/slog" "slices" + "sort" "strings" + "github.com/pkg/errors" "github.com/samber/lo" "github.com/spf13/cobra" "github.com/spf13/pflag" + "go.jetify.com/devbox/internal/boxcli/multi" "go.jetify.com/devbox/internal/boxcli/usererr" "go.jetify.com/devbox/internal/devbox" "go.jetify.com/devbox/internal/devbox/devopt" @@ -27,6 +30,7 @@ type runCmdFlags struct { pure bool listScripts bool recomputeEnv bool + allProjects bool } // runFlagDefaults are the flag default values that differ @@ -65,6 +69,12 @@ func runCmd(defaults runFlagDefaults) *cobra.Command { ) _ = command.Flags().MarkHidden("omit-nix-env") command.Flags().BoolVar(&flags.recomputeEnv, "recompute", true, "recompute environment if needed") + command.Flags().BoolVar( + &flags.allProjects, + "all-projects", + false, + "run command in all projects in the working directory, recursively. If command is not found in any project, it will be skipped.", + ) command.ValidArgs = listScripts(command, flags) @@ -72,17 +82,31 @@ func runCmd(defaults runFlagDefaults) *cobra.Command { } func listScripts(cmd *cobra.Command, flags runCmdFlags) []string { - box, err := devbox.Open(&devopt.Opts{ + devboxOpts := &devopt.Opts{ Dir: flags.config.path, Environment: flags.config.environment, Stderr: cmd.ErrOrStderr(), IgnoreWarnings: true, - }) + } + + if flags.allProjects { + boxes, err := multi.Open(devboxOpts) + if err != nil { + slog.Error("failed to open devbox", "err", err) + return nil + } + scripts := []string{} + for _, box := range boxes { + scripts = append(scripts, box.ListScripts()...) + } + sort.Strings(scripts) + return lo.Uniq(scripts) + } + box, err := devbox.Open(devboxOpts) if err != nil { slog.Error("failed to open devbox", "err", err) return nil } - return box.ListScripts() } @@ -112,15 +136,25 @@ func runScriptCmd(cmd *cobra.Command, args []string, flags runCmdFlags) error { return err } - // Check the directory exists. - box, err := devbox.Open(&devopt.Opts{ + boxes := []*devbox.Devbox{} + devboxOpts := &devopt.Opts{ Dir: path, Env: env, Environment: flags.config.environment, Stderr: cmd.ErrOrStderr(), - }) - if err != nil { - return redact.Errorf("error reading devbox.json: %w", err) + } + + if flags.allProjects { + boxes, err = multi.Open(devboxOpts) + if err != nil { + return errors.WithStack(err) + } + } else { + box, err := devbox.Open(devboxOpts) + if err != nil { + return redact.Errorf("error reading devbox.json: %w", err) + } + boxes = append(boxes, box) } envOpts := devopt.EnvOptions{ @@ -140,8 +174,23 @@ func runScriptCmd(cmd *cobra.Command, args []string, flags runCmdFlags) error { Pure: flags.pure, SkipRecompute: !flags.recomputeEnv, } - if err := box.RunScript(ctx, envOpts, script, scriptArgs); err != nil { - return redact.Errorf("error running script %q in Devbox: %w", script, err) + + if flags.allProjects { + boxes = lo.Filter(boxes, func(box *devbox.Devbox, _ int) bool { + return slices.Contains(box.ListScripts(), script) + }) + } + + for _, box := range boxes { + ux.Finfof( + cmd.ErrOrStderr(), + "Running script %q on %s\n", + script, + box.ProjectDir(), + ) + if err := box.RunScript(ctx, envOpts, script, scriptArgs); err != nil { + return redact.Errorf("error running script %q in Devbox: %w", script, err) + } } return nil } diff --git a/internal/devbox/devbox.go b/internal/devbox/devbox.go index 8d8a952a8ba..e90dab2bb64 100644 --- a/internal/devbox/devbox.go +++ b/internal/devbox/devbox.go @@ -165,8 +165,9 @@ func Open(opts *devopt.Opts) (*Devbox, error) { } ux.Fwarningf( os.Stderr, // Always stderr. box.writer should probably always be err. - "Your devbox.json contains packages in legacy format. "+ + "Your devbox.json at %s contains packages in legacy format. "+ "Please run `devbox %supdate` to update your devbox.json.\n", + box.projectDir, lo.Ternary(box.projectDir == globalPath, "global ", ""), ) }