Skip to content
Merged
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
26 changes: 25 additions & 1 deletion internal/commands/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,29 @@ func newAuthLoginCmd() *cobra.Command {
return cmd
}

// normalizeAccount accepts user-supplied account values in any of these forms
// and returns just the subdomain:
//
// mycompany
// mycompany.deployhq.com
// https://mycompany.deployhq.com/
//
// host is the configured DeployHQ host (defaults to "deployhq.com") so the
// same logic works for staging/self-hosted setups.
func normalizeAccount(input, host string) string {
s := strings.TrimSpace(input)
s = strings.TrimPrefix(s, "https://")
s = strings.TrimPrefix(s, "http://")
if i := strings.IndexAny(s, "/?#"); i >= 0 {
s = s[:i]
}
if host == "" {
host = "deployhq.com"
}
s = strings.TrimSuffix(s, "."+host)
return s
}

func runAuthLogin(opts *AuthLoginOptions) error {
env := cliCtx.Envelope
reader := bufio.NewReader(os.Stdin)
Expand All @@ -79,10 +102,11 @@ func runAuthLogin(opts *AuthLoginOptions) error {
Hint: "Use --account flag",
}
}
fmt.Fprint(env.Stderr, "Account subdomain: ") //nolint:errcheck // best-effort stderr
fmt.Fprint(env.Stderr, "Account subdomain (e.g. 'mycompany' for mycompany.deployhq.com): ") //nolint:errcheck // best-effort stderr
input, _ := reader.ReadString('\n')
opts.Account = strings.TrimSpace(input)
}
opts.Account = normalizeAccount(opts.Account, cliCtx.Config.Host)

if opts.Email == "" {
if env.NonInteractive {
Expand Down
32 changes: 32 additions & 0 deletions internal/commands/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package commands

import "testing"

func TestNormalizeAccount(t *testing.T) {
tests := []struct {
name string
input string
host string
want string
}{
{"bare subdomain", "mycompany", "", "mycompany"},
{"full hostname", "mycompany.deployhq.com", "", "mycompany"},
{"hostname with scheme", "https://mycompany.deployhq.com", "", "mycompany"},
{"hostname with scheme and trailing slash", "https://mycompany.deployhq.com/", "", "mycompany"},
{"hostname with path", "https://mycompany.deployhq.com/projects", "", "mycompany"},
{"http scheme", "http://mycompany.deployhq.com", "", "mycompany"},
{"surrounding whitespace", " mycompany.deployhq.com ", "", "mycompany"},
{"custom host", "mycompany.deployhq.dev", "deployhq.dev", "mycompany"},
{"custom host with default host suffix is not stripped", "mycompany.deployhq.com", "deployhq.dev", "mycompany.deployhq.com"},
{"hyphenated subdomain", "my-company.deployhq.com", "", "my-company"},
{"query string ignored", "mycompany.deployhq.com?foo=bar", "", "mycompany"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := normalizeAccount(tt.input, tt.host)
if got != tt.want {
t.Errorf("normalizeAccount(%q, %q) = %q, want %q", tt.input, tt.host, got, tt.want)
}
})
}
}
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ func (cfg *Config) BaseURL(account string) string {
if cfg.Host == "" {
return ""
}
// Tolerate accounts saved with the host suffix already attached.
account = strings.TrimSuffix(account, "."+cfg.Host)
return fmt.Sprintf("https://%s.%s", account, cfg.Host)
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/sdk/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ func New(account, email, apiKey string, opts ...Option) (*Client, error) {
return nil, fmt.Errorf("deployhq: api key is required")
}

// Tolerate users passing a full hostname (e.g. "mycompany.deployhq.com")
// instead of just the subdomain — otherwise we'd build .deployhq.com.deployhq.com.
account = strings.TrimSuffix(account, ".deployhq.com")

base, err := url.Parse(fmt.Sprintf("https://%s.deployhq.com", account))
if err != nil {
return nil, fmt.Errorf("deployhq: invalid account name %q: %w", account, err)
Expand Down
Loading