Skip to content

Commit 4d0694b

Browse files
authored
[shell] Remove bin wrappers 🎉 (#1584)
## Summary This removes bin wrappers. It also does the following: * Creates `refresh` and `refresh-global` aliases. * If environment is out of date, it prompts user to run the alias. * If alias is missing, it shows the ugly raw command. * If user has direnv active, we don't show message (because we refresh automatically) * We add aliases in two ways: 1) we add as part of `shellrc` for shell. 2) we add as part of shellenv for global, run, etc Caveats: * calling `refresh` causes environment to refresh including environment variables (plugin and devbox.json) * If user does not call refresh, it is possible some packages that require setup hooks will not work, but the binary is available and in PATH. ## How was it tested? On zsh, fish shells: * I installed `hello` globally, verified with `which hello` * Installed `hello` in shell, got refresh message. Did `which` again and verified still global * Did `refresh` and `which` again and verified correct. On global zsh: * Added `hello` and got `refresh-global` message. * Ran refresh and verified env was up to date. On direnv: * Installed `hello` and verified environment was up to date.
1 parent 5a94b6e commit 4d0694b

File tree

20 files changed

+141
-426
lines changed

20 files changed

+141
-426
lines changed

devbox.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,3 @@ func GlobalDataPath() (string, error) {
6565
func PrintEnvrcContent(w io.Writer, envFlags devopt.EnvFlags) error {
6666
return impl.PrintEnvrcContent(w, envFlags)
6767
}
68-
69-
// ExportifySystemPathWithoutWrappers reads $PATH, removes `virtenv/.wrappers/bin` paths,
70-
// and returns a string of the form `export PATH=....`
71-
//
72-
// This small utility function could have been inlined in the boxcli caller, but
73-
// needed the impl.exportify functionality. It does not depend on core-devbox.
74-
func ExportifySystemPathWithoutWrappers() string {
75-
return impl.ExportifySystemPathWithoutWrappers()
76-
}

devbox.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
],
77
"env": {
88
"GOENV": "off",
9-
"PATH": "$PATH:$PWD/dist"
9+
"PATH": "$PATH:$PWD/dist"
1010
},
1111
"shell": {
1212
"init_hook": [
@@ -20,9 +20,9 @@
2020
"GOOS=linux GOARCH=amd64 go build -o dist/devbox-linux-amd64 ./cmd/devbox",
2121
"GOOS=linux GOARCH=arm64 go build -o dist/devbox-linux-arm64 ./cmd/devbox"
2222
],
23-
"code": "code .",
24-
"lint": "golangci-lint run --timeout 5m && scripts/gofumpt.sh",
25-
"test": "go test -race -cover ./...",
23+
"code": "code .",
24+
"lint": "golangci-lint run --timeout 5m && scripts/gofumpt.sh",
25+
"test": "go test -race -cover ./...",
2626
"update-examples": "devbox run build && go run testscripts/testrunner/updater/main.go"
2727
}
2828
}

internal/boxcli/global.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ func globalCmd() *cobra.Command {
3434
}
3535

3636
shellEnv := shellEnvCmd(&globalShellEnvCmdFlags.recompute)
37-
shellEnv.Flags().BoolVar(
38-
&globalShellEnvCmdFlags.recompute, "recompute", false,
37+
shellEnv.Flags().BoolVarP(
38+
&globalShellEnvCmdFlags.recompute, "recompute", "r", false,
3939
"Recompute environment if needed",
4040
)
4141

internal/boxcli/root.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ func RootCmd() *cobra.Command {
7474
command.AddCommand(servicesCmd())
7575
command.AddCommand(setupCmd())
7676
command.AddCommand(shellCmd())
77-
// False to avoid recomputing the env in global shellenv:
78-
command.AddCommand(shellEnvCmd(lo.ToPtr(false)))
77+
// True to always recompute environment if needed.
78+
command.AddCommand(shellEnvCmd(lo.ToPtr(true)))
7979
command.AddCommand(updateCmd())
8080
command.AddCommand(versionCmd())
8181
// Preview commands

internal/boxcli/shellenv.go

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,6 @@ func shellEnvCmd(recomputeEnvIfNeeded *bool) *cobra.Command {
5858
flags.config.register(command)
5959
flags.envFlag.register(command)
6060

61-
command.AddCommand(shellEnvOnlyPathWithoutWrappersCmd())
62-
6361
return command
6462
}
6563

@@ -99,25 +97,3 @@ func shellEnvFunc(
9997

10098
return envStr, nil
10199
}
102-
103-
func shellEnvOnlyPathWithoutWrappersCmd() *cobra.Command {
104-
// Deprecated: will be removed after devbox 0.7.0
105-
// Don't add deprecated field to avoid printing anything to stdout
106-
command := &cobra.Command{
107-
Use: "only-path-without-wrappers",
108-
Hidden: true,
109-
Short: "[internal] Print shell command that exports the system $PATH without the bin-wrappers paths.",
110-
Args: cobra.ExactArgs(0),
111-
PreRunE: ensureNixInstalled,
112-
RunE: func(cmd *cobra.Command, args []string) error {
113-
s := shellEnvOnlyPathWithoutWrappersFunc()
114-
fmt.Fprintln(cmd.OutOrStdout(), s)
115-
return nil
116-
},
117-
}
118-
return command
119-
}
120-
121-
func shellEnvOnlyPathWithoutWrappersFunc() string {
122-
return devbox.ExportifySystemPathWithoutWrappers()
123-
}

internal/boxcli/version.go

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,14 @@ import (
99
"runtime"
1010

1111
"github.com/spf13/cobra"
12-
"go.jetpack.io/devbox/internal/wrapnix"
1312

1413
"go.jetpack.io/devbox/internal/build"
1514
"go.jetpack.io/devbox/internal/envir"
1615
"go.jetpack.io/devbox/internal/vercheck"
1716
)
1817

1918
type versionFlags struct {
20-
verbose bool
21-
updateDevboxSymlink bool
19+
verbose bool
2220
}
2321

2422
func versionCmd() *cobra.Command {
@@ -35,13 +33,6 @@ func versionCmd() *cobra.Command {
3533
command.Flags().BoolVarP(&flags.verbose, "verbose", "v", false, // value
3634
"displays additional version information",
3735
)
38-
// Make this flag hidden because:
39-
// This functionality doesn't strictly belong in this command, but we add it here
40-
// since `devbox version update` calls `devbox version -v` to trigger an update.
41-
command.Flags().BoolVarP(&flags.updateDevboxSymlink, "update-devbox-symlink", "u", false, // value
42-
"update the devbox symlink to point to the current binary",
43-
)
44-
_ = command.Flags().MarkHidden("update-devbox-symlink")
4536

4637
command.AddCommand(selfUpdateCmd())
4738
return command
@@ -71,13 +62,6 @@ func versionCmdFunc(cmd *cobra.Command, _ []string, flags versionFlags) error {
7162
fmt.Fprintf(w, "Go Version: %v\n", info.GoVersion)
7263
fmt.Fprintf(w, "Launcher: %v\n", info.LauncherVersion)
7364

74-
// TODO: in a subsequent PR, we should do this when flags.updateDevboxSymlink is true.
75-
// Not doing for now, since users who have Devbox binary prior to this edit
76-
// (before Devbox v0.5.9) will not invoke this flag in `devbox version update`.
77-
// But we still want this to run for them.
78-
if err := wrapnix.CreateDevboxSymlinkIfPossible(); err != nil {
79-
return err
80-
}
8165
} else {
8266
fmt.Fprintf(w, "%v\n", info.Version)
8367
}

internal/impl/devbox.go

Lines changed: 6 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"context"
99
"fmt"
1010
"io"
11-
"io/fs"
1211
"maps"
1312
"os"
1413
"os/exec"
@@ -30,7 +29,6 @@ import (
3029
"go.jetpack.io/devbox/internal/searcher"
3130
"go.jetpack.io/devbox/internal/shellgen"
3231
"go.jetpack.io/devbox/internal/telemetry"
33-
"go.jetpack.io/devbox/internal/wrapnix"
3432

3533
"go.jetpack.io/devbox/internal/boxcli/usererr"
3634
"go.jetpack.io/devbox/internal/cmdutil"
@@ -215,18 +213,6 @@ func (d *Devbox) RunScript(ctx context.Context, cmdName string, cmdArgs []string
215213
return err
216214
}
217215

218-
// By default we always remove bin wrappers when using `run`. This env var is
219-
// for testing. Once we completely remove bin wrappers we can remove this.
220-
// It helps simulate shell using "run".
221-
if includeBinWrappers, _ := strconv.ParseBool(
222-
os.Getenv("DEVBOX_INCLUDE_BIN_WRAPPERS_IN_PATH"),
223-
); !includeBinWrappers {
224-
env["PATH"] = envpath.RemoveFromPath(
225-
env["PATH"],
226-
wrapnix.WrapperBinPath(d.projectDir),
227-
)
228-
}
229-
230216
// Used to determine whether we're inside a shell (e.g. to prevent shell inception)
231217
// This is temporary because StartServices() needs it but should be replaced with
232218
// better alternative since devbox run and devbox shell are not the same.
@@ -258,9 +244,8 @@ func (d *Devbox) RunScript(ctx context.Context, cmdName string, cmdArgs []string
258244
return nix.RunScript(d.projectDir, strings.Join(cmdWithArgs, " "), env)
259245
}
260246

261-
// Install ensures that all the packages in the config are installed and
262-
// creates all wrappers, but does not run init hooks. It is used to power
263-
// devbox install cli command.
247+
// Install ensures that all the packages in the config are installed
248+
// but does not run init hooks. It is used to power devbox install cli command.
264249
func (d *Devbox) Install(ctx context.Context) error {
265250
ctx, task := trace.NewTask(ctx, "devboxInstall")
266251
defer task.End()
@@ -290,7 +275,7 @@ func (d *Devbox) NixEnv(ctx context.Context, opts devopt.NixEnvOpts) (string, er
290275
upToDate, _ := d.lockfile.IsUpToDateAndInstalled()
291276
if !upToDate {
292277
cmd := `eval "$(devbox global shellenv --recompute)"`
293-
if strings.HasSuffix(os.Getenv("SHELL"), "fish") {
278+
if isFishShell() {
294279
cmd = `devbox global shellenv --recompute | source`
295280
}
296281
ux.Finfo(
@@ -316,6 +301,8 @@ func (d *Devbox) NixEnv(ctx context.Context, opts devopt.NixEnvOpts) (string, er
316301
envStr = fmt.Sprintf("%s\n%s;\n", envStr, hooksStr)
317302
}
318303

304+
envStr += "\n" + d.refreshAlias()
305+
319306
return envStr, nil
320307
}
321308

@@ -863,7 +850,6 @@ func (d *Devbox) computeNixEnv(ctx context.Context, usePrintDevEnvCache bool) (m
863850
debug.Log("nix environment PATH is: %s", env)
864851

865852
// Add any vars defined in plugins.
866-
// TODO: Now that we have bin wrappers, this may can eventually be removed.
867853
// We still need to be able to add env variables to non-service binaries
868854
// (e.g. ruby). This would involve understanding what binaries are associated
869855
// to a given plugin.
@@ -875,7 +861,6 @@ func (d *Devbox) computeNixEnv(ctx context.Context, usePrintDevEnvCache bool) (m
875861
addEnvIfNotPreviouslySetByDevbox(env, pluginEnv)
876862

877863
env["PATH"] = envpath.JoinPathLists(
878-
filepath.Join(d.projectDir, plugin.WrapperBinPath),
879864
nix.ProfileBinPath(d.projectDir),
880865
env["PATH"],
881866
)
@@ -888,7 +873,7 @@ func (d *Devbox) computeNixEnv(ctx context.Context, usePrintDevEnvCache bool) (m
888873
// Add helpful env vars for a Devbox project
889874
env["DEVBOX_PROJECT_ROOT"] = d.projectDir
890875
env["DEVBOX_CONFIG_DIR"] = d.projectDir + "/devbox.d"
891-
env["DEVBOX_PACKAGES_DIR"] = d.projectDir + "/.devbox/virtenv/.wrappers"
876+
env["DEVBOX_PACKAGES_DIR"] = d.projectDir + "/" + nix.ProfilePath
892877

893878
// Include env variables in devbox.json
894879
configEnv, err := d.configEnvs(ctx, env)
@@ -1143,31 +1128,6 @@ func (d *Devbox) setCommonHelperEnvVars(env map[string]string) {
11431128
env["LIBRARY_PATH"] = envpath.JoinPathLists(profileLibDir, env["LIBRARY_PATH"])
11441129
}
11451130

1146-
// nixBins returns the paths to all the nix binaries that are installed by
1147-
// the flake. If there are conflicts, it returns the first one it finds of a
1148-
// give name. This matches how nix flakes behaves if there are conflicts in
1149-
// buildInputs
1150-
func (d *Devbox) nixBins(env map[string]string) ([]string, error) {
1151-
dirs := strings.Split(env["buildInputs"], " ")
1152-
bins := map[string]string{}
1153-
for _, dir := range dirs {
1154-
binPath := filepath.Join(dir, "bin")
1155-
if _, err := os.Stat(binPath); errors.Is(err, fs.ErrNotExist) {
1156-
continue
1157-
}
1158-
files, err := os.ReadDir(binPath)
1159-
if err != nil {
1160-
return nil, errors.WithStack(err)
1161-
}
1162-
for _, file := range files {
1163-
if _, alreadySet := bins[file.Name()]; !alreadySet {
1164-
bins[file.Name()] = filepath.Join(binPath, file.Name())
1165-
}
1166-
}
1167-
}
1168-
return lo.Values(bins), nil
1169-
}
1170-
11711131
func (d *Devbox) projectDirHash() string {
11721132
hash, _ := cuecfg.Hash(d.projectDir)
11731133
return hash
@@ -1217,24 +1177,6 @@ func (d *Devbox) parseEnvAndExcludeSpecialCases(currentEnv []string) (map[string
12171177
return env, nil
12181178
}
12191179

1220-
// ExportifySystemPathWithoutWrappers is a small utility to filter WrapperBin paths from PATH
1221-
func ExportifySystemPathWithoutWrappers() string {
1222-
path := []string{}
1223-
for _, p := range strings.Split(os.Getenv("PATH"), string(filepath.ListSeparator)) {
1224-
// Intentionally do not include projectDir with plugin.WrapperBinPath so that
1225-
// we filter out bin-wrappers for devbox-global and devbox-project.
1226-
if !strings.Contains(p, plugin.WrapperBinPath) {
1227-
path = append(path, p)
1228-
}
1229-
}
1230-
1231-
envs := map[string]string{
1232-
"PATH": strings.Join(path, string(filepath.ListSeparator)),
1233-
}
1234-
1235-
return exportify(envs)
1236-
}
1237-
12381180
func (d *Devbox) PluginManager() *plugin.Manager {
12391181
return d.pluginManager
12401182
}

internal/impl/packages.go

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"go.jetpack.io/devbox/internal/nix"
2626
"go.jetpack.io/devbox/internal/plugin"
2727
"go.jetpack.io/devbox/internal/ux"
28-
"go.jetpack.io/devbox/internal/wrapnix"
2928
)
3029

3130
// packages.go has functions for adding, removing and getting info about nix
@@ -225,11 +224,12 @@ func (d *Devbox) ensurePackagesAreInstalled(ctx context.Context, mode installMod
225224

226225
// if mode is install or uninstall, then we need to update the nix-profile
227226
// and lockfile, so we must continue below.
228-
if mode == ensure {
229-
// if mode is ensure, then we only continue if needed.
230-
if upToDate, err := d.lockfile.IsUpToDateAndInstalled(); err != nil || upToDate {
231-
return err
232-
}
227+
upToDate, err := d.lockfile.IsUpToDateAndInstalled()
228+
if err != nil {
229+
return err
230+
}
231+
// if mode is ensure and we are up to date, then we can skip the rest
232+
if mode == ensure && upToDate {
233233
fmt.Fprintln(d.stderr, "Ensuring packages are installed.")
234234
}
235235

@@ -256,38 +256,27 @@ func (d *Devbox) ensurePackagesAreInstalled(ctx context.Context, mode installMod
256256
return err
257257
}
258258

259-
// Use the printDevEnvCache if we are adding or removing or updating any package,
260-
// AND we are not in the shellenv-enabled environment of the current devbox-project.
261-
usePrintDevEnvCache := mode != ensure && !d.IsEnvEnabled()
262-
nixEnv, err := d.computeNixEnv(ctx, usePrintDevEnvCache)
263-
if err != nil {
264-
return err
265-
}
266-
267259
// Ensure we clean out packages that are no longer needed.
268260
d.lockfile.Tidy()
269261

270-
nixBins, err := d.nixBins(nixEnv)
271-
if err != nil {
272-
return err
273-
}
274-
275-
if err := wrapnix.CreateWrappers(ctx, wrapnix.CreateWrappersArgs{
276-
NixBins: nixBins,
277-
ProjectDir: d.projectDir,
278-
ShellEnvHash: nixEnv[d.shellEnvHashKey()],
279-
ShellEnvHashKey: d.shellEnvHashKey(),
280-
}); err != nil {
281-
return err
282-
}
283-
284262
// Update lockfile with new packages that are not to be installed
285263
for _, pkg := range d.configPackages() {
286264
if err := pkg.EnsureUninstallableIsInLockfile(); err != nil {
287265
return err
288266
}
289267
}
290268

269+
// If we're in a devbox shell (global or project), then the environment might
270+
// be out of date after the user installs something. If have direnv active
271+
// it should reload automatically so we don't need to refresh.
272+
if d.IsEnvEnabled() && !upToDate && !d.IsDirenvActive() {
273+
ux.Fwarning(
274+
d.stderr,
275+
"Your shell environment may be out of date. Run `%s` to update it.\n",
276+
d.refreshAliasOrCommand(),
277+
)
278+
}
279+
291280
return d.lockfile.Save()
292281
}
293282

internal/impl/pure_shell.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func findNixInPATH(env map[string]string) ([]string, error) {
4242
return nixBinsInPath, nil
4343
}
4444

45-
// Creates a symlink for devbox in .devbox/virtenv/.wrappers/bin
45+
// Creates a symlink for devbox in .devbox/bin
4646
// so that devbox can be available inside a pure shell
4747
func createDevboxSymlink(d *Devbox) error {
4848
// Get absolute path for where devbox is called

0 commit comments

Comments
 (0)