From 841e8c25a3582ab9ab9a32fe251a0115ffa357f4 Mon Sep 17 00:00:00 2001 From: Ethan <39577870+ethanndickson@users.noreply.github.com> Date: Thu, 29 May 2025 07:48:35 +0000 Subject: [PATCH] fix: handle `workspace.agent` and `agent.workspace.owner` in `coder ssh` (#18093) Closes #18088. The linked issue is misleading -- `coder config-ssh` continues to support the `coder.` prefix. The reason the command `ssh coder.workspace.agent` fails is because `coder ssh workspace.agent` wasn't supported. This PR fixes that. We know we used to support `workspace.agent`, as this is what we recommend in the Web UI: ![image](https://github.com/user-attachments/assets/702bbbc7-c586-4947-98a6-4508a481280b) This PR also adds support for `coder ssh agent.workspace.owner`, such that after running `coder config-ssh`, a command like ``` ssh agent.workspace.owner.coder ``` works, even without Coder Connect running. This is done for parity with an existing workflow that uses `ssh workspace.coder`, which either uses Coder Connect if available, or the CLI. --- cli/ssh.go | 9 +++++++++ cli/ssh_test.go | 2 ++ 2 files changed, 11 insertions(+) diff --git a/cli/ssh.go b/cli/ssh.go index 7c5bda073f973..dd0568dc5e14c 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -1569,12 +1569,14 @@ func writeCoderConnectNetInfo(ctx context.Context, networkInfoDir string) error // Converts workspace name input to owner/workspace.agent format // Possible valid input formats: // workspace +// workspace.agent // owner/workspace // owner--workspace // owner/workspace--agent // owner/workspace.agent // owner--workspace--agent // owner--workspace.agent +// agent.workspace.owner - for parity with Coder Connect func normalizeWorkspaceInput(input string) string { // Split on "/", "--", and "." parts := workspaceNameRe.Split(input, -1) @@ -1583,8 +1585,15 @@ func normalizeWorkspaceInput(input string) string { case 1: return input // "workspace" case 2: + if strings.Contains(input, ".") { + return fmt.Sprintf("%s.%s", parts[0], parts[1]) // "workspace.agent" + } return fmt.Sprintf("%s/%s", parts[0], parts[1]) // "owner/workspace" case 3: + // If the only separator is a dot, it's the Coder Connect format + if !strings.Contains(input, "/") && !strings.Contains(input, "--") { + return fmt.Sprintf("%s/%s.%s", parts[2], parts[1], parts[0]) // "owner/workspace.agent" + } return fmt.Sprintf("%s/%s.%s", parts[0], parts[1], parts[2]) // "owner/workspace.agent" default: return input // Fallback diff --git a/cli/ssh_test.go b/cli/ssh_test.go index 5fcb6205d5e45..9f85652029f50 100644 --- a/cli/ssh_test.go +++ b/cli/ssh_test.go @@ -107,12 +107,14 @@ func TestSSH(t *testing.T) { cases := []string{ "myworkspace", + "myworkspace.dev", "myuser/myworkspace", "myuser--myworkspace", "myuser/myworkspace--dev", "myuser/myworkspace.dev", "myuser--myworkspace--dev", "myuser--myworkspace.dev", + "dev.myworkspace.myuser", } for _, tc := range cases {