@@ -109,10 +109,12 @@ dev_sitrep <- function(pkg = ".", debug = FALSE) {
109109 has_build_tools = has_build_tools ,
110110 rtools_path = if (has_build_tools ) pkgbuild :: rtools_path(),
111111 devtools_version = utils :: packageVersion(" devtools" ),
112- devtools_deps = compare_deps(pak :: pkg_deps(" devtools" , dependencies = NA )),
113- pkg_deps = if (! is.null(pkg )) {
114- compare_deps(pak :: local_dev_deps(pkg $ path , dependencies = TRUE ))
115- },
112+ devtools_cran_version = pak :: pkg_deps(
113+ " devtools" ,
114+ dependencies = FALSE
115+ )$ version ,
116+ devtools_deps = pkg_dep_status(" devtools" , dependencies = NA ),
117+ pkg_deps = if (! is.null(pkg )) pkg_dep_status(pkg , dependencies = TRUE ),
116118 rstudio_version = if (is_rstudio_running()) rstudioapi :: getVersion(),
117119 rstudio_msg = if (! is_positron()) check_for_rstudio_updates()
118120 )
@@ -127,6 +129,7 @@ new_dev_sitrep <- function(
127129 has_build_tools = TRUE ,
128130 rtools_path = NULL ,
129131 devtools_version = utils :: packageVersion(" devtools" ),
132+ devtools_cran_version = devtools_version ,
130133 devtools_deps = NULL ,
131134 pkg_deps = NULL ,
132135 rstudio_version = NULL ,
@@ -142,6 +145,7 @@ new_dev_sitrep <- function(
142145 has_build_tools = has_build_tools ,
143146 rtools_path = rtools_path ,
144147 devtools_version = devtools_version ,
148+ devtools_cran_version = devtools_cran_version ,
145149 devtools_deps = devtools_deps ,
146150 pkg_deps = pkg_deps ,
147151 rstudio_version = rstudio_version ,
@@ -191,26 +195,28 @@ print.dev_sitrep <- function(x, ...) {
191195 cli :: cli_rule(" devtools" )
192196 kv_line(" version" , x $ devtools_version )
193197
198+ devtools_version <- package_version(x $ devtools_version )
199+ devtools_cran_version <- package_version(x $ devtools_cran_version )
200+ if (devtools_version < devtools_cran_version ) {
201+ all_ok <- FALSE
202+ cli :: cli_bullets(c(
203+ " !" = " {.field devtools} is out of date ({.val {devtools_version}} vs {.val {devtools_cran_version}})." ,
204+ " " = " Update it with {.run pak::pak(\" devtools\" )}."
205+ ))
206+ } else if (devtools_version > devtools_cran_version ) {
207+ cli :: cli_bullets(c(
208+ " i" = " {.field devtools} is ahead of CRAN ({.val {devtools_version}} vs {.val {devtools_cran_version}})."
209+ ))
210+ }
211+
194212 devtools_not_ok <- any(x $ devtools_deps $ status != " ok" )
195213 if (devtools_not_ok ) {
196214 all_ok <- FALSE
197-
198- behind <- x $ devtools_deps [x $ devtools_deps $ status == " behind" , ]
199- if (nrow(behind ) > 0 ) {
200- cli :: cli_bullets(c(
201- " !" = " {.field devtools} or its dependencies are out of date." ,
202- " " = " Update them with {.code pak::pak(\" devtools\" ).}"
203- ))
204- cli :: cli_verbatim(paste(" " , dep_labels(behind )))
205- }
206-
207- ahead <- x $ devtools_deps [x $ devtools_deps $ status == " ahead" , ]
208- if (nrow(ahead ) > 0 ) {
209- cli :: cli_bullets(c(
210- " i" = " {.field devtools} or its dependencies are installed from a dev version, FYI:"
211- ))
212- cli :: cli_verbatim(paste(" " , dep_labels(ahead )))
213- }
215+ report_deps_ahead_behind(
216+ x $ devtools_deps ,
217+ pkg_name = " devtools" ,
218+ update_code = ' pak::pak("devtools")'
219+ )
214220 }
215221
216222 cli :: cli_rule(" dev package" )
@@ -220,23 +226,11 @@ print.dev_sitrep <- function(x, ...) {
220226 dev_pkg_not_ok <- any(x $ pkg_deps $ status != " ok" )
221227 if (dev_pkg_not_ok ) {
222228 all_ok <- FALSE
223-
224- behind <- x $ pkg_deps [x $ pkg_deps $ status == " behind" , ]
225- if (nrow(behind ) > 0 ) {
226- cli :: cli_bullets(c(
227- " !" = " {.field {x$pkg$package}} dependencies are out of date." ,
228- " " = " Update them with {.code pak::local_install_dev_deps()}."
229- ))
230- cli :: cli_verbatim(paste(" " , dep_labels(behind )))
231- }
232-
233- ahead <- x $ pkg_deps [x $ pkg_deps $ status == " ahead" , ]
234- if (nrow(ahead ) > 0 ) {
235- cli :: cli_bullets(c(
236- " i" = " {.field {x$pkg$package}} dependencies are installed from a dev version, FYI:"
237- ))
238- cli :: cli_verbatim(paste(" " , dep_labels(ahead )))
239- }
229+ report_deps_ahead_behind(
230+ x $ pkg_deps ,
231+ pkg_name = x $ pkg $ package ,
232+ update_code = " pak::local_install_dev_deps()"
233+ )
240234 }
241235
242236 if (all_ok ) {
@@ -248,7 +242,29 @@ print.dev_sitrep <- function(x, ...) {
248242
249243# Helpers -----------------------------------------------------------------
250244
251- compare_deps <- function (deps ) {
245+ # ' Get dependency status for a package, excluding the package itself
246+ # '
247+ # ' @param pkg A package name or a package object, as returned by `as.package()`.
248+ # ' @param dependencies Which dependency types to include. Passed along to
249+ # ' `pak::pkg_deps()` or `pak::local_dev_deps()`.
250+ # ' @returns A data frame with one row per dependency and columns:
251+ # ' * package (name)
252+ # ' * latest (version)
253+ # ' * installed (version)
254+ # ' * status (one of: missing, behind, ok, ahead)
255+ # ' @noRd
256+ pkg_dep_status <- function (pkg , dependencies = NA ) {
257+ if (is_string(pkg )) {
258+ deps <- pak :: pkg_deps(pkg , dependencies = dependencies )
259+ } else if (inherits(pkg , " package" )) {
260+ deps <- pak :: local_dev_deps(pkg $ path , dependencies = dependencies )
261+ deps <- deps [deps $ package != pkg $ package , ]
262+ } else {
263+ cli :: cli_abort(
264+ " {.arg pkg} must be a string package name or a package object."
265+ )
266+ }
267+
252268 installed <- map_chr(deps $ package , function (p ) {
253269 tryCatch(
254270 as.character(utils :: packageVersion(p )),
@@ -257,7 +273,7 @@ compare_deps <- function(deps) {
257273 })
258274 status <- map2_chr(installed , deps $ version , function (inst , latest ) {
259275 if (is.na(inst )) {
260- return (" behind " )
276+ return (" missing " )
261277 }
262278 switch (
263279 as.character(utils :: compareVersion(inst , latest )),
@@ -274,6 +290,46 @@ compare_deps <- function(deps) {
274290 )
275291}
276292
293+ # ' Emit cli messages about ahead/behind dependencies
294+ # '
295+ # ' @param dep_status A data frame as returned by `compare_deps()`, with
296+ # ' columns `package`, `latest`, `installed`, `status`.
297+ # ' @param pkg_name Package name to mention in the message.
298+ # ' @param update_code Code suggestion for updating behind deps.
299+ # ' @return Called for its side effects.
300+ # ' @noRd
301+ report_deps_ahead_behind <- function (dep_status , pkg_name , update_code ) {
302+ missing <- dep_status [dep_status $ status == " missing" , ]
303+ if (nrow(missing ) > 0 ) {
304+ n <- nrow(missing )
305+ cli :: cli_bullets(c(
306+ " !" = " {n} {.field {pkg_name}} {cli::qty(n)}{?dependency is/dependencies are} not installed." ,
307+ " " = " Install {cli::qty(n)}{?it/them} with {.run {update_code}}."
308+ ))
309+ cli :: cli_verbatim(paste(" " , dep_labels(missing )))
310+ }
311+
312+ behind <- dep_status [dep_status $ status == " behind" , ]
313+ if (nrow(behind ) > 0 ) {
314+ n <- nrow(behind )
315+ cli :: cli_bullets(c(
316+ " !" = " {n} {.field {pkg_name}} {cli::qty(n)}{?dependency is/dependencies are} out of date." ,
317+ " " = " Update {cli::qty(n)}{?it/them} with {.run {update_code}}."
318+ ))
319+ cli :: cli_verbatim(paste(" " , dep_labels(behind )))
320+ }
321+
322+ ahead <- dep_status [dep_status $ status == " ahead" , ]
323+ if (nrow(ahead ) > 0 ) {
324+ n <- nrow(ahead )
325+ cli :: cli_bullets(c(
326+ " i" = " {n} {.field {pkg_name}} {cli::qty(n)}{?dependency is/dependencies are} ahead of CRAN."
327+ ))
328+ cli :: cli_verbatim(paste(" " , dep_labels(ahead )))
329+ }
330+ }
331+
332+
277333dep_labels <- function (deps ) {
278334 # helps with readability
279335 deps $ package <- format(deps $ package , justify = " left" )
@@ -283,7 +339,7 @@ dep_labels <- function(deps) {
283339
284340format_dep_line <- function (package , installed , latest , status ) {
285341 if (is.na(installed )) {
286- paste0(package , " (not installed )" )
342+ paste0(package , " (" , cli :: col_red( " missing " ), " )" )
287343 } else {
288344 status_styled <- if (status == " behind" ) {
289345 cli :: col_red(status )
0 commit comments