Skip to content

Commit 9444bae

Browse files
authored
[services] Fix issue where env var is ignored (#1570)
## Summary Fixes #1565 The bugs: * env vars specified via `--env-file` (and possibly via other methods) are ignored in nginx config. * When inside a devbox shell or a direnv context, starting services would ignore env vars. Intermediate causes: * `envsubst` binary was a bin wrapped binary. This was resetting the env vars. Fix: Never use bin wrappers when inside a `devbox run` (This was previously only scoped to using the CLI command explicitly, but now we do it any time we use RunScript function) * Previously we would only run services in new shell if we were not already in a shell or direnv. But this causes us to ignore environment variables that are set after the environment is created due to bin wrappers. Fix: always run services in new shell. Root cause: * I'm not 100% sure what caused this. It's possible the [PATH stack PR](c11031a) did, but I don't fully understand the mechanism. I'm still confident the changes in this PR are good and simplify uncertainty about how services are called. Sorry in advance for all the boolean params. We should move to structs, but I didn't want to make those changes here. ## How was it tested? * Copied setup from #1565. * Tried changing port and restarting services. * Tested outside of devbox shell * Tested inside of devbox shell * Tested with direnv enabled * Tested with direnv disabled
1 parent c167ca7 commit 9444bae

File tree

10 files changed

+112
-58
lines changed

10 files changed

+112
-58
lines changed

devbox.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ type Devbox interface {
3434
Update(ctx context.Context, opts devopt.UpdateOpts) error
3535

3636
// Interact with services
37-
ListServices(ctx context.Context) error
38-
RestartServices(ctx context.Context, services ...string) error
37+
ListServices(ctx context.Context, runInCurrentShell bool) error
38+
RestartServices(ctx context.Context, runInCurrentShell bool, services ...string) error
3939
Services() (services.Services, error)
40-
StartProcessManager(ctx context.Context, requestedServices []string, background bool, processComposeFileOrDir string) error
41-
StartServices(ctx context.Context, services ...string) error
42-
StopServices(ctx context.Context, allProjects bool, services ...string) error
40+
StartProcessManager(ctx context.Context, runInCurrentShell bool, requestedServices []string, background bool, processComposeFileOrDir string) error
41+
StartServices(ctx context.Context, runInCurrentShell bool, services ...string) error
42+
StopServices(ctx context.Context, runInCurrentShell, allProjects bool, services ...string) error
4343

4444
// Generate files
4545
Generate(ctx context.Context) error

examples/stacks/lapp-stack/devbox.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
88
],
99
"env": {
10-
"PGPORT": "5432"
10+
"PGPORT": "5432",
11+
"PGHOST": "/tmp/devbox/lapp"
1112
},
1213
"shell": {
1314
"scripts": {
@@ -19,7 +20,6 @@
1920
"init_db": "initdb",
2021
"run_test": [
2122
"mkdir -p /tmp/devbox/lapp",
22-
"export PGHOST=/tmp/devbox/lapp",
2323
"initdb",
2424
"devbox services start",
2525
"echo 'sleep 1 second for the postgres server to initialize.' && sleep 1",

examples/stacks/lepp-stack/devbox.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"env": {
1010
"NGINX_WEB_PORT": "8089",
1111
"NGINX_WEB_ROOT": "../../../my_app",
12-
"PGPORT": "5433"
12+
"PGPORT": "5433",
13+
"PGHOST": "/tmp/devbox/lepp"
1314
},
1415
"shell": {
1516
"scripts": {
@@ -21,7 +22,7 @@
2122
"init_db": "initdb",
2223
"run_test": [
2324
"mkdir -p /tmp/devbox/lepp",
24-
"export PGHOST=/tmp/devbox/lepp",
25+
"rm -rf .devbox/virtenv/postgresql/data",
2526
"initdb",
2627
"devbox services up -b",
2728
"echo 'sleep 2 second for the postgres server to initialize.' && sleep 2",

examples/stacks/lepp-stack/devbox.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
},
2323
"nginx@latest": {
2424
"last_modified": "2023-09-04T16:24:30Z",
25-
"plugin_version": "0.0.3",
25+
"plugin_version": "0.0.4",
2626
"resolved": "github:NixOS/nixpkgs/3c15feef7770eb5500a4b8792623e2d6f598c9c1#nginx",
2727
"source": "devbox-search",
2828
"version": "1.24.0",

internal/boxcli/run.go

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ package boxcli
55

66
import (
77
"fmt"
8-
"os"
98
"slices"
10-
"strconv"
119
"strings"
1210

1311
"github.com/samber/lo"
@@ -99,19 +97,12 @@ func runScriptCmd(cmd *cobra.Command, args []string, flags runCmdFlags) error {
9997
return err
10098
}
10199

102-
omitBinWrappersFromPath := true
103-
// This is for testing. Once we completely remove bin wrappers we can remove
104-
// this. It helps simulate shell using "run".
105-
if ok, _ := strconv.ParseBool(os.Getenv("DEVBOX_INCLUDE_BIN_WRAPPERS_IN_PATH")); ok {
106-
omitBinWrappersFromPath = false
107-
}
108100
// Check the directory exists.
109101
box, err := devbox.Open(&devopt.Opts{
110-
Dir: path,
111-
Stderr: cmd.ErrOrStderr(),
112-
Pure: flags.pure,
113-
Env: env,
114-
OmitBinWrappersFromPath: omitBinWrappersFromPath,
102+
Dir: path,
103+
Stderr: cmd.ErrOrStderr(),
104+
Pure: flags.pure,
105+
Env: env,
115106
})
116107
if err != nil {
117108
return redact.Errorf("error reading devbox.json: %w", err)

internal/boxcli/services.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212

1313
type servicesCmdFlags struct {
1414
envFlag
15-
config configFlags
15+
config configFlags
16+
runInCurrentShell bool
1617
}
1718

1819
type serviceUpFlags struct {
@@ -47,7 +48,13 @@ func servicesCmd(persistentPreRunE ...cobraFunc) *cobra.Command {
4748
serviceStopFlags := serviceStopFlags{}
4849
servicesCommand := &cobra.Command{
4950
Use: "services",
50-
Short: "Interact with devbox services",
51+
Short: "Interact with devbox services.",
52+
Long: "Interact with devbox services. Services start in a new shell. " +
53+
"Plugin services use environment variables specified by plugin unless " +
54+
"overridden by the user. To override plugin environment variables, use " +
55+
"the --env or --env-file flag. You may also override in devbox.json by " +
56+
"using the `env` field or exporting an environment variable in the " +
57+
"init hook.",
5158
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
5259
preruns := append([]cobraFunc{ensureNixInstalled}, persistentPreRunE...)
5360
for _, fn := range preruns {
@@ -105,6 +112,13 @@ func servicesCmd(persistentPreRunE ...cobraFunc) *cobra.Command {
105112

106113
flags.envFlag.register(servicesCommand)
107114
flags.config.registerPersistent(servicesCommand)
115+
servicesCommand.PersistentFlags().BoolVar(
116+
&flags.runInCurrentShell,
117+
"run-in-current-shell",
118+
false,
119+
"Run the command in the current shell instead of a new shell",
120+
)
121+
servicesCommand.Flag("run-in-current-shell").Hidden = true
108122
serviceUpFlags.register(upCommand)
109123
serviceStopFlags.register(stopCommand)
110124
servicesCommand.AddCommand(lsCommand)
@@ -124,7 +138,7 @@ func listServices(cmd *cobra.Command, flags servicesCmdFlags) error {
124138
return errors.WithStack(err)
125139
}
126140

127-
return box.ListServices(cmd.Context())
141+
return box.ListServices(cmd.Context(), flags.runInCurrentShell)
128142
}
129143

130144
func startServices(cmd *cobra.Command, services []string, flags servicesCmdFlags) error {
@@ -141,7 +155,7 @@ func startServices(cmd *cobra.Command, services []string, flags servicesCmdFlags
141155
return errors.WithStack(err)
142156
}
143157

144-
return box.StartServices(cmd.Context(), services...)
158+
return box.StartServices(cmd.Context(), flags.runInCurrentShell, services...)
145159
}
146160

147161
func stopServices(
@@ -165,7 +179,8 @@ func stopServices(
165179
if len(services) > 0 && flags.allProjects {
166180
return errors.New("cannot use both services and --all-projects arguments simultaneously")
167181
}
168-
return box.StopServices(cmd.Context(), flags.allProjects, services...)
182+
return box.StopServices(
183+
cmd.Context(), servicesFlags.runInCurrentShell, flags.allProjects, services...)
169184
}
170185

171186
func restartServices(
@@ -186,7 +201,7 @@ func restartServices(
186201
return errors.WithStack(err)
187202
}
188203

189-
return box.RestartServices(cmd.Context(), services...)
204+
return box.RestartServices(cmd.Context(), flags.runInCurrentShell, services...)
190205
}
191206

192207
func startProcessManager(
@@ -209,5 +224,11 @@ func startProcessManager(
209224
return errors.WithStack(err)
210225
}
211226

212-
return box.StartProcessManager(cmd.Context(), args, flags.background, flags.processComposeFile)
227+
return box.StartProcessManager(
228+
cmd.Context(),
229+
servicesFlags.runInCurrentShell,
230+
args,
231+
flags.background,
232+
flags.processComposeFile,
233+
)
213234
}

internal/impl/devbox.go

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"go.jetpack.io/devbox/internal/searcher"
3131
"go.jetpack.io/devbox/internal/shellgen"
3232
"go.jetpack.io/devbox/internal/telemetry"
33+
"go.jetpack.io/devbox/internal/wrapnix"
3334

3435
"go.jetpack.io/devbox/internal/boxcli/usererr"
3536
"go.jetpack.io/devbox/internal/cmdutil"
@@ -67,7 +68,6 @@ type Devbox struct {
6768
pure bool
6869
allowInsecureAdds bool
6970
customProcessComposeFile string
70-
OmitBinWrappersFromPath bool
7171

7272
// This is needed because of the --quiet flag.
7373
stderr io.Writer
@@ -98,7 +98,6 @@ func Open(opts *devopt.Opts) (*Devbox, error) {
9898
pure: opts.Pure,
9999
customProcessComposeFile: opts.CustomProcessComposeFile,
100100
allowInsecureAdds: opts.AllowInsecureAdds,
101-
OmitBinWrappersFromPath: opts.OmitBinWrappersFromPath,
102101
}
103102

104103
lock, err := lock.GetFile(box)
@@ -221,6 +220,19 @@ func (d *Devbox) RunScript(ctx context.Context, cmdName string, cmdArgs []string
221220
if err != nil {
222221
return err
223222
}
223+
224+
// By default we always remove bin wrappers when using `run`. This env var is
225+
// for testing. Once we completely remove bin wrappers we can remove this.
226+
// It helps simulate shell using "run".
227+
if includeBinWrappers, _ := strconv.ParseBool(
228+
os.Getenv("DEVBOX_INCLUDE_BIN_WRAPPERS_IN_PATH"),
229+
); !includeBinWrappers {
230+
env["PATH"] = envpath.RemoveFromPath(
231+
env["PATH"],
232+
wrapnix.WrapperBinPath(d.projectDir),
233+
)
234+
}
235+
224236
// Used to determine whether we're inside a shell (e.g. to prevent shell inception)
225237
// This is temporary because StartServices() needs it but should be replaced with
226238
// better alternative since devbox run and devbox shell are not the same.
@@ -526,15 +538,22 @@ func (d *Devbox) Services() (services.Services, error) {
526538
return result, nil
527539
}
528540

529-
func (d *Devbox) StartServices(ctx context.Context, serviceNames ...string) error {
530-
if !d.IsEnvEnabled() {
531-
return d.RunScript(ctx, "devbox", append([]string{"services", "start"}, serviceNames...))
541+
func (d *Devbox) StartServices(
542+
ctx context.Context, runInCurrentShell bool, serviceNames ...string,
543+
) error {
544+
if !runInCurrentShell {
545+
return d.RunScript(ctx, "devbox",
546+
append(
547+
[]string{"services", "start", "--run-in-current-shell"},
548+
serviceNames...,
549+
),
550+
)
532551
}
533552

534553
if !services.ProcessManagerIsRunning(d.projectDir) {
535554
fmt.Fprintln(d.stderr, "Process-compose is not running. Starting it now...")
536555
fmt.Fprintln(d.stderr, "\nNOTE: We recommend using `devbox services up` to start process-compose and your services")
537-
return d.StartProcessManager(ctx, serviceNames, true, "")
556+
return d.StartProcessManager(ctx, runInCurrentShell, serviceNames, true, "")
538557
}
539558

540559
svcSet, err := d.Services()
@@ -563,9 +582,9 @@ func (d *Devbox) StartServices(ctx context.Context, serviceNames ...string) erro
563582
return nil
564583
}
565584

566-
func (d *Devbox) StopServices(ctx context.Context, allProjects bool, serviceNames ...string) error {
567-
if !d.IsEnvEnabled() {
568-
args := []string{"services", "stop"}
585+
func (d *Devbox) StopServices(ctx context.Context, runInCurrentShell, allProjects bool, serviceNames ...string) error {
586+
if !runInCurrentShell {
587+
args := []string{"services", "stop", "--run-in-current-shell"}
569588
args = append(args, serviceNames...)
570589
if allProjects {
571590
args = append(args, "--all-projects")
@@ -602,9 +621,10 @@ func (d *Devbox) StopServices(ctx context.Context, allProjects bool, serviceName
602621
return nil
603622
}
604623

605-
func (d *Devbox) ListServices(ctx context.Context) error {
606-
if !d.IsEnvEnabled() {
607-
return d.RunScript(ctx, "devbox", []string{"services", "ls"})
624+
func (d *Devbox) ListServices(ctx context.Context, runInCurrentShell bool) error {
625+
if !runInCurrentShell {
626+
return d.RunScript(ctx,
627+
"devbox", []string{"services", "ls", "--run-in-current-shell"})
608628
}
609629

610630
svcSet, err := d.Services()
@@ -640,15 +660,22 @@ func (d *Devbox) ListServices(ctx context.Context) error {
640660
return nil
641661
}
642662

643-
func (d *Devbox) RestartServices(ctx context.Context, serviceNames ...string) error {
644-
if !d.IsEnvEnabled() {
645-
return d.RunScript(ctx, "devbox", append([]string{"services", "restart"}, serviceNames...))
663+
func (d *Devbox) RestartServices(
664+
ctx context.Context, runInCurrentShell bool, serviceNames ...string,
665+
) error {
666+
if !runInCurrentShell {
667+
return d.RunScript(ctx, "devbox",
668+
append(
669+
[]string{"services", "restart", "--run-in-current-shell"},
670+
serviceNames...,
671+
),
672+
)
646673
}
647674

648675
if !services.ProcessManagerIsRunning(d.projectDir) {
649676
fmt.Fprintln(d.stderr, "Process-compose is not running. Starting it now...")
650677
fmt.Fprintln(d.stderr, "\nTip: We recommend using `devbox services up` to start process-compose and your services")
651-
return d.StartProcessManager(ctx, serviceNames, true, "")
678+
return d.StartProcessManager(ctx, runInCurrentShell, serviceNames, true, "")
652679
}
653680

654681
// TODO: Restart with no services should restart the _currently running_ services. This means we should get the list of running services from the process-compose, then restart them all.
@@ -674,12 +701,13 @@ func (d *Devbox) RestartServices(ctx context.Context, serviceNames ...string) er
674701

675702
func (d *Devbox) StartProcessManager(
676703
ctx context.Context,
704+
runInCurrentShell bool,
677705
requestedServices []string,
678706
background bool,
679707
processComposeFileOrDir string,
680708
) error {
681-
if !d.IsEnvEnabled() {
682-
args := []string{"services", "up"}
709+
if !runInCurrentShell {
710+
args := []string{"services", "up", "--run-in-current-shell"}
683711
args = append(args, requestedServices...)
684712
if processComposeFileOrDir != "" {
685713
args = append(args, "--process-compose-file", processComposeFileOrDir)
@@ -853,17 +881,11 @@ func (d *Devbox) computeNixEnv(ctx context.Context, usePrintDevEnvCache bool) (m
853881
addEnvIfNotPreviouslySetByDevbox(env, pluginEnv)
854882

855883
env["PATH"] = envpath.JoinPathLists(
884+
filepath.Join(d.projectDir, plugin.WrapperBinPath),
856885
nix.ProfileBinPath(d.projectDir),
857886
env["PATH"],
858887
)
859888

860-
if !d.OmitBinWrappersFromPath {
861-
env["PATH"] = envpath.JoinPathLists(
862-
filepath.Join(d.projectDir, plugin.WrapperBinPath),
863-
env["PATH"],
864-
)
865-
}
866-
867889
env["PATH"], err = d.addUtilitiesToPath(ctx, env["PATH"])
868890
if err != nil {
869891
return nil, err

internal/impl/devopt/devboxopts.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ type Opts struct {
1212
Pure bool
1313
IgnoreWarnings bool
1414
CustomProcessComposeFile string
15-
OmitBinWrappersFromPath bool
1615
Stderr io.Writer
1716
}
1817

internal/impl/envpath/pathlists.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package envpath
22

33
import (
4+
"os"
45
"path/filepath"
56
"strings"
67
)
@@ -35,3 +36,22 @@ func JoinPathLists(pathLists ...string) string {
3536
}
3637
return strings.Join(cleaned, string(filepath.ListSeparator))
3738
}
39+
40+
func RemoveFromPath(path, pathToRemove string) string {
41+
paths := filepath.SplitList(path)
42+
43+
// Create a new slice to store the modified paths
44+
var newPaths []string
45+
46+
// Iterate through the paths and add them to the newPaths slice if they are not equal to pathToRemove
47+
for _, p := range paths {
48+
if p != pathToRemove {
49+
newPaths = append(newPaths, p)
50+
}
51+
}
52+
53+
// Join the modified paths using ":" as the delimiter
54+
newPath := strings.Join(newPaths, string(os.PathListSeparator))
55+
56+
return newPath
57+
}

0 commit comments

Comments
 (0)