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
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export(kyber_file)
export(list_team_members)
export(list_teams)
export(md_agenda)
export(remove_org_members)
export(remove_team_members)
export(short_names)
import(knitr)
importFrom(cli,cli_abort)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# kyber (development version)

* Added two new functions (#174):
- `remove_team_members()` for removing a list of people from a GitHub team
- `remove_org_members()` for removing people from an organization.

# kyber 0.2.0

* Ensure total duration of call agendas == sum of section durations (#66)
Expand Down
127 changes: 111 additions & 16 deletions R/add_team_members.R
Original file line number Diff line number Diff line change
@@ -1,18 +1,124 @@
#' Add GitHub Users to a Team
#'
#' This also adds them to the organization that contains the team. All users
#' will get an invitation by email and on GitHub.com, that they need to accept.
#' This invitation expires after seven days.
#'
#' @param team The name of the team.
#' @param members A vector of GitHub usernames.
#' @param org The GitHub organization that owns the team and the repository.
#' @importFrom purrr map safely map_lgl
#'
#' @returns list of responses from the GitHub API
#'
#' @export
#' @examples
#' \dontrun{
#'
#' kyber::add_team_members("2021-ilm-rotj-team", members = c("erinmr", "seankross"))
#' add_team_members("2021-ilm-rotj-team", members = c("not-ateucher", "seankross"))
#' }
add_team_members <- function(team, members, org = "openscapes") {
resp <- add_remove_team_members_impl_(team, members, org, method = "PUT")
invisible(resp)
}

#' Remove GitHub Users from a Team
#'
#' @inheritParams add_team_members
#'
#' @returns list of responses from the GitHub API
#'
#' @export
#' @examples
#' \dontrun{
#' remove_team_members("2021-ilm-rotj-team", members = "not-ateucher"))
#' }
remove_team_members <- function(team, members, org = "openscapes") {
resp <- add_remove_team_members_impl_(team, members, org, method = "DELETE")
invisible(resp)
}

add_remove_team_members_impl_ <- function(
team,
members,
org,
method = c("PUT", "DELETE")
) {
check_gh_pat()

members <- check_user_names(members)

# role for default `PUT` method when adding members
params <- list(role = "member")

if (method == "DELETE") {
team_members <- list_team_members(team = team, org = org)
missing_members <- setdiff(members, team_members)
if (length(missing_members) == length(members)) {
cli::cli_abort(
"None of the specified members are part of the {.val {team}} team"
)
}
if (length(missing_members)) {
cli::cli_warn(
"User{cli::qty(missing_members)}{?s} {.val {missing_members}} {?is/are} not part of the {.val {team}} team"
)
}
# No role when removing members, so pass empty list of params
params <- list()
}

responses <- list()

for (i in seq_along(members)) {
responses[[i]] <- gh(
"/orgs/{org}/teams/{team_slug}/memberships/{username}",
org = org,
team_slug = team,
username = members[i],
.method = method,
.params = params
)
}

responses
}

#' Remove GitHub organization members
#'
#' @inheritParams add_team_members
#'
#' @returns list of responses from the GitHub API
#'
#' @export
#' @examples
#' \dontrun{
#' remove_org_members(members = "not-ateucher")
#' }
remove_org_members <- function(members, org = "openscapes") {
check_gh_pat()

members <- check_user_names(members)

responses <- list()

for (i in seq_along(members)) {
responses[[i]] <- gh(
"DELETE /orgs/{org}/members/{username}",
org = org,
username = members[i]
)
}

invisible(responses)
}

#' Check usernames and return valid usernames
#'
#' @param members character vector or usernames
#'
#' @returns valid usernames
#' @noRd
check_user_names <- function(members) {
if (!identical(members, gsub("\\s", "", members))) {
stop(
"GitHub usernames: ",
Expand All @@ -25,6 +131,7 @@ add_team_members <- function(team, members, org = "openscapes") {
map(safely(~ gh("/users/{username}", username = .x)))

invalid_usernames <- responses %>% map_lgl(~ is.null(.x$"result"))

if (any(invalid_usernames)) {
warning(
"GitHub username(s): ",
Expand All @@ -35,20 +142,8 @@ add_team_members <- function(team, members, org = "openscapes") {

# All usernames are invalid
if (sum(invalid_usernames) == length(invalid_usernames)) {
return(invisible(responses))
cli::cli_abort("All usernames are invalid")
}

members <- members[!invalid_usernames]

responses <- list()
for (i in seq_along(members)) {
responses[[i]] <- gh(
"PUT /orgs/{org}/teams/{team_slug}/memberships/{username}",
org = org,
team_slug = team,
username = members[i],
role = "member"
)
}
invisible(responses)
members[!invalid_usernames]
}
10 changes: 7 additions & 3 deletions man/add_team_members.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions man/remove_org_members.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions man/remove_team_members.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 20 additions & 0 deletions tests/manual/test-gh-member-teams.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
teams_created <- create_team("2021-ilm-rotj-team", maintainers = "ateucher")

teams_created

members_added <- add_team_members("2021-ilm-rotj-team", "not-ateucher")

members_added

# Need to go to not-ateucher account and accept invitation

list_team_members("2021-ilm-rotj-team")

members_removed <- remove_team_members(
"2021-ilm-rotj-team",
members = "not-ateucher"
)

list_team_members("2021-ilm-rotj-team")

org_members_removed <- remove_org_members("not-ateucher")
2 changes: 1 addition & 1 deletion tests/testthat/_snaps/create_readme.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Templates can be created.
# Templates can be created and cohort name populated

Code
readLines(test_readme, n = 3)
Expand Down