diff --git a/internal/devbox/devbox.go b/internal/devbox/devbox.go index f9ee29d96fc..9676bd63fbc 100644 --- a/internal/devbox/devbox.go +++ b/internal/devbox/devbox.go @@ -270,7 +270,16 @@ func (d *Devbox) RunScript(ctx context.Context, envOpts devopt.EnvOptions, cmdNa // better alternative since devbox run and devbox shell are not the same. env["DEVBOX_SHELL_ENABLED"] = "1" - // wrap the arg in double-quotes, and escape any double-quotes inside it + // wrap the arg in double-quotes, and escape any double-quotes inside it. + // + // TODO(gcurtis): this breaks quote-removal in parameter expansion, + // command substitution, and arithmetic expansion: + // + // $ unset x + // $ echo ${x:-"my file"} + // my file + // $ devbox run -- echo '${x:-"my file"}' + // "my file" for idx, arg := range cmdArgs { cmdArgs[idx] = strconv.Quote(arg) } @@ -278,7 +287,8 @@ func (d *Devbox) RunScript(ctx context.Context, envOpts devopt.EnvOptions, cmdNa var cmdWithArgs []string if _, ok := d.cfg.Scripts()[cmdName]; ok { // it's a script, so replace the command with the script file's path. - cmdWithArgs = append([]string{shellgen.ScriptPath(d.ProjectDir(), cmdName)}, cmdArgs...) + script := shellgen.ScriptPath(d.ProjectDir(), cmdName) + cmdWithArgs = append([]string{strconv.Quote(script)}, cmdArgs...) } else { // Arbitrary commands should also run the hooks, so we write them to a file as well. However, if the // command args include env variable evaluations, then they'll be evaluated _before_ the hooks run, @@ -293,7 +303,8 @@ func (d *Devbox) RunScript(ctx context.Context, envOpts devopt.EnvOptions, cmdNa if err != nil { return err } - cmdWithArgs = []string{shellgen.ScriptPath(d.ProjectDir(), arbitraryCmdFilename)} + script := shellgen.ScriptPath(d.ProjectDir(), arbitraryCmdFilename) + cmdWithArgs = []string{strconv.Quote(script)} env["DEVBOX_RUN_CMD"] = strings.Join(append([]string{cmdName}, cmdArgs...), " ") } diff --git a/internal/devbox/shellrc.tmpl b/internal/devbox/shellrc.tmpl index 6f0e5f521a8..44b4780d19a 100644 --- a/internal/devbox/shellrc.tmpl +++ b/internal/devbox/shellrc.tmpl @@ -56,7 +56,7 @@ working_dir="$(pwd)" cd "{{ .ProjectDir }}" || exit # Source the hooks file, which contains the project's init hooks and plugin hooks. -. {{ .HooksFilePath }} +. "{{ .HooksFilePath }}" cd "$working_dir" || exit diff --git a/internal/devbox/shellrc_fish.tmpl b/internal/devbox/shellrc_fish.tmpl index a50b3fb2ea6..26ecfb7eaf2 100644 --- a/internal/devbox/shellrc_fish.tmpl +++ b/internal/devbox/shellrc_fish.tmpl @@ -59,7 +59,7 @@ set workingDir (pwd) cd "{{ .ProjectDir }}" || exit # Source the hooks file, which contains the project's init hooks and plugin hooks. -source {{ .HooksFilePath }} +source "{{ .HooksFilePath }}" cd "$workingDir" || exit diff --git a/internal/devbox/testdata/shellrc/basic/shellrc.golden b/internal/devbox/testdata/shellrc/basic/shellrc.golden index 9e110a4a68d..19e5813c67f 100644 --- a/internal/devbox/testdata/shellrc/basic/shellrc.golden +++ b/internal/devbox/testdata/shellrc/basic/shellrc.golden @@ -21,7 +21,7 @@ working_dir="$(pwd)" cd "/path/to/projectDir" || exit # Source the hooks file, which contains the project's init hooks and plugin hooks. -. /path/to/projectDir/.devbox/gen/scripts/.hooks.sh +. "/path/to/projectDir/.devbox/gen/scripts/.hooks.sh" cd "$working_dir" || exit diff --git a/internal/devbox/testdata/shellrc/noshellrc/shellrc.golden b/internal/devbox/testdata/shellrc/noshellrc/shellrc.golden index e7ac3d7ba06..f21ece857f6 100644 --- a/internal/devbox/testdata/shellrc/noshellrc/shellrc.golden +++ b/internal/devbox/testdata/shellrc/noshellrc/shellrc.golden @@ -15,7 +15,7 @@ working_dir="$(pwd)" cd "/path/to/projectDir" || exit # Source the hooks file, which contains the project's init hooks and plugin hooks. -. /path/to/projectDir/.devbox/gen/scripts/.hooks.sh +. "/path/to/projectDir/.devbox/gen/scripts/.hooks.sh" cd "$working_dir" || exit diff --git a/internal/nix/nix.go b/internal/nix/nix.go index 8104354f0eb..6af93f01018 100644 --- a/internal/nix/nix.go +++ b/internal/nix/nix.go @@ -23,6 +23,7 @@ import ( "go.jetpack.io/devbox/internal/boxcli/featureflag" "go.jetpack.io/devbox/internal/boxcli/usererr" "go.jetpack.io/devbox/internal/redact" + "go.jetpack.io/devbox/nix/flake" "golang.org/x/mod/semver" "go.jetpack.io/devbox/internal/debug" @@ -73,13 +74,14 @@ func (*Nix) PrintDevEnv(ctx context.Context, args *PrintDevEnvArgs) (*PrintDevEn if err != nil { return nil, errors.WithStack(err) } + ref := flake.Ref{Type: flake.TypePath, Path: flakeDirResolved} if len(data) == 0 { cmd := command("print-dev-env", "--json") if featureflag.ImpurePrintDevEnv.Enabled() { cmd.Args = append(cmd.Args, "--impure") } - cmd.Args = append(cmd.Args, "path:"+flakeDirResolved) + cmd.Args = append(cmd.Args, ref) slog.Debug("running print-dev-env cmd", "cmd", cmd) data, err = cmd.Output(ctx) if insecure, insecureErr := IsExitErrorInsecurePackage(err, "" /*pkgName*/, "" /*installable*/); insecure { diff --git a/internal/shellgen/tmpl/script-wrapper.tmpl b/internal/shellgen/tmpl/script-wrapper.tmpl index 1ddd4c4c491..de191c431be 100644 --- a/internal/shellgen/tmpl/script-wrapper.tmpl +++ b/internal/shellgen/tmpl/script-wrapper.tmpl @@ -1,5 +1,5 @@ -{{/* - This wraps user scripts in devbox.json. The idea is to only run the init +{{/* + This wraps user scripts in devbox.json. The idea is to only run the init hooks once, even if the init hook calls devbox run again. This will also protect against using devbox service in the init hook. @@ -8,7 +8,7 @@ */ -}} if [ -z "${{ .SkipInitHookHash }}" ]; then - . {{ .InitHookPath }} + . "{{ .InitHookPath }}" fi {{ .Body }} diff --git a/testscripts/basic/path_whitespace.test.txt b/testscripts/basic/path_whitespace.test.txt new file mode 100644 index 00000000000..a52941a006f --- /dev/null +++ b/testscripts/basic/path_whitespace.test.txt @@ -0,0 +1,23 @@ +# Test that Devbox handles whitespace in project paths. + +mkdir 'my project' +cd 'my project' + +exec devbox run -- hello +stdout 'Hello, world!' + +exec devbox run -- touch 'file1 with spaces' +exists 'file1 with spaces' + +exec devbox run test +exists 'file2 with spaces' + +-- my project/devbox.json -- +{ + "packages": ["hello@latest"], + "shell": { + "scripts": { + "test": "touch 'file2 with spaces'" + } + } +}