@@ -168,21 +168,22 @@ func (d *Devbox) Shell(ctx context.Context) error {
168
168
ctx , task := trace .NewTask (ctx , "devboxShell" )
169
169
defer task .End ()
170
170
171
- if err := d .ensurePackagesAreInstalled (ctx , ensure ); err != nil {
172
- return err
173
- }
174
- fmt .Fprintln (d .stderr , "Starting a devbox shell..." )
175
-
176
171
profileDir , err := d .profilePath ()
177
172
if err != nil {
178
173
return err
179
174
}
180
175
181
- envs , err := d .nixEnv (ctx )
176
+ envs , err := d .ensurePackagesAreInstalledAndComputeEnv (ctx )
182
177
if err != nil {
183
178
return err
184
179
}
180
+
181
+ fmt .Fprintln (d .stderr , "Starting a devbox shell..." )
182
+
185
183
// Used to determine whether we're inside a shell (e.g. to prevent shell inception)
184
+ // TODO: This is likely obsolete but we need to decide what happens when
185
+ // the user does shell-ception. One option is to leave the current shell and
186
+ // join a new one (that way they are not in nested shells.)
186
187
envs [envir .DevboxShellEnabled ] = "1"
187
188
188
189
if err = createDevboxSymlink (d ); err != nil {
@@ -210,15 +211,11 @@ func (d *Devbox) RunScript(ctx context.Context, cmdName string, cmdArgs []string
210
211
ctx , task := trace .NewTask (ctx , "devboxRun" )
211
212
defer task .End ()
212
213
213
- if err := d .ensurePackagesAreInstalled (ctx , ensure ); err != nil {
214
- return err
215
- }
216
-
217
214
if err := shellgen .WriteScriptsToFiles (d ); err != nil {
218
215
return err
219
216
}
220
217
221
- env , err := d .nixEnv (ctx )
218
+ env , err := d .ensurePackagesAreInstalledAndComputeEnv (ctx )
222
219
if err != nil {
223
220
return err
224
221
}
@@ -274,22 +271,39 @@ func (d *Devbox) ListScripts() []string {
274
271
return keys
275
272
}
276
273
277
- func (d * Devbox ) NixEnv (ctx context.Context , includeHooks bool ) (string , error ) {
274
+ func (d * Devbox ) NixEnv (ctx context.Context , opts devopt. NixEnvOpts ) (string , error ) {
278
275
ctx , task := trace .NewTask (ctx , "devboxNixEnv" )
279
276
defer task .End ()
280
277
281
- if err := d .ensurePackagesAreInstalled (ctx , ensure ); err != nil {
282
- return "" , err
278
+ var envs map [string ]string
279
+ var err error
280
+
281
+ if opts .DontRecomputeEnvironment {
282
+ upToDate , _ := d .lockfile .IsUpToDateAndInstalled ()
283
+ if ! upToDate {
284
+ cmd := `eval "$(devbox global shellenv --recompute)"`
285
+ if strings .HasSuffix (os .Getenv ("SHELL" ), "fish" ) {
286
+ cmd = `devbox global shellenv --recompute | source`
287
+ }
288
+ ux .Finfo (
289
+ d .stderr ,
290
+ "Your devbox environment may be out of date. Please run \n \n %s\n \n " ,
291
+ cmd ,
292
+ )
293
+ }
294
+
295
+ envs , err = d .computeNixEnv (ctx , true /*usePrintDevEnvCache*/ )
296
+ } else {
297
+ envs , err = d .ensurePackagesAreInstalledAndComputeEnv (ctx )
283
298
}
284
299
285
- envs , err := d .nixEnv (ctx )
286
300
if err != nil {
287
301
return "" , err
288
302
}
289
303
290
304
envStr := exportify (envs )
291
305
292
- if includeHooks {
306
+ if opts . RunHooks {
293
307
hooksStr := ". " + shellgen .ScriptPath (d .ProjectDir (), shellgen .HooksFilename )
294
308
envStr = fmt .Sprintf ("%s\n %s;\n " , envStr , hooksStr )
295
309
}
@@ -301,12 +315,7 @@ func (d *Devbox) EnvVars(ctx context.Context) ([]string, error) {
301
315
ctx , task := trace .NewTask (ctx , "devboxEnvVars" )
302
316
defer task .End ()
303
317
// this only returns env variables for the shell environment excluding hooks
304
- // and excluding "export " prefix in "export key=value" format
305
- if err := d .ensurePackagesAreInstalled (ctx , ensure ); err != nil {
306
- return nil , err
307
- }
308
-
309
- envs , err := d .nixEnv (ctx )
318
+ envs , err := d .ensurePackagesAreInstalledAndComputeEnv (ctx )
310
319
if err != nil {
311
320
return nil , err
312
321
}
@@ -903,25 +912,23 @@ func (d *Devbox) computeNixEnv(ctx context.Context, usePrintDevEnvCache bool) (m
903
912
return env , d .addHashToEnv (env )
904
913
}
905
914
906
- // nixEnv is a wrapper around computeNixEnv that returns a cached result if
907
- // it has previously computed and the local lock file is up to date.
908
- // Note that this is in-memory cache of the final environment, and not the same
909
- // as the nix print-dev-env cache which is stored in a file.
910
- func (d * Devbox ) nixEnv (ctx context.Context ) (map [string ]string , error ) {
915
+ // ensurePackagesAreInstalledAndComputeEnv does what it says :P
916
+ func (d * Devbox ) ensurePackagesAreInstalledAndComputeEnv (
917
+ ctx context.Context ,
918
+ ) (map [string ]string , error ) {
911
919
defer debug .FunctionTimer ().End ()
912
920
913
- usePrintDevEnvCache := false
914
-
915
- // If lockfile is up-to-date, we can use the print-dev-env cache.
916
- upToDate , err := d .lockfile .IsUpToDateAndInstalled ()
917
- if err != nil {
921
+ // When ensurePackagesAreInstalled is called with ensure=true, it always
922
+ // returns early if the lockfile is up to date. So we don't need to check here
923
+ if err := d .ensurePackagesAreInstalled (ctx , ensure ); err != nil {
918
924
return nil , err
919
925
}
920
- if upToDate {
921
- usePrintDevEnvCache = true
922
- }
923
926
924
- return d .computeNixEnv (ctx , usePrintDevEnvCache )
927
+ // Since ensurePackagesAreInstalled calls computeNixEnv when not up do date,
928
+ // it's ok to use usePrintDevEnvCache=true here always. This does end up
929
+ // doing some non-nix work twice if lockfile is not up to date.
930
+ // TODO: Improve this to avoid extra work.
931
+ return d .computeNixEnv (ctx , true )
925
932
}
926
933
927
934
func (d * Devbox ) nixPrintDevEnvCachePath () string {
0 commit comments