Skip to content

Reduce number of manual git release steps #1746

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Mar 5, 2023
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
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.
Comment on lines +215 to +216
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed a mention of devtools::release(), since submit_cran() is ultimately called in that case as well. Also removed the mention of CRAN-RELEASE since it's been long enough now, since we switched to CRAN-SUBMISSION (late 2021).

#' `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