diff --git a/NAMESPACE b/NAMESPACE index 2c3fde50d..76b7dcdc2 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -31,7 +31,9 @@ export(edit_r_profile) export(edit_rstudio_prefs) export(edit_rstudio_snippets) export(edit_template) +export(gh_lock_branch) export(gh_token_help) +export(gh_unlock_branch) export(git_branch_default) export(git_default_branch) export(git_default_branch_configure) diff --git a/R/github_token.R b/R/github_token.R index ed5b5b9c1..42a3438c3 100644 --- a/R/github_token.R +++ b/R/github_token.R @@ -280,10 +280,12 @@ scold_for_scopes <- function(scopes) { suggestions <- c( "*" = if (!has_repo) "{.val repo}: needed to fully access user's repos", - "*" = if (!has_workflow) - "{.val workflow}: needed to manage GitHub Actions workflow files", - "*" = if (!has_user_email) + "*" = if (!has_workflow) { + "{.val workflow}: needed to manage GitHub Actions workflow files" + }, + "*" = if (!has_user_email) { "{.val user:email}: needed to read user's email addresses" + } ) message <- c( "!" = "Token lacks recommended scopes:", @@ -294,3 +296,48 @@ scold_for_scopes <- function(scopes) { ) ui_bullets(message) } + +#' Lock and unlock a branch on GitHub +#' +#' These functions lock and unlock a branch on GitHub so that it's not possible +#' for anyone to make any changes. This is used as part of the release process +#' to ensure that you don't accidentally merge any pull requests or push any +#' commits while you are waiting for CRAN to get back to you. +#' +#' @export +#' @param branch The branch to lock/unlock. If not supplied, uses the +#' default branch with is usually "main" or "master". +gh_lock_branch <- function(branch = NULL) { + cfg <- github_remote_config(github_get = TRUE) + repo <- target_repo(cfg) + branch <- branch %||% git_default_branch_(cfg) + + invisible(gh::gh( + "PUT /repos/{owner}/{repo}/branches/{branch}/protection", + owner = repo$repo_owner, + repo = repo$repo_name, + branch = branch, + # required parameters + required_status_checks = NA, + enforce_admins = TRUE, + required_pull_request_reviews = NA, + restrictions = NA, + # paramter that actually does what we want + lock_branch = TRUE + )) +} + +#' @export +#' @rdname gh_lock_branch +gh_unlock_branch <- function(branch = NULL) { + cfg <- github_remote_config(github_get = TRUE) + repo <- target_repo(cfg) + branch <- branch %||% git_default_branch_(cfg) + + invisible(gh::gh( + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection", + owner = repo$repo_owner, + repo = repo$repo_name, + branch = branch + )) +} diff --git a/R/release.R b/R/release.R index 62bb4dd35..e2599cd95 100644 --- a/R/release.R +++ b/R/release.R @@ -81,7 +81,7 @@ release_checklist <- function(version, on_cran, target_repo = NULL) { milestone_num <- gh_milestone_number(target_repo, version) c( - if (!on_cran) + if (!on_cran) { c( "First release:", "", @@ -100,7 +100,8 @@ release_checklist <- function(version, on_cran, target_repo = NULL) { ), todo("Review "), "" - ), + ) + }, "Prepare for release:", "", todo("`git pull`"), @@ -137,6 +138,7 @@ release_checklist <- function(version, on_cran, target_repo = NULL) { "", "Submit to CRAN:", "", + todo("`usethis::gh_lock_branch()"), todo("`usethis::use_version('{type}')`"), todo("`devtools::submit_cran()`"), todo("Approve email"), @@ -144,6 +146,7 @@ release_checklist <- function(version, on_cran, target_repo = NULL) { "Wait for CRAN...", "", todo("Accepted :tada:"), + todo("`usethis::gh_unlock_branch()"), todo("Finish & publish blog post", type != "patch"), todo("Add link to blog post in pkgdown news menu", type != "patch"), todo("`usethis::use_github_release()`"), diff --git a/_pkgdown.yml b/_pkgdown.yml index a099f2ecd..bda74c2d1 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -121,6 +121,7 @@ reference: - starts_with("use_github") - git_sitrep - create_github_token + - gh_lock_branch - gh_token_help - git_vaccinate - use_git_config diff --git a/man/gh_lock_branch.Rd b/man/gh_lock_branch.Rd new file mode 100644 index 000000000..28ec1f815 --- /dev/null +++ b/man/gh_lock_branch.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/github_token.R +\name{gh_lock_branch} +\alias{gh_lock_branch} +\alias{gh_unlock_branch} +\title{Lock and unlock a branch on GitHub} +\usage{ +gh_lock_branch(branch = NULL) + +gh_unlock_branch(branch = NULL) +} +\arguments{ +\item{branch}{The branch to lock/unlock. If not supplied, uses the +default branch with is usually "main" or "master".} +} +\description{ +These functions lock and unlock a branch on GitHub so that it's not possible +for anyone to make any changes. This is used as part of the release process +to ensure that you don't accidentally merge any pull requests or push any +commits while you are waiting for CRAN to get back to you. +} diff --git a/tests/testthat/_snaps/release.md b/tests/testthat/_snaps/release.md index 382b2592e..728e5b996 100644 --- a/tests/testthat/_snaps/release.md +++ b/tests/testthat/_snaps/release.md @@ -26,6 +26,7 @@ Submit to CRAN: + * [ ] `usethis::gh_lock_branch() * [ ] `usethis::use_version('minor')` * [ ] `devtools::submit_cran()` * [ ] Approve email @@ -33,6 +34,7 @@ Wait for CRAN... * [ ] Accepted :tada: + * [ ] `usethis::gh_unlock_branch() * [ ] Finish & publish blog post * [ ] Add link to blog post in pkgdown news menu * [ ] `usethis::use_github_release()` @@ -61,6 +63,7 @@ Submit to CRAN: + * [ ] `usethis::gh_lock_branch() * [ ] `usethis::use_version('patch')` * [ ] `devtools::submit_cran()` * [ ] Approve email @@ -68,6 +71,7 @@ Wait for CRAN... * [ ] Accepted :tada: + * [ ] `usethis::gh_unlock_branch() * [ ] `usethis::use_github_release()` * [ ] `usethis::use_dev_version(push = TRUE)` * [ ] `usethis::use_news_md()` @@ -94,6 +98,7 @@ Submit to CRAN: + * [ ] `usethis::gh_lock_branch() * [ ] `usethis::use_version('major')` * [ ] `devtools::submit_cran()` * [ ] Approve email @@ -101,6 +106,7 @@ Wait for CRAN... * [ ] Accepted :tada: + * [ ] `usethis::gh_unlock_branch() * [ ] Finish & publish blog post * [ ] Add link to blog post in pkgdown news menu * [ ] `usethis::use_github_release()` @@ -150,6 +156,7 @@ Submit to CRAN: + * [ ] `usethis::gh_lock_branch() * [ ] `usethis::use_version('major')` * [ ] `devtools::submit_cran()` * [ ] Approve email @@ -157,6 +164,7 @@ Wait for CRAN... * [ ] Accepted :tada: + * [ ] `usethis::gh_unlock_branch() * [ ] Finish & publish blog post * [ ] Add link to blog post in pkgdown news menu * [ ] `usethis::use_github_release()` @@ -185,6 +193,7 @@ Submit to CRAN: + * [ ] `usethis::gh_lock_branch() * [ ] `usethis::use_version('major')` * [ ] `devtools::submit_cran()` * [ ] Approve email @@ -192,6 +201,7 @@ Wait for CRAN... * [ ] Accepted :tada: + * [ ] `usethis::gh_unlock_branch() * [ ] Finish & publish blog post * [ ] Add link to blog post in pkgdown news menu * [ ] `usethis::use_github_release()`