Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion cli/configssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ func (r *RootCmd) configSSH() *serpent.Command {
}
}
root := r.createConfig()
homedir, err := os.UserHomeDir()
homedir, err := realHomeDir()
if err != nil {
return xerrors.Errorf("user home dir failed: %w", err)
}
Expand Down Expand Up @@ -880,6 +880,20 @@ func currentBinPath(w io.Writer) (string, error) {
return exePath, nil
}

// realHomeDir returns the user's actual home directory.
// In snap environments, os.UserHomeDir() returns the snap's isolated home
// directory (e.g., ~/snap/coder/x3/), but we need the actual user's home
// directory for SSH config. This function checks for SNAP_REAL_HOME first,
// which contains the actual home directory in snap environments.
func realHomeDir() (string, error) {
// In snap environments, SNAP_REAL_HOME contains the actual user home.
if snapHome := os.Getenv("SNAP_REAL_HOME"); snapHome != "" {
return snapHome, nil
}
// Fall back to the standard method for non-snap environments.
return os.UserHomeDir()
}

// diffBytes takes two byte slices and diffs them as if they were in a
// file named name.
// nolint: revive // Color is an option, not a control coupling.
Expand Down
51 changes: 51 additions & 0 deletions cli/configssh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -753,3 +753,54 @@ func TestConfigSSH_FileWriteAndOptionsFlow(t *testing.T) {
})
}
}

func TestConfigSSH_SnapEnvironment(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("Snap is not available on Windows")
}

client := coderdtest.New(t, nil)
_ = coderdtest.CreateFirstUser(t, client)

// Create a temporary directory that simulates the real home
realHome := t.TempDir()
realSSHDir := filepath.Join(realHome, ".ssh")
err := os.MkdirAll(realSSHDir, 0o700)
require.NoError(t, err)
realSSHConfig := filepath.Join(realSSHDir, "config")

// Create a separate directory that simulates the snap home
snapHome := t.TempDir()

// Set SNAP_REAL_HOME to point to the real home directory
t.Setenv("SNAP_REAL_HOME", realHome)
// Set HOME to the snap directory to simulate snap environment
t.Setenv("HOME", snapHome)

// Run config-ssh with default path (~/.ssh/config)
// It should use SNAP_REAL_HOME and write to realSSHConfig
args := []string{
"config-ssh",
"--yes", // Skip confirmation prompts
}
inv, root := clitest.New(t, args...)
clitest.SetupConfig(t, client, root)

err = inv.Run()
require.NoError(t, err, "config-ssh should succeed in snap environment")

// Verify that the config file was written to the REAL home directory,
// not the snap home directory
_, err = os.Stat(realSSHConfig)
require.NoError(t, err, "config file should exist in real home directory")

// Verify the config file contains the expected coder section
content, err := os.ReadFile(realSSHConfig)
require.NoError(t, err)
require.Contains(t, string(content), "# ------------START-CODER-----------", "config should contain coder section")

// Verify that nothing was written to the snap home directory
snapSSHConfig := filepath.Join(snapHome, ".ssh", "config")
_, err = os.Stat(snapSSHConfig)
require.True(t, os.IsNotExist(err), "config file should NOT exist in snap home directory")
}
Loading