Skip to content

Add --recompute flag for commands: shell and run #2391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 25 additions & 8 deletions internal/boxcli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,16 @@ import (
"go.jetpack.io/devbox/internal/devbox"
"go.jetpack.io/devbox/internal/devbox/devopt"
"go.jetpack.io/devbox/internal/redact"
"go.jetpack.io/devbox/internal/ux"
)

type runCmdFlags struct {
envFlag
config configFlags
omitNixEnv bool
pure bool
listScripts bool
config configFlags
omitNixEnv bool
pure bool
listScripts bool
recomputeEnv bool
}

// runFlagDefaults are the flag default values that differ
Expand Down Expand Up @@ -62,6 +64,7 @@ func runCmd(defaults runFlagDefaults) *cobra.Command {
"shell environment will omit the env-vars from print-dev-env",
)
_ = command.Flags().MarkHidden("omit-nix-env")
command.Flags().BoolVar(&flags.recomputeEnv, "recompute", true, "recompute environment if needed")

command.ValidArgs = listScripts(command, flags)

Expand All @@ -84,6 +87,7 @@ func listScripts(cmd *cobra.Command, flags runCmdFlags) []string {
}

func runScriptCmd(cmd *cobra.Command, args []string, flags runCmdFlags) error {
ctx := cmd.Context()
if len(args) == 0 || flags.listScripts {
scripts := listScripts(cmd, flags)
if len(scripts) == 0 {
Expand Down Expand Up @@ -111,19 +115,32 @@ func runScriptCmd(cmd *cobra.Command, args []string, flags runCmdFlags) error {
// Check the directory exists.
box, err := devbox.Open(&devopt.Opts{
Dir: path,
Env: env,
Environment: flags.config.environment,
Stderr: cmd.ErrOrStderr(),
Env: env,
})
if err != nil {
return redact.Errorf("error reading devbox.json: %w", err)
}

envOpts := devopt.EnvOptions{
OmitNixEnv: flags.omitNixEnv,
Pure: flags.pure,
Hooks: devopt.LifecycleHooks{
OnStaleState: func() {
if !flags.recomputeEnv {
ux.FHidableWarning(
ctx,
cmd.ErrOrStderr(),
devbox.StateOutOfDateMessage,
"with --recompute=true",
)
}
},
},
OmitNixEnv: flags.omitNixEnv,
Pure: flags.pure,
SkipRecompute: !flags.recomputeEnv,
}
if err := box.RunScript(cmd.Context(), envOpts, script, scriptArgs); err != nil {
if err := box.RunScript(ctx, envOpts, script, scriptArgs); err != nil {
return redact.Errorf("error running script %q in Devbox: %w", script, err)
}
return nil
Expand Down
32 changes: 25 additions & 7 deletions internal/boxcli/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ import (
"go.jetpack.io/devbox/internal/devbox"
"go.jetpack.io/devbox/internal/devbox/devopt"
"go.jetpack.io/devbox/internal/envir"
"go.jetpack.io/devbox/internal/ux"
)

type shellCmdFlags struct {
envFlag
config configFlags
omitNixEnv bool
printEnv bool
pure bool
config configFlags
omitNixEnv bool
printEnv bool
pure bool
recomputeEnv bool
}

// shellFlagDefaults are the flag default values that differ
Expand Down Expand Up @@ -53,17 +55,20 @@ func shellCmd(defaults shellFlagDefaults) *cobra.Command {
"shell environment will omit the env-vars from print-dev-env",
)
_ = command.Flags().MarkHidden("omit-nix-env")
command.Flags().BoolVar(&flags.recomputeEnv, "recompute", true, "recompute environment if needed")

flags.config.register(command)
flags.envFlag.register(command)
return command
}

func runShellCmd(cmd *cobra.Command, flags shellCmdFlags) error {
ctx := cmd.Context()
env, err := flags.Env(flags.config.path)
if err != nil {
return err
}

// Check the directory exists.
box, err := devbox.Open(&devopt.Opts{
Dir: flags.config.path,
Expand Down Expand Up @@ -91,9 +96,22 @@ func runShellCmd(cmd *cobra.Command, flags shellCmdFlags) error {
return shellInceptionErrorMsg("devbox shell")
}

return box.Shell(cmd.Context(), devopt.EnvOptions{
OmitNixEnv: flags.omitNixEnv,
Pure: flags.pure,
return box.Shell(ctx, devopt.EnvOptions{
Hooks: devopt.LifecycleHooks{
OnStaleState: func() {
if !flags.recomputeEnv {
ux.FHidableWarning(
ctx,
cmd.ErrOrStderr(),
devbox.StateOutOfDateMessage,
"with --recompute=true",
)
}
},
},
OmitNixEnv: flags.omitNixEnv,
Pure: flags.pure,
SkipRecompute: !flags.recomputeEnv,
})
}

Expand Down
14 changes: 13 additions & 1 deletion internal/boxcli/shellenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,23 @@ func shellEnvFunc(
}

envStr, err := box.EnvExports(ctx, devopt.EnvExportsOpts{
DontRecomputeEnvironment: !flags.recomputeEnv,
EnvOptions: devopt.EnvOptions{
Hooks: devopt.LifecycleHooks{
OnStaleState: func() {
if !flags.recomputeEnv {
ux.FHidableWarning(
ctx,
cmd.ErrOrStderr(),
devbox.StateOutOfDateMessage,
box.RefreshAliasOrCommand(),
)
}
},
},
OmitNixEnv: flags.omitNixEnv,
PreservePathStack: flags.preservePathStack,
Pure: flags.pure,
SkipRecompute: !flags.recomputeEnv,
},
NoRefreshAlias: flags.noRefreshAlias,
RunHooks: flags.runInitHook,
Expand Down
55 changes: 26 additions & 29 deletions internal/devbox/devbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,22 +351,7 @@ func (d *Devbox) EnvExports(ctx context.Context, opts devopt.EnvExportsOpts) (st
var envs map[string]string
var err error

if opts.DontRecomputeEnvironment {
upToDate, _ := d.lockfile.IsUpToDateAndInstalled(isFishShell())
if !upToDate {
ux.FHidableWarning(
ctx,
d.stderr,
StateOutOfDateMessage,
d.refreshAliasOrCommand(),
)
}

envs, err = d.computeEnv(ctx, true /*usePrintDevEnvCache*/, opts.EnvOptions)
} else {
envs, err = d.ensureStateIsUpToDateAndComputeEnv(ctx, opts.EnvOptions)
}

envs, err = d.ensureStateIsUpToDateAndComputeEnv(ctx, opts.EnvOptions)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -819,23 +804,35 @@ func (d *Devbox) ensureStateIsUpToDateAndComputeEnv(
) (map[string]string, error) {
defer debug.FunctionTimer().End()

// When ensureStateIsUpToDate is called with ensure=true, it always
// returns early if the lockfile is up to date. So we don't need to check here
if err := d.ensureStateIsUpToDate(ctx, ensure); isConnectionError(err) {
if !fileutil.Exists(d.nixPrintDevEnvCachePath()) {
ux.Ferrorf(
upToDate, err := d.lockfile.IsUpToDateAndInstalled(isFishShell())
if err != nil {
return nil, err
}
if !upToDate {
if envOpts.Hooks.OnStaleState != nil {
envOpts.Hooks.OnStaleState()
}
}

if !envOpts.SkipRecompute {
// When ensureStateIsUpToDate is called with ensure=true, it always
// returns early if the lockfile is up to date. So we don't need to check here
if err := d.ensureStateIsUpToDate(ctx, ensure); isConnectionError(err) {
if !fileutil.Exists(d.nixPrintDevEnvCachePath()) {
ux.Ferrorf(
d.stderr,
"Error connecting to the internet and no cached environment found. Aborting.\n",
)
return nil, err
}
ux.Fwarningf(
d.stderr,
"Error connecting to the internet and no cached environment found. Aborting.\n",
"Error connecting to the internet. Will attempt to use cached environment.\n",
)
} else if err != nil {
// Some other non connection error, just return it.
return nil, err
}
ux.Fwarningf(
d.stderr,
"Error connecting to the internet. Will attempt to use cached environment.\n",
)
} else if err != nil {
// Some other non connection error, just return it.
return nil, err
}

// Since ensureStateIsUpToDate calls computeEnv when not up do date,
Expand Down
14 changes: 10 additions & 4 deletions internal/devbox/devopt/devboxopts.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,24 @@ type UpdateOpts struct {
}

type EnvExportsOpts struct {
DontRecomputeEnvironment bool
EnvOptions EnvOptions
NoRefreshAlias bool
RunHooks bool
EnvOptions EnvOptions
NoRefreshAlias bool
RunHooks bool
}

// 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{}`.
type EnvOptions struct {
Hooks LifecycleHooks
OmitNixEnv bool
PreservePathStack bool
Pure bool
SkipRecompute bool
}

type LifecycleHooks struct {
// OnStaleState is called when the Devbox state is out of date, AND it is not being recomputed.
OnStaleState func()
}
2 changes: 1 addition & 1 deletion internal/devbox/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ func (d *Devbox) ensureStateIsUpToDate(ctx context.Context, mode installMode) er
ctx,
d.stderr,
StateOutOfDateMessage,
d.refreshAliasOrCommand(),
d.RefreshAliasOrCommand(),
)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/devbox/refresh.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (d *Devbox) isGlobal() bool {
// In some cases (e.g. 2 non-global projects somehow active at the same time),
// refresh might not match. This is a tiny edge case, so no need to make UX
// great, we just print out the entire command.
func (d *Devbox) refreshAliasOrCommand() string {
func (d *Devbox) RefreshAliasOrCommand() string {
if !d.isRefreshAliasSet() {
// even if alias is not set, it might still be set by the end of this process
return fmt.Sprintf("`%s` or `%s`", d.refreshAliasName(), d.refreshCmd())
Expand Down
Loading