Skip to content

Commit 19224b0

Browse files
committed
add more plumbing
1 parent a9b4796 commit 19224b0

File tree

4 files changed

+72
-4
lines changed

4 files changed

+72
-4
lines changed

buildinfo/buildinfo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func Version() string {
5151
version += revision
5252
}
5353
})
54-
return version
54+
return "v1.5.0"
5555
}
5656

5757
// VersionsMatch compares the two versions. It assumes the versions match if

cli/login.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ func login() *cobra.Command {
6767
}
6868

6969
client := codersdk.New(serverURL)
70+
71+
// Try to check the version of the server prior to logging in.
72+
// It may be useful to warn the user if they are trying to login
73+
// on a very old client.
74+
err = checkVersions(cmd, client)
75+
if err != nil {
76+
return xerrors.Errorf("check versions: %w", err)
77+
}
78+
7079
hasInitialUser, err := client.HasFirstUser(cmd.Context())
7180
if err != nil {
7281
return xerrors.Errorf("has initial user: %w", err)

cli/root.go

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"golang.org/x/xerrors"
1111

12+
"github.com/charmbracelet/lipgloss"
1213
"github.com/kirsle/configdir"
1314
"github.com/mattn/go-isatty"
1415
"github.com/spf13/cobra"
@@ -39,6 +40,13 @@ const (
3940
varNoOpen = "no-open"
4041
varForceTty = "force-tty"
4142
notLoggedInMessage = "You are not logged in. Try logging in using 'coder login <url>'."
43+
44+
envNoVersionCheck = "CODER_NO_VERSION_WARNING"
45+
)
46+
47+
var (
48+
errUnauthenticated = xerrors.New(notLoggedInMessage)
49+
varNoVersionCheck = false
4250
)
4351

4452
func init() {
@@ -57,9 +65,23 @@ func Root() *cobra.Command {
5765
SilenceUsage: true,
5866
Long: `Coder — A tool for provisioning self-hosted development environments.
5967
`,
60-
PersistentPreRun: func(cmd *cobra.Command, _ []string) {
68+
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
69+
if varNoVersionCheck {
70+
return nil
71+
}
6172

73+
client, err := createClient(cmd)
74+
// If the client is unauthenticated we can ignore the check.
75+
// The child commands should handle an unauthenticated client.
76+
if xerrors.Is(err, errUnauthenticated) {
77+
return nil
78+
}
79+
if err != nil {
80+
return xerrors.Errorf("create client: %w", err)
81+
}
82+
return checkVersions(cmd, client)
6283
},
84+
6385
Example: ` Start a Coder server.
6486
` + cliui.Styles.Code.Render("$ coder server") + `
6587
@@ -99,6 +121,7 @@ func Root() *cobra.Command {
99121

100122
cmd.PersistentFlags().String(varURL, "", "Specify the URL to your deployment.")
101123
cmd.PersistentFlags().String(varToken, "", "Specify an authentication token.")
124+
cliflag.BoolVarP(cmd.PersistentFlags(), &varNoVersionCheck, "no-version-warning", "", envNoVersionCheck, false, "Suppress warning when client and server versions do not match.")
102125
cliflag.String(cmd.PersistentFlags(), varAgentToken, "", "CODER_AGENT_TOKEN", "", "Specify an agent authentication token.")
103126
_ = cmd.PersistentFlags().MarkHidden(varAgentToken)
104127
cliflag.String(cmd.PersistentFlags(), varAgentURL, "", "CODER_AGENT_URL", "", "Specify the URL for an agent to access your deployment.")
@@ -143,7 +166,7 @@ func createClient(cmd *cobra.Command) (*codersdk.Client, error) {
143166
if err != nil {
144167
// If the configuration files are absent, the user is logged out
145168
if os.IsNotExist(err) {
146-
return nil, xerrors.New(notLoggedInMessage)
169+
return nil, errUnauthenticated
147170
}
148171
return nil, err
149172
}
@@ -158,7 +181,7 @@ func createClient(cmd *cobra.Command) (*codersdk.Client, error) {
158181
if err != nil {
159182
// If the configuration files are absent, the user is logged out
160183
if os.IsNotExist(err) {
161-
return nil, xerrors.New(notLoggedInMessage)
184+
return nil, errUnauthenticated
162185
}
163186
return nil, err
164187
}
@@ -332,3 +355,26 @@ func FormatCobraError(err error, cmd *cobra.Command) string {
332355
helpErrMsg := fmt.Sprintf("Run '%s --help' for usage.", cmd.CommandPath())
333356
return cliui.Styles.Error.Render(err.Error() + "\n" + helpErrMsg)
334357
}
358+
359+
func checkVersions(cmd *cobra.Command, client *codersdk.Client) error {
360+
clientVersion := buildinfo.Version()
361+
362+
info, err := client.BuildInfo(cmd.Context())
363+
if err != nil {
364+
return xerrors.Errorf("build info: %w", err)
365+
}
366+
367+
if !buildinfo.VersionsMatch(clientVersion, info.Version) {
368+
warn := cliui.Styles.Warn.Copy().Align(lipgloss.Left)
369+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), warn.Render("client/server versions do not match"))
370+
fmt.Println()
371+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), warn.Render("client version: %s"), clientVersion)
372+
fmt.Println()
373+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), warn.Render("server version: %s"), info.Version)
374+
fmt.Println()
375+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), warn.Render("download the appropriate version from https://github.com/coder/coder/releases/tag/%s"), info.TrimmedVersion())
376+
fmt.Println()
377+
}
378+
379+
return nil
380+
}

codersdk/buildinfo.go

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

33
import (
4+
"bytes"
45
"context"
56
"encoding/json"
67
"net/http"
@@ -16,6 +17,18 @@ type BuildInfoResponse struct {
1617
Version string `json:"version"`
1718
}
1819

20+
// TrimmedVersion trims build information from the version.
21+
// E.g. 'v0.7.4-devel+11573034' -> 'v0.7.4'.
22+
func (b BuildInfoResponse) TrimmedVersion() string {
23+
// Linter doesn't like strings.Index...
24+
idx := bytes.Index([]byte(b.Version), []byte("-devel"))
25+
if idx < 0 {
26+
return string(b.Version)
27+
}
28+
29+
return b.Version[:idx]
30+
}
31+
1932
// BuildInfo returns build information for this instance of Coder.
2033
func (c *Client) BuildInfo(ctx context.Context) (BuildInfoResponse, error) {
2134
res, err := c.Request(ctx, http.MethodGet, "/api/v2/buildinfo", nil)

0 commit comments

Comments
 (0)