-
Notifications
You must be signed in to change notification settings - Fork 164
Expand file tree
/
Copy pathinit.R
More file actions
395 lines (311 loc) · 12.1 KB
/
init.R
File metadata and controls
395 lines (311 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
the$init_running <- FALSE
#' Use renv in a project
#'
#' @description
#' Call `renv::init()` to start using renv in the current project. This will:
#'
#' 1. Set up project infrastructure (as described in [scaffold()]) including
#' the project library and the `.Rprofile` that ensures renv will be
#' used in all future sessions,
#'
#' 1. Discover the packages that are currently being used in your project
#' (via [dependencies()]), and install them into the project library
#' (as described in [hydrate()]),
#'
#' 1. Create a lockfile that records the state of the project library so it
#' can be restored by others (as described in [snapshot()]),
#'
#' 1. Restart R (if running inside RStudio).
#'
#' If you call `renv::init()` with a project that is already using renv, it will
#' attempt to do the right thing: it will restore the project library if it's
#' missing, or otherwise ask you what to do.
#'
#' # Repositories
#'
#' If the default \R repositories have not already been set, renv will use
#' the [Posit Public Package Manager](https://packagemanager.posit.co/) CRAN
#' mirror for package installation. The primary benefit to using this mirror is
#' that it can provide pre-built binaries for \R packages on a variety of
#' commonly-used Linux distributions. This behavior can be configured or
#' disabled if desired -- see the options in [renv::config()] for more details.
#'
#' @inherit renv-params
#'
#' @param project The project directory. When `NULL` (the default), the current
#' working directory will be used. The \R working directory will be
#' changed to match the requested project directory.
#'
#' @param settings A list of [settings] to be used with the newly-initialized
#' project.
#'
#' @param bare Boolean; initialize the project with an empty project library,
#' without attempting to discover and install \R package dependencies?
#'
#' @param force Boolean; force initialization? By default, renv will refuse
#' to initialize the home directory as a project, to defend against accidental
#' misusages of `init()`.
#'
#' @param repos The \R repositories to be used in this project.
#' See **Repositories** for more details.
#'
#' @param bioconductor The version of Bioconductor to be used with this project.
#' Setting this may be appropriate if renv is unable to determine that your
#' project depends on a package normally available from Bioconductor. Set this
#' to `TRUE` to use the default version of Bioconductor recommended by the
#' `BiocManager` package. When `NULL` (the default), the value is inferred
#' from the `bioconductor.init` configuration option -- see [config] for more
#' details.
#'
#' @param load Boolean; should the project be loaded after it is initialized?
#'
#' @param restart Boolean; attempt to restart the \R session after initializing
#' the project? A session restart will be attempted if the `"restart"` \R
#' option is set by the frontend hosting \R.
#'
#' @export
#'
#' @example examples/examples-init.R
init <- function(project = NULL,
...,
profile = NULL,
settings = NULL,
bare = FALSE,
force = FALSE,
repos = NULL,
bioconductor = NULL,
load = TRUE,
restart = interactive())
{
renv_consent_check()
renv_scope_error_handler()
renv_dots_check(...)
renv_scope_binding(the, "init_running", TRUE)
project <- renv_path_normalize(project %||% getwd())
renv_project_lock(project = project)
# initialize profile
if (!is.null(profile))
renv_profile_set(profile)
# normalize repos
repos <- renv_repos_normalize(repos %||% renv_init_repos())
renv_scope_options(repos = repos)
# form path to lockfile, library
library <- renv_paths_library(project = project)
lockfile <- renv_lockfile_path(project)
# ask user what type of project this is
type <- settings$snapshot.type %||% renv_init_type(project)
settings$snapshot.type <- type
# initialize bioconductor pieces
biocver <- renv_init_bioconductor(bioconductor, project)
if (!is.null(biocver)) {
# validate that this version of bioconductor is appropriate
renv_bioconductor_validate(version = biocver)
# make sure a Bioconductor package manager is installed
renv_bioconductor_init(library = library)
# retrieve bioconductor repositories appropriate for this project
repos <- renv_bioconductor_repos(project = project, version = biocver)
renv_scope_options(repos = repos)
# notify user
writef("- Using Bioconductor version '%s'.", biocver)
settings[["bioconductor.version"]] <- biocver
}
# prepare and move into project directory
renv_init_validate_project(project, force)
renv_init_settings(project, settings)
# for bare inits, just activate the project
if (bare) {
renv_imbue_impl(project)
return(renv_init_fini(project, profile, load, restart))
}
# compute and cache dependencies to (a) reveal problems early and (b) compute once
renv_snapshot_dependencies(project, type = type, dev = TRUE)
# determine appropriate action
action <- renv_init_action(project, library, lockfile, bioconductor)
cancel_if(empty(action) || identical(action, "cancel"))
# compute library paths for this project
libpaths <- renv_init_libpaths(project = project)
# perform the action
if (action == "init") {
renv_scope_options(renv.config.dependency.errors = "ignored")
renv_imbue_impl(project, library = library)
hydrate(library = library, repos = repos, prompt = FALSE, report = FALSE, project = project)
snapshot(library = libpaths, repos = repos, prompt = FALSE, project = project, force = TRUE)
} else if (action == "restore") {
ensure_directory(library)
renv_sandbox_activate(project = project)
restore(project = project, library = libpaths, repos = repos, prompt = FALSE)
}
# activate the newly-hydrated project
renv_init_fini(project, profile, load, restart)
}
renv_init_fini <- function(project, profile, load, restart) {
renv_activate_impl(
project = project,
profile = profile,
version = renv_metadata_version(),
load = load,
restart = restart
)
invisible(project)
}
renv_init_action <- function(project, library, lockfile, bioconductor) {
# if the user has asked for bioconductor, treat this as a re-initialization
if (!is.null(bioconductor))
return("init")
# figure out appropriate action
case(
# if both the library and lockfile exist, ask user for intended action
file.exists(lockfile)
~ renv_init_action_conflict_lockfile(project, library, lockfile),
# if a private library exists but no lockfile, ask whether we should use it
file.exists(library)
~ renv_init_action_conflict_library(project, library, lockfile),
# otherwise, we just want to initialize the project
~ "init"
)
}
renv_init_action_conflict_lockfile <- function(project, library, lockfile) {
if (!interactive())
return("nothing")
title <- "This project already has a lockfile. What would you like to do?"
choices <- c(
restore = "Restore the project from the lockfile.",
init = "Discard the lockfile and re-initialize the project.",
nothing = "Activate the project without snapshotting or installing any packages.",
cancel = "Abort project initialization."
)
selection <- tryCatch(
utils::select.list(choices, title = title, graphics = FALSE),
interrupt = identity
)
if (inherits(selection, "interrupt"))
return(NULL)
names(selection)
}
renv_init_action_conflict_library <- function(project, library, lockfile) {
if (!interactive())
return("nothing")
# if the project library exists, but it's empty,
# treat this as a request to initialize the project
# https://github.com/rstudio/renv/issues/1668
db <- installed_packages(lib.loc = library, priority = NA_character_)
if (nrow(db) == 0L)
return("init")
# if only renv is installed, but it matches the version of renv being used
renvonly <-
NROW(db) == 1L &&
db[["Package"]] == "renv" &&
db[["Version"]] == renv_package_version("renv")
if (renvonly)
return("init")
title <- "This project already has a private library. What would you like to do?"
choices <- c(
nothing = "Activate the project and use the existing library.",
init = "Re-initialize the project with a new library.",
cancel = "Abort project initialization."
)
selection <- tryCatch(
utils::select.list(choices, title = title, graphics = FALSE),
interrupt = identity
)
if (inherits(selection, "interrupt"))
return(NULL)
names(selection)
}
renv_init_validate_project <- function(project, force) {
# allow all project directories when force = TRUE
if (force)
return(TRUE)
# disallow attempts to initialize renv in the home directory
home <- path.expand("~/")
msg <- if (renv_file_same(project, home))
"refusing to initialize project in home directory"
else if (renv_path_within(home, project))
sprintf("refusing to initialize project in directory '%s'", project)
if (!is.null(msg)) {
msg <- paste(msg, "-- use renv::init(force = TRUE) to override")
stopf(msg)
}
}
renv_init_settings <- function(project, settings) {
defaults <- renv_settings_get(project)
merged <- renv_settings_merge(defaults, settings)
renv_settings_persist(project, merged)
invisible(merged)
}
renv_init_bioconductor <- function(bioconductor, project) {
# if we're re-initializing a project that appears to depend
# on Bioconductor, then use the latest Bioconductor release
if (is.null(bioconductor)) {
lockpath <- renv_paths_lockfile(project = project)
if (file.exists(lockpath)) {
lockfile <- renv_lockfile_read(lockpath)
bioconductor <- !is.null(lockfile[["Bioconductor"]])
}
}
# allow override via option when null
bioconductor <- bioconductor %||% config$bioconductor.init()
# resolve bioconductor argument
version <- case(
is.character(bioconductor) ~ bioconductor,
identical(bioconductor, TRUE) ~ renv_bioconductor_version(project, refresh = TRUE),
identical(bioconductor, FALSE) ~ NULL
)
# resolve symbolic version names like "devel" or "release"
# to their numeric equivalents for the lockfile
if (length(version) && version %in% c("devel", "release", "future"))
version <- format(renv_remotes_resolve_bioc_version(version))
version
}
renv_init_repos <- function(repos = getOption("repos")) {
# if PPM is disabled, just use default repositories
repos <- convert(repos, "list")
if (!renv_ppm_enabled())
return(repos)
# check whether the user has opted into using PPM by default
enabled <- config$ppm.default()
if (!enabled)
return(repos)
# check for default repositories
#
# note that if the user is using RStudio, we only want to override
# the repositories if they haven't explicitly set their own repo URL
#
# https://github.com/rstudio/renv/issues/1782
rstudio <- structure(
list(CRAN = "https://cran.rstudio.com/"),
RStudio = TRUE
)
isdefault <-
identical(repos, list(CRAN = "@CRAN@")) ||
identical(repos, rstudio)
if (isdefault) {
repos[["CRAN"]] <- config$ppm.url()
}
repos
}
renv_init_type <- function(project) {
# check if the user has already requested a snapshot type
type <- renv_settings_get(project, name = "snapshot.type", default = NULL)
if (!is.null(type))
return(type)
# if we don't have a DESCRIPTION file, use the default
if (!file.exists(file.path(project, "DESCRIPTION")))
return(settings$snapshot.type(project = project))
# otherwise, ask the user if they want to explicitly enumerate their
# R package dependencies in the DESCRIPTION file
choice <- menu(
title = c(
"This project contains a DESCRIPTION file.",
"Which files should renv use for dependency discovery in this project?"
),
choices = c(
explicit = "Use only the DESCRIPTION file. (explicit mode)",
implicit = "Use all files in this project. (implicit mode)"
)
)
if (identical(choice, "cancel"))
cancel()
writef("- Using '%s' snapshot type. Please see `?renv::snapshot` for more details.\n", choice)
choice
}