Skip to content

Commit 8811ade

Browse files
authored
direnv integration - updating PATH (#213)
## Summary - Added a small command that updates the PATH to use devbox packages when users integration direnv and cd into a devbox project. - Added guidelines on how to setup `direnv` and integrate with devbox. ## How was it tested? - Have direnv installed and hooked to your shell - Create an empty directory - `devbox init` - `devbox shell` then `exit` - `touch .envrc` then put the following inside it: ```bash use_devbox() { watch_file devbox.json eval $(devbox setup-direnv) } use devbox ``` - `direnv allow` - Compare running `which python3` or `which go` from inside the directory and outside
1 parent be10969 commit 8811ade

File tree

4 files changed

+89
-20
lines changed

4 files changed

+89
-20
lines changed

boxcli/shell.go

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,42 +13,56 @@ import (
1313
"go.jetpack.io/devbox"
1414
)
1515

16+
type shellFlags struct {
17+
PrintEnv bool
18+
}
19+
1620
func ShellCmd() *cobra.Command {
21+
flags := &shellFlags{}
1722
command := &cobra.Command{
1823
Use: "shell [<dir>] -- [<cmd>]",
1924
Short: "Start a new shell or run a command with access to your packages",
2025
Long: "Start a new shell or run a command with access to your packages. \nIf invoked without `cmd`, this will start an interactive shell based on the devbox.json in your current directory, or the directory provided with `dir`. \nIf invoked with a `cmd`, this will start a shell based on the devbox.json provided in `dir`, run the command, and then exit.",
2126
Args: validateShellArgs,
2227
PersistentPreRunE: nixShellPersistentPreRunE,
23-
RunE: runShellCmd,
28+
RunE: runShellCmd(flags),
2429
}
30+
command.Flags().BoolVar(
31+
&flags.PrintEnv, "print-env", false, "Print script to setup shell environment")
2532
return command
2633
}
2734

28-
func runShellCmd(cmd *cobra.Command, args []string) error {
29-
path, cmds := parseShellArgs(cmd, args)
35+
func runShellCmd(flags *shellFlags) runFunc {
36+
return func(cmd *cobra.Command, args []string) error {
37+
path, cmds := parseShellArgs(cmd, args)
3038

31-
// Check the directory exists.
32-
box, err := devbox.Open(path, os.Stdout)
33-
if err != nil {
34-
return errors.WithStack(err)
35-
}
39+
// Check the directory exists.
40+
box, err := devbox.Open(path, os.Stdout)
41+
if err != nil {
42+
return errors.WithStack(err)
43+
}
3644

37-
if devbox.IsDevboxShellEnabled() {
38-
return errors.New("You are already in an active devbox shell.\nRun 'exit' before calling devbox shell again. Shell inception is not supported.")
39-
}
45+
if flags.PrintEnv {
46+
// return here to prevent opening a devbox shell
47+
return box.PrintShellEnv()
48+
}
4049

41-
if len(cmds) > 0 {
42-
err = box.Exec(cmds...)
43-
} else {
44-
err = box.Shell()
45-
}
50+
if devbox.IsDevboxShellEnabled() {
51+
return errors.New("You are already in an active devbox shell.\nRun 'exit' before calling devbox shell again. Shell inception is not supported.")
52+
}
53+
54+
if len(cmds) > 0 {
55+
err = box.Exec(cmds...)
56+
} else {
57+
err = box.Shell()
58+
}
4659

47-
var exitErr *exec.ExitError
48-
if errors.As(err, &exitErr) {
49-
return nil
60+
var exitErr *exec.ExitError
61+
if errors.As(err, &exitErr) {
62+
return nil
63+
}
64+
return err
5065
}
51-
return err
5266
}
5367

5468
func nixShellPersistentPreRunE(cmd *cobra.Command, args []string) error {

devbox.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,17 @@ func (d *Devbox) Exec(cmds ...string) error {
207207
return nix.Exec(nixDir, cmds)
208208
}
209209

210+
func (d *Devbox) PrintShellEnv() error {
211+
profileBinDir, err := d.profileBinDir()
212+
if err != nil {
213+
return errors.WithStack(err)
214+
}
215+
// TODO: For now we just updated the PATH but this may need to evolve
216+
// to essentially a parsed shellrc.tmpl
217+
fmt.Fprintf(d.writer, "export PATH=\"%s:$PATH\"", profileBinDir)
218+
return nil
219+
}
220+
210221
// saveCfg writes the config file to the devbox directory.
211222
func (d *Devbox) saveCfg() error {
212223
cfgPath := filepath.Join(d.srcDir, configFilename)

docs/app/docs/cli_reference/devbox_shell.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ devbox shell [<dir>] -- [<cmd>] [flags]
1515
## Options
1616

1717
```text
18+
--print-env Print a script to setup a devbox shell environment
1819
-h, --help help for shell
1920
```
2021

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
title: direnv configuration
3+
---
4+
5+
6+
## direnv
7+
___
8+
[direnv](https://direnv.net) is an open source environment management tool that allows setting unique environment variables per directory in your file system. This guide covers how to configure direnv to seemlessly work with a devbox project.
9+
10+
### Prerequisites
11+
* Install direnv and hook it to your shell. Follow [this guide](https://direnv.net/#basic-installation) if you haven't done it.
12+
13+
### Setting up with Devbox Shell and direnv
14+
15+
Note: If you already have a devbox project you may skip to step 3.
16+
17+
1. `devbox init` if you don't have a devbox.json in the root directory of your project.
18+
2. `devbox shell -- 'ls'` to activate devbox shell temporarily and make sure dependencies mentioned in your devbox.json are installed.
19+
3. Create a new file, name it `.envrc` and put the following snippet inside it:
20+
```bash
21+
use_devbox() {
22+
watch_file devbox.json
23+
eval $(devbox shell --print-env)
24+
}
25+
use devbox
26+
```
27+
4. Run `direnv allow` to give permission to `direnv` to setup your environment variables.
28+
5. At this point, your project directory is setup so that every time you `cd` into it, the binaries from your devbox shell will be used. To test this, you can compare running `which python3` from your project directory and outside.
29+
30+
### Global settings for direnv
31+
32+
Note that every time changes are made to `devbox.json` via `devbox add ...`, `devbox rm ...` or directly editing the file, requires `direnv allow` to run so that `direnv` can setup the new changes.
33+
34+
Alternatively, a project directory can be whitelisted so that changes will be automatically picked up by `direnv`. This is done by adding following snippet to direnv config file typically at `~/.config/direnv/direnv.toml`. You can create the file and directory if it doesn't exist.
35+
36+
```toml
37+
[whitelist]
38+
prefix = [ "/absolute/path/to/project" ]
39+
40+
```
41+
<!-- TODO: add steps for vscode integration -->
42+
43+
If this guide is missing something, feel free to contribute by opening a [pull request](https://github.com/jetpack-io/devbox/pulls) in Github.

0 commit comments

Comments
 (0)