Skip to content

Commit 684a933

Browse files
authored
[version check] Avoid re-printing update notice in child devbox commands (#1007)
## Summary Some devbox commands like `devbox shell` may invoke other devbox commands. For example, `devbox shell` generates a shellrc file that calls `devbox log`. This results in multiple "update version" notices being printed for the same devbox command invocation. To avoid this, we set an env-var after printing the notice, and unset that env-var when this devbox command will exit. Note, I keep this env-var inside vercheck to maintain encapsulation, instead of putting it in envir package. I also pushed some of the skipping checks from `root.go` inside `vercheck` for (1) better encapsulation (2) adding test cases for them. ## How was it tested? `go test ./internal/vercheck/...` For manual testing: 1. `set -gx DEVBOX_LATEST_VERSION 0.5.0` 2. modify binary to `vercheck` to have `currentDevboxVersion = 0.4.8` and `isDevBuild = false`. 3. run `devbox shell` BEFORE: multiple update notices would print AFTER: one notice was printed
1 parent 0ac0b2d commit 684a933

File tree

3 files changed

+89
-27
lines changed

3 files changed

+89
-27
lines changed

internal/boxcli/root.go

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import (
1111
"strings"
1212

1313
"github.com/spf13/cobra"
14-
"go.jetpack.io/devbox/internal/build"
15-
1614
"go.jetpack.io/devbox/internal/boxcli/midcobra"
1715
"go.jetpack.io/devbox/internal/cloud/openssh/sshshim"
1816
"go.jetpack.io/devbox/internal/debug"
@@ -38,16 +36,7 @@ func RootCmd() *cobra.Command {
3836
if flags.quiet {
3937
cmd.SetErr(io.Discard)
4038
}
41-
42-
// Skip CheckVersion for `devbox global shellenv` because users will include that
43-
// command in their shellrc files, and we don't want to bother them everytime they
44-
// open their terminals.
45-
//
46-
// Skip CheckVersion for engineers building devbox during development.
47-
if !strings.HasPrefix(cmd.CommandPath(), "devbox global shellenv") &&
48-
!build.IsDev {
49-
vercheck.CheckVersion(cmd.ErrOrStderr())
50-
}
39+
vercheck.CheckVersion(cmd.ErrOrStderr(), cmd.CommandPath())
5140
},
5241
RunE: func(cmd *cobra.Command, args []string) error {
5342
return cmd.Help()

internal/vercheck/vercheck.go

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,60 @@ import (
1515

1616
"github.com/fatih/color"
1717
"github.com/pkg/errors"
18-
"golang.org/x/mod/semver"
19-
18+
"github.com/samber/lo"
2019
"go.jetpack.io/devbox/internal/boxcli/usererr"
21-
"go.jetpack.io/devbox/internal/build"
2220
"go.jetpack.io/devbox/internal/cmdutil"
2321
"go.jetpack.io/devbox/internal/envir"
2422
"go.jetpack.io/devbox/internal/ux"
2523
"go.jetpack.io/devbox/internal/xdg"
24+
"golang.org/x/mod/semver"
2625
)
2726

2827
// Keep this in-sync with latest version in launch.sh.
2928
// If this version is newer than the version in launch.sh, we'll print a notice.
3029
const expectedLauncherVersion = "v0.2.0"
3130

31+
// envName determines whether the version check has already occurred.
32+
// We set this env-var so that this devbox command invoking other devbox commands
33+
// do not re-run the version check and print the notice again.
34+
const envName = "__DEVBOX_VERSION_CHECK"
35+
3236
// currentDevboxVersion is the version of the devbox CLI binary that is currently running.
3337
// We use this variable so that we can mock it in tests.
34-
var currentDevboxVersion = build.Version
38+
var currentDevboxVersion = "0.4.8" // build.Version
39+
40+
// isDevBuild determines whether this CLI binary was built during development, or published
41+
// as a release.
42+
// We use this variable so we can mock it in tests.
43+
var isDevBuild = false // build.IsDev
44+
45+
var commandSkipList = []string{
46+
"devbox global shellenv",
47+
"devbox shellenv",
48+
}
3549

3650
// CheckVersion checks the launcher and binary versions and prints a notice if
3751
// they are out of date.
38-
func CheckVersion(w io.Writer) {
52+
//
53+
// It will set the checkVersionEnvName to indicate that the version check was done.
54+
// Callers should call ClearEnvVar after their work is done.
55+
func CheckVersion(w io.Writer, commandPath string) {
56+
if isDevBuild {
57+
return
58+
}
59+
60+
if os.Getenv(envName) == "1" {
61+
return
62+
}
3963

4064
if envir.IsDevboxCloud() {
4165
return
4266
}
4367

68+
if lo.Contains(commandSkipList, commandPath) {
69+
return
70+
}
71+
4472
// check launcher version
4573
launcherNotice := launcherVersionNotice()
4674
if launcherNotice != "" {
@@ -56,6 +84,8 @@ func CheckVersion(w io.Writer) {
5684
// TODO: use ux.FNotice
5785
color.New(color.FgYellow).Fprintf(w, devboxNotice)
5886
}
87+
88+
os.Setenv(envName, "1")
5989
}
6090

6191
// SelfUpdate updates the devbox launcher and devbox CLI binary.

internal/vercheck/vercheck_test.go

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package vercheck
55

66
import (
77
"bytes"
8+
"os"
89
"strings"
910
"testing"
1011

@@ -13,40 +14,46 @@ import (
1314

1415
func TestCheckVersion(t *testing.T) {
1516

16-
t.Run("no_devbox_cloud", func(t *testing.T) {
17+
isDevBuild = false
18+
19+
t.Run("skip_if_devbox_cloud", func(t *testing.T) {
20+
defer os.Unsetenv(envName)
1721
// if devbox cloud
1822
t.Setenv(envir.DevboxRegion, "true")
1923
buf := new(bytes.Buffer)
20-
CheckVersion(buf)
24+
CheckVersion(buf, "devbox shell")
2125
if buf.String() != "" {
2226
t.Errorf("expected empty string, got %q", buf.String())
2327
}
2428
t.Setenv(envir.DevboxRegion, "")
2529
})
2630

2731
// no launcher version or latest-version env var
28-
t.Run("no_launcher_version_or_latest_version", func(t *testing.T) {
32+
t.Run("skip_if_no_launcher_version_or_latest_version", func(t *testing.T) {
33+
defer os.Unsetenv(envName)
2934
t.Setenv(envir.LauncherVersion, "")
3035
t.Setenv(envir.DevboxLatestVersion, "")
3136
buf := new(bytes.Buffer)
32-
CheckVersion(buf)
37+
CheckVersion(buf, "devbox shell")
3338
if buf.String() != "" {
3439
t.Errorf("expected empty string, got %q", buf.String())
3540
}
3641
})
3742

38-
t.Run("launcher_version_outdated", func(t *testing.T) {
43+
t.Run("print_if_launcher_version_outdated", func(t *testing.T) {
44+
defer os.Unsetenv(envName)
3945
// set older launcher version
4046
t.Setenv(envir.LauncherVersion, "v0.1.0")
4147

4248
buf := new(bytes.Buffer)
43-
CheckVersion(buf)
49+
CheckVersion(buf, "devbox shell")
4450
if !strings.Contains(buf.String(), "New launcher available") {
4551
t.Errorf("expected notice about new launcher version, got %q", buf.String())
4652
}
4753
})
4854

49-
t.Run("binary_version_outdated", func(t *testing.T) {
55+
t.Run("print_if_binary_version_outdated", func(t *testing.T) {
56+
defer os.Unsetenv(envName)
5057
// set the launcher version so that it is not outdated
5158
t.Setenv(envir.LauncherVersion, strings.TrimPrefix(expectedLauncherVersion, "v"))
5259

@@ -57,13 +64,14 @@ func TestCheckVersion(t *testing.T) {
5764
currentDevboxVersion = "v0.4.8"
5865

5966
buf := new(bytes.Buffer)
60-
CheckVersion(buf)
67+
CheckVersion(buf, "devbox shell")
6168
if !strings.Contains(buf.String(), "New devbox available") {
6269
t.Errorf("expected notice about new devbox version, got %q", buf.String())
6370
}
6471
})
6572

66-
t.Run("all_versions_up_to_date", func(t *testing.T) {
73+
t.Run("skip_if_all_versions_up_to_date", func(t *testing.T) {
74+
defer os.Unsetenv(envName)
6775

6876
// set the launcher version so that it is not outdated
6977
t.Setenv(envir.LauncherVersion, strings.TrimPrefix(expectedLauncherVersion, "v"))
@@ -75,9 +83,44 @@ func TestCheckVersion(t *testing.T) {
7583
t.Setenv(envir.DevboxLatestVersion, "0.4.8")
7684

7785
buf := new(bytes.Buffer)
78-
CheckVersion(buf)
86+
CheckVersion(buf, "devbox shell")
87+
if buf.String() != "" {
88+
t.Errorf("expected empty string, got %q", buf.String())
89+
}
90+
})
91+
92+
t.Run("skip_if_dev_build", func(t *testing.T) {
93+
defer os.Unsetenv(envName)
94+
isDevBuild = true
95+
defer func() { isDevBuild = false }()
96+
97+
// set older launcher version
98+
t.Setenv(envir.LauncherVersion, "v0.1.0")
99+
100+
buf := new(bytes.Buffer)
101+
CheckVersion(buf, "devbox shell")
79102
if buf.String() != "" {
80103
t.Errorf("expected empty string, got %q", buf.String())
81104
}
82105
})
106+
107+
t.Run("skip_if_command_path_skipped", func(t *testing.T) {
108+
defer os.Unsetenv(envName)
109+
110+
for _, cmdPath := range commandSkipList {
111+
cmdPathUnderscored := strings.ReplaceAll(cmdPath, " ", "_")
112+
t.Run("skip_if_cmd_path_is_"+cmdPathUnderscored, func(t *testing.T) {
113+
114+
// set older launcher version
115+
t.Setenv(envir.LauncherVersion, "v0.1.0")
116+
117+
buf := new(bytes.Buffer)
118+
CheckVersion(buf, cmdPath)
119+
if buf.String() != "" {
120+
t.Errorf("expected empty string, got %q", buf.String())
121+
}
122+
})
123+
}
124+
})
125+
83126
}

0 commit comments

Comments
 (0)