Skip to content

Commit e61da20

Browse files
authored
[auth] save Github username to local file (#429)
## Summary During the `devbox cloud shell` command execution, we now save the user's Github username in `~/.config/devbox/ssh/github_username`. This should improve the UX since they won't need to keep re-entering this information. We wait for the `gateway auth` to successfully return a VM before saving the info in the `github_username` file. Because the gateway verifies that the user's github username is valid. In case the user wishes to override the cached github username, they can use the new `--username` or `-u` flag in `devbox cloud shell`. ## How was it tested? ``` > devbox cloud shell .... > cat ~/.config/devbox/ssh/github_username savil ``` I then edited `github_username` to have an incorrect value like `savil2`. And cleared the socker in `~/.config/devbox/ssh/sockets` to ensure the CLI client calls the gateway again. This time `devbox cloud shell` gave an error about using a bad username `savil2`. I then did: `devbox cloud shell -u savil` to fix the situation.
1 parent aa9f515 commit e61da20

File tree

4 files changed

+90
-14
lines changed

4 files changed

+90
-14
lines changed

internal/boxcli/cloud.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515

1616
type cloudShellCmdFlags struct {
1717
config configFlags
18+
19+
githubUsername string
1820
}
1921

2022
func CloudCmd() *cobra.Command {
@@ -43,6 +45,9 @@ func cloudShellCmd() *cobra.Command {
4345
}
4446

4547
flags.config.register(command)
48+
command.Flags().StringVarP(
49+
&flags.githubUsername, "username", "u", "", "Github username to use for ssh",
50+
)
4651
return command
4752
}
4853

@@ -74,5 +79,5 @@ func runCloudShellCmd(cmd *cobra.Command, flags *cloudShellCmdFlags) error {
7479
if err != nil {
7580
return errors.WithStack(err)
7681
}
77-
return cloud.Shell(box.ProjectDir(), cmd.ErrOrStderr())
82+
return cloud.Shell(cmd.ErrOrStderr(), box.ProjectDir(), flags.githubUsername)
7883
}

internal/cloud/cloud.go

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,20 @@ import (
2727
"go.jetpack.io/devbox/internal/debug"
2828
)
2929

30-
func Shell(projectDir string, w io.Writer) error {
30+
func Shell(w io.Writer, projectDir string, githubUsername string) error {
3131
c := color.New(color.FgMagenta).Add(color.Bold)
3232
c.Fprintln(w, "Devbox Cloud")
3333
fmt.Fprintln(w, "Remote development environments powered by Nix")
3434
fmt.Fprint(w, "\n")
3535

3636
username, vmHostname := parseVMEnvVar()
37+
// The flag for githubUsername overrides any env-var, since flags are a more
38+
// explicit action compared to an env-var which could be latently present.
39+
if githubUsername != "" {
40+
username = githubUsername
41+
}
3742
if username == "" {
38-
stepGithubUsername := stepper.Start("Detecting your Github username...")
39-
var err error
40-
username, err = queryGithubUsername()
41-
if err == nil && username != "" {
42-
stepGithubUsername.Success("Username: %s", username)
43-
} else {
44-
stepGithubUsername.Fail("Unable to resolve username")
45-
// The query for Github username is best effort, and if it fails to resolve
46-
// we fallback to prompting the user, and suggesting the local computer username.
47-
username = promptUsername()
48-
}
43+
username = getGithubUsername()
4944
}
5045
debug.Log("username: %s", username)
5146

@@ -80,6 +75,14 @@ func Shell(projectDir string, w io.Writer) error {
8075
var region string
8176
vmHostname, region = getVirtualMachine(sshClient)
8277
stepVM.Success("Created a virtual machine in %s", fly.RegionName(region))
78+
79+
// We save the username to local file only after we get a successful response
80+
// from the gateway, because the gateway will verify that the user's SSH keys
81+
// match their claimed username from github.
82+
err = openssh.SaveGithubUsernameToLocalFile(username)
83+
if err != nil {
84+
debug.Log("Failed to save username: %v", err)
85+
}
8386
}
8487
}
8588
debug.Log("vm_hostname: %s", vmHostname)
@@ -109,17 +112,39 @@ func PortForward(local, remote string) error {
109112
return exec.Command("ssh", "-N", vmHostname, "-L", portMap).Run()
110113
}
111114

115+
func getGithubUsername() string {
116+
117+
username, err := openssh.GithubUsernameFromLocalFile()
118+
if err != nil || username == "" {
119+
if err != nil {
120+
debug.Log("failed to get auth.Username. Error: %v", err)
121+
}
122+
123+
username, err = queryGithubUsername()
124+
if err == nil && username != "" {
125+
debug.Log("Username from ssh -T [email protected]: %s", username)
126+
} else {
127+
// The query for Github username is best effort, and if it fails to resolve
128+
// we fallback to prompting the user, and suggesting the local computer username.
129+
username = promptUsername()
130+
}
131+
} else {
132+
debug.Log("Username from locally-cached file: %s", username)
133+
}
134+
return username
135+
}
136+
112137
func promptUsername() string {
113138
username := ""
114139
prompt := &survey.Input{
115140
Message: "What is your github username?",
116141
Default: os.Getenv("USER"),
117142
}
118-
debug.Log("Failed to get username from Github. Falling back to suggesting $USER: %s", prompt.Default)
119143
err := survey.AskOne(prompt, &username, survey.WithValidator(survey.Required))
120144
if err != nil {
121145
log.Fatal(err)
122146
}
147+
debug.Log("Username from prompting user: %s", username)
123148
return username
124149
}
125150

internal/cloud/openssh/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ func EnsureDirExists(path string, perm fs.FileMode, chmod bool) error {
224224
return nil
225225
}
226226

227+
// returns path to ~/.config/devbox/ssh
227228
func devboxSSHDir() (string, error) {
228229
home, err := os.UserHomeDir()
229230
if err != nil {

internal/cloud/openssh/username.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package openssh
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
7+
"github.com/pkg/errors"
8+
"go.jetpack.io/devbox/internal/fileutil"
9+
)
10+
11+
func GithubUsernameFromLocalFile() (string, error) {
12+
filePath, err := usernameFilePath()
13+
if err != nil {
14+
return "", err
15+
}
16+
if !fileutil.Exists(filePath) {
17+
return "", nil
18+
}
19+
20+
username, err := os.ReadFile(filePath)
21+
if err != nil {
22+
return "", errors.WithStack(err)
23+
}
24+
return string(username), nil
25+
}
26+
27+
func SaveGithubUsernameToLocalFile(username string) error {
28+
29+
filePath, err := usernameFilePath()
30+
if err != nil {
31+
return errors.WithStack(err)
32+
}
33+
34+
err = os.WriteFile(filePath, []byte(username), 0600)
35+
return errors.WithStack(err)
36+
}
37+
38+
func usernameFilePath() (string, error) {
39+
sshDir, err := devboxSSHDir()
40+
if err != nil {
41+
return "", errors.WithStack(err)
42+
}
43+
44+
return filepath.Join(sshDir, "github_username"), nil
45+
}

0 commit comments

Comments
 (0)