ci: enable nestif linter (#9363)

This commit is contained in:
Ammar Bandukwala 2023-08-30 16:50:43 -05:00 committed by GitHub
parent d29696296f
commit 8f3b4075c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 199 additions and 150 deletions

View File

@ -131,7 +131,8 @@ linters-settings:
- trialer
nestif:
min-complexity: 4 # Min complexity of if statements (def 5, goal 4)
# goal: 10
min-complexity: 20
revive:
# see https://github.com/mgechev/revive#available-rules for details.
@ -237,6 +238,7 @@ linters:
# create a good culture around cognitive complexity.
# - gocyclo
- gocognit
- nestif
- goimports
- gomodguard
- gosec

View File

@ -37,6 +37,95 @@ func init() {
browser.Stdout = io.Discard
}
func promptFirstUsername(inv *clibase.Invocation) (string, error) {
currentUser, err := user.Current()
if err != nil {
return "", xerrors.Errorf("get current user: %w", err)
}
username, err := cliui.Prompt(inv, cliui.PromptOptions{
Text: "What " + cliui.DefaultStyles.Field.Render("username") + " would you like?",
Default: currentUser.Username,
})
if errors.Is(err, cliui.Canceled) {
return "", nil
}
if err != nil {
return "", err
}
return username, nil
}
func promptFirstPassword(inv *clibase.Invocation) (string, error) {
retry:
password, err := cliui.Prompt(inv, cliui.PromptOptions{
Text: "Enter a " + cliui.DefaultStyles.Field.Render("password") + ":",
Secret: true,
Validate: func(s string) error {
return userpassword.Validate(s)
},
})
if err != nil {
return "", xerrors.Errorf("specify password prompt: %w", err)
}
confirm, err := cliui.Prompt(inv, cliui.PromptOptions{
Text: "Confirm " + cliui.DefaultStyles.Field.Render("password") + ":",
Secret: true,
Validate: cliui.ValidateNotEmpty,
})
if err != nil {
return "", xerrors.Errorf("confirm password prompt: %w", err)
}
if confirm != password {
_, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Error.Render("Passwords do not match"))
goto retry
}
return password, nil
}
func (r *RootCmd) loginWithPassword(
inv *clibase.Invocation,
client *codersdk.Client,
email, password string,
) error {
resp, err := client.LoginWithPassword(inv.Context(), codersdk.LoginWithPasswordRequest{
Email: email,
Password: password,
})
if err != nil {
return xerrors.Errorf("login with password: %w", err)
}
sessionToken := resp.SessionToken
config := r.createConfig()
err = config.Session().Write(sessionToken)
if err != nil {
return xerrors.Errorf("write session token: %w", err)
}
client.SetSessionToken(sessionToken)
// Nice side-effect: validates the token.
u, err := client.User(inv.Context(), "me")
if err != nil {
return xerrors.Errorf("get user: %w", err)
}
_, _ = fmt.Fprintf(
inv.Stdout,
cliui.DefaultStyles.Paragraph.Render(
fmt.Sprintf(
"Welcome to Coder, %s! You're authenticated.",
cliui.DefaultStyles.Keyword.Render(u.Username),
),
)+"\n",
)
return nil
}
func (r *RootCmd) login() *clibase.Cmd {
const firstUserTrialEnv = "CODER_FIRST_USER_TRIAL"
@ -91,41 +180,30 @@ func (r *RootCmd) login() *clibase.Cmd {
_, _ = fmt.Fprintln(inv.Stderr, cliui.DefaultStyles.Warn.Render(err.Error()))
}
hasInitialUser, err := client.HasFirstUser(ctx)
hasFirstUser, err := client.HasFirstUser(ctx)
if err != nil {
return xerrors.Errorf("Failed to check server %q for first user, is the URL correct and is coder accessible from your browser? Error - has initial user: %w", serverURL.String(), err)
}
if !hasInitialUser {
if !hasFirstUser {
_, _ = fmt.Fprintf(inv.Stdout, Caret+"Your Coder deployment hasn't been set up!\n")
if username == "" {
if !isTTY(inv) {
return xerrors.New("the initial user cannot be created in non-interactive mode. use the API")
}
_, err := cliui.Prompt(inv, cliui.PromptOptions{
Text: "Would you like to create the first user?",
Default: cliui.ConfirmYes,
IsConfirm: true,
})
if errors.Is(err, cliui.Canceled) {
return nil
}
if err != nil {
return err
}
currentUser, err := user.Current()
username, err = promptFirstUsername(inv)
if err != nil {
return xerrors.Errorf("get current user: %w", err)
}
username, err = cliui.Prompt(inv, cliui.PromptOptions{
Text: "What " + cliui.DefaultStyles.Field.Render("username") + " would you like?",
Default: currentUser.Username,
})
if errors.Is(err, cliui.Canceled) {
return nil
}
if err != nil {
return xerrors.Errorf("pick username prompt: %w", err)
return err
}
}
@ -141,37 +219,14 @@ func (r *RootCmd) login() *clibase.Cmd {
},
})
if err != nil {
return xerrors.Errorf("specify email prompt: %w", err)
return err
}
}
if password == "" {
var matching bool
for !matching {
password, err = cliui.Prompt(inv, cliui.PromptOptions{
Text: "Enter a " + cliui.DefaultStyles.Field.Render("password") + ":",
Secret: true,
Validate: func(s string) error {
return userpassword.Validate(s)
},
})
if err != nil {
return xerrors.Errorf("specify password prompt: %w", err)
}
confirm, err := cliui.Prompt(inv, cliui.PromptOptions{
Text: "Confirm " + cliui.DefaultStyles.Field.Render("password") + ":",
Secret: true,
Validate: cliui.ValidateNotEmpty,
})
if err != nil {
return xerrors.Errorf("confirm password prompt: %w", err)
}
matching = confirm == password
if !matching {
_, _ = fmt.Fprintln(inv.Stdout, cliui.DefaultStyles.Error.Render("Passwords do not match"))
}
password, err = promptFirstPassword(inv)
if err != nil {
return err
}
}
@ -193,29 +248,19 @@ func (r *RootCmd) login() *clibase.Cmd {
if err != nil {
return xerrors.Errorf("create initial user: %w", err)
}
resp, err := client.LoginWithPassword(ctx, codersdk.LoginWithPasswordRequest{
Email: email,
Password: password,
})
err := r.loginWithPassword(inv, client, email, password)
if err != nil {
return xerrors.Errorf("login with password: %w", err)
return err
}
sessionToken := resp.SessionToken
config := r.createConfig()
err = config.Session().Write(sessionToken)
if err != nil {
return xerrors.Errorf("write session token: %w", err)
}
err = config.URL().Write(serverURL.String())
err = r.createConfig().URL().Write(serverURL.String())
if err != nil {
return xerrors.Errorf("write server url: %w", err)
}
_, _ = fmt.Fprintf(inv.Stdout,
cliui.DefaultStyles.Paragraph.Render(fmt.Sprintf("Welcome to Coder, %s! You're authenticated.", cliui.DefaultStyles.Keyword.Render(username)))+"\n")
_, _ = fmt.Fprintf(inv.Stdout,
_, _ = fmt.Fprintf(
inv.Stdout,
cliui.DefaultStyles.Paragraph.Render("Get started by creating a template: "+cliui.DefaultStyles.Code.Render("coder templates init"))+"\n")
return nil
}

View File

@ -97,16 +97,15 @@ func TestLogin(t *testing.T) {
t.Run("InitialUserFlags", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, nil)
doneChan := make(chan struct{})
root, _ := clitest.New(t, "login", client.URL.String(), "--first-user-username", "testuser", "--first-user-email", "user@coder.com", "--first-user-password", "SomeSecurePassword!", "--first-user-trial")
pty := ptytest.New(t).Attach(root)
go func() {
defer close(doneChan)
err := root.Run()
assert.NoError(t, err)
}()
inv, _ := clitest.New(
t, "login", client.URL.String(),
"--first-user-username", "testuser", "--first-user-email", "user@coder.com",
"--first-user-password", "SomeSecurePassword!", "--first-user-trial",
)
pty := ptytest.New(t).Attach(inv)
w := clitest.StartWithWaiter(t, inv)
pty.ExpectMatch("Welcome to Coder")
<-doneChan
w.RequireSuccess()
})
t.Run("InitialUserTTYConfirmPasswordFailAndReprompt", func(t *testing.T) {

View File

@ -279,10 +279,15 @@ func (h *Handler) serveHTML(resp http.ResponseWriter, request *http.Request, req
return false
}
func execTmpl(tmpl *template.Template, state htmlState) ([]byte, error) {
var buf bytes.Buffer
err := tmpl.Execute(&buf, state)
return buf.Bytes(), err
}
// renderWithState will render the file using the given nonce if the file exists
// as a template. If it does not, it will return an error.
func (h *Handler) renderHTMLWithState(r *http.Request, filePath string, state htmlState) ([]byte, error) {
var buf bytes.Buffer
if filePath == "" {
filePath = "index.html"
}
@ -307,96 +312,94 @@ func (h *Handler) renderHTMLWithState(r *http.Request, filePath string, state ht
RedirectToLogin: false,
SessionTokenFunc: nil,
})
if ok && apiKey != nil && actor != nil {
ctx := dbauthz.As(r.Context(), actor.Actor)
if !ok || apiKey == nil || actor == nil {
return execTmpl(tmpl, state)
}
var eg errgroup.Group
var user database.User
orgIDs := []uuid.UUID{}
eg.Go(func() error {
var err error
user, err = h.opts.Database.GetUserByID(ctx, apiKey.UserID)
return err
})
eg.Go(func() error {
memberIDs, err := h.opts.Database.GetOrganizationIDsByMemberIDs(ctx, []uuid.UUID{apiKey.UserID})
if errors.Is(err, sql.ErrNoRows) || len(memberIDs) == 0 {
return nil
ctx := dbauthz.As(r.Context(), actor.Actor)
var eg errgroup.Group
var user database.User
orgIDs := []uuid.UUID{}
eg.Go(func() error {
var err error
user, err = h.opts.Database.GetUserByID(ctx, apiKey.UserID)
return err
})
eg.Go(func() error {
memberIDs, err := h.opts.Database.GetOrganizationIDsByMemberIDs(ctx, []uuid.UUID{apiKey.UserID})
if errors.Is(err, sql.ErrNoRows) || len(memberIDs) == 0 {
return nil
}
if err != nil {
return nil
}
orgIDs = memberIDs[0].OrganizationIDs
return err
})
err := eg.Wait()
if err == nil {
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
user, err := json.Marshal(db2sdk.User(user, orgIDs))
if err == nil {
state.User = html.EscapeString(string(user))
}
if err != nil {
return nil
}
orgIDs = memberIDs[0].OrganizationIDs
return err
})
err := eg.Wait()
if err == nil {
var wg sync.WaitGroup
}()
entitlements := h.Entitlements.Load()
if entitlements != nil {
wg.Add(1)
go func() {
defer wg.Done()
user, err := json.Marshal(db2sdk.User(user, orgIDs))
entitlements, err := json.Marshal(entitlements)
if err == nil {
state.User = html.EscapeString(string(user))
state.Entitlements = html.EscapeString(string(entitlements))
}
}()
entitlements := h.Entitlements.Load()
if entitlements != nil {
wg.Add(1)
go func() {
defer wg.Done()
entitlements, err := json.Marshal(entitlements)
if err == nil {
state.Entitlements = html.EscapeString(string(entitlements))
}
}()
}
if h.AppearanceFetcher != nil {
wg.Add(1)
go func() {
defer wg.Done()
cfg, err := h.AppearanceFetcher(ctx)
if err == nil {
appearance, err := json.Marshal(cfg)
if err == nil {
state.Appearance = html.EscapeString(string(appearance))
}
}
}()
}
if h.RegionsFetcher != nil {
wg.Add(1)
go func() {
defer wg.Done()
regions, err := h.RegionsFetcher(ctx)
if err == nil {
regions, err := json.Marshal(regions)
if err == nil {
state.Regions = html.EscapeString(string(regions))
}
}
}()
}
experiments := h.Experiments.Load()
if experiments != nil {
wg.Add(1)
go func() {
defer wg.Done()
experiments, err := json.Marshal(experiments)
if err == nil {
state.Experiments = html.EscapeString(string(experiments))
}
}()
}
wg.Wait()
}
if h.AppearanceFetcher != nil {
wg.Add(1)
go func() {
defer wg.Done()
cfg, err := h.AppearanceFetcher(ctx)
if err == nil {
appearance, err := json.Marshal(cfg)
if err == nil {
state.Appearance = html.EscapeString(string(appearance))
}
}
}()
}
if h.RegionsFetcher != nil {
wg.Add(1)
go func() {
defer wg.Done()
regions, err := h.RegionsFetcher(ctx)
if err == nil {
regions, err := json.Marshal(regions)
if err == nil {
state.Regions = html.EscapeString(string(regions))
}
}
}()
}
experiments := h.Experiments.Load()
if experiments != nil {
wg.Add(1)
go func() {
defer wg.Done()
experiments, err := json.Marshal(experiments)
if err == nil {
state.Experiments = html.EscapeString(string(experiments))
}
}()
}
wg.Wait()
}
err := tmpl.Execute(&buf, state)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
return execTmpl(tmpl, state)
}
// noopResponseWriter is a response writer that does nothing.