Skip to content

Commit 0eda044

Browse files
authored
[shellenv] Remove nix profile from path, add shellenv command (#667)
## Summary This removes nix profile from path when using unified environment. It adds a new shellenv command and prints instructions when package is added inside devbox shell. @gcurtis I chose `eval $(devbox shellenv)` over `. <(devbox shellenv)` to be consistent with the global instructions and also because it's easy to miss the period when we print it. Not sure if there's a difference. Also is this fish compatible or do we need different instructions for fish? Lastly, you'll notice `hash -r` is not there anymore. In my experiments running `eval $(devbox shellenv)` causes the hash table to clear. I believe this happens because `PATH` has changed, but I'm not 100% sure. ## How was it tested? <img width="762" alt="image" src="https://user-images.githubusercontent.com/544948/220529645-42a6578f-e733-4943-9478-bb415a904a40.png"> ```bash ➜ devbox shell ➜ devbox add php82 php82Extensions.memcached ➜ which php php not found ➜ eval $(devbox shellenv) ➜ which php /nix/store/5yylkc71wmlzii2vl40021vgbfwqmajb-php-with-extensions-8.2.1/bin/php ➜ php -m | grep memcached memcached ```
1 parent 0fdee09 commit 0eda044

File tree

13 files changed

+114
-27
lines changed

13 files changed

+114
-27
lines changed

devbox.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type Devbox interface {
3131
GenerateEnvrc(force bool, source string) error
3232
Info(pkg string, markdown bool) error
3333
ListScripts() []string
34-
PrintEnv() (string, error)
34+
PrintEnv(setFullPath bool) (string, error)
3535
PrintGlobalList() error
3636
PullGlobal(path string) error
3737
// Remove removes Nix packages from the config so that it no longer exists in

devbox.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"build-linux": "GOOS=linux go build -o dist/devbox-linux cmd/devbox/main.go",
1414
"build-linux-amd64": "GOOS=linux GOARCH=amd64 go build -o dist/devbox-linux-amd64 cmd/devbox/main.go",
1515
"code": "code .",
16-
"lint": "golangci-lint run"
16+
"lint": "golangci-lint run",
17+
"test": "go test -race -cover ./..."
1718
}
1819
},
1920
"nixpkgs": {

internal/boxcli/global.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ func globalPullCmd() *cobra.Command {
103103
func globalShellenvCmd() *cobra.Command {
104104
return &cobra.Command{
105105
Use: "shellenv",
106-
Short: "Print shell commands that add Devbox packages to your PATH",
106+
Short: "Print shell commands that add global Devbox packages to your PATH",
107107
Run: func(*cobra.Command, []string) {
108108
fmt.Print(impl.GenerateShellEnv())
109109
},

internal/boxcli/root.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func RootCmd() *cobra.Command {
5151
command.AddCommand(ServicesCmd())
5252
command.AddCommand(SetupCmd())
5353
command.AddCommand(ShellCmd())
54+
command.AddCommand(shellEnvCmd())
5455
command.AddCommand(VersionCmd())
5556
command.AddCommand(genDocsCmd())
5657

internal/boxcli/shell.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ func runShellCmd(cmd *cobra.Command, args []string, flags shellCmdFlags) error {
6565
}
6666

6767
if flags.PrintEnv {
68-
script, err := box.PrintEnv()
68+
script, err := box.PrintEnv(false)
6969
if err != nil {
7070
return err
7171
}

internal/boxcli/shellenv.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2022 Jetpack Technologies Inc and contributors. All rights reserved.
2+
// Use of this source code is governed by the license in the LICENSE file.
3+
4+
package boxcli
5+
6+
import (
7+
"fmt"
8+
9+
"github.com/spf13/cobra"
10+
"go.jetpack.io/devbox"
11+
)
12+
13+
type shellEnvCmdFlags struct {
14+
config configFlags
15+
}
16+
17+
func shellEnvCmd() *cobra.Command {
18+
flags := shellEnvCmdFlags{}
19+
command := &cobra.Command{
20+
Use: "shellenv",
21+
Short: "Print shell commands that add Devbox packages to your PATH",
22+
Args: cobra.ExactArgs(0),
23+
PreRunE: ensureNixInstalled,
24+
RunE: func(cmd *cobra.Command, args []string) error {
25+
s, err := shellEnvFunc(cmd, flags)
26+
if err != nil {
27+
return err
28+
}
29+
fmt.Fprintln(cmd.OutOrStdout(), s)
30+
return nil
31+
},
32+
}
33+
34+
flags.config.register(command)
35+
return command
36+
}
37+
38+
func shellEnvFunc(cmd *cobra.Command, flags shellEnvCmdFlags) (string, error) {
39+
box, err := devbox.Open(flags.config.path, cmd.ErrOrStderr())
40+
if err != nil {
41+
return "", err
42+
}
43+
44+
return box.PrintEnv(true)
45+
}

internal/impl/devbox.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ func (d *Devbox) Exec(cmds ...string) error {
443443
}
444444
}
445445

446-
func (d *Devbox) PrintEnv() (string, error) {
446+
func (d *Devbox) PrintEnv(setFullPath bool) (string, error) {
447447
script := ""
448448
if featureflag.UnifiedEnv.Disabled() {
449449
envs, err := plugin.Env(d.packages(), d.projectDir)
@@ -455,7 +455,7 @@ func (d *Devbox) PrintEnv() (string, error) {
455455
}
456456
return script, nil
457457
}
458-
envs, err := d.computeNixEnv(false)
458+
envs, err := d.computeNixEnv(setFullPath)
459459
if err != nil {
460460
return "", err
461461
}
@@ -760,8 +760,7 @@ func (d *Devbox) printPackageUpdateMessage(
760760
// 1. Start with the PATH as defined by nix (through nix print-dev-env).
761761
// 2. Clean the host PATH of any nix paths.
762762
// 3. Append the cleaned host PATH (tradeoff between reproducibility and ease of use).
763-
// 4. Prepend the devbox-managed nix profile path (which is needed to support devbox add inside shell--can we do without it?).
764-
// 5. Prepend the paths of any plugins (tbd whether it's actually needed).
763+
// 4. Prepend the paths of any plugins (tbd whether it's actually needed).
765764
func (d *Devbox) computeNixEnv(setFullPath bool) (map[string]string, error) {
766765

767766
vaf, err := nix.PrintDevEnv(d.nixShellFilePath(), d.nixFlakesFilePath())
@@ -826,18 +825,14 @@ func (d *Devbox) computeNixEnv(setFullPath bool) (map[string]string, error) {
826825

827826
// PATH handling.
828827
pluginVirtenvPath := d.pluginVirtenvPath() // TODO: consider removing this; not being used?
829-
nixProfilePath, err := d.profileBinPath()
830-
if err != nil {
831-
return nil, err
832-
}
833828
nixPath := env["PATH"]
834829
hostPath := nix.CleanEnvPath(os.Getenv("PATH"), os.Getenv("NIX_PROFILES"))
835830

836831
// NOTE: for devbox shell, we need to defer the PATH setting, because a user's init file may prepend
837832
// stuff to PATH, which will then take precedence over the devbox-set PATH. Instead, we do the path
838833
// prepending in shellrc.tmpl. I chose to use the `setFullPath` variable instead of something like
839834
// `isShell` to discourage the addition of more logic that makes shell/run differ more.
840-
pathPrepend := fmt.Sprintf("%s:%s:%s", pluginVirtenvPath, nixProfilePath, nixPath)
835+
pathPrepend := fmt.Sprintf("%s:%s", pluginVirtenvPath, nixPath)
841836
if setFullPath {
842837
env["PATH"] = fmt.Sprintf("%s:%s", pathPrepend, hostPath)
843838
} else {

internal/plugin/info.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/fatih/color"
1111
"github.com/pkg/errors"
1212
"github.com/samber/lo"
13+
"go.jetpack.io/devbox/internal/boxcli/featureflag"
1314
)
1415

1516
func PrintReadme(
@@ -134,6 +135,14 @@ func printInfoInstructions(pkg string, w io.Writer) error {
134135
}
135136

136137
func PrintEnvUpdateMessage(pkgs []string, projectDir string, w io.Writer) error {
138+
if featureflag.UnifiedEnv.Enabled() {
139+
color.New(color.FgYellow).Fprint(
140+
w,
141+
"\nTo update your shell and ensure your new packages are usable, "+
142+
"please run:\n\neval $(devbox shellenv)\n\n\n",
143+
)
144+
return nil
145+
}
137146
commands := []string{"hash -r"}
138147
for _, pkg := range pkgs {
139148
if path := getEnvFilePathIfExist(pkg, projectDir); path != "" {

testscripts/run/path.test.txt

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,10 @@ stdout '/some/clean/path'
2222
# Path contains plugin virtual env path
2323
stdout '.devbox/virtenv/bin'
2424

25-
# Path contains devbox-managed nix profile path
26-
stdout '.devbox/nix/profile/default/bin'
27-
2825
# Path contains path to installed nix packages
2926
stdout '-which-'
3027

31-
# Verify PATH is set in correct order: virtual env path, nix profile, nix packages, host path.
32-
path.order '.devbox/virtenv/bin' '.devbox/nix/profile/default/bin' '-which-' 'some/clean/path'
28+
# Verify PATH is set in correct order: virtual env path nix packages, host path.
29+
path.order '.devbox/virtenv/bin' '-which-' 'some/clean/path'
3330

34-
# TODO: verify that bashrc file prepends do not prepend before nix paths.
31+
# TODO: verify that bashrc file prepends do not prepend before nix paths.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
exec devbox init
2+
3+
# test adding and running hello
4+
exec devbox add hello
5+
! exec hello
6+
! stdout .
7+
8+
# source shellenv and test again
9+
exec devbox shellenv
10+
source.path
11+
exec hello
12+
stdout 'Hello, world!'

0 commit comments

Comments
 (0)