Permalink
Cannot retrieve contributors at this time
248 lines (228 sloc)
8.6 KB
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #' Connect a local repo with GitHub | |
| #' | |
| #' @description | |
| #' `use_github()` takes a local project, creates an associated repo on GitHub, | |
| #' adds it to your local repo as the `origin` remote, and makes an initial push | |
| #' to synchronize. `use_github()` requires that your project already be a Git | |
| #' repository, which you can accomplish with [use_git()], if needed. | |
| #' | |
| #' `use_github_links()` populates the `URL` and `BugReports` fields of a | |
| #' GitHub-using R package with appropriate links (unless they already exist). | |
| #' | |
| #' @section Authentication: | |
| #' A new GitHub repo will be created via the GitHub API, therefore you must | |
| #' make a [GitHub personal access token | |
| #' (PAT)](https://github.com/settings/tokens) available. You can either | |
| #' provide this directly via the `auth_token` argument or store it in an | |
| #' environment variable. Use [browse_github_pat()] to get help obtaining and | |
| #' storing your PAT. See [gh::gh_whoami()] for even more detail. | |
| #' | |
| #' The argument `protocol` reflects how you wish to authenticate with GitHub | |
| #' for this repo in the long run. This determines the form of the URL for the | |
| #' `origin` remote: | |
| #' * `protocol = "ssh"`: `git@@github.com:<USERNAME>/<REPO>.git` | |
| #' * `protocol = "https"`: `https://github.com/<USERNAME>/<REPO>.git` | |
| #' | |
| #' For `protocol = "ssh"`, it is assumed that public and private keys are in the | |
| #' default locations, `~/.ssh/id_rsa.pub` and `~/.ssh/id_rsa`, respectively, and | |
| #' that `ssh-agent` is configured to manage any associated passphrase. | |
| #' Alternatively, specify a [git2r::cred_ssh_key()] object via the `credentials` | |
| #' parameter. Read more about ssh setup in [Happy | |
| #' Git](http://happygitwithr.com/ssh-keys.html), especially the [troubleshooting | |
| #' section](http://happygitwithr.com/ssh-keys.html#ssh-troubleshooting). | |
| #' | |
| #' @inheritParams use_git | |
| #' @param organisation If supplied, the repo will be created under this | |
| #' organisation. You must have access to create repositories. | |
| #' @param auth_token Provide a personal access token (PAT) from | |
| #' <https://github.com/settings/tokens>. If `NULL`, will use the logic | |
| #' described in [gh::gh_whoami()] to look for a token stored in an environment | |
| #' variable. Use [browse_github_pat()] to help set up your PAT. | |
| #' @param private If `TRUE`, creates a private repository. | |
| #' @param host GitHub API host to use. Override with the endpoint-root for your | |
| #' GitHub enterprise instance, for example, | |
| #' "https://github.hostname.com/api/v3" | |
| #' @param protocol transfer protocol, either "ssh" (the default) or "https" | |
| #' @param credentials A [git2r::cred_ssh_key()] specifying specific ssh | |
| #' credentials or `NULL` for default ssh key and ssh-agent behaviour. | |
| #' @export | |
| #' @examples | |
| #' \dontrun{ | |
| #' create_package("test-pkg") # creates package in current working directory | |
| #' | |
| #' ## now, working inside "test-pkg", initialize git repository | |
| #' use_git() | |
| #' | |
| #' ## create github repository and configure as git remote | |
| #' use_github() ## to use default ssh protocol | |
| #' use_github(protocol = "https") ## to use https | |
| #' } | |
| use_github <- function(organisation = NULL, | |
| private = FALSE, | |
| protocol = c("ssh", "https"), | |
| credentials = NULL, | |
| auth_token = NULL, | |
| host = NULL) { | |
| check_uses_git() | |
| if (uses_github(proj_get())) { | |
| done("GitHub is already initialized") | |
| return(invisible()) | |
| } | |
| pkg <- project_data() | |
| repo_name <- pkg$Project %||% gsub("\n", " ", pkg$Package) | |
| repo_desc <- pkg$Title %||% "" | |
| if (interactive()) { | |
| todo("Check title and description") | |
| code_block( | |
| paste0("Name: ", repo_name), | |
| paste0("Description: ", repo_desc), | |
| copy = FALSE | |
| ) | |
| if (nope("Are title and description ok?")) { | |
| return(invisible()) | |
| } | |
| } else { | |
| done("Setting title and description") | |
| code_block( | |
| paste0("Name: ", repo_name), | |
| paste0("Description: ", repo_desc), | |
| copy = FALSE | |
| ) | |
| } | |
| done("Creating GitHub repository") | |
| if (is.null(organisation)) { | |
| create <- gh::gh( | |
| "POST /user/repos", | |
| name = repo_name, | |
| description = repo_desc, | |
| private = private, | |
| .api_url = host, | |
| .token = auth_token | |
| ) | |
| } else { | |
| create <- gh::gh( | |
| "POST /orgs/:org/repos", | |
| org = organisation, | |
| name = repo_name, | |
| description = repo_desc, | |
| private = private, | |
| .api_url = host, | |
| .token = auth_token | |
| ) | |
| } | |
| done("Adding GitHub remote") | |
| r <- git2r::repository(proj_get()) | |
| protocol <- match.arg(protocol) | |
| origin_url <- switch(protocol, | |
| https = create$clone_url, | |
| ssh = create$ssh_url | |
| ) | |
| git2r::remote_add(r, "origin", origin_url) | |
| if (is_package()) { | |
| done("Adding GitHub links to DESCRIPTION") | |
| use_github_links(auth_token = auth_token, host = host) | |
| if (git_uncommitted()) { | |
| git2r::add(r, "DESCRIPTION") | |
| git2r::commit(r, "Add GitHub links to DESCRIPTION") | |
| } | |
| } | |
| done("Pushing to GitHub and setting remote tracking branch") | |
| if (protocol == "ssh") { | |
| ## [1] push via ssh required for success setting remote tracking branch | |
| ## [2] to get passphrase from ssh-agent, you must use NULL credentials | |
| git2r::push(r, "origin", "refs/heads/master", credentials = credentials) | |
| } else { ## protocol == "https" | |
| ## in https case, when GITHUB_PAT is passed as password, | |
| ## the username is immaterial, but git2r doesn't know that | |
| cred <- git2r::cred_user_pass("EMAIL", auth_token) | |
| git2r::push(r, "origin", "refs/heads/master", credentials = cred) | |
| } | |
| ## utils::head instead of git2r::head due to the conversion of git2r's head | |
| ## from S4 --> S3 method in v0.21.0 --> 0.21.0.9000 | |
| git2r::branch_set_upstream(utils::head(r), "origin/master") | |
| view_url(create$html_url) | |
| invisible(NULL) | |
| } | |
| #' @export | |
| #' @rdname use_github | |
| use_github_links <- function(auth_token = NULL, | |
| host = "https://api.github.com") { | |
| check_uses_github() | |
| info <- gh::gh_tree_remote(proj_get()) | |
| res <- gh::gh( | |
| "GET /repos/:owner/:repo", | |
| owner = info$username, | |
| repo = info$repo, | |
| .api_url = host, | |
| .token = auth_token | |
| ) | |
| use_description_field("URL", res$html_url) | |
| use_description_field("BugReports", file.path(res$html_url, "issues")) | |
| invisible() | |
| } | |
| #' Create a GitHub personal access token | |
| #' | |
| #' Opens a browser window to the GitHub page where you can generate a [Personal | |
| #' Access | |
| #' Token](https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line). | |
| #' Make sure you have signed up for a free [GitHub.com](https://github.com/) | |
| #' account and that you are signed in. Click "Generate token" after you have | |
| #' verified the scopes. Copy the token right away! You probably want to store it | |
| #' in `.Renviron` as the `GITHUB_PAT` environment variable. [edit_r_environ()] | |
| #' can help you do that. Use [gh::gh_whoami()] to get information on an existing | |
| #' token. | |
| #' | |
| #' @param scopes Character vector of token permissions. These are just defaults | |
| #' that will be pre-selected in the web form. You can select the final values | |
| #' on the GitHub page. Read more about GitHub API scopes at | |
| #' <https://developer.github.com/apps/building-oauth-apps/scopes-for-oauth-apps/>. | |
| #' | |
| #' @param description Short description or nickname for the token. It helps you | |
| #' distinguish various tokens on GitHub. | |
| #' @inheritParams use_github | |
| #' @export | |
| #' @examples | |
| #' \dontrun{ | |
| #' browse_github_pat() | |
| #' ## COPY THE PAT!!! | |
| #' ## almost certainly to be followed by ... | |
| #' edit_r_environ() | |
| #' ## which helps you store the PAT as an env var | |
| #' } | |
| browse_github_pat <- function(scopes = c("repo", "gist"), | |
| description = "R:GITHUB_PAT", | |
| host = "https://github.com") { | |
| scopes <- collapse(scopes, ",") | |
| url <- sprintf( | |
| "%s/settings/tokens/new?scopes=%s&description=%s", | |
| host, scopes, description | |
| ) | |
| view_url(url) | |
| todo( | |
| "Call ", code("edit_r_environ()"), " to open ", value(".Renviron"), | |
| ", and store your PAT with a line like:\n", "GITHUB_PAT=xxxyyyzzz" | |
| ) | |
| todo("Make sure ", value(".Renviron"), " ends with a newline!") | |
| } | |
| uses_github <- function(base_path = proj_get()) { | |
| tryCatch({ | |
| gh::gh_tree_remote(base_path) | |
| TRUE | |
| }, error = function(e) FALSE) | |
| } | |
| check_uses_github <- function(base_path = proj_get()) { | |
| if (uses_github(base_path)) { | |
| return(invisible()) | |
| } | |
| stop( | |
| "Cannot detect that package already uses GitHub.\n", | |
| "Do you need to run ", code("use_github()"), "?", | |
| call. = FALSE | |
| ) | |
| } | |
| ## use from gh when/if exported | |
| ## https://github.com/r-lib/gh/issues/74 | |
| gh_token <- function() { | |
| token <- Sys.getenv('GITHUB_PAT', "") | |
| if (token == "") Sys.getenv("GITHUB_TOKEN", "") else token | |
| } |