Skip to content

Commit

Permalink
Reduce number of manual git release steps (#1746)
Browse files Browse the repository at this point in the history
* Reduce number of manual release steps

* `use_release_issue()` checks default branch and pulls automatically.
* `use_github_release()` automatically pushes, if safe.
* `use_github_release()` just creates the release.
* `use_version()` gains `push` argument.

Fixes #1385. Closes #1678.

* Restore initial git pull

* Extract out git_push_first()

* Apply suggestions from code review

Co-authored-by: Jennifer (Jenny) Bryan <jenny.f.bryan@gmail.com>

* WS

* Re-document

* Tweak argument order

* Make some cli helper wrappers

* Don't mess with function order

* Update NEWS

* Tweak word

* Tweak comment

* Polish docs

* Don't mention release()

---------

Co-authored-by: Jennifer (Jenny) Bryan <jenny.f.bryan@gmail.com>
  • Loading branch information
hadley and jennybc authored Mar 5, 2023
1 parent cb4285b commit 6c2012b
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 87 deletions.
8 changes: 8 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# usethis (development version)

* `use_version()` gains a `push` argument to optionally push the result after
committing. This is used to eliminate a manual step from the
`use_release_issue()` checklist (#1385).

* `use_github_release()` now automatically pushes to GitHub (if safe) (#1385)
and automatically publishes the release, rather than requiring you to edit
and publish the draft.

* `use_tidy_logo()` is a new function that calls `use_logo()` on the appropriate
hex sticker PNG file at <https://github.com/rstudio/hex-stickers> (@ateucher,
#1871).
Expand Down
15 changes: 3 additions & 12 deletions R/github.R
Original file line number Diff line number Diff line change
Expand Up @@ -158,26 +158,17 @@ use_github <- function(organisation = NULL,

if (is_package()) {
# we tryCatch(), because we can't afford any failure here to result in not
# making the first push and configuring default branch
# doing the first push and configuring the default branch
# such an incomplete setup is hard to diagnose / repair post hoc
tryCatch(
use_github_links(),
error = function(e) NULL
)
}

repo <- git_repo()
remref <- glue("origin/{default_branch}")
ui_done("
Pushing {ui_value(default_branch)} branch to GitHub and setting \\
{ui_value(remref)} as upstream branch")
gert::git_push(
remote = "origin",
set_upstream = TRUE,
repo = repo,
verbose = FALSE
)
git_push_first(default_branch, "origin")

repo <- git_repo()
gbl <- gert::git_branch_list(local = TRUE, repo = repo)
if (nrow(gbl) > 1) {
ui_done("
Expand Down
15 changes: 4 additions & 11 deletions R/pr.R
Original file line number Diff line number Diff line change
Expand Up @@ -383,22 +383,15 @@ pr_push <- function() {
)
title <- glue("Which repo do you want to push to?")
choice <- utils::menu(choices, graphics = FALSE, title = title)
remote <- names(choices)[[choice]]
remote <- names(choices)[[choice]]
} else {
remote <- "origin"
}
ui_done("
Pushing local {ui_value(branch)} branch to {ui_value(remote)} remote.")
gert::git_push(remote = remote, verbose = FALSE, repo = repo)

git_push_first(branch, remote)
} else {
check_branch_pulled(use = "pr_pull()")
ui_done("Pushing local {ui_value(branch)} branch to {ui_value(remref)}.")
gert::git_push(
remote = remref_remote(remref),
refspec = glue("refs/heads/{branch}:refs/heads/{remref_branch(remref)}"),
verbose = FALSE,
repo = repo
)
git_push(branch, remref)
}

# Prompt to create PR if does not exist yet
Expand Down
2 changes: 1 addition & 1 deletion R/proj-desc.R
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ proj_desc_create <- function(name, fields = list(), roxygen = TRUE) {
write_over(proj_path("DESCRIPTION"), read_utf8(tf))

# explicit check of "usethis.quiet" since I'm not doing the printing
if (!getOption("usethis.quiet", default = FALSE)) {
if (!is_quiet()) {
desc$print()
}
}
Expand Down
53 changes: 29 additions & 24 deletions R/release.R
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,9 @@ release_checklist <- function(version, on_cran) {
"Wait for CRAN...",
"",
todo("Accepted :tada:"),
todo("`git push`"),
todo("`usethis::use_github_release()`"),
todo("`usethis::use_dev_version()`"),
todo("`git push`"),
todo("`usethis::use_dev_version(push = TRUE)`"),
todo("`usethis::use_news_md()`", !has_news),
todo("Finish blog post", type != "patch"),
todo("Tweet", type != "patch"),
todo("Add link to blog post in pkgdown news menu", type != "patch")
Expand Down Expand Up @@ -207,28 +206,27 @@ release_type <- function(version) {
}
}

#' Draft a GitHub release
#' Publish a GitHub release
#'
#' @description
#' Creates a __draft__ GitHub release for the current package. Once you are
#' satisfied that it is correct, you will need to publish the release from
#' GitHub. The key pieces of info are which commit / SHA to tag, the associated
#' package version, and the relevant NEWS entries.
#'
#' If you use `devtools::release()` or `devtools::submit_cran()` to submit to
#' CRAN, information about the submitted state is captured in a CRAN-SUBMISSION
#' or CRAN-RELEASE file. `use_github_release()` uses this info to populate the
#' draft GitHub release and, after success, deletes the CRAN-SUBMISSION or
#' CRAN-RELEASE file.
#' Pushes the current branch (if safe) then publishes a GitHub release for the
#' latest CRAN submission.
#'
#' In the absence of such a file, we must fall back to assuming the current
#' state (SHA of `HEAD`, package version, NEWS) is the submitted state.
#' If you use [devtools::submit_cran()] to submit to CRAN, information about the
#' submitted state is captured in a `CRAN-SUBMISSION` file.
#' `use_github_release()` uses this info to populate the GitHub release notes
#' and, after success, deletes the file. In the absence of such a file, we
#' assume that current state (SHA of `HEAD`, package version, NEWS) is the
#' submitted state.
#'
#' @param host,auth_token `r lifecycle::badge("deprecated")`: No longer
#' consulted now that usethis allows the gh package to lookup a token based on
#' a URL determined from the current project's GitHub remotes.
#' @param publish If `TRUE`, publishes a release. If `FALSE`, creates a draft
#' release.
#' @export
use_github_release <- function(host = deprecated(),
use_github_release <- function(publish = TRUE,
host = deprecated(),
auth_token = deprecated()) {
check_is_package("use_github_release()")
if (lifecycle::is_present(host)) {
Expand All @@ -239,7 +237,7 @@ use_github_release <- function(host = deprecated(),
}

tr <- target_repo(github_get = TRUE, ok_configs = c("ours", "fork"))
check_can_push(tr = tr, "to draft a release")
check_can_push(tr = tr, "to create a release")

dat <- get_release_data(tr)
release_name <- glue("{dat$Package} {dat$Version}")
Expand All @@ -248,26 +246,33 @@ use_github_release <- function(host = deprecated(),
kv_line("Tag name", tag_name)
kv_line("SHA", dat$SHA)

if (git_can_push()) {
git_push()
}
check_github_has_SHA(SHA = dat$SHA, tr = tr)

on_cran <- !is.null(cran_version())
news <- get_release_news(SHA = dat$SHA, tr = tr, on_cran = on_cran)

gh <- gh_tr(tr)

ui_cli_inform("Publishing {tag_name} release to GitHub")
release <- gh(
"POST /repos/{owner}/{repo}/releases",
name = release_name, tag_name = tag_name,
target_commitish = dat$SHA, body = news, draft = TRUE
name = release_name,
tag_name = tag_name,
target_commitish = dat$SHA,
body = news,
draft = !publish
)
ui_cli_inform("Release at {.url {release$html_url}}")

if (!is.null(dat$file)) {
ui_done("{ui_path(dat$file)} deleted")
ui_cli_inform("Deleting {.path {dat$file}}")
file_delete(dat$file)
}

Sys.sleep(1)
view_url(release$html_url)
ui_todo("Publish the release via \"Edit draft\" > \"Publish release\"")
invisible()
}

get_release_data <- function(tr = target_repo(github_get = TRUE)) {
Expand Down
19 changes: 16 additions & 3 deletions R/ui.R
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 +299,17 @@ ui_bullet <- function(x, bullet = cli::symbol$bullet) {

# All UI output must eventually go through ui_inform() so that it
# can be quieted with 'usethis.quiet' when needed.
ui_inform <- function(..., quiet = getOption("usethis.quiet", default = FALSE)) {
if (!quiet) {
ui_inform <- function(...) {
if (!is_quiet()) {
inform(paste0(...))
}

invisible()
}

is_quiet <- function() {
isTRUE(getOption("usethis.quiet", default = FALSE))
}

# Sitrep helpers ---------------------------------------------------------------

hd_line <- function(name) {
Expand All @@ -318,3 +321,13 @@ kv_line <- function(key, value, .envir = parent.frame()) {
key <- glue(key, .envir = .envir)
ui_inform(glue("{cli::symbol$bullet} {key}: {value}"))
}


# cli wrappers ------------------------------------------------------------

ui_cli_inform <- function(..., .envir = parent.frame()) {
if (!is_quiet()) {
cli::cli_inform(..., .envir = .envir)
}
invisible()
}
56 changes: 54 additions & 2 deletions R/utils-git.R
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ git_status <- function(untracked) {
}

# Commit -----------------------------------------------------------------------
git_ask_commit <- function(message, untracked, paths = NULL) {
git_ask_commit <- function(message, untracked, push = FALSE, paths = NULL) {
if (!is_interactive() || !uses_git()) {
return(invisible())
}
Expand Down Expand Up @@ -168,9 +168,22 @@ git_ask_commit <- function(message, untracked, paths = NULL) {
paste0("* ", ui_paths)
))

if (ui_yeah("Is it ok to commit {if (n == 1) 'it' else 'them'}?")) {
# Only push if no remote & a single change
push <- push && git_can_push(max_local = 1)

msg <- paste0(
"Is it ok to commit ",
if (push) "and push ",
if (n == 1) 'it' else 'them',
"?"
)
if (ui_yeah(msg)) {
git_commit(paths, message)
if (push) {
git_push()
}
}

invisible()
}

Expand Down Expand Up @@ -317,6 +330,45 @@ git_branch_compare <- function(branch = git_branch(), remref = NULL) {
list(local_only = out$ahead, remote_only = out$behind)
}

git_can_push <- function(max_local = Inf, branch = git_branch(), remref = NULL) {
remref <- remref %||% git_branch_tracking(branch)
if (is.null(remref)) {
return(FALSE)
}
comp <- git_branch_compare(branch, remref)
comp$remote_only == 0 && comp$local_only <= max_local
}

git_push <- function(branch = git_branch(), remref = NULL, verbose = TRUE) {
remref <- remref %||% git_branch_tracking(branch)
if (verbose) {
ui_done("Pushing local {ui_value(branch)} branch to {ui_value(remref)}.")
}

gert::git_push(
remote = remref_remote(remref),
refspec = glue("refs/heads/{branch}:refs/heads/{remref_branch(remref)}"),
verbose = FALSE,
repo = git_repo()
)
}

git_push_first <- function(branch = git_branch(), remote = "origin", verbose = TRUE) {
if (verbose) {
remref <- glue("{remote}/{branch}")
ui_done("
Pushing {ui_value(branch)} branch to GitHub and setting \\
{ui_value(remref)} as upstream branch"
)
}
gert::git_push(
remote = remote,
set_upstream = TRUE,
verbose = FALSE,
repo = git_repo()
)
}

# Checks ------------------------------------------------------------------

check_current_branch <- function(is = NULL, is_not = NULL,
Expand Down
12 changes: 8 additions & 4 deletions R/version.R
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
#' ```

#' `use_version()` increments the "Version" field in `DESCRIPTION`, adds a new
#' heading to `NEWS.md` (if it exists), and commits those changes (if package
#' uses Git). It makes the same update to a line like `PKG_version = "x.y.z";`
#' in `src/version.c` (if it exists).
#' heading to `NEWS.md` (if it exists), commits those changes (if package uses
#' Git), and optionally pushes (if safe to do so). It makes the same update to a
#' line like `PKG_version = "x.y.z";` in `src/version.c` (if it exists).
#'

#' `use_dev_version()` increments to a development version, e.g. from 1.0.0 to
Expand Down Expand Up @@ -46,8 +46,10 @@
NULL

#' @rdname use_version
#' @param push If `TRUE`, also attempts to push the commits to the remote
#' branch.
#' @export
use_version <- function(which = NULL) {
use_version <- function(which = NULL, push = FALSE) {
if (is.null(which) && !is_interactive()) {
return(invisible(FALSE))
}
Expand All @@ -74,8 +76,10 @@ use_version <- function(which = NULL) {
git_ask_commit(
glue("Increment version number to {new_ver}"),
untracked = TRUE,
push = push,
paths = c("DESCRIPTION", "NEWS.md", path("src", "version.c"))
)

invisible(TRUE)
}

Expand Down
31 changes: 17 additions & 14 deletions man/use_github_release.Rd

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

Loading

0 comments on commit 6c2012b

Please sign in to comment.