Skip to content

Commit

Permalink
internal/devbox: handle whitespace in paths (#2292)
Browse files Browse the repository at this point in the history
  • Loading branch information
gcurtis committed Sep 24, 2024
1 parent f8f5511 commit ac07204
Show file tree
Hide file tree
Showing 8 changed files with 47 additions and 11 deletions.
17 changes: 14 additions & 3 deletions internal/devbox/devbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,25 @@ 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)
}

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,
Expand All @@ -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...), " ")
}

Expand Down
2 changes: 1 addition & 1 deletion internal/devbox/shellrc.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion internal/devbox/shellrc_fish.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion internal/devbox/testdata/shellrc/basic/shellrc.golden
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion internal/devbox/testdata/shellrc/noshellrc/shellrc.golden
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 3 additions & 1 deletion internal/nix/nix.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down
6 changes: 3 additions & 3 deletions internal/shellgen/tmpl/script-wrapper.tmpl
Original file line number Diff line number Diff line change
@@ -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.

Expand All @@ -8,7 +8,7 @@
*/ -}}

if [ -z "${{ .SkipInitHookHash }}" ]; then
. {{ .InitHookPath }}
. "{{ .InitHookPath }}"
fi

{{ .Body }}
23 changes: 23 additions & 0 deletions testscripts/basic/path_whitespace.test.txt
Original file line number Diff line number Diff line change
@@ -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'"
}
}
}

0 comments on commit ac07204

Please sign in to comment.