Skip to content
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
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
^\.github$
^CRAN-RELEASE$
^data-raw$
^minver$
9 changes: 6 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,13 @@ jobs:
- r: release
env:
- BUILD_PKGDOWN=true
- r: 3.5
- r: 3.4
- r: 3.3
- r: 3.2
# old fansi needs R 3.3
- r: 3.3
env:
- MIN_VERSIONS=TRUE
- r: 3.4
- r: 3.5
fast_finish: true

#env
Expand Down
19 changes: 9 additions & 10 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,17 @@ LazyData: true
URL: https://github.com/r-lib/pillar
BugReports: https://github.com/r-lib/pillar/issues
Imports:
cli (>= 1.1.0),
cli,
crayon (>= 1.3.4),
fansi (>= 0.4.0),
methods,
rlang (>= 0.3.4),
utf8 (>= 1.1.4),
vctrs (>= 0.1.0)
fansi,
rlang (>= 0.3.0),
utf8 (>= 1.1.0),
vctrs
Suggests:
knitr (>= 1.22),
lubridate (>= 1.7.4),
testthat (>= 2.1.1),
withr (>= 2.1.2)
knitr,
lubridate,
testthat,
withr
Roxygen: list(markdown = TRUE)
RoxygenNote: 6.1.1
Collate:
Expand Down
2 changes: 2 additions & 0 deletions minver/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.rds
lib/
20 changes: 20 additions & 0 deletions minver/01-gen.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
library(tidyverse)

# pkgdep <- pkgdep::as_pkgdep(available.packages())
# gen <- pkgdep::get_all_deps("tibble", pkgdep)

my_package <- basename(getwd())

db <- cranly::clean_CRAN_db()
network <- cranly::build_network(db)
tree <- cranly::compute_dependence_tree(network, my_package)

deps <- desc::desc_get_deps()

tree %>%
arrange(generation) %>%
distinct(package, .keep_all = TRUE) %>%
arrange(-generation) %>%
inner_join(deps, by = "package") %>%
filter(version != "*") %>%
saveRDS("minver/gen.rds")
18 changes: 18 additions & 0 deletions minver/02-baseline.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
library(tidyverse)

gen <-
readRDS("minver/gen.rds") %>%
as_tibble()

gen_crandb <-
gen %>%
select(-type, -version) %>%
mutate(crandb = map(package, ~ crandb::package(., version = "all")))

baseline <-
gen_crandb %>%
mutate(version = map(crandb, ~ names(pluck(., "versions")))) %>%
mutate(first_version = map_chr(version, 1)) %>%
select(-crandb)

saveRDS(baseline, "minver/baseline.rds")
9 changes: 9 additions & 0 deletions minver/03-candidate.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
library(tidyverse)

baseline <- readRDS("minver/baseline.rds")

baseline %>%
select(package = 1) %>%
pwalk(desc::desc_set_dep)

saveRDS(baseline, "minver/candidate-0001.rds")
9 changes: 9 additions & 0 deletions minver/04-lib.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
library(tidyverse)

lib <- "minver/lib"
unlink(lib, recursive = TRUE, force = TRUE)
dir.create(lib, showWarnings = FALSE)

invisible(remotes::install_deps)
withr::with_libpaths(lib, remotes::install_deps(dependencies = TRUE))
withr::with_libpaths(lib, install.packages("memoise"))
90 changes: 90 additions & 0 deletions minver/05-next-candidate.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
library(tidyverse)

bisect_first_min_version <- function(version) {
na_versions <- which(map_int(version, length) > 1)
if (length(na_versions) == 0) stop("All versions determined, move to next script.", call. = FALSE)
first_na_version <- na_versions[[1]]

version_candidates <- version[[first_na_version]]

version_idx <- quantile(seq_along(version_candidates), 0.5, type = 1)

min_version <- rep(NA_character_, length(version))

pinned <- seq_len(first_na_version - 1)
min_version[pinned] <- unlist(version[pinned])
min_version[[first_na_version]] <- version_candidates[[version_idx]]
min_version
}

update_bisect_result <- function(version, next_min_version, failure) {
first_na_version <- which(map_int(version, length) > 1)[[1]]

version_candidates <- package_version(version[[first_na_version]])
pivot <- package_version(next_min_version[[first_na_version]])

if (failure) {
version_candidates <- version_candidates[ version_candidates > pivot ]
} else {
version_candidates <- version_candidates[ version_candidates <= pivot ]
}

version[[first_na_version]] <- as.character(version_candidates)
version
}

candidate_files <- dir("minver", pattern = "candidate-....[.]rds")

candidate_file <- tail(candidate_files, 1)[[1]]

candidate <-
readRDS(file.path("minver", candidate_file)) %>%
mutate(next_min_version = bisect_first_min_version(version)) %>%
mutate(dep = if_else(is.na(next_min_version), "*", paste0(">= ", next_min_version)))

print(candidate)

pinned <-
candidate %>%
filter(!is.na(next_min_version)) %>%
select(package = 1, version = next_min_version) %>%
arrange(-row_number())

print(pinned)

libpath <- tempfile("lib")
dir.create(libpath)

existing <- setdiff(dir("minver/lib"), pinned$package)

stopifnot(all(file.symlink(normalizePath(file.path("minver/lib", existing)), libpath)))

invisible(remotes::install_version)
invisible(rcmdcheck::rcmdcheck)

Sys.setenv(NOT_CRAN = "true")

result <- safely(
~ withr::with_libpaths(
libpath,
{
pwalk(pinned, remotes::install_version, dependencies = character(), upgrade = "never")
rcmdcheck::rcmdcheck(error_on = "note")
}
)
)()

failure <- is.null(result$result)
failure

n <- length(candidate_files) + 1

new_candidate <-
candidate %>%
mutate(version = update_bisect_result(version, next_min_version, failure)) %>%
select(-next_min_version, -dep)

new_candidate %>%
saveRDS(sprintf("minver/candidate-%.4d.rds", n))

new_candidate
13 changes: 13 additions & 0 deletions minver/06-finalize.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
library(tidyverse)

candidate_files <- dir("minver", pattern = "candidate-....[.]rds")

candidate_file <- tail(candidate_files, 1)[[1]]

final <-
readRDS(file.path("minver", candidate_file)) %>%
unnest() %>%
mutate(cond = if_else(version == first_version, "*", paste0(">= ", version))) %>%
select(package, version = cond)

pwalk(final, desc::desc_set_dep)
26 changes: 26 additions & 0 deletions minver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# minver

A workflow for finding the minimum required versions of all packages.

## How does it work?

1. It creates a baseline library via `remotes::install_deps(dependencies = TRUE)` .

2. Imported packages are sorted topologically, packages that depend on other packages come first. Versions are erased from the `DESCRIPTION` .

3. For each imported package, a bisection along the available versions (obtained via `crandb::package(version = "all")`) is made. The baseline library is copied (using symlinks), and the packages for which minimum versions are to be tested are installed using `remotes::install_version()`. The test criterion is a successful `R CMD check` without NOTEs.

4. The minimum version for each package is written to the `DESCRIPTION` .

## Run it

Copy the `minver` directory into the root of your package, call `usethis::use_build_ignore("minver")`.

Run each script `??-*.R` in sequence, the `05-*.R` script must be run repeatedly until the error `"All versions determined, move to next script."` is raised.

The following files are created:

- `gen.rds`: Imported packages sorted by "generation" (topologically)
- `baseline.rds`: All CRAN versions of imported packages
- `lib/`: The baseline library
- `candidate-####.rds`: Intermediate results of the bisection
28 changes: 28 additions & 0 deletions tic.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,34 @@ do_package_checks()
if (ci_has_env("DEV_VERSIONS")) {
get_stage("install") %>%
add_step(step_install_github(c("r-lib/rlang", "r-lib/cli", "r-lib/crayon", "brodieG/fansi", "patperry/r-utf8", "r-lib/vctrs")))
} else if (ci_has_env("MIN_VERSIONS")) {
# Make sure all other packages are installed when we downgrade
get_stage("before_script") %>%
add_code_step(
{
deps <- desc::desc_get_deps()
deps <- deps[deps$type == "Imports", ]

# revdepcheck needs recent cli, installing in the same library
deps <- deps[deps$package != "cli", ]

is_first <- (deps$version == "*")
all_first_packages <- lapply(deps$package[is_first], crandb::package, version = "all")
all_first_version_details <- lapply(all_first_packages, `[[`, "versions")
all_first_versions <- lapply(all_first_version_details, names)
first_versions <- vapply(all_first_versions, `[[`, 1, FUN.VALUE = character(1))

version <- rlang::rep_along(NA_character_, is_first)
version[is_first] <- first_versions
version[!is_first] <- gsub(">= ", "", deps$version[!is_first])

Map(remotes::install_version, deps$package, version)
},
{
remotes::install_cran("desc")
remotes::install_github("r-hub/crandb")
}
)
}

if (ci_has_env("BUILD_PKGDOWN") && ci_get_branch() == "master") {
Expand Down