Skip to content

Commit 9cb96a3

Browse files
authored
Merge pull request #174 from Openscapes/github-team-tools
GitHub team tools
2 parents 4d194ed + a22d33e commit 9cb96a3

File tree

8 files changed

+195
-20
lines changed

8 files changed

+195
-20
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export(kyber_file)
2020
export(list_team_members)
2121
export(list_teams)
2222
export(md_agenda)
23+
export(remove_org_members)
24+
export(remove_team_members)
2325
export(short_names)
2426
import(knitr)
2527
importFrom(cli,cli_abort)

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# kyber (development version)
22

3+
* Added two new functions (#174):
4+
- `remove_team_members()` for removing a list of people from a GitHub team
5+
- `remove_org_members()` for removing people from an organization.
6+
37
# kyber 0.2.0
48

59
* Ensure total duration of call agendas == sum of section durations (#66)

R/add_team_members.R

Lines changed: 111 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,124 @@
11
#' Add GitHub Users to a Team
22
#'
3+
#' This also adds them to the organization that contains the team. All users
4+
#' will get an invitation by email and on GitHub.com, that they need to accept.
5+
#' This invitation expires after seven days.
6+
#'
37
#' @param team The name of the team.
48
#' @param members A vector of GitHub usernames.
59
#' @param org The GitHub organization that owns the team and the repository.
610
#' @importFrom purrr map safely map_lgl
11+
#'
12+
#' @returns list of responses from the GitHub API
13+
#'
714
#' @export
815
#' @examples
916
#' \dontrun{
10-
#'
11-
#' kyber::add_team_members("2021-ilm-rotj-team", members = c("erinmr", "seankross"))
17+
#' add_team_members("2021-ilm-rotj-team", members = c("not-ateucher", "seankross"))
1218
#' }
1319
add_team_members <- function(team, members, org = "openscapes") {
20+
resp <- add_remove_team_members_impl_(team, members, org, method = "PUT")
21+
invisible(resp)
22+
}
23+
24+
#' Remove GitHub Users from a Team
25+
#'
26+
#' @inheritParams add_team_members
27+
#'
28+
#' @returns list of responses from the GitHub API
29+
#'
30+
#' @export
31+
#' @examples
32+
#' \dontrun{
33+
#' remove_team_members("2021-ilm-rotj-team", members = "not-ateucher"))
34+
#' }
35+
remove_team_members <- function(team, members, org = "openscapes") {
36+
resp <- add_remove_team_members_impl_(team, members, org, method = "DELETE")
37+
invisible(resp)
38+
}
39+
40+
add_remove_team_members_impl_ <- function(
41+
team,
42+
members,
43+
org,
44+
method = c("PUT", "DELETE")
45+
) {
1446
check_gh_pat()
1547

48+
members <- check_user_names(members)
49+
50+
# role for default `PUT` method when adding members
51+
params <- list(role = "member")
52+
53+
if (method == "DELETE") {
54+
team_members <- list_team_members(team = team, org = org)
55+
missing_members <- setdiff(members, team_members)
56+
if (length(missing_members) == length(members)) {
57+
cli::cli_abort(
58+
"None of the specified members are part of the {.val {team}} team"
59+
)
60+
}
61+
if (length(missing_members)) {
62+
cli::cli_warn(
63+
"User{cli::qty(missing_members)}{?s} {.val {missing_members}} {?is/are} not part of the {.val {team}} team"
64+
)
65+
}
66+
# No role when removing members, so pass empty list of params
67+
params <- list()
68+
}
69+
70+
responses <- list()
71+
72+
for (i in seq_along(members)) {
73+
responses[[i]] <- gh(
74+
"/orgs/{org}/teams/{team_slug}/memberships/{username}",
75+
org = org,
76+
team_slug = team,
77+
username = members[i],
78+
.method = method,
79+
.params = params
80+
)
81+
}
82+
83+
responses
84+
}
85+
86+
#' Remove GitHub organization members
87+
#'
88+
#' @inheritParams add_team_members
89+
#'
90+
#' @returns list of responses from the GitHub API
91+
#'
92+
#' @export
93+
#' @examples
94+
#' \dontrun{
95+
#' remove_org_members(members = "not-ateucher")
96+
#' }
97+
remove_org_members <- function(members, org = "openscapes") {
98+
check_gh_pat()
99+
100+
members <- check_user_names(members)
101+
102+
responses <- list()
103+
104+
for (i in seq_along(members)) {
105+
responses[[i]] <- gh(
106+
"DELETE /orgs/{org}/members/{username}",
107+
org = org,
108+
username = members[i]
109+
)
110+
}
111+
112+
invisible(responses)
113+
}
114+
115+
#' Check usernames and return valid usernames
116+
#'
117+
#' @param members character vector or usernames
118+
#'
119+
#' @returns valid usernames
120+
#' @noRd
121+
check_user_names <- function(members) {
16122
if (!identical(members, gsub("\\s", "", members))) {
17123
stop(
18124
"GitHub usernames: ",
@@ -25,6 +131,7 @@ add_team_members <- function(team, members, org = "openscapes") {
25131
map(safely(~ gh("/users/{username}", username = .x)))
26132

27133
invalid_usernames <- responses %>% map_lgl(~ is.null(.x$"result"))
134+
28135
if (any(invalid_usernames)) {
29136
warning(
30137
"GitHub username(s): ",
@@ -35,20 +142,8 @@ add_team_members <- function(team, members, org = "openscapes") {
35142

36143
# All usernames are invalid
37144
if (sum(invalid_usernames) == length(invalid_usernames)) {
38-
return(invisible(responses))
145+
cli::cli_abort("All usernames are invalid")
39146
}
40147

41-
members <- members[!invalid_usernames]
42-
43-
responses <- list()
44-
for (i in seq_along(members)) {
45-
responses[[i]] <- gh(
46-
"PUT /orgs/{org}/teams/{team_slug}/memberships/{username}",
47-
org = org,
48-
team_slug = team,
49-
username = members[i],
50-
role = "member"
51-
)
52-
}
53-
invisible(responses)
148+
members[!invalid_usernames]
54149
}

man/add_team_members.Rd

Lines changed: 7 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/remove_org_members.Rd

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/remove_team_members.Rd

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
teams_created <- create_team("2021-ilm-rotj-team", maintainers = "ateucher")
2+
3+
teams_created
4+
5+
members_added <- add_team_members("2021-ilm-rotj-team", "not-ateucher")
6+
7+
members_added
8+
9+
# Need to go to not-ateucher account and accept invitation
10+
11+
list_team_members("2021-ilm-rotj-team")
12+
13+
members_removed <- remove_team_members(
14+
"2021-ilm-rotj-team",
15+
members = "not-ateucher"
16+
)
17+
18+
list_team_members("2021-ilm-rotj-team")
19+
20+
org_members_removed <- remove_org_members("not-ateucher")

tests/testthat/_snaps/create_readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Templates can be created.
1+
# Templates can be created and cohort name populated
22

33
Code
44
readLines(test_readme, n = 3)

0 commit comments

Comments
 (0)