diff --git a/cli/cliui/agent.go b/cli/cliui/agent.go
index f1f14dfef1..43b2375123 100644
--- a/cli/cliui/agent.go
+++ b/cli/cliui/agent.go
@@ -27,7 +27,7 @@ type AgentOptions struct {
Fetch func(context.Context) (codersdk.WorkspaceAgent, error)
FetchInterval time.Duration
WarnInterval time.Duration
- NoWait bool // If true, don't wait for the agent to be ready.
+ Wait bool // If true, wait for the agent to be ready (startup script).
}
// Agent displays a spinning indicator that waits for a workspace agent to connect.
@@ -96,7 +96,7 @@ func Agent(ctx context.Context, writer io.Writer, opts AgentOptions) error {
// we do this just before starting the spinner to avoid needless
// spinning.
if agent.Status == codersdk.WorkspaceAgentConnected &&
- agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && opts.NoWait {
+ agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && !opts.Wait {
showMessage()
return nil
}
@@ -140,7 +140,7 @@ func Agent(ctx context.Context, writer io.Writer, opts AgentOptions) error {
// NOTE(mafredri): Once we have access to the workspace agent's
// startup script logs, we can show them here.
// https://github.com/coder/coder/issues/2957
- if agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && !opts.NoWait {
+ if agent.StartupScriptBehavior == codersdk.WorkspaceAgentStartupScriptBehaviorBlocking && opts.Wait {
switch agent.LifecycleState {
case codersdk.WorkspaceAgentLifecycleReady:
return nil
@@ -183,7 +183,7 @@ func waitingMessage(agent codersdk.WorkspaceAgent, opts AgentOptions) (m *messag
Prompt: "Don't panic, your workspace is booting up!",
}
defer func() {
- if agent.Status == codersdk.WorkspaceAgentConnected && opts.NoWait {
+ if agent.Status == codersdk.WorkspaceAgentConnected && !opts.Wait {
m.Spin = ""
}
if m.Spin != "" {
@@ -225,7 +225,7 @@ func waitingMessage(agent codersdk.WorkspaceAgent, opts AgentOptions) (m *messag
case codersdk.WorkspaceAgentConnected:
m.Spin = fmt.Sprintf("Waiting for %s to become ready...", DefaultStyles.Field.Render(agent.Name))
m.Prompt = "Don't panic, your workspace agent has connected and the workspace is getting ready!"
- if opts.NoWait {
+ if !opts.Wait {
m.Prompt = "Your workspace is still getting ready, it may be in an incomplete state."
}
diff --git a/cli/cliui/agent_test.go b/cli/cliui/agent_test.go
index efed058e4f..6d54544a45 100644
--- a/cli/cliui/agent_test.go
+++ b/cli/cliui/agent_test.go
@@ -140,7 +140,7 @@ func TestAgent_StartupTimeout(t *testing.T) {
},
FetchInterval: time.Millisecond,
WarnInterval: time.Millisecond,
- NoWait: false,
+ Wait: true,
})
return err
},
@@ -199,7 +199,7 @@ func TestAgent_StartErrorExit(t *testing.T) {
},
FetchInterval: time.Millisecond,
WarnInterval: 60 * time.Second,
- NoWait: false,
+ Wait: true,
})
return err
},
@@ -255,7 +255,7 @@ func TestAgent_NoWait(t *testing.T) {
},
FetchInterval: time.Millisecond,
WarnInterval: time.Second,
- NoWait: true,
+ Wait: false,
})
return err
},
@@ -325,7 +325,7 @@ func TestAgent_StartupScriptBehaviorNonBlocking(t *testing.T) {
},
FetchInterval: time.Millisecond,
WarnInterval: time.Second,
- NoWait: false,
+ Wait: true,
})
return err
},
diff --git a/cli/portforward.go b/cli/portforward.go
index fd0f2bcfab..9d3d63030d 100644
--- a/cli/portforward.go
+++ b/cli/portforward.go
@@ -92,6 +92,7 @@ func (r *RootCmd) portForward() *clibase.Cmd {
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
},
+ Wait: false,
})
if err != nil {
return xerrors.Errorf("await agent: %w", err)
diff --git a/cli/speedtest.go b/cli/speedtest.go
index 986088e2ea..c9d63bbf36 100644
--- a/cli/speedtest.go
+++ b/cli/speedtest.go
@@ -45,6 +45,7 @@ func (r *RootCmd) speedtest() *clibase.Cmd {
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
},
+ Wait: false,
})
if err != nil && !xerrors.Is(err, cliui.AgentStartError) {
return xerrors.Errorf("await agent: %w", err)
diff --git a/cli/ssh.go b/cli/ssh.go
index 3a22ab1172..05573b3e2a 100644
--- a/cli/ssh.go
+++ b/cli/ssh.go
@@ -42,6 +42,7 @@ var (
autostopNotifyCountdown = []time.Duration{30 * time.Minute}
)
+//nolint:gocyclo
func (r *RootCmd) ssh() *clibase.Cmd {
var (
stdio bool
@@ -49,6 +50,7 @@ func (r *RootCmd) ssh() *clibase.Cmd {
forwardGPG bool
identityAgent string
wsPollInterval time.Duration
+ waitEnum string
noWait bool
logDir string
logToFile bool
@@ -105,6 +107,30 @@ func (r *RootCmd) ssh() *clibase.Cmd {
return err
}
+ // Select the startup script behavior based on template configuration or flags.
+ var wait bool
+ switch waitEnum {
+ case "yes":
+ wait = true
+ case "no":
+ wait = false
+ case "auto":
+ switch workspaceAgent.StartupScriptBehavior {
+ case codersdk.WorkspaceAgentStartupScriptBehaviorBlocking:
+ wait = true
+ case codersdk.WorkspaceAgentStartupScriptBehaviorNonBlocking:
+ wait = false
+ default:
+ return xerrors.Errorf("unknown startup script behavior %q", workspaceAgent.StartupScriptBehavior)
+ }
+ default:
+ return xerrors.Errorf("unknown wait value %q", waitEnum)
+ }
+ // The `--no-wait` flag is deprecated, but for now, check it.
+ if noWait {
+ wait = false
+ }
+
templateVersion, err := client.TemplateVersion(ctx, workspace.LatestBuild.TemplateVersionID)
if err != nil {
return err
@@ -134,7 +160,7 @@ func (r *RootCmd) ssh() *clibase.Cmd {
Fetch: func(ctx context.Context) (codersdk.WorkspaceAgent, error) {
return client.WorkspaceAgent(ctx, workspaceAgent.ID)
},
- NoWait: noWait,
+ Wait: wait,
})
if err != nil {
if xerrors.Is(err, context.Canceled) {
@@ -313,6 +339,13 @@ func (r *RootCmd) ssh() *clibase.Cmd {
return nil
},
}
+ waitOption := clibase.Option{
+ Flag: "wait",
+ Env: "CODER_SSH_WAIT",
+ Description: "Specifies whether or not to wait for the startup script to finish executing. Auto means that the agent startup script behavior configured in the workspace template is used.",
+ Default: "auto",
+ Value: clibase.EnumOf(&waitEnum, "yes", "no", "auto"),
+ }
cmd.Options = clibase.OptionSet{
{
Flag: "stdio",
@@ -347,11 +380,13 @@ func (r *RootCmd) ssh() *clibase.Cmd {
Default: "1m",
Value: clibase.DurationOf(&wsPollInterval),
},
+ waitOption,
{
Flag: "no-wait",
Env: "CODER_SSH_NO_WAIT",
- Description: "Specifies whether to wait for a workspace to become ready before logging in (only applicable when the startup script behavior is blocking). Note that the workspace agent may still be in the process of executing the startup script and the workspace may be in an incomplete state.",
+ Description: "Enter workspace immediately after the agent has connected. This is the default if the template has configured the agent startup script behavior as non-blocking.",
Value: clibase.BoolOf(&noWait),
+ UseInstead: []clibase.Option{waitOption},
},
{
Flag: "log-dir",
diff --git a/cli/testdata/coder_ssh_--help.golden b/cli/testdata/coder_ssh_--help.golden
index 036a1a357e..4880365501 100644
--- a/cli/testdata/coder_ssh_--help.golden
+++ b/cli/testdata/coder_ssh_--help.golden
@@ -25,15 +25,18 @@ Start a shell into a workspace
Enable diagnostic logging to file.
--no-wait bool, $CODER_SSH_NO_WAIT
- Specifies whether to wait for a workspace to become ready before
- logging in (only applicable when the startup script behavior is
- blocking). Note that the workspace agent may still be in the process
- of executing the startup script and the workspace may be in an
- incomplete state.
-
+ Enter workspace immediately after the agent has connected. This is the
+ default if the template has configured the agent startup script
+ behavior as non-blocking.
+ DEPRECATED
--stdio bool, $CODER_SSH_STDIO
Specifies whether to emit SSH output over stdin/stdout.
+ --wait yes|no|auto, $CODER_SSH_WAIT (default: auto)
+ Specifies whether or not to wait for the startup script to finish
+ executing. Auto means that the agent startup script behavior
+ configured in the workspace template is used.
+
--workspace-poll-interval duration, $CODER_WORKSPACE_POLL_INTERVAL (default: 1m)
Specifies how often to poll for workspace automated shutdown.
diff --git a/coderd/database/dbfake/databasefake.go b/coderd/database/dbfake/databasefake.go
index 9294335106..f1a6ffe05b 100644
--- a/coderd/database/dbfake/databasefake.go
+++ b/coderd/database/dbfake/databasefake.go
@@ -3060,6 +3060,7 @@ func (q *fakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.Inser
Architecture: arg.Architecture,
OperatingSystem: arg.OperatingSystem,
Directory: arg.Directory,
+ StartupScriptBehavior: arg.StartupScriptBehavior,
StartupScript: arg.StartupScript,
InstanceMetadata: arg.InstanceMetadata,
ResourceMetadata: arg.ResourceMetadata,
diff --git a/docs/cli/ssh.md b/docs/cli/ssh.md
index 7d580b23b0..e6f03f6c32 100644
--- a/docs/cli/ssh.md
+++ b/docs/cli/ssh.md
@@ -65,7 +65,7 @@ Enable diagnostic logging to file.
| Type | bool
|
| Environment | $CODER_SSH_NO_WAIT
|
-Specifies whether to wait for a workspace to become ready before logging in (only applicable when the startup script behavior is blocking). Note that the workspace agent may still be in the process of executing the startup script and the workspace may be in an incomplete state.
+Enter workspace immediately after the agent has connected. This is the default if the template has configured the agent startup script behavior as non-blocking.
### --stdio
@@ -76,6 +76,16 @@ Specifies whether to wait for a workspace to become ready before logging in (onl
Specifies whether to emit SSH output over stdin/stdout.
+### --wait
+
+| | |
+| ----------- | ---------------------------- | --- | ------------ |
+| Type | enum[yes | no | auto]
|
+| Environment | $CODER_SSH_WAIT
|
+| Default | auto
|
+
+Specifies whether or not to wait for the startup script to finish executing. Auto means that the agent startup script behavior configured in the workspace template is used.
+
### --workspace-poll-interval
| | |