From d8cc7f7222afc896639c06419d7fc4b4e7e46ddb Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Wed, 9 Oct 2024 14:55:11 +0000 Subject: [PATCH 01/15] Add getting GitHub repositories for individual user. Together with new feature a new method was introduced to check if owner is a user or an organization. --- R/EngineGraphQLGitHub.R | 28 +++++++++++++------ R/GQLQueryGitHub.R | 59 +++++++++++++++++++++++++++-------------- R/GitHost.R | 27 +++++++++++++++++-- 3 files changed, 84 insertions(+), 30 deletions(-) diff --git a/R/EngineGraphQLGitHub.R b/R/EngineGraphQLGitHub.R index f8dd3831..80ba97a3 100644 --- a/R/EngineGraphQLGitHub.R +++ b/R/EngineGraphQLGitHub.R @@ -41,16 +41,22 @@ EngineGraphQLGitHub <- R6::R6Class( }, # Pull all repositories from organization - get_repos_from_org = function(org = NULL) { + get_repos_from_org = function(org = NULL, + type = c("organization", "user")) { full_repos_list <- list() next_page <- TRUE repo_cursor <- "" while (next_page) { repos_response <- private$get_repos_page( - org = org, + login = org, + type = type, repo_cursor = repo_cursor ) - repositories <- repos_response$data$repositoryOwner$repositories + repositories <- if (type == "organization") { + repos_response$data$repositoryOwner$repositories + } else { + repos_response$data$user$repositories + } repos_list <- repositories$nodes next_page <- repositories$pageInfo$hasNextPage if (is.null(next_page)) next_page <- FALSE @@ -162,14 +168,20 @@ EngineGraphQLGitHub <- R6::R6Class( private = list( # Wrapper over building GraphQL query and response. - get_repos_page = function(org = NULL, + get_repos_page = function(login = NULL, + type, repo_cursor = "") { - repos_query <- self$gql_query$repos_by_org( - repo_cursor = repo_cursor - ) + repos_query <- if (type == "organization") { + self$gql_query$repos_by_org() + } else { + self$gql_query$repos_by_user() + } response <- self$gql_response( gql_query = repos_query, - vars = list("org" = org) + vars = list( + "login" = login, + "repoCursor" = repo_cursor + ) ) return(response) }, diff --git a/R/GQLQueryGitHub.R b/R/GQLQueryGitHub.R index 38548b86..555d3269 100644 --- a/R/GQLQueryGitHub.R +++ b/R/GQLQueryGitHub.R @@ -34,32 +34,49 @@ GQLQueryGitHub <- R6::R6Class("GQLQueryGitHub", ) }, + #' @description Query to check if a given string is user or organization. + user_or_org_query = + ' + query ($login: String!) { + user(login: $login) { + __typename + login + } + organization(login: $login) { + __typename + login + } + }' + , + #' @description Prepare query to get repositories from GitHub. - #' @param org An organization of repositories. - #' @param repo_cursor An end cursor for repositories page. #' @return A query. - repos_by_org = function(repo_cursor = "") { - if (nchar(repo_cursor) == 0) { - after_cursor <- repo_cursor - } else { - after_cursor <- paste0('after: "', repo_cursor, '" ') - } + repos_by_org = function() { paste0(' - query GetReposByOrg($org: String!) { - repositoryOwner(login: $org) { + query GetReposByOrg($login: String! $repoCursor: String!) { + repositoryOwner(login: $login) { ... on Organization { - repositories(first: 100 ', after_cursor, ') { - ', private$repository_field(), ' - } + ', private$repositories_field(), ' } } }') }, + #' @description Prepare query to get repositories from GitHub. + #' @return A query. + repos_by_user = function() { + paste0(' + query GetUsersRepos($login: String! $repoCursor: String!){ + user(login: $login) { + ', private$repositories_field(), ' + } + }' + ) + }, + #' @description Prepare query to get info on a GitHub user. - #' @param login A login of a user. #' @return A query. - user = function(login) { + user = function() { paste0(' query GetUser($user: String!) { user(login: $user) { @@ -212,9 +229,10 @@ GQLQueryGitHub <- R6::R6Class("GQLQueryGitHub", }, # @description Helper to prepare repository query. - repository_field = function() { - paste0( - 'totalCount + repositories_field = function() { + ' + repositories(first: 100 after: $repoCursor) { + totalCount pageInfo { endCursor hasNextPage @@ -240,8 +258,9 @@ GQLQueryGitHub <- R6::R6Class("GQLQueryGitHub", login } repo_url: url - }' - ) + } + } + ' } ) ) diff --git a/R/GitHost.R b/R/GitHost.R index 3b01b5fb..e390d3a1 100644 --- a/R/GitHost.R +++ b/R/GitHost.R @@ -412,13 +412,34 @@ GitHost <- R6::R6Class( ) private$repos_fullnames <- repos orgs_repos <- private$extract_repos_and_orgs(repos) - private$orgs <- names(orgs_repos) + private$orgs <- private$set_owner_type( + owners = names(orgs_repos) + ) private$repos <- unname(unlist(orgs_repos)) private$orgs_repos <- orgs_repos } } }, + # Set owner type + set_owner_type = function(owners) { + graphql_engine <- private$engines$graphql + user_or_org_query <- graphql_engine$gql_query$user_or_org_query + login_types <- purrr::map(owners, function(owner) { + response <- graphql_engine$gql_response( + gql_query = user_or_org_query, + vars = list( + "login" = owner + ) + ) + type <- purrr::discard(response$data, is.null) %>% + names() + attr(owner, "type") <- type + return(owner) + }) + return(login_types) + }, + # Check if repositories exist check_repositories = function(repos, verbose) { if (verbose) { @@ -593,6 +614,7 @@ GitHost <- R6::R6Class( } graphql_engine <- private$engines$graphql repos_table <- purrr::map(private$orgs, function(org) { + type <- attr(org, "type") org <- utils::URLdecode(org) if (!private$scan_all && verbose) { show_message( @@ -604,7 +626,8 @@ GitHost <- R6::R6Class( } repos <- private$set_repos(org) repos_table <- graphql_engine$get_repos_from_org( - org = org + org = org, + type = type ) %>% private$prepare_repos_table_from_graphql() if (!is.null(repos)) { From eb965dd051b635e8e9361cb09b9b810ca2ffebf2 Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Wed, 9 Oct 2024 14:56:05 +0000 Subject: [PATCH 02/15] Bump version. --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 130709c4..27e860a9 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: GitStats Title: Get Statistics from GitHub and GitLab -Version: 2.1.0.9000 +Version: 2.1.0.9001 Authors@R: c( person(given = "Maciej", family = "Banas", email = "banasmaciek@gmail.com", role = c("aut", "cre")), person(given = "Kamil", family = "Koziej", email = "koziej.k@gmail.com", role = "aut"), From 6477c7efc6ba68d03c75f7b7b697e4ad723c61f1 Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Thu, 10 Oct 2024 07:27:32 +0000 Subject: [PATCH 03/15] Prepare test for helper setting type attributes. --- R/test_helpers.R | 12 ++++++++++-- tests/testthat/helper-fixtures.R | 20 ++++++++++++++++++++ tests/testthat/test-GitHost-helpers.R | 23 +++++++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 tests/testthat/test-GitHost-helpers.R diff --git a/R/test_helpers.R b/R/test_helpers.R index e582c54e..75a8e38d 100644 --- a/R/test_helpers.R +++ b/R/test_helpers.R @@ -36,12 +36,16 @@ GitHostGitHubTest <- R6::R6Class( private$check_if_public(host) private$token <- token private$set_graphql_url() - private$set_orgs_and_repos(orgs, repos, verbose = FALSE) + private$set_orgs_and_repos_mocked(orgs, repos) private$setup_test_engines() private$set_searching_scope(orgs, repos, verbose = FALSE) } ), private = list( + set_orgs_and_repos_mocked = function(orgs, repos) { + private$orgs <- orgs + private$repos <- repos + }, setup_test_engines = function() { private$engines$rest <- TestEngineRestGitHub$new( token = private$token, @@ -72,12 +76,16 @@ GitHostGitLabTest <- R6::R6Class( private$check_if_public(host) private$token <- token private$set_graphql_url() - private$set_orgs_and_repos(orgs, repos, verbose = FALSE) + private$set_orgs_and_repos_mocked(orgs, repos) private$setup_test_engines() private$set_searching_scope(orgs, repos, verbose = FALSE) } ), private = list( + set_orgs_and_repos_mocked = function(orgs, repos) { + private$orgs <- orgs + private$repos <- repos + }, setup_test_engines = function() { private$engines$rest <- TestEngineRestGitLab$new( token = private$token, diff --git a/tests/testthat/helper-fixtures.R b/tests/testthat/helper-fixtures.R index 53de5a07..9e673f50 100644 --- a/tests/testthat/helper-fixtures.R +++ b/tests/testthat/helper-fixtures.R @@ -732,3 +732,23 @@ test_fixtures$gitlab_languages_response <- list( "Python" = 100, "R" = 50 ) + +test_fixtures$github_user_login <- list( + "data" = list( + "user" = list( + "__typename" = "User", + "login" = "test_user" + ), + "organization" = NULL + ) +) + +test_fixtures$github_org_login <- list( + "data" = list( + "user" = NULL, + "organization" = list( + "__typename" = "Organization", + "login" = "test_org" + ) + ) +) diff --git a/tests/testthat/test-GitHost-helpers.R b/tests/testthat/test-GitHost-helpers.R new file mode 100644 index 00000000..ade90edd --- /dev/null +++ b/tests/testthat/test-GitHost-helpers.R @@ -0,0 +1,23 @@ +test_that("set_owner_types sets attributes to owners list", { + mockery::stub( + github_testhost_priv$set_owner_type, + "graphql_engine$gql_response", + test_fixtures$github_user_login + ) + owner <- github_testhost_priv$set_owner_type( + owners = c("test_user") + ) + expect_equal(attr(owner[[1]], "type"), "user") + expect_equal(owner[[1]], "test_user", ignore_attr = TRUE) + + mockery::stub( + github_testhost_priv$set_owner_type, + "graphql_engine$gql_response", + test_fixtures$github_org_login + ) + owner <- github_testhost_priv$set_owner_type( + owners = c("test_org") + ) + expect_equal(attr(owner[[1]], "type"), "organization") + expect_equal(owner[[1]], "test_org", ignore_attr = TRUE) +}) From c39069e19b112551ff69b14a62ce55463d659dc7 Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Thu, 10 Oct 2024 09:51:32 +0000 Subject: [PATCH 04/15] Add getting GitLab repos for user. --- R/EngineGraphQLGitLab.R | 36 +++++++++---- R/GQLQueryGitLab.R | 115 +++++++++++++++++++++++++--------------- R/GitHost.R | 21 +------- R/GitHostGitHub.R | 19 +++++++ R/GitHostGitLab.R | 24 ++++++++- 5 files changed, 138 insertions(+), 77 deletions(-) diff --git a/R/EngineGraphQLGitLab.R b/R/EngineGraphQLGitLab.R index 7dc0b38a..e9c5d025 100644 --- a/R/EngineGraphQLGitLab.R +++ b/R/EngineGraphQLGitLab.R @@ -46,19 +46,22 @@ EngineGraphQLGitLab <- R6::R6Class( }, # Iterator over pulling pages of repositories. - get_repos_from_org = function(org = NULL) { + get_repos_from_org = function(org = NULL, + type = c("organization", "user")) { full_repos_list <- list() next_page <- TRUE repo_cursor <- "" while (next_page) { repos_response <- private$get_repos_page( org = org, + type = type, repo_cursor = repo_cursor ) - if (length(repos_response$data$group) == 0) { - cli::cli_abort("Empty") + core_response <- if (type == "organization") { + repos_response$data$group$projects + } else { + repos_response$data$projects } - core_response <- repos_response$data$group$projects repos_list <- core_response$edges next_page <- core_response$pageInfo$hasNextPage if (is.null(next_page)) next_page <- FALSE @@ -259,14 +262,25 @@ EngineGraphQLGitLab <- R6::R6Class( # Wrapper over building GraphQL query and response. get_repos_page = function(org = NULL, + type = "organization", repo_cursor = "") { - repos_query <- self$gql_query$repos_by_org( - repo_cursor = repo_cursor - ) - response <- self$gql_response( - gql_query = repos_query, - vars = list("org" = org) - ) + if (type == "organization") { + response <- self$gql_response( + gql_query = self$gql_query$repos_by_org(), + vars = list( + "org" = org, + "repo_cursor" = repo_cursor + ) + ) + } else { + response <- self$gql_response( + gql_query = self$gql_query$repos_by_user(), + vars = list( + "username" = org, + "repo_cursor" = repo_cursor + ) + ) + } return(response) }, diff --git a/R/GQLQueryGitLab.R b/R/GQLQueryGitLab.R index e6777dc6..6f6270bf 100644 --- a/R/GQLQueryGitLab.R +++ b/R/GQLQueryGitLab.R @@ -23,54 +23,44 @@ GQLQueryGitLab <- R6::R6Class("GQLQueryGitLab", }' }, + #' @field user_or_org_query Query to check if a given string is user or + #' organization. + user_or_org_query = + ' + query ($username: String! $grouppath: ID!) { + user(username: $username) { + __typename + username + } + group(fullPath: $grouppath) { + __typename + fullPath + } + }' + , + #' @description Prepare query to get repositories from GitLab. - #' @param repo_cursor An end cursor for repositories page. #' @return A query. - repos_by_org = function(repo_cursor = "") { - if (nchar(repo_cursor) == 0) { - after_cursor <- repo_cursor - } else { - after_cursor <- paste0('after: "', repo_cursor, '" ') - } - paste0('query GetReposByOrg($org: ID!) { - group(fullPath: $org) { - projects(first: 100 ', after_cursor, ') { - count - pageInfo { - hasNextPage - endCursor - } - edges { - node { - repo_id: id - repo_name: name - repo_path: path - ... on Project { - repository { - rootRef - } - } - stars: starCount - forks: forksCount - created_at: createdAt - last_activity_at: lastActivityAt - languages { - name - } - issues: issueStatusCounts { - all - closed - opened - } - group { - path - } - repo_url: webUrl - } + repos_by_org = function() { + paste0(' + query GetReposByOrg($org: ID! $repo_cursor: String!) { + group(fullPath: $org) { + projects(first: 100 after: $repo_cursor) { + ', private$projects_field_content, ' } } - } - }') + }') + }, + + #' @description Prepare query to get repositories from GitLab. + #' @return A query. + repos_by_user = function() { + paste0(' + query GetUserRepos ($username: String! $repo_cursor: String!) { + projects(search: $username searchNamespaces: true after: $repo_cursor first: 100) { + ', private$projects_field_content, ' + } + }') }, #' @description Prepare query to get info on a GitLab user. @@ -216,5 +206,42 @@ GQLQueryGitLab <- R6::R6Class("GQLQueryGitLab", } }' } + ), + private = list( + projects_field_content = + ' + count + pageInfo { + hasNextPage + endCursor + } + edges { + node { + repo_id: id + repo_name: name + repo_path: path + ... on Project { + repository { + rootRef + } + } + stars: starCount + forks: forksCount + created_at: createdAt + last_activity_at: lastActivityAt + languages { + name + } + issues: issueStatusCounts { + all + closed + opened + } + namespace { + path + } + repo_url: webUrl + } + }' ) ) diff --git a/R/GitHost.R b/R/GitHost.R index e390d3a1..c0fd164f 100644 --- a/R/GitHost.R +++ b/R/GitHost.R @@ -421,25 +421,6 @@ GitHost <- R6::R6Class( } }, - # Set owner type - set_owner_type = function(owners) { - graphql_engine <- private$engines$graphql - user_or_org_query <- graphql_engine$gql_query$user_or_org_query - login_types <- purrr::map(owners, function(owner) { - response <- graphql_engine$gql_response( - gql_query = user_or_org_query, - vars = list( - "login" = owner - ) - ) - type <- purrr::discard(response$data, is.null) %>% - names() - attr(owner, "type") <- type - return(owner) - }) - return(login_types) - }, - # Check if repositories exist check_repositories = function(repos, verbose) { if (verbose) { @@ -614,7 +595,7 @@ GitHost <- R6::R6Class( } graphql_engine <- private$engines$graphql repos_table <- purrr::map(private$orgs, function(org) { - type <- attr(org, "type") + type <- attr(org, "type") %||% "organization" org <- utils::URLdecode(org) if (!private$scan_all && verbose) { show_message( diff --git a/R/GitHostGitHub.R b/R/GitHostGitHub.R index 37942bee..916892ed 100644 --- a/R/GitHostGitHub.R +++ b/R/GitHostGitHub.R @@ -88,6 +88,25 @@ GitHostGitHub <- R6::R6Class( private$endpoints$repositories <- glue::glue("{private$api_url}/repos") }, + # Set owner type + set_owner_type = function(owners) { + graphql_engine <- private$engines$graphql + user_or_org_query <- graphql_engine$gql_query$user_or_org_query + login_types <- purrr::map(owners, function(owner) { + response <- graphql_engine$gql_response( + gql_query = user_or_org_query, + vars = list( + "login" = owner + ) + ) + type <- purrr::discard(response$data, is.null) %>% + names() + attr(owner, "type") <- type + return(owner) + }) + return(login_types) + }, + # Setup REST and GraphQL engines setup_engines = function() { private$engines$rest <- EngineRestGitHub$new( diff --git a/R/GitHostGitLab.R b/R/GitHostGitLab.R index bf1ffe45..7b702034 100644 --- a/R/GitHostGitLab.R +++ b/R/GitHostGitLab.R @@ -147,6 +147,26 @@ GitHostGitLab <- R6::R6Class("GitHostGitLab", private$endpoints$repositories <- glue::glue("{private$api_url}/projects") }, + # Set owner type + set_owner_type = function(owners) { + graphql_engine <- private$engines$graphql + user_or_org_query <- graphql_engine$gql_query$user_or_org_query + login_types <- purrr::map(owners, function(owner) { + response <- graphql_engine$gql_response( + gql_query = user_or_org_query, + vars = list( + "username" = owner, + "grouppath" = owner + ) + ) + type <- purrr::discard(response$data, is.null) %>% + names() + attr(owner, "type") <- type + return(owner) + }) + return(login_types) + }, + # Setup REST and GraphQL engines setup_engines = function() { private$engines$rest <- EngineRestGitLab$new( @@ -220,8 +240,8 @@ GitHostGitLab <- R6::R6Class("GitHostGitLab", repo$issues_closed <- repo$issues$closed repo$issues <- NULL repo$last_activity_at <- as.POSIXct(repo$last_activity_at) - repo$organization <- repo$group$path - repo$group <- NULL + repo$organization <- repo$namespace$path + repo$namespace <- NULL repo$repo_path <- NULL # temporary to close issue 338 data.frame(repo) }) %>% From a06f3714e35ac060dcc49efc619b842636aedc68 Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Thu, 10 Oct 2024 09:51:48 +0000 Subject: [PATCH 05/15] Update NEWS. --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 24fc24b9..70f0ac66 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # GitStats (development version) +- Added possibility to get repositories for individual users with `get_repos()` ([#492](https://github.com/r-world-devs/GitStats/issues/492)). Earlier this was only possible for GitHub organizations and GitLab groups. - Fixed getting large search responses for GitHub ([#491](https://github.com/r-world-devs/GitStats/issues/491)). # GitStats 2.1.0 From 325c05efb8a48e6ac13eddd691960e3457084225 Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Thu, 10 Oct 2024 11:32:29 +0000 Subject: [PATCH 06/15] Fix code and tests. --- R/EngineGraphQLGitHub.R | 5 +++-- R/EngineGraphQLGitLab.R | 3 ++- R/test_helpers.R | 12 ++++++++++-- tests/testthat/_snaps/01-get_repos-GitHub.md | 2 +- tests/testthat/_snaps/01-get_repos-GitLab.md | 2 +- tests/testthat/helper-fixtures.R | 2 +- tests/testthat/test-01-get_repos-GitHub.R | 6 ++++-- tests/testthat/test-01-get_repos-GitLab.R | 14 +++++++++----- 8 files changed, 31 insertions(+), 15 deletions(-) diff --git a/R/EngineGraphQLGitHub.R b/R/EngineGraphQLGitHub.R index 80ba97a3..d8b75bea 100644 --- a/R/EngineGraphQLGitHub.R +++ b/R/EngineGraphQLGitHub.R @@ -169,7 +169,7 @@ EngineGraphQLGitHub <- R6::R6Class( # Wrapper over building GraphQL query and response. get_repos_page = function(login = NULL, - type, + type = c("organization", "user"), repo_cursor = "") { repos_query <- if (type == "organization") { self$gql_query$repos_by_org() @@ -239,7 +239,8 @@ EngineGraphQLGitHub <- R6::R6Class( get_repos_data = function(org, repos = NULL) { repos_list <- self$get_repos_from_org( - org = org + org = org, + type = "organization" ) if (!is.null(repos)) { repos_list <- purrr::keep(repos_list, ~ .$repo_name %in% repos) diff --git a/R/EngineGraphQLGitLab.R b/R/EngineGraphQLGitLab.R index e9c5d025..eae77ad3 100644 --- a/R/EngineGraphQLGitLab.R +++ b/R/EngineGraphQLGitLab.R @@ -293,7 +293,8 @@ EngineGraphQLGitLab <- R6::R6Class( get_repos_data = function(org, repos = NULL) { repos_list <- self$get_repos_from_org( - org = org + org = org, + type = "organization" ) if (!is.null(repos)) { repos_list <- purrr::keep(repos_list, ~ .$node$repo_path %in% repos) diff --git a/R/test_helpers.R b/R/test_helpers.R index 75a8e38d..656c250b 100644 --- a/R/test_helpers.R +++ b/R/test_helpers.R @@ -44,7 +44,11 @@ GitHostGitHubTest <- R6::R6Class( private = list( set_orgs_and_repos_mocked = function(orgs, repos) { private$orgs <- orgs - private$repos <- repos + if (!is.null(repos)) { + private$repos <- repos + orgs_repos <- private$extract_repos_and_orgs(repos) + private$orgs <- names(orgs_repos) + } }, setup_test_engines = function() { private$engines$rest <- TestEngineRestGitHub$new( @@ -84,7 +88,11 @@ GitHostGitLabTest <- R6::R6Class( private = list( set_orgs_and_repos_mocked = function(orgs, repos) { private$orgs <- orgs - private$repos <- repos + if (!is.null(repos)) { + private$repos <- repos + orgs_repos <- private$extract_repos_and_orgs(repos) + private$orgs <- names(orgs_repos) + } }, setup_test_engines = function() { private$engines$rest <- TestEngineRestGitLab$new( diff --git a/tests/testthat/_snaps/01-get_repos-GitHub.md b/tests/testthat/_snaps/01-get_repos-GitHub.md index ed8eeb9b..0c71258f 100644 --- a/tests/testthat/_snaps/01-get_repos-GitHub.md +++ b/tests/testthat/_snaps/01-get_repos-GitHub.md @@ -3,7 +3,7 @@ Code gh_repos_by_org_query Output - [1] "\n query GetReposByOrg($org: String!) {\n repositoryOwner(login: $org) {\n ... on Organization {\n repositories(first: 100 ) {\n totalCount\n pageInfo {\n endCursor\n hasNextPage\n }\n nodes {\n repo_id: id\n repo_name: name\n default_branch: defaultBranchRef {\n name\n }\n stars: stargazerCount\n forks: forkCount\n created_at: createdAt\n last_activity_at: pushedAt\n languages (first: 5) { nodes {name} }\n issues_open: issues (first: 100 states: [OPEN]) {\n totalCount\n }\n issues_closed: issues (first: 100 states: [CLOSED]) {\n totalCount\n }\n organization: owner {\n login\n }\n repo_url: url\n }\n }\n }\n }\n }" + [1] "\n query GetReposByOrg($login: String! $repoCursor: String!) {\n repositoryOwner(login: $login) {\n ... on Organization {\n \n repositories(first: 100 after: $repoCursor) {\n totalCount\n pageInfo {\n endCursor\n hasNextPage\n }\n nodes {\n repo_id: id\n repo_name: name\n default_branch: defaultBranchRef {\n name\n }\n stars: stargazerCount\n forks: forkCount\n created_at: createdAt\n last_activity_at: pushedAt\n languages (first: 5) { nodes {name} }\n issues_open: issues (first: 100 states: [OPEN]) {\n totalCount\n }\n issues_closed: issues (first: 100 states: [CLOSED]) {\n totalCount\n }\n organization: owner {\n login\n }\n repo_url: url\n }\n }\n \n }\n }\n }" # `prepare_repos_table()` prepares repos table diff --git a/tests/testthat/_snaps/01-get_repos-GitLab.md b/tests/testthat/_snaps/01-get_repos-GitLab.md index 5335a073..c6dab9da 100644 --- a/tests/testthat/_snaps/01-get_repos-GitLab.md +++ b/tests/testthat/_snaps/01-get_repos-GitLab.md @@ -3,7 +3,7 @@ Code gl_repos_by_org_query Output - [1] "query GetReposByOrg($org: ID!) {\n group(fullPath: $org) {\n projects(first: 100 ) {\n count\n pageInfo {\n hasNextPage\n endCursor\n }\n edges {\n node {\n repo_id: id\n repo_name: name\n repo_path: path\n ... on Project {\n repository {\n rootRef\n }\n }\n stars: starCount\n forks: forksCount\n created_at: createdAt\n last_activity_at: lastActivityAt\n languages {\n name\n }\n issues: issueStatusCounts {\n all\n closed\n opened\n }\n group {\n path\n }\n repo_url: webUrl\n }\n }\n }\n }\n }" + [1] "\n query GetReposByOrg($org: ID! $repo_cursor: String!) {\n group(fullPath: $org) {\n projects(first: 100 after: $repo_cursor) {\n \n count\n pageInfo {\n hasNextPage\n endCursor\n }\n edges {\n node {\n repo_id: id\n repo_name: name\n repo_path: path\n ... on Project {\n repository {\n rootRef\n }\n }\n stars: starCount\n forks: forksCount\n created_at: createdAt\n last_activity_at: lastActivityAt\n languages {\n name\n }\n issues: issueStatusCounts {\n all\n closed\n opened\n }\n namespace {\n path\n }\n repo_url: webUrl\n }\n }\n }\n }\n }" # GitHost prepares table from GitLab repositories response diff --git a/tests/testthat/helper-fixtures.R b/tests/testthat/helper-fixtures.R index 9e673f50..78019509 100644 --- a/tests/testthat/helper-fixtures.R +++ b/tests/testthat/helper-fixtures.R @@ -163,7 +163,7 @@ gitlab_project_node <- list( "closed" = 8, "opened" = 2 ), - "group" = list( + "namespace" = list( "path" = "test_group" ), "repo_url" = "https://test_gitlab_url.com" diff --git a/tests/testthat/test-01-get_repos-GitHub.R b/tests/testthat/test-01-get_repos-GitHub.R index 50437b1d..047b4dcc 100644 --- a/tests/testthat/test-01-get_repos-GitHub.R +++ b/tests/testthat/test-01-get_repos-GitHub.R @@ -14,7 +14,8 @@ test_that("`get_repos_page()` pulls repos page from GitHub organization", { test_fixtures$github_repos_by_org_response ) gh_repos_page <- test_graphql_github_priv$get_repos_page( - org = "test_org" + login = "test_org", + type = "organization" ) expect_gh_repos_gql_response( gh_repos_page @@ -29,7 +30,8 @@ test_that("`get_repos_from_org()` prepares formatted list", { test_mocker$use("gh_repos_page") ) gh_repos_from_org <- test_graphql_github$get_repos_from_org( - org = "test_org" + org = "test_org", + type = "organization" ) expect_list_contains( gh_repos_from_org[[1]], diff --git a/tests/testthat/test-01-get_repos-GitLab.R b/tests/testthat/test-01-get_repos-GitLab.R index 67846f96..dac1588e 100644 --- a/tests/testthat/test-01-get_repos-GitLab.R +++ b/tests/testthat/test-01-get_repos-GitLab.R @@ -14,7 +14,8 @@ test_that("`get_repos_page()` pulls repos page from GitLab group", { test_fixtures$gitlab_repos_by_org_response ) gl_repos_page <- test_graphql_gitlab_priv$get_repos_page( - org = "test_org" + org = "test_org", + type = "organization" ) expect_gl_repos_gql_response( gl_repos_page @@ -29,14 +30,15 @@ test_that("`get_repos_from_org()` prepares formatted list", { test_mocker$use("gl_repos_page") ) gl_repos_from_org <- test_graphql_gitlab$get_repos_from_org( - org = "test_org" + org = "test_org", + type = "organization" ) expect_equal( names(gl_repos_from_org[[1]]$node), c( "repo_id", "repo_name", "repo_path", "repository", "stars", "forks", "created_at", "last_activity_at", - "languages", "issues", "group", "repo_url" + "languages", "issues", "namespace", "repo_url" ) ) test_mocker$cache(gl_repos_from_org) @@ -49,7 +51,8 @@ test_that("`get_repos_from_org()` does not fail when GraphQL response is not com test_fixtures$empty_gql_response ) gl_repos_from_org <- test_graphql_gitlab$get_repos_from_org( - org = "test_org" + org = "test_org", + type = "organization" ) expect_type( gl_repos_from_org, @@ -65,7 +68,8 @@ test_that("`get_repos_from_org()` does not fail when GraphQL response is not com test_fixtures$half_empty_gql_response ) gl_repos_from_org <- test_graphql_gitlab$get_repos_from_org( - org = "test_org" + org = "test_org", + type = "organization" ) expect_type( gl_repos_from_org, From 6186fe54af0b928d5aacdc2051f6000c31882c57 Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Thu, 10 Oct 2024 11:36:19 +0000 Subject: [PATCH 07/15] Update docs. --- R/gitstats_functions.R | 6 +++--- man/get_repos.Rd | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/R/gitstats_functions.R b/R/gitstats_functions.R index d98afac4..a4cbd1c2 100644 --- a/R/gitstats_functions.R +++ b/R/gitstats_functions.R @@ -89,9 +89,9 @@ set_gitlab_host <- function(gitstats_object, #' @title Get data on repositories #' @name get_repos -#' @description Pulls data on all repositories for an organization or those -#' with a given text in code blobs (`with_code` parameter) or a file -#' (`with_files` parameter) and parse it into table format. +#' @description Pulls data on all repositories for an organization, individual +#' user or those with a given text in code blobs (`with_code` parameter) or a +#' file (`with_files` parameter) and parse it into table format. #' @param gitstats_object A GitStats object. #' @param add_contributors A logical parameter to decide whether to add #' information about repositories' contributors to the repositories output diff --git a/man/get_repos.Rd b/man/get_repos.Rd index ac33c371..6bf5406b 100644 --- a/man/get_repos.Rd +++ b/man/get_repos.Rd @@ -52,9 +52,9 @@ output is switched off.} A data.frame. } \description{ -Pulls data on all repositories for an organization or those -with a given text in code blobs (\code{with_code} parameter) or a file -(\code{with_files} parameter) and parse it into table format. +Pulls data on all repositories for an organization, individual +user or those with a given text in code blobs (\code{with_code} parameter) or a +file (\code{with_files} parameter) and parse it into table format. } \examples{ \dontrun{ From 4fd12cc6d55182db93927f1cd819f8f6a8cfdb62 Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Thu, 10 Oct 2024 12:06:05 +0000 Subject: [PATCH 08/15] Add tests to cover new feature. --- tests/testthat/helper-expect-responses.R | 18 +++++++--- tests/testthat/helper-fixtures.R | 40 +++++++++++++++++++++++ tests/testthat/test-01-get_repos-GitHub.R | 38 +++++++++++++++++++++ tests/testthat/test-01-get_repos-GitLab.R | 38 +++++++++++++++++++++ 4 files changed, 130 insertions(+), 4 deletions(-) diff --git a/tests/testthat/helper-expect-responses.R b/tests/testthat/helper-expect-responses.R index 798275e3..a0590472 100644 --- a/tests/testthat/helper-expect-responses.R +++ b/tests/testthat/helper-expect-responses.R @@ -48,20 +48,25 @@ expect_gh_repos_rest_response <- function(object) { }) } -expect_gl_repos_gql_response <- function(object) { +expect_gl_repos_gql_response <- function(object, type = "organization") { expect_type( object, "list" ) + repo_node <- if (type == "organization") { + object$data$group$projects$edges[[1]]$node + } else { + object$data$projects$edges[[1]]$node + } expect_list_contains( - object$data$group$projects$edges[[1]]$node, + repo_node, c( "id", "name", "repository", "stars", "forks", "created_at", "last_activity_at" ) ) } -expect_gh_repos_gql_response <- function(object) { +expect_gh_repos_gql_response <- function(object, type = "organization") { expect_type( object, "list" @@ -70,8 +75,13 @@ expect_gh_repos_gql_response <- function(object) { object, "data" ) + repo_node <- if (type == "organization") { + object$data$repositoryOwner$repositories$nodes[[1]] + } else { + object$data$user$repositories$nodes[[1]] + } expect_list_contains( - object$data$repositoryOwner$repositories$nodes[[1]], + repo_node, c( "id", "name", "stars", "forks", "created_at", "last_activity_at", "languages", "issues_open", "issues_closed", diff --git a/tests/testthat/helper-fixtures.R b/tests/testthat/helper-fixtures.R index 78019509..6d574213 100644 --- a/tests/testthat/helper-fixtures.R +++ b/tests/testthat/helper-fixtures.R @@ -138,6 +138,27 @@ test_fixtures$github_repos_by_org_response <- list( ) ) +test_fixtures$github_repos_by_user_response <- list( + "data" = list( + "user" = list( + "repositories" = list( + "totalCount" = 5, + "pageInfo" = list( + "endCursor" = "xyx", + "hasNextPage" = FALSE + ), + "nodes" = list( + github_repository_node, + github_repository_node, + github_repository_node, + github_repository_node, + github_repository_node + ) + ) + ) + ) +) + gitlab_project_node <- list( "node" = list( "repo_id" = "gid://gitlab/Project/61399846", @@ -191,6 +212,25 @@ test_fixtures$gitlab_repos_by_org_response <- list( ) ) +test_fixtures$gitlab_repos_by_user_response <- list( + "data" = list( + "projects" = list( + "count" = 5, + "pageInfo" = list( + "hasNextPage" = FALSE, + "endCursor" = "xyz" + ), + "edges" = list( + gitlab_project_node, + gitlab_project_node, + gitlab_project_node, + gitlab_project_node, + gitlab_project_node + ) + ) + ) +) + github_commit_edge <- list( "node" = list( "id" = "xxx", diff --git a/tests/testthat/test-01-get_repos-GitHub.R b/tests/testthat/test-01-get_repos-GitHub.R index 047b4dcc..3c09ab50 100644 --- a/tests/testthat/test-01-get_repos-GitHub.R +++ b/tests/testthat/test-01-get_repos-GitHub.R @@ -44,6 +44,44 @@ test_that("`get_repos_from_org()` prepares formatted list", { test_mocker$cache(gh_repos_from_org) }) +test_that("`get_repos_page()` pulls repos page from GitHub user", { + mockery::stub( + test_graphql_github_priv$get_repos_page, + "self$gql_response", + test_fixtures$github_repos_by_user_response + ) + gh_repos_user_page <- test_graphql_github_priv$get_repos_page( + login = "test_user", + type = "user" + ) + expect_gh_repos_gql_response( + gh_repos_user_page, + type = "user" + ) + test_mocker$cache(gh_repos_user_page) +}) + +test_that("`get_repos_from_org()` prepares formatted list", { + mockery::stub( + test_graphql_github$get_repos_from_org, + "private$get_repos_page", + test_mocker$use("gh_repos_user_page") + ) + gh_repos_from_user <- test_graphql_github$get_repos_from_org( + org = "test_user", + type = "user" + ) + expect_list_contains( + gh_repos_from_user[[1]], + c( + "id", "name", "stars", "forks", "created_at", + "last_activity_at", "languages", "issues_open", "issues_closed", + "contributors", "repo_url" + ) + ) + test_mocker$cache(gh_repos_from_user) +}) + # REST Engine search repos by code test_that("`search_response()` performs search with limit under 1000", { diff --git a/tests/testthat/test-01-get_repos-GitLab.R b/tests/testthat/test-01-get_repos-GitLab.R index dac1588e..0fbe209f 100644 --- a/tests/testthat/test-01-get_repos-GitLab.R +++ b/tests/testthat/test-01-get_repos-GitLab.R @@ -44,6 +44,44 @@ test_that("`get_repos_from_org()` prepares formatted list", { test_mocker$cache(gl_repos_from_org) }) +test_that("`get_repos_page()` pulls repos page from GitLab user", { + mockery::stub( + test_graphql_gitlab_priv$get_repos_page, + "self$gql_response", + test_fixtures$gitlab_repos_by_user_response + ) + gl_repos_user_page <- test_graphql_gitlab_priv$get_repos_page( + org = "test_user", + type = "user" + ) + expect_gl_repos_gql_response( + gl_repos_user_page, + type = "user" + ) + test_mocker$cache(gl_repos_user_page) +}) + +test_that("`get_repos_from_org()` prepares formatted list", { + mockery::stub( + test_graphql_gitlab$get_repos_from_org, + "private$get_repos_page", + test_mocker$use("gl_repos_user_page") + ) + gl_repos_from_user <- test_graphql_gitlab$get_repos_from_org( + org = "test_user", + type = "user" + ) + expect_equal( + names(gl_repos_from_user[[1]]$node), + c( + "repo_id", "repo_name", "repo_path", "repository", + "stars", "forks", "created_at", "last_activity_at", + "languages", "issues", "namespace", "repo_url" + ) + ) + test_mocker$cache(gl_repos_from_user) +}) + test_that("`get_repos_from_org()` does not fail when GraphQL response is not complete", { mockery::stub( test_graphql_gitlab$get_repos_from_org, From e1cbbcc8da418f35107cefbe65c2c959f2ee5e44 Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Thu, 10 Oct 2024 12:50:17 +0000 Subject: [PATCH 09/15] Adjust getting files to new feature. --- R/EngineGraphQLGitHub.R | 8 +- R/EngineGraphQLGitLab.R | 143 ++++++++++-------- R/GitHost.R | 4 + .../_snaps/get_files_content-GitLab.md | 6 +- .../testthat/test-get_files_content-GitLab.R | 2 + 5 files changed, 96 insertions(+), 67 deletions(-) diff --git a/R/EngineGraphQLGitHub.R b/R/EngineGraphQLGitHub.R index d8b75bea..ed03264f 100644 --- a/R/EngineGraphQLGitHub.R +++ b/R/EngineGraphQLGitHub.R @@ -93,6 +93,7 @@ EngineGraphQLGitHub <- R6::R6Class( # Pull all given files from all repositories of an organization. get_files_from_org = function(org, + type, repos, file_paths, host_files_structure, @@ -101,6 +102,7 @@ EngineGraphQLGitHub <- R6::R6Class( progress = TRUE) { repo_data <- private$get_repos_data( org = org, + type = type, repos = repos ) repositories <- repo_data[["repositories"]] @@ -123,6 +125,7 @@ EngineGraphQLGitHub <- R6::R6Class( # Pull all files from all repositories of an organization. get_files_structure_from_org = function(org, + type, repos, pattern = NULL, depth = Inf, @@ -130,6 +133,7 @@ EngineGraphQLGitHub <- R6::R6Class( progress = TRUE) { repo_data <- private$get_repos_data( org = org, + type = type, repos = repos ) repositories <- repo_data[["repositories"]] @@ -237,10 +241,10 @@ EngineGraphQLGitHub <- R6::R6Class( return(response) }, - get_repos_data = function(org, repos = NULL) { + get_repos_data = function(org, type, repos = NULL) { repos_list <- self$get_repos_from_org( org = org, - type = "organization" + type = type ) if (!is.null(repos)) { repos_list <- purrr::keep(repos_list, ~ .$repo_name %in% repos) diff --git a/R/EngineGraphQLGitLab.R b/R/EngineGraphQLGitLab.R index eae77ad3..ccf47280 100644 --- a/R/EngineGraphQLGitLab.R +++ b/R/EngineGraphQLGitLab.R @@ -84,6 +84,7 @@ EngineGraphQLGitLab <- R6::R6Class( # pulled files_structure. In such a case GitStats will switch from this function # to iterator over repositories (multiple queries), as it is done for GitHub. get_files_from_org = function(org, + type, repos, file_paths, host_files_structure, @@ -103,73 +104,87 @@ EngineGraphQLGitLab <- R6::R6Class( } else if (is.null(host_files_structure) && only_text_files) { file_paths <- file_paths[!grepl(non_text_files_pattern, file_paths)] } - while (next_page) { - files_query <- self$gql_query$files_by_org( - end_cursor = end_cursor - ) - files_response <- tryCatch( - { - self$gql_response( - gql_query = files_query, - vars = list( - "org" = org, - "file_paths" = file_paths + if (type == "organization") { + while (next_page) { + files_query <- self$gql_query$files_by_org( + end_cursor = end_cursor + ) + files_response <- tryCatch( + { + self$gql_response( + gql_query = files_query, + vars = list( + "org" = org, + "file_paths" = file_paths + ) ) - ) - }, - error = function(e) { - list() - } - ) - if (private$is_query_error(files_response)) { - if (verbose) { - purrr::walk(files_response$errors, ~ cli::cli_alert_warning(.)) - } - if (private$is_complexity_error(files_response)) { + }, + error = function(e) { + list() + } + ) + if (private$is_query_error(files_response)) { if (verbose) { - cli::cli_alert_info( - cli::col_br_cyan("I will switch to pulling files per repository.") + purrr::walk(files_response$errors, ~ cli::cli_alert_warning(.)) + } + if (private$is_complexity_error(files_response)) { + if (verbose) { + cli::cli_alert_info( + cli::col_br_cyan("I will switch to pulling files per repository.") + ) + } + full_files_list <- self$get_files_from_org_per_repo( + org = org, + type = type, + repos = repos, + file_paths = file_paths, + host_files_structure = host_files_structure, + only_text_files = only_text_files, + verbose = verbose, + progress = progress ) + return(full_files_list) } - full_files_list <- self$get_files_from_org_per_repo( - org = org, - repos = repos, - file_paths = file_paths, - host_files_structure = host_files_structure, - only_text_files = only_text_files, - verbose = verbose, - progress = progress - ) - return(full_files_list) } + if (length(files_response$data$group) == 0 && verbose) { + cli::cli_alert_danger("Empty response.") + } + projects <- files_response$data$group$projects + files_list <- purrr::map(projects$edges, function(edge) { + edge$node + }) %>% + purrr::discard(~ length(.$repository$blobs$nodes) == 0) + if (is.null(files_list)) files_list <- list() + if (length(files_list) > 0) { + next_page <- files_response$pageInfo$hasNextPage + } else { + next_page <- FALSE + } + if (is.null(next_page)) next_page <- FALSE + if (next_page) { + end_cursor <- files_response$pageInfo$endCursor + } else { + end_cursor <- "" + } + full_files_list <- append(full_files_list, files_list) } - if (length(files_response$data$group) == 0 && verbose) { - cli::cli_alert_danger("Empty response.") - } - projects <- files_response$data$group$projects - files_list <- purrr::map(projects$edges, function(edge) { - edge$node - }) %>% - purrr::discard(~ length(.$repository$blobs$nodes) == 0) - if (is.null(files_list)) files_list <- list() - if (length(files_list) > 0) { - next_page <- files_response$pageInfo$hasNextPage - } else { - next_page <- FALSE + if (!is.null(repos)) { + full_files_list <- purrr::keep(full_files_list, function(project) { + repo_name <- private$get_repo_name_from_url(project$webUrl) + repo_name %in% repos + }) } - if (is.null(next_page)) next_page <- FALSE - if (next_page) { - end_cursor <- files_response$pageInfo$endCursor - } else { - end_cursor <- "" - } - full_files_list <- append(full_files_list, files_list) - } - if (!is.null(repos)) { - full_files_list <- purrr::keep(full_files_list, function(project) { - repo_name <- private$get_repo_name_from_url(project$webUrl) - repo_name %in% repos - }) + } else { + full_files_list <- self$get_files_from_org_per_repo( + org = org, + type = type, + repos = repos, + file_paths = file_paths, + host_files_structure = host_files_structure, + only_text_files = only_text_files, + verbose = verbose, + progress = progress + ) } return(full_files_list) }, @@ -178,6 +193,7 @@ EngineGraphQLGitLab <- R6::R6Class( # one query way applied with get_files_from_org() fails due to its complexity. # For more info see docs above. get_files_from_org_per_repo = function(org, + type, repos, file_paths = NULL, host_files_structure = NULL, @@ -187,6 +203,7 @@ EngineGraphQLGitLab <- R6::R6Class( if (is.null(repos)) { repo_data <- private$get_repos_data( org = org, + type = type, repos = repos ) repos <- repo_data[["repositories"]] @@ -216,6 +233,7 @@ EngineGraphQLGitLab <- R6::R6Class( return(org_files_list) }, get_files_structure_from_org = function(org, + type, repos, pattern = NULL, depth = Inf, @@ -223,6 +241,7 @@ EngineGraphQLGitLab <- R6::R6Class( progress = TRUE) { repo_data <- private$get_repos_data( org = org, + type = type, repos = repos ) repositories <- repo_data[["repositories"]] @@ -291,10 +310,10 @@ EngineGraphQLGitLab <- R6::R6Class( return(repo_name) }, - get_repos_data = function(org, repos = NULL) { + get_repos_data = function(org, type, repos = NULL) { repos_list <- self$get_repos_from_org( org = org, - type = "organization" + type = type ) if (!is.null(repos)) { repos_list <- purrr::keep(repos_list, ~ .$node$repo_path %in% repos) diff --git a/R/GitHost.R b/R/GitHost.R index c0fd164f..9b552fd8 100644 --- a/R/GitHost.R +++ b/R/GitHost.R @@ -886,8 +886,10 @@ GitHost <- R6::R6Class( information = user_msg ) } + type <- attr(org, "type") %||% "organization" graphql_engine$get_files_from_org( org = org, + type = type, repos = repos, file_paths = file_path, host_files_structure = host_files_structure, @@ -932,8 +934,10 @@ GitHost <- R6::R6Class( information = user_info ) } + type <- attr(org, "type") %||% "organization" graphql_engine$get_files_structure_from_org( org = org, + type = type, repos = private$repos, pattern = pattern, depth = depth, diff --git a/tests/testthat/_snaps/get_files_content-GitLab.md b/tests/testthat/_snaps/get_files_content-GitLab.md index 3dcd9476..076446d5 100644 --- a/tests/testthat/_snaps/get_files_content-GitLab.md +++ b/tests/testthat/_snaps/get_files_content-GitLab.md @@ -16,9 +16,9 @@ Code gitlab_files_response_by_repos <- test_graphql_gitlab$get_files_from_org(org = "mbtests", - repos = NULL, file_paths = c("DESCRIPTION", "project_metadata.yaml", - "README.md"), host_files_structure = NULL, only_text_files = TRUE, verbose = TRUE, - progress = FALSE) + type = "organization", repos = NULL, file_paths = c("DESCRIPTION", + "project_metadata.yaml", "README.md"), host_files_structure = NULL, + only_text_files = TRUE, verbose = TRUE, progress = FALSE) Message i I will switch to pulling files per repository. diff --git a/tests/testthat/test-get_files_content-GitLab.R b/tests/testthat/test-get_files_content-GitLab.R index 9ea69b2e..c034666b 100644 --- a/tests/testthat/test-get_files_content-GitLab.R +++ b/tests/testthat/test-get_files_content-GitLab.R @@ -57,6 +57,7 @@ test_that("GitLab GraphQL Engine pulls files from a group", { ) gitlab_files_response <- test_graphql_gitlab$get_files_from_org( org = "mbtests", + type = "organization", repos = NULL, file_paths = "meta_data.yaml", only_text_files = TRUE, @@ -113,6 +114,7 @@ test_that("Gitlab GraphQL switches to pulling files per repositories when query expect_snapshot( gitlab_files_response_by_repos <- test_graphql_gitlab$get_files_from_org( org = "mbtests", + type = "organization", repos = NULL, file_paths = c("DESCRIPTION", "project_metadata.yaml", "README.md"), host_files_structure = NULL, From b6b214e0dc7a4626c7f0721099c9e0b1f920e003 Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Thu, 10 Oct 2024 16:02:54 +0000 Subject: [PATCH 10/15] Add publishing of code coverage. --- .github/workflows/test-coverage.yaml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 0edc999d..49d370bc 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -1,5 +1,3 @@ -# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples -# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: branches: [master] @@ -14,6 +12,7 @@ jobs: env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} GITLAB_PAT_PUBLIC: ${{ secrets.GITLAB_PAT}} + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} USE_RENV: "FALSE" steps: @@ -28,7 +27,7 @@ jobs: extra-packages: any::covr needs: coverage - - name: Test coverage + - name: Code coverage run: | covr::codecov( quiet = FALSE, @@ -37,6 +36,15 @@ jobs: ) shell: Rscript {0} + - name: Upload coverage report to Codecov + uses: codecov/codecov-action@v2 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./coverage.xml + flags: unittests + name: codecov-umbrella + fail_ci_if_error: true + - name: Show testthat output if: always() run: | From 7059be5f16c1154410b4f2fd794411a0517f627e Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Fri, 11 Oct 2024 08:26:22 +0000 Subject: [PATCH 11/15] Try fix workflow. --- .github/workflows/test-coverage.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 49d370bc..2f02eda1 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -40,7 +40,6 @@ jobs: uses: codecov/codecov-action@v2 with: token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.xml flags: unittests name: codecov-umbrella fail_ci_if_error: true From 30fa9078492396e436852254e10036f7c0815fbe Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Fri, 11 Oct 2024 08:45:15 +0000 Subject: [PATCH 12/15] Add example workflow. --- inst/get_repos_for_individual_users.R | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 inst/get_repos_for_individual_users.R diff --git a/inst/get_repos_for_individual_users.R b/inst/get_repos_for_individual_users.R new file mode 100644 index 00000000..03a60a7f --- /dev/null +++ b/inst/get_repos_for_individual_users.R @@ -0,0 +1,11 @@ +create_gitstats() %>% + set_github_host( + repos = "ddsjoberg/gtsummary" + ) %>% + get_repos() + +create_gitstats() %>% + set_github_host( + repos = "hadley/elmer" + ) %>% + get_repos() From d34a6212f1dc333db90543ba972b0ed5f85b93e9 Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Fri, 11 Oct 2024 08:53:05 +0000 Subject: [PATCH 13/15] Try generate codecov report. --- .github/workflows/test-coverage.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 2f02eda1..93349055 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -36,10 +36,15 @@ jobs: ) shell: Rscript {0} + - name: Generate code coverage report + run: | + Rscript -e "covr::report(covr::package_coverage(), file='coverage.xml')" + - name: Upload coverage report to Codecov uses: codecov/codecov-action@v2 with: token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.xml flags: unittests name: codecov-umbrella fail_ci_if_error: true From 674be9b817b66d34b4ef5c1bdc77a1f32b4d204e Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Fri, 11 Oct 2024 09:13:42 +0000 Subject: [PATCH 14/15] Update renv. --- renv.lock | 467 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 450 insertions(+), 17 deletions(-) diff --git a/renv.lock b/renv.lock index 26ad6a5d..dabdc725 100644 --- a/renv.lock +++ b/renv.lock @@ -50,6 +50,13 @@ ], "Hash": "543776ae6848fde2f48ff3816d0628bc" }, + "brew": { + "Package": "brew", + "Version": "1.0-10", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "8f4a384e19dccd8c65356dc096847b76" + }, "brio": { "Package": "brio", "Version": "1.1.4", @@ -107,14 +114,24 @@ }, "cli": { "Package": "cli", - "Version": "3.6.2", + "Version": "3.6.3", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "utils" ], - "Hash": "1216ac65ac55ec0058a6f75d7ca0fd52" + "Hash": "b21916dd77a27642b447374a5d30ecf3" + }, + "clipr": { + "Package": "clipr", + "Version": "0.8.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "utils" + ], + "Hash": "3f038e5ac7f41d4ac41ce658c85e3042" }, "commonmark": { "Package": "commonmark", @@ -145,6 +162,20 @@ ], "Hash": "e8a1e41acf02548751f45c718d55aa6a" }, + "credentials": { + "Package": "credentials", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "askpass", + "curl", + "jsonlite", + "openssl", + "sys" + ], + "Hash": "c7844b32098dcbd1c59cbd8dddb4ecc6" + }, "curl": { "Package": "curl", "Version": "5.2.1", @@ -168,6 +199,40 @@ ], "Hash": "99b79fcbd6c4d1ce087f5c5c758b384f" }, + "devtools": { + "Package": "devtools", + "Version": "2.4.5", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "desc", + "ellipsis", + "fs", + "lifecycle", + "memoise", + "miniUI", + "pkgbuild", + "pkgdown", + "pkgload", + "profvis", + "rcmdcheck", + "remotes", + "rlang", + "roxygen2", + "rversions", + "sessioninfo", + "stats", + "testthat", + "tools", + "urlchecker", + "usethis", + "utils", + "withr" + ], + "Hash": "ea5bc8b4a6a01e4f12d98b58329930bb" + }, "diffobj": { "Package": "diffobj", "Version": "0.3.5", @@ -237,6 +302,17 @@ ], "Hash": "fedd9d00c2944ff00a0e2696ccf048ec" }, + "ellipsis": { + "Package": "ellipsis", + "Version": "0.3.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "rlang" + ], + "Hash": "bb0eec2fe32e88d9e2836c2f73ea2077" + }, "evaluate": { "Package": "evaluate", "Version": "0.23", @@ -301,33 +377,76 @@ ], "Hash": "15e9634c0fcd294799e9b2e929ed1b86" }, + "gert": { + "Package": "gert", + "Version": "2.0.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "askpass", + "credentials", + "openssl", + "rstudioapi", + "sys", + "zip" + ], + "Hash": "f70d3fe2d9e7654213a946963d1591eb" + }, + "gh": { + "Package": "gh", + "Version": "1.4.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "gitcreds", + "glue", + "httr2", + "ini", + "jsonlite", + "lifecycle", + "rlang" + ], + "Hash": "fbbbc48eba7a6626a08bb365e44b563b" + }, + "gitcreds": { + "Package": "gitcreds", + "Version": "0.1.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "ab08ac61f3e1be454ae21911eb8bc2fe" + }, "glue": { "Package": "glue", - "Version": "1.7.0", + "Version": "1.8.0", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "methods" ], - "Hash": "e0b3a53876554bd45879e596cdb10a52" + "Hash": "5899f1eaa825580172bb56c08266f37c" }, "highr": { "Package": "highr", - "Version": "0.10", + "Version": "0.11", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "xfun" ], - "Hash": "06230136b2d2b9ba5805e1963fa6e890" + "Hash": "d65ba49117ca223614f71b60d85b8ab7" }, "htmltools": { "Package": "htmltools", - "Version": "0.5.8", + "Version": "0.5.8.1", "Source": "Repository", - "Repository": "CRAN", + "Repository": "RSPM", "Requirements": [ "R", "base64enc", @@ -337,7 +456,37 @@ "rlang", "utils" ], - "Hash": "149431ee39aba5bdc264112c8ff94444" + "Hash": "81d371a9cc60640e74e4ab6ac46dcedc" + }, + "htmlwidgets": { + "Package": "htmlwidgets", + "Version": "1.6.4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "grDevices", + "htmltools", + "jsonlite", + "knitr", + "rmarkdown", + "yaml" + ], + "Hash": "04291cc45198225444a397606810ac37" + }, + "httpuv": { + "Package": "httpuv", + "Version": "1.6.15", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "Rcpp", + "later", + "promises", + "utils" + ], + "Hash": "d55aa087c47a63ead0f6fc10f8fa1ee0" }, "httr": { "Package": "httr", @@ -387,6 +536,13 @@ ], "Hash": "e957e989ea17f937964f0d46b0f0bca0" }, + "ini": { + "Package": "ini", + "Version": "0.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "6154ec2223172bce8162d4153cda21f7" + }, "jquerylib": { "Package": "jquerylib", "Version": "0.1.4", @@ -409,7 +565,7 @@ }, "knitr": { "Package": "knitr", - "Version": "1.45", + "Version": "1.48", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -421,7 +577,18 @@ "xfun", "yaml" ], - "Hash": "1ec462871063897135c1bcbe0fc8f07d" + "Hash": "acf380f300c721da9fde7df115a5f86f" + }, + "later": { + "Package": "later", + "Version": "1.3.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "Rcpp", + "rlang" + ], + "Hash": "a3e051d405326b8b0012377434c62b37" }, "lifecycle": { "Package": "lifecycle", @@ -480,6 +647,18 @@ ], "Hash": "18e9c28c1d3ca1560ce30658b22ce104" }, + "miniUI": { + "Package": "miniUI", + "Version": "0.1.1.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "htmltools", + "shiny", + "utils" + ], + "Hash": "fec5f52652d60615fdb3957b3d74324a" + }, "mockery": { "Package": "mockery", "Version": "0.4.4", @@ -600,6 +779,16 @@ "Repository": "RSPM", "Hash": "a555924add98c99d2f411e37e7d25e9f" }, + "prettyunits": { + "Package": "prettyunits", + "Version": "1.2.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "6b01fc98b1e86c4f705ce9dcfd2f57c7" + }, "processx": { "Package": "processx", "Version": "3.8.4", @@ -613,6 +802,37 @@ ], "Hash": "0c90a7d71988856bad2a2a45dd871bb9" }, + "profvis": { + "Package": "profvis", + "Version": "0.3.8", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "htmlwidgets", + "purrr", + "rlang", + "stringr", + "vctrs" + ], + "Hash": "aa5a3864397ce6ae03458f98618395a1" + }, + "promises": { + "Package": "promises", + "Version": "1.3.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R6", + "Rcpp", + "fastmap", + "later", + "magrittr", + "rlang", + "stats" + ], + "Hash": "434cd5388a3979e74be5c219bcd6e77d" + }, "ps": { "Package": "ps", "Version": "1.7.6", @@ -660,6 +880,28 @@ ], "Hash": "5e3c5dc0b071b21fa128676560dbe94d" }, + "rcmdcheck": { + "Package": "rcmdcheck", + "Version": "1.4.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R6", + "callr", + "cli", + "curl", + "desc", + "digest", + "pkgbuild", + "prettyunits", + "rprojroot", + "sessioninfo", + "utils", + "withr", + "xopen" + ], + "Hash": "8f25ebe2ec38b1f2aef3b0d2ef76f6c4" + }, "rematch2": { "Package": "rematch2", "Version": "2.1.2", @@ -670,6 +912,20 @@ ], "Hash": "76c9e04c712a05848ae7a23d2f170a40" }, + "remotes": { + "Package": "remotes", + "Version": "2.5.0", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "methods", + "stats", + "tools", + "utils" + ], + "Hash": "3ee025083e66f18db6cf27b56e23e141" + }, "renv": { "Package": "renv", "Version": "1.0.5", @@ -682,18 +938,18 @@ }, "rlang": { "Package": "rlang", - "Version": "1.1.3", + "Version": "1.1.4", "Source": "Repository", "Repository": "CRAN", "Requirements": [ "R", "utils" ], - "Hash": "42548638fae05fd9a9b5f3f437fbbbe2" + "Hash": "3eec01f8b1dee337674b2e34ab1f9bc1" }, "rmarkdown": { "Package": "rmarkdown", - "Version": "2.26", + "Version": "2.28", "Source": "Repository", "Repository": "CRAN", "Requirements": [ @@ -712,7 +968,33 @@ "xfun", "yaml" ], - "Hash": "9b148e7f95d33aac01f31282d49e4f44" + "Hash": "062470668513dcda416927085ee9bdc7" + }, + "roxygen2": { + "Package": "roxygen2", + "Version": "7.3.2", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "R6", + "brew", + "cli", + "commonmark", + "cpp11", + "desc", + "knitr", + "methods", + "pkgload", + "purrr", + "rlang", + "stringi", + "stringr", + "utils", + "withr", + "xml2" + ], + "Hash": "6ee25f9054a70f44d615300ed531ba8d" }, "rprojroot": { "Package": "rprojroot", @@ -724,6 +1006,25 @@ ], "Hash": "4c8415e0ec1e29f3f4f6fc108bef0144" }, + "rstudioapi": { + "Package": "rstudioapi", + "Version": "0.16.0", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "96710351d642b70e8f02ddeb237c46a7" + }, + "rversions": { + "Package": "rversions", + "Version": "2.1.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "curl", + "utils", + "xml2" + ], + "Hash": "a9881dfed103e83f9de151dc17002cd1" + }, "sass": { "Package": "sass", "Version": "0.4.9", @@ -738,6 +1039,62 @@ ], "Hash": "d53dbfddf695303ea4ad66f86e99b95d" }, + "sessioninfo": { + "Package": "sessioninfo", + "Version": "1.2.2", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "tools", + "utils" + ], + "Hash": "3f9796a8d0a0e8c6eb49a4b029359d1f" + }, + "shiny": { + "Package": "shiny", + "Version": "1.8.1.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "R6", + "bslib", + "cachem", + "commonmark", + "crayon", + "fastmap", + "fontawesome", + "glue", + "grDevices", + "htmltools", + "httpuv", + "jsonlite", + "later", + "lifecycle", + "methods", + "mime", + "promises", + "rlang", + "sourcetools", + "tools", + "utils", + "withr", + "xtable" + ], + "Hash": "54b26646816af9960a4c64d8ceec75d6" + }, + "sourcetools": { + "Package": "sourcetools", + "Version": "0.1.7-1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R" + ], + "Hash": "5f5a7629f956619d519205ec475fe647" + }, "spelling": { "Package": "spelling", "Version": "2.3.0", @@ -896,6 +1253,51 @@ ], "Hash": "be7a76845222ad20adb761f462eed3ea" }, + "urlchecker": { + "Package": "urlchecker", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "curl", + "tools", + "xml2" + ], + "Hash": "409328b8e1253c8d729a7836fe7f7a16" + }, + "usethis": { + "Package": "usethis", + "Version": "2.2.3", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "cli", + "clipr", + "crayon", + "curl", + "desc", + "fs", + "gert", + "gh", + "glue", + "jsonlite", + "lifecycle", + "purrr", + "rappdirs", + "rlang", + "rprojroot", + "rstudioapi", + "stats", + "utils", + "whisker", + "withr", + "yaml" + ], + "Hash": "d524fd42c517035027f866064417d7e6" + }, "utf8": { "Package": "utf8", "Version": "1.2.4", @@ -959,15 +1361,16 @@ }, "xfun": { "Package": "xfun", - "Version": "0.43", + "Version": "0.48", "Source": "Repository", "Repository": "CRAN", "Requirements": [ + "R", "grDevices", "stats", "tools" ], - "Hash": "ab6371d8653ce5f2f9290f4ec7b42a8e" + "Hash": "89e455b87c84e227eb7f60a1b4e5fe1f" }, "xml2": { "Package": "xml2", @@ -982,12 +1385,42 @@ ], "Hash": "1d0336142f4cd25d8d23cd3ba7a8fb61" }, + "xopen": { + "Package": "xopen", + "Version": "1.0.1", + "Source": "Repository", + "Repository": "CRAN", + "Requirements": [ + "R", + "processx" + ], + "Hash": "423df1e86d5533fcb73c6b02b4923b49" + }, + "xtable": { + "Package": "xtable", + "Version": "1.8-4", + "Source": "Repository", + "Repository": "RSPM", + "Requirements": [ + "R", + "stats", + "utils" + ], + "Hash": "b8acdf8af494d9ec19ccb2481a9b11c2" + }, "yaml": { "Package": "yaml", "Version": "2.3.8", "Source": "Repository", "Repository": "CRAN", "Hash": "29240487a071f535f5e5d5a323b7afbd" + }, + "zip": { + "Package": "zip", + "Version": "2.3.1", + "Source": "Repository", + "Repository": "RSPM", + "Hash": "fcc4bd8e6da2d2011eb64a5e5cc685ab" } } } From 3f6e9efff1e344e728d6889eece224b7095b292f Mon Sep 17 00:00:00 2001 From: Maciej Banas Date: Fri, 11 Oct 2024 09:22:07 +0000 Subject: [PATCH 15/15] Remove code coverage. --- .github/workflows/test-coverage.yaml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml index 93349055..648d9a22 100644 --- a/.github/workflows/test-coverage.yaml +++ b/.github/workflows/test-coverage.yaml @@ -36,19 +36,6 @@ jobs: ) shell: Rscript {0} - - name: Generate code coverage report - run: | - Rscript -e "covr::report(covr::package_coverage(), file='coverage.xml')" - - - name: Upload coverage report to Codecov - uses: codecov/codecov-action@v2 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: coverage.xml - flags: unittests - name: codecov-umbrella - fail_ci_if_error: true - - name: Show testthat output if: always() run: |