Skip to content

Commit db08965

Browse files
authored
Take CRAN-SUBMISSION or CRAN-RELEASE much more seriously (#1383)
* Consult CRAN-RELEASE for release sha Closes #1380 * Create a helper to marshal data, from old or new CRAN-RELEASE files PR to change what devtools writes: r-lib/devtools#2379 * With old-style CRAN-RELEASE, the short SHA length isn't guaranteed Apparently the meaning of `--short` in `git-rev-parse` varies. On my machine it's 8. * Add tests * Adapt to CRAN-SUBMISSION; get DESCRIPTION and NEWS.md from GitHub if necessary * Add NEWS * Update tests
1 parent 447d8c9 commit db08965

File tree

5 files changed

+216
-36
lines changed

5 files changed

+216
-36
lines changed

NEWS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ usethis has a more sophisticated understanding of the default branch and gains s
3131
arbitrary file on GitHub that the user has permission to read. It supports
3232
targeting a specific branch, tag, or commit and can follow a symlink (#1407).
3333
`use_github_file()` now powers `use_github_action()` and friends.
34+
35+
* `use_github_release()` is much more diligent about using any information left
36+
behind by `devtools::submit_cran()` or `devtools::release()`. Specifically,
37+
this applies to determining which SHA is to be tagged in the release. And this
38+
SHA, in turn, determines the consulted versions of DESCRIPTION (for package
39+
version) and NEWS.md (for release notes) (#1380).
3440

3541
* `use_release_issue()` also takes bullets from `release_questions()`,
3642
for compatibility with `devtools::release()`.

R/release.R

Lines changed: 131 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -148,17 +148,28 @@ release_type <- function(version) {
148148

149149
#' Draft a GitHub release
150150
#'
151-
#' Creates a __draft__ GitHub release for the current package using the current
152-
#' version and `NEWS.md`. If you are comfortable that it is correct, you will
153-
#' need to publish the release from GitHub. It also deletes `CRAN-RELEASE` and
154-
#' checks that you've pushed all commits to GitHub.
151+
#' @description
152+
#' Creates a __draft__ GitHub release for the current package. Once you are
153+
#' satisfied that it is correct, you will need to publish the release from
154+
#' GitHub. The key pieces of info are which commit / SHA to tag, the associated
155+
#' package version, and the relevant NEWS entries.
156+
#'
157+
#' If you use `devtools::release()` or `devtools::submit_cran()` to submit to
158+
#' CRAN, information about the submitted state is captured in a CRAN-SUBMISSION
159+
#' or CRAN-RELEASE file. `use_github_release()` uses this info to populate the
160+
#' draft GitHub release and, after success, deletes the CRAN-SUBMISSION or
161+
#' CRAN-RELEASE file.
162+
#'
163+
#' In the absence of such a file, we must fall back to assuming the current
164+
#' state (SHA of `HEAD`, package version, NEWS) is the submitted state.
155165
#'
156-
#' @param host,auth_token `r lifecycle::badge("deprecated")`: No longer consulted
157-
#' now that usethis allows the gh package to lookup a token based on a URL
158-
#' determined from the current project's GitHub remotes.
166+
#' @param host,auth_token `r lifecycle::badge("deprecated")`: No longer
167+
#' consulted now that usethis allows the gh package to lookup a token based on
168+
#' a URL determined from the current project's GitHub remotes.
159169
#' @export
160170
use_github_release <- function(host = deprecated(),
161171
auth_token = deprecated()) {
172+
check_is_package("use_github_release()")
162173
if (lifecycle::is_present(host)) {
163174
deprecate_warn_host("use_github_release")
164175
}
@@ -173,37 +184,129 @@ use_github_release <- function(host = deprecated(),
173184
is required to draft a release.")
174185
}
175186

176-
challenge_non_default_branch(
177-
"Are you sure you want to create a release on a non-default branch?"
178-
)
179-
check_branch_pushed()
180-
181-
cran_release <- proj_path("CRAN-RELEASE")
182-
if (file_exists(cran_release)) {
183-
file_delete(cran_release)
184-
}
185-
186-
path <- proj_path("NEWS.md")
187-
if (file_exists(path)) {
188-
news <- news_latest(read_utf8(path))
189-
} else {
190-
news <- "Initial release"
191-
}
187+
dat <- get_release_data(tr)
188+
news <- get_release_news(SHA = dat$SHA, tr = tr)
192189

193-
package <- package_data()
190+
release_name <- glue("{dat$Package} {dat$Version}")
191+
tag_name <- glue("v{dat$Version}")
192+
kv_line("Release name", release_name)
193+
kv_line("Tag name", tag_name)
194+
kv_line("SHA", dat$SHA)
194195

195196
gh <- gh_tr(tr)
196197
release <- gh(
197198
"POST /repos/{owner}/{repo}/releases",
198-
tag_name = paste0("v", package$Version),
199-
target_commitish = gert::git_info(repo = git_repo())$commit,
200-
name = paste0(package$Package, " ", package$Version),
201-
body = news, draft = TRUE
199+
name = release_name, tag_name = tag_name,
200+
target_commitish = dat$SHA, body = news, draft = TRUE
202201
)
203202

203+
if (!is.null(dat$file)) {
204+
ui_done("{ui_path(dat$file)} deleted")
205+
file_delete(dat$file)
206+
}
207+
204208
view_url(release$html_url)
209+
ui_todo("Publish the release via \"Edit draft\" > \"Publish release\"")
205210
}
206211

212+
get_release_data <- function(tr = target_repo(github_get = TRUE)) {
213+
package <- package_data()
214+
cran_submission <-
215+
path_first_existing(proj_path(c("CRAN-SUBMISSION", "CRAN-RELEASE")))
216+
217+
if (is.null(cran_submission)) {
218+
ui_done("Using current HEAD commit for the release")
219+
challenge_non_default_branch()
220+
check_branch_pushed()
221+
return(list(
222+
Package = package$Package,
223+
Version = package$Version,
224+
SHA = gert::git_info(repo = git_repo())$commit
225+
))
226+
}
227+
228+
if (path_file(cran_submission) == "CRAN-SUBMISSION") {
229+
# new style ----
230+
# Version: 2.4.2
231+
# Date: 2021-10-13 20:40:36 UTC
232+
# SHA: fbe18b5a22be8ebbb61fa7436e826ba8d7f485a9
233+
out <- as.list(read.dcf(cran_submission)[1, ])
234+
}
235+
236+
if (path_file(cran_submission) == "CRAN-RELEASE") {
237+
gh <- gh_tr(tr)
238+
# old style ----
239+
# This package was submitted to CRAN on 2021-10-13.
240+
# Once it is accepted, delete this file and tag the release (commit e10658f5).
241+
lines <- read_utf8(cran_submission)
242+
str_extract <- function(marker, pattern) {
243+
re_match(grep(marker, lines, value = TRUE), pattern)$.match
244+
}
245+
date <- str_extract("submitted.*on", "[0-9]{4}-[0-9]{2}-[0-9]{2}")
246+
sha <- str_extract("commit", "[[:xdigit:]]{7,40}")
247+
if (nchar(sha) != 40) {
248+
# the release endpoint requires the full sha
249+
sha <-
250+
gh("/repos/{owner}/{repo}/commits/{commit_sha}", commit_sha = sha)$sha
251+
}
252+
253+
HEAD <- gert::git_info(repo = git_repo())$commit
254+
if (HEAD == sha) {
255+
version <- package$Version
256+
} else {
257+
tf <- glue("{package_data()$Package}-DESCRIPTION-{substr(sha, 1, 7)}-")
258+
tf <- withr::local_tempfile(pattern = tf)
259+
gh(
260+
"/repos/{owner}/{repo}/contents/{path}",
261+
path = "DESCRIPTION", ref = sha,
262+
.destfile = tf,
263+
.accept = "application/vnd.github.v3.raw"
264+
)
265+
version <- desc::desc(file = tf)$get_version()
266+
}
267+
268+
out <- list(
269+
Version = version,
270+
Date = Sys.Date(),
271+
SHA = sha
272+
)
273+
}
274+
275+
out$Package <- package$Package
276+
out$file <- cran_submission
277+
ui_done("
278+
{ui_path(out$file)} file found, from a submission on {as.Date(out$Date)}")
279+
280+
out
281+
}
282+
283+
get_release_news <- function(SHA = gert::git_info(repo = git_repo())$commit,
284+
tr = target_repo(github_get = TRUE)) {
285+
package <- package_data()
286+
HEAD <- gert::git_info(repo = git_repo())$commit
287+
288+
if (HEAD == SHA) {
289+
news_path <- proj_path("NEWS.md")
290+
} else {
291+
news_path <- glue("{package_data()$Package}-NEWS-{substr(SHA, 1, 7)}-")
292+
news_path <- withr::local_tempfile(pattern = news_path)
293+
gh <- purrr::possibly(gh_tr(tr), otherwise = NULL)
294+
gh(
295+
"/repos/{owner}/{repo}/contents/{path}",
296+
path = "NEWS.md", ref = SHA,
297+
.destfile = news_path,
298+
.accept = "application/vnd.github.v3.raw"
299+
)
300+
}
301+
302+
if (file_exists(news_path)) {
303+
news <- news_latest(read_utf8(news_path))
304+
} else {
305+
news <- "Initial release"
306+
}
307+
308+
news
309+
}
207310

208311
cran_version <- function(package = project_name(),
209312
available = utils::available.packages()) {

R/spelling.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use_spell_check <- function(vignettes = TRUE,
1818
lang = "en-US",
1919
error = FALSE) {
20-
check_is_package("use_spell_check")
20+
check_is_package("use_spell_check()")
2121
check_installed("spelling")
2222
use_dependency("spelling", "Suggests")
2323
use_description_field("Language", lang, overwrite = TRUE)

man/use_github_release.Rd

Lines changed: 16 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-release.R

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,65 @@ test_that("returns empty string if no bullets", {
7676
)
7777
expect_equal(news_latest(lines), "")
7878
})
79+
80+
# draft release ----------------------------------------------------------------
81+
test_that("get_release_data() works if no file found", {
82+
skip_if_no_git_user()
83+
84+
local_interactive(FALSE)
85+
create_local_package()
86+
use_git()
87+
gert::git_add(".gitignore")
88+
gert::git_commit("we need at least one commit")
89+
90+
res <- get_release_data()
91+
expect_equal(res$Version, "0.0.0.9000")
92+
expect_match(res$SHA, "[[:xdigit:]]{40}")
93+
})
94+
95+
test_that("get_release_data() works for old-style CRAN-RELEASE", {
96+
skip_if_no_git_user()
97+
98+
local_interactive(FALSE)
99+
create_local_package()
100+
use_git()
101+
gert::git_add(".gitignore")
102+
gert::git_commit("we need at least one commit")
103+
HEAD <- gert::git_info(repo = git_repo())$commit
104+
105+
write_utf8(
106+
proj_path("CRAN-RELEASE"),
107+
glue("
108+
This package was submitted to CRAN on YYYY-MM-DD.
109+
Once it is accepted, delete this file and tag the release (commit {HEAD}).")
110+
)
111+
112+
res <- get_release_data(tr = list(repo_spec = "OWNER/REPO"))
113+
expect_equal(res$Version, "0.0.0.9000")
114+
expect_equal(res$SHA, HEAD)
115+
expect_equal(path_file(res$file), "CRAN-RELEASE")
116+
})
117+
118+
test_that("get_release_data() works for new-style CRAN-RELEASE", {
119+
skip_if_no_git_user()
120+
121+
local_interactive(FALSE)
122+
create_local_package()
123+
use_git()
124+
gert::git_add(".gitignore")
125+
gert::git_commit("we need at least one commit")
126+
HEAD <- gert::git_info(repo = git_repo())$commit
127+
128+
write_utf8(
129+
proj_path("CRAN-SUBMISSION"),
130+
glue("
131+
Version: 1.2.3
132+
Date: 2021-10-14 23:57:41 UTC
133+
SHA: {HEAD}")
134+
)
135+
136+
res <- get_release_data(tr = list(repo_spec = "OWNER/REPO"))
137+
expect_equal(res$Version, "1.2.3")
138+
expect_equal(res$SHA, HEAD)
139+
expect_equal(path_file(res$file), "CRAN-SUBMISSION")
140+
})

0 commit comments

Comments
 (0)