diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a99d32e31..788350d236 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,12 @@ All notable changes to `src-cli` are documented in this file. ### Changed +- Renamed `src users clean` command to `src users prune` [#901](https://github.com/sourcegraph/src-cli/pull/901) + ### Fixed +- Fix network timeout in `src users clean` occuring in instances with many users [#901](https://github.com/sourcegraph/src-cli/pull/901) + ### Removed ## 4.3.0 diff --git a/cmd/src/users.go b/cmd/src/users.go index 38d4cfdc00..20c2810120 100644 --- a/cmd/src/users.go +++ b/cmd/src/users.go @@ -20,7 +20,7 @@ The commands are: get gets a user create creates a user account delete deletes a user account - clean deletes inactive users + prune deletes inactive users tag add/remove a tag on a user Use "src users [command] -h" for more information about a command. @@ -90,3 +90,11 @@ type UserUsageStatistics struct { LastActiveTime string LastActiveCodeHostIntegrationTime string } + +type SiteUser struct { + ID string + Username string + Email string + SiteAdmin bool + LastActiveAt string +} diff --git a/cmd/src/users_clean.go b/cmd/src/users_prune.go similarity index 75% rename from cmd/src/users_clean.go rename to cmd/src/users_prune.go index 4651383dc4..54bd05c87e 100644 --- a/cmd/src/users_clean.go +++ b/cmd/src/users_prune.go @@ -19,12 +19,12 @@ This command removes users from a Sourcegraph instance who have been inactive fo Examples: - $ src users clean -days 182 + $ src users prune -days 182 - $ src users clean -remove-admin -remove-never-active + $ src users prune -remove-admin -remove-null-users ` - flagSet := flag.NewFlagSet("clean", flag.ExitOnError) + flagSet := flag.NewFlagSet("prune", flag.ExitOnError) usageFunc := func() { fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src users %s':\n", flagSet.Name()) flagSet.PrintDefaults() @@ -32,8 +32,8 @@ Examples: } var ( daysToDelete = flagSet.Int("days", 60, "Days threshold on which to remove users, must be 60 days or greater and defaults to this value ") - removeAdmin = flagSet.Bool("remove-admin", false, "clean admin accounts") - removeNoLastActive = flagSet.Bool("remove-never-active", false, "removes users with null lastActive value") + removeAdmin = flagSet.Bool("remove-admin", false, "prune admin accounts") + removeNoLastActive = flagSet.Bool("remove-null-users", false, "removes users with no last active value") skipConfirmation = flagSet.Bool("force", false, "skips user confirmation step allowing programmatic use") apiFlags = api.NewFlags(flagSet) ) @@ -51,7 +51,7 @@ Examples: client := cfg.apiClient(apiFlags, flagSet.Output()) currentUserQuery := ` -query { +query getCurrentUser { currentUser { username } @@ -68,29 +68,36 @@ query { return err } - usersQuery := ` -query Users() { - users() { - nodes { - ...UserFields - } - } + getInactiveUsersQuery := ` +query getInactiveUsers { + site { + users { + nodes { + username + email + siteAdmin + lastActiveAt + } + } + } } -` + userFragment +` - // get users to delete var usersResult struct { - Users struct { - Nodes []User + Site struct { + Users struct { + Nodes []SiteUser + } } } - if ok, err := client.NewRequest(usersQuery, nil).Do(ctx, &usersResult); err != nil || !ok { + + if ok, err := client.NewRequest(getInactiveUsersQuery, nil).Do(ctx, &usersResult); err != nil || !ok { return err } usersToDelete := make([]UserToDelete, 0) - for _, user := range usersResult.Users.Nodes { - daysSinceLastUse, wasLastActive, err := computeDaysSinceLastUse(user) + for _, user := range usersResult.Site.Users.Nodes { + daysSinceLastUse, hasLastActive, err := computeDaysSinceLastUse(user) if err != nil { return err } @@ -98,13 +105,13 @@ query Users() { if user.Username == currentUserResult.Data.CurrentUser.Username { continue } - if !wasLastActive && !*removeNoLastActive { + if !hasLastActive && !*removeNoLastActive { continue } if !*removeAdmin && user.SiteAdmin { continue } - if daysSinceLastUse <= *daysToDelete && wasLastActive { + if daysSinceLastUse <= *daysToDelete && hasLastActive { continue } deleteUser := UserToDelete{user, daysSinceLastUse} @@ -146,13 +153,13 @@ query Users() { } // computes days since last usage from current day and time and UsageStatistics.LastActiveTime, uses time.Parse -func computeDaysSinceLastUse(user User) (timeDiff int, wasLastActive bool, _ error) { - // handle for null lastActiveTime returned from - if user.UsageStatistics.LastActiveTime == "" { - wasLastActive = false - return 0, wasLastActive, nil +func computeDaysSinceLastUse(user SiteUser) (timeDiff int, hasLastActive bool, _ error) { + // handle for null LastActiveAt returned from + if user.LastActiveAt == "" { + hasLastActive = false + return 0, hasLastActive, nil } - timeLast, err := time.Parse(time.RFC3339, user.UsageStatistics.LastActiveTime) + timeLast, err := time.Parse(time.RFC3339, user.LastActiveAt) if err != nil { return 0, false, err } @@ -162,7 +169,7 @@ func computeDaysSinceLastUse(user User) (timeDiff int, wasLastActive bool, _ err } // Issue graphQL api request to remove user -func removeUser(user User, client api.Client, ctx context.Context) error { +func removeUser(user SiteUser, client api.Client, ctx context.Context) error { query := `mutation DeleteUser($user: ID!) { deleteUser(user: $user) { alwaysNil @@ -178,7 +185,7 @@ func removeUser(user User, client api.Client, ctx context.Context) error { } type UserToDelete struct { - User User + User SiteUser DaysSinceLastUse int } @@ -189,8 +196,8 @@ func confirmUserRemoval(usersToRemove []UserToDelete) (bool, error) { t.SetOutputMirror(os.Stdout) t.AppendHeader(table.Row{"Username", "Email", "Days Since Last Active"}) for _, user := range usersToRemove { - if len(user.User.Emails) > 0 { - t.AppendRow([]interface{}{user.User.Username, user.User.Emails[0].Email, user.DaysSinceLastUse}) + if user.User.Email != "" { + t.AppendRow([]interface{}{user.User.Username, user.User.Email, user.DaysSinceLastUse}) t.AppendSeparator() } else { t.AppendRow([]interface{}{user.User.Username, "", user.DaysSinceLastUse})