Skip to content

Commit 8a3fb09

Browse files
Merge pull request #172 from hdresearch/signup-email-flag
feat: add --email flag to `vers signup`, persist email and SSH key path in config
2 parents 5f89c95 + 3383622 commit 8a3fb09

4 files changed

Lines changed: 57 additions & 25 deletions

File tree

cmd/login.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func loginWithGit() error {
9191

9292
// Step 2: Find SSH public key
9393
fmt.Print("Looking up SSH public key... ")
94-
sshPubKey, err := auth.FindSSHPublicKey()
94+
sshPubKey, _, err := auth.FindSSHPublicKey()
9595
if err != nil {
9696
fmt.Println("✗")
9797
return err

cmd/signup.go

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,32 @@ import (
1111
)
1212

1313
var (
14-
signupGit bool
15-
signupOrg string
14+
signupGit bool
15+
signupOrg string
16+
signupEmail string
1617
)
1718

1819
// signupWithGit authenticates using the Shell Auth flow with git email + SSH key.
1920
func signupWithGit() error {
20-
// Step 1: Get git email
21-
fmt.Print("Looking up git email... ")
22-
email, err := auth.GetGitEmail()
23-
if err != nil {
24-
fmt.Println("✗")
25-
return err
21+
// Step 1: Get email
22+
var email string
23+
if signupEmail != "" {
24+
email = signupEmail
25+
fmt.Printf("Using email: %s\n", email)
26+
} else {
27+
fmt.Print("Looking up git email... ")
28+
var err error
29+
email, err = auth.GetGitEmail()
30+
if err != nil {
31+
fmt.Println("✗")
32+
return err
33+
}
34+
fmt.Println(email)
2635
}
27-
fmt.Println(email)
2836

2937
// Step 2: Find SSH public key
3038
fmt.Print("Looking up SSH public key... ")
31-
sshPubKey, err := auth.FindSSHPublicKey()
39+
sshPubKey, sshKeyPath, err := auth.FindSSHPublicKey()
3240
if err != nil {
3341
fmt.Println("✗")
3442
return err
@@ -131,8 +139,15 @@ func signupWithGit() error {
131139
return err
132140
}
133141

134-
if err := auth.SaveAPIKey(keyResp.APIKey); err != nil {
135-
return fmt.Errorf("error saving API key: %w", err)
142+
config, err := auth.LoadConfig()
143+
if err != nil {
144+
return fmt.Errorf("error loading config: %w", err)
145+
}
146+
config.APIKey = keyResp.APIKey
147+
config.Email = email
148+
config.SSHKeyPath = sshKeyPath
149+
if err := auth.SaveConfig(config); err != nil {
150+
return fmt.Errorf("error saving config: %w", err)
136151
}
137152

138153
fmt.Printf("\n✓ Successfully authenticated with Vers (org: %s)\n", keyResp.OrgName)
@@ -142,14 +157,15 @@ func signupWithGit() error {
142157
var signupCmd = &cobra.Command{
143158
Use: "signup",
144159
Short: "Create a Vers account and authenticate",
145-
Long: `Sign up for the Vers platform using your git email and SSH key.
160+
Long: `Sign up for the Vers platform using your email and SSH key.
146161
147162
By default, signup uses your git email and SSH public key to create
148163
an account. A verification email is sent — click the link and you're in.
149164
150-
vers signup Sign up with git email + SSH key (default)
151-
vers signup --org myorg Pick org non-interactively (for scripts/agents)
152-
vers signup --git=false Prompt for an API key instead
165+
vers signup Sign up with git email + SSH key (default)
166+
vers signup --email you@example.com Use a specific email instead of git config
167+
vers signup --org myorg Pick org non-interactively (for scripts/agents)
168+
vers signup --git=false Prompt for an API key instead
153169
154170
If you already have an account, this will log you in.`,
155171
RunE: func(cmd *cobra.Command, args []string) error {
@@ -183,4 +199,5 @@ func init() {
183199
rootCmd.AddCommand(signupCmd)
184200
signupCmd.Flags().BoolVar(&signupGit, "git", true, "Authenticate using your git email and SSH key (default: true)")
185201
signupCmd.Flags().StringVar(&signupOrg, "org", "", "Organization name (skips interactive selection)")
202+
signupCmd.Flags().StringVar(&signupEmail, "email", "", "Email address (overrides git config user.email)")
186203
}

internal/auth/auth.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ const DEFAULT_VERS_URL_STR = "https://api.vers.sh"
1515

1616
// Config represents the structure of the .versrc file
1717
type Config struct {
18-
APIKey string `json:"apiKey"`
18+
APIKey string `json:"apiKey"`
19+
Email string `json:"email,omitempty"`
20+
SSHKeyPath string `json:"sshKeyPath,omitempty"`
1921
}
2022

2123
// GetConfigPath returns the path to the .versrc file in the user's home directory

internal/auth/shellauth.go

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ func GetGitEmail() (string, error) {
9191
}
9292

9393
// FindSSHPublicKey finds the user's SSH public key, checking common locations.
94-
// Returns the key contents (not the path).
95-
func FindSSHPublicKey() (string, error) {
94+
// Returns the key contents and the path where it was found.
95+
func FindSSHPublicKey() (key string, keyPath string, err error) {
9696
home, err := os.UserHomeDir()
9797
if err != nil {
98-
return "", fmt.Errorf("could not find home directory: %w", err)
98+
return "", "", fmt.Errorf("could not find home directory: %w", err)
9999
}
100100

101101
// Check common SSH key paths in preference order
@@ -110,13 +110,26 @@ func FindSSHPublicKey() (string, error) {
110110
if err != nil {
111111
continue
112112
}
113-
key := strings.TrimSpace(string(data))
114-
if key != "" {
115-
return key, nil
113+
k := strings.TrimSpace(string(data))
114+
if k != "" {
115+
return k, path, nil
116116
}
117117
}
118118

119-
return "", fmt.Errorf("no SSH public key found — checked: %s\nGenerate one with: ssh-keygen -t ed25519", strings.Join(candidates, ", "))
119+
return "", "", fmt.Errorf("no SSH public key found — checked: %s\nGenerate one with: ssh-keygen -t ed25519", strings.Join(candidates, ", "))
120+
}
121+
122+
// ReadSSHPublicKey reads an SSH public key from a specific path.
123+
func ReadSSHPublicKey(path string) (string, error) {
124+
data, err := os.ReadFile(path)
125+
if err != nil {
126+
return "", fmt.Errorf("could not read SSH public key at %s: %w", path, err)
127+
}
128+
key := strings.TrimSpace(string(data))
129+
if key == "" {
130+
return "", fmt.Errorf("SSH public key at %s is empty", path)
131+
}
132+
return key, nil
120133
}
121134

122135
// shellAuthBaseURL returns the base URL for shell auth endpoints.

0 commit comments

Comments
 (0)