Skip to content

Commit 1573ae8

Browse files
authored
[Devbox] introduce devopt.EnvOptions (#2159)
## Summary From the EnvOptions docblock: ``` // EnvOptions configure the Devbox Environment in the `computeEnv` function. // - These options are commonly set by flags in some Devbox commands // like `shellenv`, `shell` and `run`. // - The struct is designed for the "common case" to be zero-initialized as `EnvOptions{}`. ``` This gets rid of the pseudo-global state in the `Devbox` struct where we were setting `pure`, `preservePathStack` and `omitNixEnv` values. ## How was it tested? TODO: - [x] CICD should pass - [x] Ensure `devbox global` and `devbox` omitNixEnv works as expected - [x] Ensure `pure` works as expected for `devbox shell --pure`.
1 parent f0a562e commit 1573ae8

File tree

7 files changed

+80
-59
lines changed

7 files changed

+80
-59
lines changed

internal/boxcli/run.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ func listScripts(cmd *cobra.Command, flags runCmdFlags) []string {
7373
Dir: flags.config.path,
7474
Environment: flags.config.environment,
7575
Stderr: cmd.ErrOrStderr(),
76-
Pure: flags.pure,
7776
IgnoreWarnings: true,
7877
})
7978
if err != nil {
@@ -114,15 +113,17 @@ func runScriptCmd(cmd *cobra.Command, args []string, flags runCmdFlags) error {
114113
Dir: path,
115114
Environment: flags.config.environment,
116115
Stderr: cmd.ErrOrStderr(),
117-
OmitNixEnv: flags.omitNixEnv,
118-
Pure: flags.pure,
119116
Env: env,
120117
})
121118
if err != nil {
122119
return redact.Errorf("error reading devbox.json: %w", err)
123120
}
124121

125-
if err := box.RunScript(cmd.Context(), script, scriptArgs); err != nil {
122+
envOpts := devopt.EnvOptions{
123+
OmitNixEnv: flags.omitNixEnv,
124+
Pure: flags.pure,
125+
}
126+
if err := box.RunScript(cmd.Context(), envOpts, script, scriptArgs); err != nil {
126127
return redact.Errorf("error running script %q in Devbox: %w", script, err)
127128
}
128129
return nil

internal/boxcli/shell.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ func runShellCmd(cmd *cobra.Command, flags shellCmdFlags) error {
6969
Dir: flags.config.path,
7070
Env: env,
7171
Environment: flags.config.environment,
72-
OmitNixEnv: flags.omitNixEnv,
73-
Pure: flags.pure,
7472
Stderr: cmd.ErrOrStderr(),
7573
})
7674
if err != nil {
@@ -93,7 +91,10 @@ func runShellCmd(cmd *cobra.Command, flags shellCmdFlags) error {
9391
return shellInceptionErrorMsg("devbox shell")
9492
}
9593

96-
return box.Shell(cmd.Context())
94+
return box.Shell(cmd.Context(), devopt.EnvOptions{
95+
OmitNixEnv: flags.omitNixEnv,
96+
Pure: flags.pure,
97+
})
9798
}
9899

99100
func shellInceptionErrorMsg(cmdPath string) error {

internal/boxcli/shellenv.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,10 @@ func shellEnvFunc(
9595
return "", err
9696
}
9797
box, err := devbox.Open(&devopt.Opts{
98-
Dir: flags.config.path,
99-
Environment: flags.config.environment,
100-
OmitNixEnv: flags.omitNixEnv,
101-
Stderr: cmd.ErrOrStderr(),
102-
PreservePathStack: flags.preservePathStack,
103-
Pure: flags.pure,
104-
Env: env,
98+
Dir: flags.config.path,
99+
Environment: flags.config.environment,
100+
Stderr: cmd.ErrOrStderr(),
101+
Env: env,
105102
})
106103
if err != nil {
107104
return "", err
@@ -115,8 +112,13 @@ func shellEnvFunc(
115112

116113
envStr, err := box.EnvExports(cmd.Context(), devopt.EnvExportsOpts{
117114
DontRecomputeEnvironment: !flags.recomputeEnv,
118-
NoRefreshAlias: flags.noRefreshAlias,
119-
RunHooks: flags.runInitHook,
115+
EnvOptions: devopt.EnvOptions{
116+
OmitNixEnv: flags.omitNixEnv,
117+
PreservePathStack: flags.preservePathStack,
118+
Pure: flags.pure,
119+
},
120+
NoRefreshAlias: flags.noRefreshAlias,
121+
RunHooks: flags.runInitHook,
120122
})
121123
if err != nil {
122124
return "", err

internal/devbox/devbox.go

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,11 @@ const (
5959
type Devbox struct {
6060
cfg *devconfig.Config
6161
env map[string]string
62-
omitNixEnv bool
6362
environment string
6463
lockfile *lock.File
6564
nix nix.Nixer
6665
projectDir string
6766
pluginManager *plugin.Manager
68-
preservePathStack bool
69-
pure bool
7067
customProcessComposeFile string
7168

7269
// This is needed because of the --quiet flag.
@@ -98,14 +95,11 @@ func Open(opts *devopt.Opts) (*Devbox, error) {
9895
box := &Devbox{
9996
cfg: cfg,
10097
env: opts.Env,
101-
omitNixEnv: opts.OmitNixEnv,
10298
environment: environment,
10399
nix: &nix.Nix{},
104100
projectDir: projectDir,
105101
pluginManager: plugin.NewManager(),
106102
stderr: opts.Stderr,
107-
preservePathStack: opts.PreservePathStack,
108-
pure: opts.Pure,
109103
customProcessComposeFile: opts.CustomProcessComposeFile,
110104
}
111105

@@ -200,11 +194,11 @@ func (d *Devbox) Generate(ctx context.Context) error {
200194
return errors.WithStack(shellgen.GenerateForPrintEnv(ctx, d))
201195
}
202196

203-
func (d *Devbox) Shell(ctx context.Context) error {
197+
func (d *Devbox) Shell(ctx context.Context, envOpts devopt.EnvOptions) error {
204198
ctx, task := trace.NewTask(ctx, "devboxShell")
205199
defer task.End()
206200

207-
envs, err := d.ensureStateIsUpToDateAndComputeEnv(ctx)
201+
envs, err := d.ensureStateIsUpToDateAndComputeEnv(ctx, envOpts)
208202
if err != nil {
209203
return err
210204
}
@@ -228,15 +222,22 @@ func (d *Devbox) Shell(ctx context.Context) error {
228222
WithShellStartTime(telemetry.ShellStart()),
229223
}
230224

231-
shell, err := NewDevboxShell(d, opts...)
225+
shell, err := NewDevboxShell(d, envOpts, opts...)
232226
if err != nil {
233227
return err
234228
}
235229

236230
return shell.Run()
237231
}
238232

239-
func (d *Devbox) RunScript(ctx context.Context, cmdName string, cmdArgs []string) error {
233+
// runDevboxServicesScript invokes RunScript with the envOptions set to the appropriate
234+
// defaults for the `devbox services` scenario.
235+
// TODO: move to services.go
236+
func (d *Devbox) runDevboxServicesScript(ctx context.Context, cmdArgs []string) error {
237+
return d.RunScript(ctx, devopt.EnvOptions{}, "devbox", cmdArgs)
238+
}
239+
240+
func (d *Devbox) RunScript(ctx context.Context, envOpts devopt.EnvOptions, cmdName string, cmdArgs []string) error {
240241
ctx, task := trace.NewTask(ctx, "devboxRun")
241242
defer task.End()
242243

@@ -256,7 +257,7 @@ func (d *Devbox) RunScript(ctx context.Context, cmdName string, cmdArgs []string
256257
env[d.SkipInitHookEnvName()] = "true"
257258
} else {
258259
var err error
259-
env, err = d.ensureStateIsUpToDateAndComputeEnv(ctx)
260+
env, err = d.ensureStateIsUpToDateAndComputeEnv(ctx, envOpts)
260261
if err != nil {
261262
return err
262263
}
@@ -341,9 +342,9 @@ func (d *Devbox) EnvExports(ctx context.Context, opts devopt.EnvExportsOpts) (st
341342
)
342343
}
343344

344-
envs, err = d.computeEnv(ctx, true /*usePrintDevEnvCache*/)
345+
envs, err = d.computeEnv(ctx, true /*usePrintDevEnvCache*/, opts.EnvOptions)
345346
} else {
346-
envs, err = d.ensureStateIsUpToDateAndComputeEnv(ctx)
347+
envs, err = d.ensureStateIsUpToDateAndComputeEnv(ctx, opts.EnvOptions)
347348
}
348349

349350
if err != nil {
@@ -368,7 +369,7 @@ func (d *Devbox) EnvVars(ctx context.Context) ([]string, error) {
368369
ctx, task := trace.NewTask(ctx, "devboxEnvVars")
369370
defer task.End()
370371
// this only returns env variables for the shell environment excluding hooks
371-
envs, err := d.ensureStateIsUpToDateAndComputeEnv(ctx)
372+
envs, err := d.ensureStateIsUpToDateAndComputeEnv(ctx, devopt.EnvOptions{})
372373
if err != nil {
373374
return nil, err
374375
}
@@ -578,11 +579,12 @@ func (d *Devbox) Services() (services.Services, error) {
578579
return result, nil
579580
}
580581

582+
// TODO: move to services.go
581583
func (d *Devbox) StartServices(
582584
ctx context.Context, runInCurrentShell bool, serviceNames ...string,
583585
) error {
584586
if !runInCurrentShell {
585-
return d.RunScript(ctx, "devbox",
587+
return d.runDevboxServicesScript(ctx,
586588
append(
587589
[]string{"services", "start", "--run-in-current-shell"},
588590
serviceNames...,
@@ -622,14 +624,15 @@ func (d *Devbox) StartServices(
622624
return nil
623625
}
624626

627+
// TODO: move to services.go
625628
func (d *Devbox) StopServices(ctx context.Context, runInCurrentShell, allProjects bool, serviceNames ...string) error {
626629
if !runInCurrentShell {
627630
args := []string{"services", "stop", "--run-in-current-shell"}
628631
args = append(args, serviceNames...)
629632
if allProjects {
630633
args = append(args, "--all-projects")
631634
}
632-
return d.RunScript(ctx, "devbox", args)
635+
return d.runDevboxServicesScript(ctx, args)
633636
}
634637

635638
if allProjects {
@@ -661,10 +664,10 @@ func (d *Devbox) StopServices(ctx context.Context, runInCurrentShell, allProject
661664
return nil
662665
}
663666

667+
// TODO: move to services.go
664668
func (d *Devbox) ListServices(ctx context.Context, runInCurrentShell bool) error {
665669
if !runInCurrentShell {
666-
return d.RunScript(ctx,
667-
"devbox", []string{"services", "ls", "--run-in-current-shell"})
670+
return d.runDevboxServicesScript(ctx, []string{"services", "ls", "--run-in-current-shell"})
668671
}
669672

670673
svcSet, err := d.Services()
@@ -700,11 +703,12 @@ func (d *Devbox) ListServices(ctx context.Context, runInCurrentShell bool) error
700703
return nil
701704
}
702705

706+
// TODO: move to services.go
703707
func (d *Devbox) RestartServices(
704708
ctx context.Context, runInCurrentShell bool, serviceNames ...string,
705709
) error {
706710
if !runInCurrentShell {
707-
return d.RunScript(ctx, "devbox",
711+
return d.runDevboxServicesScript(ctx,
708712
append(
709713
[]string{"services", "restart", "--run-in-current-shell"},
710714
serviceNames...,
@@ -739,6 +743,7 @@ func (d *Devbox) RestartServices(
739743
return nil
740744
}
741745

746+
// TODO: move to services.go
742747
func (d *Devbox) StartProcessManager(
743748
ctx context.Context,
744749
runInCurrentShell bool,
@@ -762,7 +767,7 @@ func (d *Devbox) StartProcessManager(
762767
args = append(args, "--pcflags", flag)
763768
}
764769

765-
return d.RunScript(ctx, "devbox", args)
770+
return d.runDevboxServicesScript(ctx, args)
766771
}
767772

768773
svcs, err := d.Services()
@@ -886,13 +891,17 @@ func (d *Devbox) execPrintDevEnv(ctx context.Context, usePrintDevEnvCache bool)
886891
// Note that the shellrc.tmpl template (which sources this environment) does
887892
// some additional processing. The computeEnv environment won't necessarily
888893
// represent the final "devbox run" or "devbox shell" environments.
889-
func (d *Devbox) computeEnv(ctx context.Context, usePrintDevEnvCache bool) (map[string]string, error) {
894+
func (d *Devbox) computeEnv(
895+
ctx context.Context,
896+
usePrintDevEnvCache bool,
897+
envOpts devopt.EnvOptions,
898+
) (map[string]string, error) {
890899
defer debug.FunctionTimer().End()
891900
defer trace.StartRegion(ctx, "devboxComputeEnv").End()
892901

893902
// Append variables from current env if --pure is not passed
894903
currentEnv := os.Environ()
895-
env, err := d.parseEnvAndExcludeSpecialCases(currentEnv)
904+
env, err := d.parseEnvAndExcludeSpecialCases(currentEnv, envOpts.Pure)
896905
if err != nil {
897906
return nil, err
898907
}
@@ -910,7 +919,7 @@ func (d *Devbox) computeEnv(ctx context.Context, usePrintDevEnvCache bool) (map[
910919
originalEnv := make(map[string]string, len(env))
911920
maps.Copy(originalEnv, env)
912921

913-
if !d.omitNixEnv {
922+
if !envOpts.OmitNixEnv {
914923
nixEnv, err := d.execPrintDevEnv(ctx, usePrintDevEnvCache)
915924
if err != nil {
916925
return nil, err
@@ -989,13 +998,13 @@ func (d *Devbox) computeEnv(ctx context.Context, usePrintDevEnvCache bool) (map[
989998
devboxEnvPath = envpath.JoinPathLists(devboxEnvPath, runXPaths)
990999

9911000
pathStack := envpath.Stack(env, originalEnv)
992-
pathStack.Push(env, d.ProjectDirHash(), devboxEnvPath, d.preservePathStack)
1001+
pathStack.Push(env, d.ProjectDirHash(), devboxEnvPath, envOpts.PreservePathStack)
9931002
env["PATH"] = pathStack.Path(env)
9941003
slog.Debug("new path stack is", "path_stack", pathStack)
9951004

9961005
slog.Debug("computed environment PATH", "path", env["PATH"])
9971006

998-
if !d.pure {
1007+
if !envOpts.Pure {
9991008
// preserve the original XDG_DATA_DIRS by prepending to it
10001009
env["XDG_DATA_DIRS"] = envpath.JoinPathLists(env["XDG_DATA_DIRS"], os.Getenv("XDG_DATA_DIRS"))
10011010
}
@@ -1011,6 +1020,7 @@ func (d *Devbox) computeEnv(ctx context.Context, usePrintDevEnvCache bool) (map[
10111020
// while ensuring these reflect the current (up to date) state of the project.
10121021
func (d *Devbox) ensureStateIsUpToDateAndComputeEnv(
10131022
ctx context.Context,
1023+
envOpts devopt.EnvOptions,
10141024
) (map[string]string, error) {
10151025
defer debug.FunctionTimer().End()
10161026

@@ -1037,7 +1047,7 @@ func (d *Devbox) ensureStateIsUpToDateAndComputeEnv(
10371047
// it's ok to use usePrintDevEnvCache=true here always. This does end up
10381048
// doing some non-nix work twice if lockfile is not up to date.
10391049
// TODO: Improve this to avoid extra work.
1040-
return d.computeEnv(ctx, true /*usePrintDevEnvCache*/)
1050+
return d.computeEnv(ctx, true /*usePrintDevEnvCache*/, envOpts)
10411051
}
10421052

10431053
func (d *Devbox) nixPrintDevEnvCachePath() string {
@@ -1256,7 +1266,7 @@ func (d *Devbox) addHashToEnv(env map[string]string) error {
12561266

12571267
// parseEnvAndExcludeSpecialCases converts env as []string to map[string]string
12581268
// In case of pure shell, it leaks HOME and it leaks PATH with some modifications
1259-
func (d *Devbox) parseEnvAndExcludeSpecialCases(currentEnv []string) (map[string]string, error) {
1269+
func (d *Devbox) parseEnvAndExcludeSpecialCases(currentEnv []string, pure bool) (map[string]string, error) {
12601270
env := make(map[string]string, len(currentEnv))
12611271
for _, kv := range currentEnv {
12621272
key, val, found := strings.Cut(kv, "=")
@@ -1270,13 +1280,13 @@ func (d *Devbox) parseEnvAndExcludeSpecialCases(currentEnv []string) (map[string
12701280
// - HOME required for devbox binary to work
12711281
// - PATH to find the nix installation. It is cleaned for pure mode below.
12721282
// - TERM to enable colored text in the pure shell
1273-
if !d.pure || key == "HOME" || key == "PATH" || key == "TERM" {
1283+
if !pure || key == "HOME" || key == "PATH" || key == "TERM" {
12741284
env[key] = val
12751285
}
12761286
}
12771287

12781288
// handling special case for PATH
1279-
if d.pure {
1289+
if pure {
12801290
// Setting a custom env variable to differentiate pure and regular shell
12811291
env["DEVBOX_PURE_SHELL"] = "1"
12821292
// Finding nix executables in path and passing it through

0 commit comments

Comments
 (0)