From 0687f662082af080249b099a4473cd30acb2c8f2 Mon Sep 17 00:00:00 2001
From: Caleb Brose <5447118+cmbrose@users.noreply.github.com>
Date: Tue, 28 Jun 2022 16:26:18 -0500
Subject: [PATCH 01/11] Use `codespaces.auto` instead for the automatic ssh
keys
---
pkg/cmd/codespace/ssh.go | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/pkg/cmd/codespace/ssh.go b/pkg/cmd/codespace/ssh.go
index 292bae0d7..c869f536e 100644
--- a/pkg/cmd/codespace/ssh.go
+++ b/pkg/cmd/codespace/ssh.go
@@ -24,6 +24,8 @@ import (
"github.com/spf13/cobra"
)
+const automaticPrivateKeyName = "codespaces.auto"
+
type sshOptions struct {
codespace string
profile string
@@ -44,6 +46,11 @@ func newSSHCmd(app *App) *cobra.Command {
Long: heredoc.Doc(`
The 'ssh' command is used to SSH into a codespace. In its simplest form, you can
run 'gh cs ssh', select a codespace interactively, and connect.
+
+ By default, the 'ssh' command will create a public/private ssh key pair to
+ authenticate with the codespace (if they haven't already been created) inside the
+ ~/.ssh directory. Any private keys for which the public key has been uploaded to
+ your GitHub profile may also be used instead, if desired.
The 'ssh' command also supports deeper integration with OpenSSH using a
'--config' option that generates per-codespace ssh configuration in OpenSSH
@@ -125,7 +132,7 @@ func (a *App) SSH(ctx context.Context, sshArgs []string, opts sshOptions) (err e
startSSHOptions := liveshare.StartSSHServerOptions{}
if shouldGenerateSSHKeys(args, opts) && sshContext.HasKeygen() {
- keyPair, err := sshContext.GenerateSSHKey("codespaces", "")
+ keyPair, err := sshContext.GenerateSSHKey(automaticPrivateKeyName, "")
if err != nil && !errors.Is(err, ssh.ErrKeyAlreadyExists) {
return fmt.Errorf("failed to generate ssh keys: %w", err)
}
@@ -381,6 +388,11 @@ func newCpCmd(app *App) *cobra.Command {
be evaluated on the remote machine, subject to expansion of tildes, braces, globs,
environment variables, and backticks. For security, do not use this flag with arguments
provided by untrusted users; see for discussion.
+
+ By default, the 'cp' command will create a public/private ssh key pair to authenticate with
+ the codespace (if they haven't already been created) inside the ~/.ssh directory. Any private
+ keys for which the public key has been uploaded to your GitHub profile may also be used
+ instead, if desired.
`, "`"),
Example: heredoc.Doc(`
$ gh codespace cp -e README.md 'remote:/workspaces/$RepositoryName/'
From 19b540081149cf312acbb9c52ad35dba079992f4 Mon Sep 17 00:00:00 2001
From: Caleb Brose <5447118+cmbrose@users.noreply.github.com>
Date: Wed, 29 Jun 2022 10:46:44 -0500
Subject: [PATCH 02/11] Handle back compat
---
pkg/cmd/codespace/ssh.go | 76 +++++++++++++++++++++++----
pkg/cmd/codespace/ssh_test.go | 98 +++++++++++++++++++++++++++++++++++
2 files changed, 164 insertions(+), 10 deletions(-)
diff --git a/pkg/cmd/codespace/ssh.go b/pkg/cmd/codespace/ssh.go
index c869f536e..3072d251b 100644
--- a/pkg/cmd/codespace/ssh.go
+++ b/pkg/cmd/codespace/ssh.go
@@ -24,6 +24,10 @@ import (
"github.com/spf13/cobra"
)
+// In 2.13.0 these commands started automatically generating key pairs named 'codespaces' and 'codespaces.pub'
+// which could collide with suggested the ssh config also named 'codespaces'. We now use 'codespaces.auto'
+// and 'codespaces.auto.pub' in order to avoid that collision.
+const automaticPrivateKeyNameOld = "codespaces"
const automaticPrivateKeyName = "codespaces.auto"
type sshOptions struct {
@@ -48,9 +52,7 @@ func newSSHCmd(app *App) *cobra.Command {
run 'gh cs ssh', select a codespace interactively, and connect.
By default, the 'ssh' command will create a public/private ssh key pair to
- authenticate with the codespace (if they haven't already been created) inside the
- ~/.ssh directory. Any private keys for which the public key has been uploaded to
- your GitHub profile may also be used instead, if desired.
+ authenticate with the codespace inside the ~/.ssh directory.
The 'ssh' command also supports deeper integration with OpenSSH using a
'--config' option that generates per-codespace ssh configuration in OpenSSH
@@ -131,9 +133,9 @@ func (a *App) SSH(ctx context.Context, sshArgs []string, opts sshOptions) (err e
sshContext := ssh.Context{}
startSSHOptions := liveshare.StartSSHServerOptions{}
- if shouldGenerateSSHKeys(args, opts) && sshContext.HasKeygen() {
- keyPair, err := sshContext.GenerateSSHKey(automaticPrivateKeyName, "")
- if err != nil && !errors.Is(err, ssh.ErrKeyAlreadyExists) {
+ if shouldUseAutomaticSSHKeys(args, opts) {
+ keyPair, err := setupAutomaticSSHKeys(sshContext)
+ if err != nil {
return fmt.Errorf("failed to generate ssh keys: %w", err)
}
@@ -214,7 +216,7 @@ func (a *App) SSH(ctx context.Context, sshArgs []string, opts sshOptions) (err e
}
}
-func shouldGenerateSSHKeys(args []string, opts sshOptions) bool {
+func shouldUseAutomaticSSHKeys(args []string, opts sshOptions) bool {
if opts.profile != "" {
// The profile may specify the identity file so cautiously don't override anything with that option
return false
@@ -235,6 +237,62 @@ func shouldGenerateSSHKeys(args []string, opts sshOptions) bool {
return true
}
+func setupAutomaticSSHKeys(sshContext ssh.Context) (*ssh.KeyPair, error) {
+ keyPair := checkAndUpdateOldKeyPair(sshContext)
+ if keyPair != nil {
+ return keyPair, nil
+ }
+
+ keyPair, err := sshContext.GenerateSSHKey(automaticPrivateKeyName, "")
+ if err != nil && !errors.Is(err, ssh.ErrKeyAlreadyExists) {
+ return nil, err
+ }
+
+ return keyPair, nil
+}
+
+func checkAndUpdateOldKeyPair(sshContext ssh.Context) *ssh.KeyPair {
+ publicKeys, err := sshContext.LocalPublicKeys()
+ if err != nil {
+ return nil
+ }
+
+ for _, publicKey := range publicKeys {
+ if !strings.HasSuffix(publicKey, automaticPrivateKeyNameOld+".pub") {
+ continue
+ }
+
+ privateKey := strings.TrimSuffix(publicKey, ".pub")
+ _, err := os.Stat(privateKey)
+ if err != nil {
+ continue
+ }
+
+ // Both old public and private keys exist, rename them to the new name
+
+ privateKeyNew := strings.Replace(privateKey, automaticPrivateKeyNameOld, automaticPrivateKeyName, -1)
+ err = os.Rename(privateKey, privateKeyNew)
+ if err != nil {
+ return nil
+ }
+
+ publicKeyNew := strings.Replace(publicKey, automaticPrivateKeyNameOld, automaticPrivateKeyName, -1)
+ err = os.Rename(publicKey, publicKeyNew)
+ if err != nil {
+ return nil
+ }
+
+ keyPair := &ssh.KeyPair{
+ PublicKeyPath: publicKeyNew,
+ PrivateKeyPath: privateKeyNew,
+ }
+
+ return keyPair
+ }
+
+ return nil
+}
+
func (a *App) printOpenSSHConfig(ctx context.Context, opts sshOptions) (err error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
@@ -390,9 +448,7 @@ func newCpCmd(app *App) *cobra.Command {
provided by untrusted users; see for discussion.
By default, the 'cp' command will create a public/private ssh key pair to authenticate with
- the codespace (if they haven't already been created) inside the ~/.ssh directory. Any private
- keys for which the public key has been uploaded to your GitHub profile may also be used
- instead, if desired.
+ the codespace inside the ~/.ssh directory.
`, "`"),
Example: heredoc.Doc(`
$ gh codespace cp -e README.md 'remote:/workspaces/$RepositoryName/'
diff --git a/pkg/cmd/codespace/ssh_test.go b/pkg/cmd/codespace/ssh_test.go
index 63a8961f3..753772c7d 100644
--- a/pkg/cmd/codespace/ssh_test.go
+++ b/pkg/cmd/codespace/ssh_test.go
@@ -2,10 +2,14 @@ package codespace
import (
"context"
+ "io/ioutil"
+ "os"
+ "path/filepath"
"testing"
"github.com/cli/cli/v2/internal/codespaces/api"
"github.com/cli/cli/v2/pkg/iostreams"
+ "github.com/cli/cli/v2/pkg/ssh"
)
func TestPendingOperationDisallowsSSH(t *testing.T) {
@@ -20,6 +24,100 @@ func TestPendingOperationDisallowsSSH(t *testing.T) {
}
}
+func TestAutomaticSSHKeyPairs(t *testing.T) {
+ dir := t.TempDir()
+
+ sshContext := ssh.Context{
+ ConfigDir: dir,
+ }
+
+ tests := []struct {
+ // These files exist when calling setupAutomaticSSHKeys
+ existingFiles []string
+ // These files should exist after setupAutomaticSSHKeys finishes
+ wantFinalFiles []string
+ }{
+ // Basic case: no existing keys, they should be created
+ {
+ nil,
+ []string{automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
+ },
+ // Basic case: keys already exist
+ {
+ []string{automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
+ []string{automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
+ },
+ // Backward compatibility: both old keys exist, they should be renamed
+ {
+ []string{automaticPrivateKeyNameOld, automaticPrivateKeyNameOld + ".pub"},
+ []string{automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
+ },
+ // Backward compatibility: old private key exists but not the public key, the new keys should be created
+ {
+ []string{automaticPrivateKeyNameOld},
+ []string{automaticPrivateKeyNameOld, automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
+ },
+ // Backward compatibility: old public key exists but not the private key, the new keys should be created
+ {
+ []string{automaticPrivateKeyNameOld + ".pub"},
+ []string{automaticPrivateKeyNameOld + ".pub", automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
+ },
+ }
+
+ for _, tt := range tests {
+ err := os.RemoveAll(dir)
+ if err != nil {
+ t.Errorf("Failed to clean test directory: %v", err)
+ }
+ os.MkdirAll(dir, 0711)
+ if err != nil {
+ t.Errorf("Failed to set up test directory: %v", err)
+ }
+
+ for _, file := range tt.existingFiles {
+ if _, err := os.Create(filepath.Join(dir, file)); err != nil {
+ t.Errorf("Failed to setup test files: %v", err)
+ }
+ }
+
+ keyPair, err := setupAutomaticSSHKeys(sshContext)
+ if err != nil {
+ t.Errorf("Unexpected error from setupAutomaticSSHKeys: %v", err)
+ }
+ if keyPair == nil {
+ t.Error("Unexpected nil KeyPair from setupAutomaticSSHKeys")
+ }
+
+ // Check that all the expected files are present
+ for _, file := range tt.wantFinalFiles {
+ if _, err := os.Stat(filepath.Join(dir, file)); err != nil {
+ t.Errorf("Want file %q to exist after setupAutomaticSSHKeys but it doesn't", file)
+ }
+ }
+
+ // Check that no unexpected files are present
+ allExistingFiles, err := ioutil.ReadDir(dir)
+ if err != nil {
+ t.Errorf("Failed to list files in test directory: %v", err)
+ }
+ for _, file := range allExistingFiles {
+ filename := file.Name()
+ isWantedFile := false
+ for _, wantedFile := range tt.wantFinalFiles {
+ if filename == wantedFile {
+ isWantedFile = true
+ break
+ }
+ }
+
+ if !isWantedFile {
+ t.Errorf("Unexpected file %q exists after setupAutomaticSSHKeys", filename)
+ }
+ }
+ }
+
+}
+
func testingSSHApp() *App {
user := &api.User{Login: "monalisa"}
disabledCodespace := &api.Codespace{
From c5b07762d16e6a85df80a323518931f155253132 Mon Sep 17 00:00:00 2001
From: Caleb Brose <5447118+cmbrose@users.noreply.github.com>
Date: Wed, 29 Jun 2022 10:51:17 -0500
Subject: [PATCH 03/11] Check the key paths too
---
pkg/cmd/codespace/ssh_test.go | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/pkg/cmd/codespace/ssh_test.go b/pkg/cmd/codespace/ssh_test.go
index 753772c7d..2939739d7 100644
--- a/pkg/cmd/codespace/ssh_test.go
+++ b/pkg/cmd/codespace/ssh_test.go
@@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
+ "strings"
"testing"
"github.com/cli/cli/v2/internal/codespaces/api"
@@ -87,6 +88,12 @@ func TestAutomaticSSHKeyPairs(t *testing.T) {
if keyPair == nil {
t.Error("Unexpected nil KeyPair from setupAutomaticSSHKeys")
}
+ if !strings.HasSuffix(keyPair.PrivateKeyPath, automaticPrivateKeyName) {
+ t.Errorf("Expected private key path %v, got %v", automaticPrivateKeyName, keyPair.PrivateKeyPath)
+ }
+ if !strings.HasSuffix(keyPair.PublicKeyPath, automaticPrivateKeyName+".pub") {
+ t.Errorf("Expected public key path %v, got %v", automaticPrivateKeyName+".pub", keyPair.PublicKeyPath)
+ }
// Check that all the expected files are present
for _, file := range tt.wantFinalFiles {
From 2ac379f6891954266edfde683efb1193b3cdfbe9 Mon Sep 17 00:00:00 2001
From: Caleb Brose <5447118+cmbrose@users.noreply.github.com>
Date: Wed, 29 Jun 2022 10:54:07 -0500
Subject: [PATCH 04/11] Comment
---
pkg/cmd/codespace/ssh.go | 3 +++
1 file changed, 3 insertions(+)
diff --git a/pkg/cmd/codespace/ssh.go b/pkg/cmd/codespace/ssh.go
index 3072d251b..5ded0a8a7 100644
--- a/pkg/cmd/codespace/ssh.go
+++ b/pkg/cmd/codespace/ssh.go
@@ -251,6 +251,9 @@ func setupAutomaticSSHKeys(sshContext ssh.Context) (*ssh.KeyPair, error) {
return keyPair, nil
}
+// checkAndUpdateOldKeyPair handles backward compatibility with the old keypair names.
+// If the old public and private keys both exist they are renamed to the new name.
+// The return value is non-nil only if the rename happens.
func checkAndUpdateOldKeyPair(sshContext ssh.Context) *ssh.KeyPair {
publicKeys, err := sshContext.LocalPublicKeys()
if err != nil {
From 77ecd0a1472fc9b8ea80457532cad29631c69cfd Mon Sep 17 00:00:00 2001
From: Caleb Brose <5447118+cmbrose@users.noreply.github.com>
Date: Wed, 29 Jun 2022 10:56:31 -0500
Subject: [PATCH 05/11] Rename public key first for edge cases
---
pkg/cmd/codespace/ssh.go | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/pkg/cmd/codespace/ssh.go b/pkg/cmd/codespace/ssh.go
index 5ded0a8a7..762f7db9c 100644
--- a/pkg/cmd/codespace/ssh.go
+++ b/pkg/cmd/codespace/ssh.go
@@ -273,14 +273,14 @@ func checkAndUpdateOldKeyPair(sshContext ssh.Context) *ssh.KeyPair {
// Both old public and private keys exist, rename them to the new name
- privateKeyNew := strings.Replace(privateKey, automaticPrivateKeyNameOld, automaticPrivateKeyName, -1)
- err = os.Rename(privateKey, privateKeyNew)
+ publicKeyNew := strings.Replace(publicKey, automaticPrivateKeyNameOld, automaticPrivateKeyName, -1)
+ err = os.Rename(publicKey, publicKeyNew)
if err != nil {
return nil
}
- publicKeyNew := strings.Replace(publicKey, automaticPrivateKeyNameOld, automaticPrivateKeyName, -1)
- err = os.Rename(publicKey, publicKeyNew)
+ privateKeyNew := strings.Replace(privateKey, automaticPrivateKeyNameOld, automaticPrivateKeyName, -1)
+ err = os.Rename(privateKey, privateKeyNew)
if err != nil {
return nil
}
From 1bae759d3e3953d31f1219fb1e51a8803af0e8a5 Mon Sep 17 00:00:00 2001
From: Caleb Brose <5447118+cmbrose@users.noreply.github.com>
Date: Wed, 29 Jun 2022 11:10:50 -0500
Subject: [PATCH 06/11] Don't use strings.Replace
---
pkg/cmd/codespace/ssh.go | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/pkg/cmd/codespace/ssh.go b/pkg/cmd/codespace/ssh.go
index 762f7db9c..4cc08e084 100644
--- a/pkg/cmd/codespace/ssh.go
+++ b/pkg/cmd/codespace/ssh.go
@@ -273,13 +273,15 @@ func checkAndUpdateOldKeyPair(sshContext ssh.Context) *ssh.KeyPair {
// Both old public and private keys exist, rename them to the new name
- publicKeyNew := strings.Replace(publicKey, automaticPrivateKeyNameOld, automaticPrivateKeyName, -1)
+ sshDir := filepath.Dir(publicKey)
+
+ publicKeyNew := filepath.Join(sshDir, automaticPrivateKeyName+".pub")
err = os.Rename(publicKey, publicKeyNew)
if err != nil {
return nil
}
- privateKeyNew := strings.Replace(privateKey, automaticPrivateKeyNameOld, automaticPrivateKeyName, -1)
+ privateKeyNew := filepath.Join(sshDir, automaticPrivateKeyName)
err = os.Rename(privateKey, privateKeyNew)
if err != nil {
return nil
From fb4ad53dd074ca6786389fe3ae46f531c1958dff Mon Sep 17 00:00:00 2001
From: Caleb Brose <5447118+cmbrose@users.noreply.github.com>
Date: Wed, 29 Jun 2022 11:17:24 -0500
Subject: [PATCH 07/11] Check MkdirAll error
---
pkg/cmd/codespace/ssh_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pkg/cmd/codespace/ssh_test.go b/pkg/cmd/codespace/ssh_test.go
index 2939739d7..895a53b9f 100644
--- a/pkg/cmd/codespace/ssh_test.go
+++ b/pkg/cmd/codespace/ssh_test.go
@@ -70,7 +70,7 @@ func TestAutomaticSSHKeyPairs(t *testing.T) {
if err != nil {
t.Errorf("Failed to clean test directory: %v", err)
}
- os.MkdirAll(dir, 0711)
+ err = os.MkdirAll(dir, 0711)
if err != nil {
t.Errorf("Failed to set up test directory: %v", err)
}
From 1aa457499d37a1987f3b664a9e1f10968c9afb94 Mon Sep 17 00:00:00 2001
From: Caleb Brose <5447118+cmbrose@users.noreply.github.com>
Date: Wed, 29 Jun 2022 11:27:18 -0500
Subject: [PATCH 08/11] Use Fatal instead to avoid nil-ref
---
pkg/cmd/codespace/ssh_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pkg/cmd/codespace/ssh_test.go b/pkg/cmd/codespace/ssh_test.go
index 895a53b9f..eb1b93ab9 100644
--- a/pkg/cmd/codespace/ssh_test.go
+++ b/pkg/cmd/codespace/ssh_test.go
@@ -86,7 +86,7 @@ func TestAutomaticSSHKeyPairs(t *testing.T) {
t.Errorf("Unexpected error from setupAutomaticSSHKeys: %v", err)
}
if keyPair == nil {
- t.Error("Unexpected nil KeyPair from setupAutomaticSSHKeys")
+ t.Fatal("Unexpected nil KeyPair from setupAutomaticSSHKeys")
}
if !strings.HasSuffix(keyPair.PrivateKeyPath, automaticPrivateKeyName) {
t.Errorf("Expected private key path %v, got %v", automaticPrivateKeyName, keyPair.PrivateKeyPath)
From b5348f661e2e0842965fc53cad70129f8ee76079 Mon Sep 17 00:00:00 2001
From: Caleb Brose <5447118+cmbrose@users.noreply.github.com>
Date: Wed, 29 Jun 2022 11:36:27 -0500
Subject: [PATCH 09/11] Handle case of partial name match
---
pkg/cmd/codespace/ssh.go | 2 +-
pkg/cmd/codespace/ssh_test.go | 5 +++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/pkg/cmd/codespace/ssh.go b/pkg/cmd/codespace/ssh.go
index 4cc08e084..e52422e51 100644
--- a/pkg/cmd/codespace/ssh.go
+++ b/pkg/cmd/codespace/ssh.go
@@ -261,7 +261,7 @@ func checkAndUpdateOldKeyPair(sshContext ssh.Context) *ssh.KeyPair {
}
for _, publicKey := range publicKeys {
- if !strings.HasSuffix(publicKey, automaticPrivateKeyNameOld+".pub") {
+ if filepath.Base(publicKey) != automaticPrivateKeyNameOld+".pub" {
continue
}
diff --git a/pkg/cmd/codespace/ssh_test.go b/pkg/cmd/codespace/ssh_test.go
index eb1b93ab9..3f2a8a23b 100644
--- a/pkg/cmd/codespace/ssh_test.go
+++ b/pkg/cmd/codespace/ssh_test.go
@@ -63,6 +63,11 @@ func TestAutomaticSSHKeyPairs(t *testing.T) {
[]string{automaticPrivateKeyNameOld + ".pub"},
[]string{automaticPrivateKeyNameOld + ".pub", automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
},
+ // Backward compatibility (edge case): files exist which contains old key name as a substring, the new keys should be created
+ {
+ []string{"foo" + automaticPrivateKeyNameOld + ".pub", "foo" + automaticPrivateKeyNameOld},
+ []string{"foo" + automaticPrivateKeyNameOld + ".pub", "foo" + automaticPrivateKeyNameOld, automaticPrivateKeyName, automaticPrivateKeyName + ".pub"},
+ },
}
for _, tt := range tests {
From 4eac4fbf4cca256082d6baca260f572d84305f42 Mon Sep 17 00:00:00 2001
From: Caleb Brose <5447118+cmbrose@users.noreply.github.com>
Date: Wed, 29 Jun 2022 11:48:23 -0500
Subject: [PATCH 10/11] Make a new dir per test to work around windows failures
---
pkg/cmd/codespace/ssh_test.go | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)
diff --git a/pkg/cmd/codespace/ssh_test.go b/pkg/cmd/codespace/ssh_test.go
index 3f2a8a23b..adad394d3 100644
--- a/pkg/cmd/codespace/ssh_test.go
+++ b/pkg/cmd/codespace/ssh_test.go
@@ -26,12 +26,6 @@ func TestPendingOperationDisallowsSSH(t *testing.T) {
}
func TestAutomaticSSHKeyPairs(t *testing.T) {
- dir := t.TempDir()
-
- sshContext := ssh.Context{
- ConfigDir: dir,
- }
-
tests := []struct {
// These files exist when calling setupAutomaticSSHKeys
existingFiles []string
@@ -71,15 +65,14 @@ func TestAutomaticSSHKeyPairs(t *testing.T) {
}
for _, tt := range tests {
- err := os.RemoveAll(dir)
- if err != nil {
- t.Errorf("Failed to clean test directory: %v", err)
- }
- err = os.MkdirAll(dir, 0711)
- if err != nil {
- t.Errorf("Failed to set up test directory: %v", err)
+ dir := t.TempDir()
+
+ sshContext := ssh.Context{
+ ConfigDir: dir,
}
+ defer os.RemoveAll(dir)
+
for _, file := range tt.existingFiles {
if _, err := os.Create(filepath.Join(dir, file)); err != nil {
t.Errorf("Failed to setup test files: %v", err)
From b2fe32901128e45c5fc9f365b150614deb2a8e7e Mon Sep 17 00:00:00 2001
From: cmbrose <5447118+cmbrose@users.noreply.github.com>
Date: Wed, 29 Jun 2022 12:28:21 -0500
Subject: [PATCH 11/11] Close files to actually fix windows
---
pkg/cmd/codespace/ssh_test.go | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/pkg/cmd/codespace/ssh_test.go b/pkg/cmd/codespace/ssh_test.go
index adad394d3..8bc6c517f 100644
--- a/pkg/cmd/codespace/ssh_test.go
+++ b/pkg/cmd/codespace/ssh_test.go
@@ -71,12 +71,13 @@ func TestAutomaticSSHKeyPairs(t *testing.T) {
ConfigDir: dir,
}
- defer os.RemoveAll(dir)
-
for _, file := range tt.existingFiles {
- if _, err := os.Create(filepath.Join(dir, file)); err != nil {
+ f, err := os.Create(filepath.Join(dir, file))
+ if err != nil {
t.Errorf("Failed to setup test files: %v", err)
}
+ // If the file isn't closed here windows will have errors about file already in use
+ f.Close()
}
keyPair, err := setupAutomaticSSHKeys(sshContext)