Repository: cli/cli
File: pkg/cmd/codespace/ssh.go (Go)

Code before:
				}
			}

			sshUsers <- result
		}()
	}

	go func() {
		wg.Wait()
		close(sshUsers)
	}()

	// While the above fetches are running, ensure that the user has keys installed.
	// That lets us report a more useful error message if they don't.
	if err = checkAuthorizedKeys(ctx, a.apiClient); err != nil {
		return err
	}

	t, err := template.New("ssh_config").Parse(heredoc.Doc(`
		Host cs.{{.Name}}.{{.EscapedRef}}
			User {{.SSHUser}}
			ProxyCommand {{.GHExec}} cs ssh -c {{.Name}} --stdio -- -i {{.AutomaticIdentityFilePath}}
			UserKnownHostsFile=/dev/null
			StrictHostKeyChecking no
			LogLevel quiet
			ControlMaster auto
			IdentityFile {{.AutomaticIdentityFilePath}}

	`))
	if err != nil {
		return fmt.Errorf("error formatting template: %w", err)
	}

	sshDir, err := config.HomeDirPath(".ssh")
	if err != nil {
		return fmt.Errorf("error finding .ssh directory: %w", err)
	}

	automaticIdentityFilePath := path.Join(sshDir, automaticPrivateKeyName)

	ghExec := a.executable.Executable()
	for result := range sshUsers {
		if result.err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", result.err)
			status = cmdutil.SilentError
			continue
		}

		// codespaceSSHConfig contains values needed to write an OpenSSH host
		// configuration for a single codespace. For example:
		//

Review comment (bug):
if I understand it correctly, we don't need this line of change.
it seems `printOpenSSHConfig` won't generate or upload the key, and this `-i` arg will disable the automatic key generation as well. so if the user run `gh cs ssh --config` and `ssh` consecutively, the auth will fail, right?
also ssh args are not used when `--stdio` is present, this can be confusing.

i'd suggest we generate and upload keys when generating the config instead of when this `--stdio` command is executed. otherwise there can be a bad case where `automaticKeySSHKeysExist` being false, `hasUploadedPublicKeyForConfig` being true, and when the user is initiating frequent connections, for example when autocompleting remote file paths for `scp` command.

Code after:
				if err != nil {
					result.err = fmt.Errorf("error getting ssh server details: %w", err)
				} else {
					result.codespace = cs
				}
			}

			sshUsers <- result
		}()
	}

	go func() {
		wg.Wait()
		close(sshUsers)
	}()

	// While the above fetches are running, ensure that the user has keys installed.
	// That lets us report a more useful error message if they don't.
	if err = checkAuthorizedKeys(ctx, a.apiClient); err != nil {
		return err
	}

	t, err := template.New("ssh_config").Parse(heredoc.Doc(`
		Host cs.{{.Name}}.{{.EscapedRef}}
			User {{.SSHUser}}
			ProxyCommand {{.GHExec}} cs ssh -c {{.Name}} --stdio -- -i {{.AutomaticIdentityFilePath}}
			UserKnownHostsFile=/dev/null
			StrictHostKeyChecking no
			LogLevel quiet
			ControlMaster auto
			IdentityFile {{.AutomaticIdentityFilePath}}

	`))
	if err != nil {
		return fmt.Errorf("error formatting template: %w", err)
	}

	automaticIdentityFilePath, err := automaticPrivateKeyPath()
	if err != nil {
		return fmt.Errorf("error finding .ssh directory: %w", err)
	}

	ghExec := a.executable.Executable()
	for result := range sshUsers {
		if result.err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", result.err)
			status = cmdutil.SilentError
			continue
		}

		// codespaceSSHConfig contains values needed to write an OpenSSH host