mirror of
https://github.com/coder/coder.git
synced 2025-07-18 14:17:22 +00:00
feat: show banner when workspace is outdated (#4926)
* feat: show banner when workspace is outdated * Address PR comments * Fix: writer
This commit is contained in:
13
cli/root.go
13
cli/root.go
@ -417,6 +417,17 @@ func isTTY(cmd *cobra.Command) bool {
|
|||||||
// This accepts a reader to work with Cobra's "OutOrStdout"
|
// This accepts a reader to work with Cobra's "OutOrStdout"
|
||||||
// function for simple testing.
|
// function for simple testing.
|
||||||
func isTTYOut(cmd *cobra.Command) bool {
|
func isTTYOut(cmd *cobra.Command) bool {
|
||||||
|
return isTTYWriter(cmd, cmd.OutOrStdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isTTYErr returns whether the passed reader is a TTY or not.
|
||||||
|
// This accepts a reader to work with Cobra's "ErrOrStderr"
|
||||||
|
// function for simple testing.
|
||||||
|
func isTTYErr(cmd *cobra.Command) bool {
|
||||||
|
return isTTYWriter(cmd, cmd.ErrOrStderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isTTYWriter(cmd *cobra.Command, writer func() io.Writer) bool {
|
||||||
// If the `--force-tty` command is available, and set,
|
// If the `--force-tty` command is available, and set,
|
||||||
// assume we're in a tty. This is primarily for cases on Windows
|
// assume we're in a tty. This is primarily for cases on Windows
|
||||||
// where we may not be able to reliably detect this automatically (ie, tests)
|
// where we may not be able to reliably detect this automatically (ie, tests)
|
||||||
@ -424,7 +435,7 @@ func isTTYOut(cmd *cobra.Command) bool {
|
|||||||
if forceTty && err == nil {
|
if forceTty && err == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
file, ok := cmd.OutOrStdout().(*os.File)
|
file, ok := writer().(*os.File)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
21
cli/ssh.go
21
cli/ssh.go
@ -5,6 +5,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -72,6 +73,11 @@ func ssh() *cobra.Command {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateWorkspaceBanner, outdated := verifyWorkspaceOutdated(client, workspace)
|
||||||
|
if outdated && isTTYErr(cmd) {
|
||||||
|
_, _ = fmt.Fprintln(cmd.ErrOrStderr(), updateWorkspaceBanner)
|
||||||
|
}
|
||||||
|
|
||||||
// OpenSSH passes stderr directly to the calling TTY.
|
// OpenSSH passes stderr directly to the calling TTY.
|
||||||
// This is required in "stdio" mode so a connecting indicator can be displayed.
|
// This is required in "stdio" mode so a connecting indicator can be displayed.
|
||||||
err = cliui.Agent(ctx, cmd.ErrOrStderr(), cliui.AgentOptions{
|
err = cliui.Agent(ctx, cmd.ErrOrStderr(), cliui.AgentOptions{
|
||||||
@ -343,3 +349,18 @@ func notifyCondition(ctx context.Context, client *codersdk.Client, workspaceID u
|
|||||||
return deadline.Truncate(time.Minute), callback
|
return deadline.Truncate(time.Minute), callback
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify if the user workspace is outdated and prepare an actionable message for user.
|
||||||
|
func verifyWorkspaceOutdated(client *codersdk.Client, workspace codersdk.Workspace) (string, bool) {
|
||||||
|
if !workspace.Outdated {
|
||||||
|
return "", false // workspace is up-to-date
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaceLink := buildWorkspaceLink(client.URL, workspace)
|
||||||
|
return fmt.Sprintf("👋 Your workspace is outdated! Update it here: %s\n", workspaceLink), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the user workspace link which navigates to the Coder web UI.
|
||||||
|
func buildWorkspaceLink(serverURL *url.URL, workspace codersdk.Workspace) *url.URL {
|
||||||
|
return serverURL.ResolveReference(&url.URL{Path: fmt.Sprintf("@%s/%s", workspace.OwnerName, workspace.Name)})
|
||||||
|
}
|
||||||
|
58
cli/ssh_internal_test.go
Normal file
58
cli/ssh_internal_test.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/coder/coder/codersdk"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
fakeOwnerName = "fake-owner-name"
|
||||||
|
fakeServerURL = "https://fake-foo-url"
|
||||||
|
fakeWorkspaceName = "fake-workspace-name"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVerifyWorkspaceOutdated(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
serverURL, err := url.Parse(fakeServerURL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
client := codersdk.Client{URL: serverURL}
|
||||||
|
|
||||||
|
t.Run("Up-to-date", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
workspace := codersdk.Workspace{Name: fakeWorkspaceName, OwnerName: fakeOwnerName}
|
||||||
|
|
||||||
|
_, outdated := verifyWorkspaceOutdated(&client, workspace)
|
||||||
|
|
||||||
|
assert.False(t, outdated, "workspace should be up-to-date")
|
||||||
|
})
|
||||||
|
t.Run("Outdated", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
workspace := codersdk.Workspace{Name: fakeWorkspaceName, OwnerName: fakeOwnerName, Outdated: true}
|
||||||
|
|
||||||
|
updateWorkspaceBanner, outdated := verifyWorkspaceOutdated(&client, workspace)
|
||||||
|
|
||||||
|
assert.True(t, outdated, "workspace should be outdated")
|
||||||
|
assert.NotEmpty(t, updateWorkspaceBanner, "workspace banner should be present")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildWorkspaceLink(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
serverURL, err := url.Parse(fakeServerURL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
workspace := codersdk.Workspace{Name: fakeWorkspaceName, OwnerName: fakeOwnerName}
|
||||||
|
workspaceLink := buildWorkspaceLink(serverURL, workspace)
|
||||||
|
|
||||||
|
assert.Equal(t, workspaceLink.String(), fakeServerURL+"/@"+fakeOwnerName+"/"+fakeWorkspaceName)
|
||||||
|
}
|
Reference in New Issue
Block a user