-
Notifications
You must be signed in to change notification settings - Fork 70
Rename users clean or prune -- use aggregated user statistics #901
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
82da68e
01a9c95
d545ac9
6f5e4a2
8dec9ce
20a30d8
9d29a24
4504fbe
c1dd0ee
ed3d229
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,21 +19,21 @@ 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() | ||
| fmt.Println(usage) | ||
| } | ||
| 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,43 +68,50 @@ query { | |
| return err | ||
| } | ||
|
|
||
| usersQuery := ` | ||
| query Users() { | ||
| users() { | ||
| nodes { | ||
| ...UserFields | ||
| } | ||
| } | ||
| getInactiveUsersQuery := ` | ||
| query getInactiveUsers { | ||
| site { | ||
| users { | ||
| nodes { | ||
| username | ||
| siteAdmin | ||
| lastActiveAt | ||
| } | ||
| } | ||
| } | ||
|
Comment on lines
+73
to
+82
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Intendation is off
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is hitting the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/sourcegraph/src-cli/pull/903/files Indentation fix |
||
| } | ||
| ` + 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 | ||
| } | ||
| // never remove user issuing command | ||
| 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}) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are such command/API changes usually made in
srccli? Should this be a major version increase or should we keep backward compatibility leaving bothcleanandprunecommands and marking one deprecated?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost all the functionality changes in terms of the command run locally, the old
cleancommand had some big timeout issues see #848. The name change fromcleantoprunewas mostly just a style change. I don't think this command is being widely used so I think the change is alright. To my knowledge no one has put this on a cronjob or anything like that yet.