r-lib / pak

A fresh approach to package installation
https://pak.r-lib.org
646 stars 57 forks source link

Feature request: dry-run #472

Closed dmurdoch closed 1 year ago

dmurdoch commented 1 year ago

I asked about this for remotes (https://github.com/r-lib/remotes/issues/748), and part of the response was that you are focussing on pak. So I think the same request makes sense here: a function to check whether currently installed packages are what pak would install and report on what changes are needed.

gaborcsardi commented 1 year ago

Yes, currently you can use the underlying machinery of pak directly to achieve that. E.g.

prop <- pkgdepends::new_pkg_installation_proposal(
  "ggplot2", 
  config = list(dependencies = TRUE)
)
prop$set_solve_policy("upgrade")
prop$solve()
prop$show_solution()

Will print something like

+ cachem        1.0.6    → 1.0.7
+ classInt      0.4-7    → 0.4-9
+ e1071         1.7-11   → 1.7-13
+ fastmap       1.1.0    → 1.1.1
+ foreign       0.8-83   → 0.8-84    ⬇ (339.48 kB)
+ Formula       1.2-4    → 1.2-5
+ ggplot2       3.3.6    → 3.4.1
+ ggplot2movies            0.0.1     ⬇ (1.25 MB)
+ gtable        0.3.1    → 0.3.2
+ hexbin                   1.28.2    ⬇ (1.47 MB)
+ Hmisc         4.7-1    → 5.0-1
+ htmlwidgets   1.6.1    → 1.6.2
+ httr          1.4.4    → 1.4.5
+ mapproj       1.2.8    → 1.2.11    ⬇ (84.49 kB)
+ maps          3.4.0    → 3.4.1     ⬇ (3.11 MB)
+ maptools      1.1-4    → 1.1-6     ⬇ (2.14 MB)
+ MASS          7.3-58.2 → 7.3-58.3  ⬇ (1.16 MB)
+ mgcv          1.8-41   → 1.8-42    ⬇ (3.57 MB)
+ multcomp      1.4-20   → 1.4-23
+ openssl       2.0.5    → 2.0.6
+ ragg          1.2.4    → 1.2.5
+ rgeos                    0.6-2     ⬇ (1.61 MB)
+ s2            1.1.0    → 1.1.2
+ sass          0.4.4    → 0.4.5
+ sf            1.0-8    → 1.0-12
+ sp            1.5-0    → 1.6-0
+ survival      3.5-0    → 3.5-5     ⬇ (6.74 MB)
+ svglite       2.1.0    → 2.1.1     ⬇ (905.24 kB)
+ testthat      3.1.6    → 3.1.7
+ tibble        3.1.8    → 3.2.1
+ tinytex       0.43     → 0.44
+ units         0.8-0    → 0.8-1
+ vctrs         0.5.2    → 0.6.0    👷🏻‍♂️🔧
+ vdiffr        1.0.4    → 1.0.5
+ wk            0.6.0    → 0.7.2
dmurdoch commented 1 year ago

Thanks. I think that does contain the information needed to put together the install, but I'm not sure how to use it.

For more context: the book I was talking about is a bookdown project online here: https://github.com/RConsortium/rtrs-wg/ . One of us has put together a "fake package" to record the dependencies needed by the book. It's actually just a DESCRIPTION file, here: https://github.com/RConsortium/rtrs-wg/tree/main/tools/fakepkg . This is used by the CI to build the book, and I'm currently looking for a way to do these things:

  1. Check that I have the requested versions of all the packages before I try a build. I think your code lets me do that now. Thanks!
  2. Put instructions into the book so that a reader could install enough packages to build the book or try out the code in it. I'm not sure how to do that yet. These instructions could be a one-liner involving pak and the fake package, but I think it would be better to do something like what is there now: a sequence of calls to remotes or pak to install the packages by name.

Our hope is that all the packages will be on CRAN when the book is done, but they aren't all there yet, and it's pretty likely that there will be times in the future when bug fixes will mean people need to go to Github or elsewhere for the packages.

gaborcsardi commented 1 year ago

Sure, readers can install packages the same way, and you can keep the DESCRIPTION in the repo updated as needed:

❯ pak::pkg_install("RConsortium/rtrs-wg/tools/fakepkg")
✔ Loading metadata database ... done

→ Will install 17 packages.
→ Will update 2 packages.
→ Will download 6 CRAN packages (15.80 MB), cached: 13 (6.21 MB).
+ doconv                     0.3.2        ⬇ (309.36 kB)
+ formatters                 0.4.0.9000  👷‍♂️🔧 (GitHub: b4eafe7)
+ ggplot2           3.3.6  → 3.4.1
+ huxtable                   5.5.2        ⬇ (1.56 MB)
+ kableExtra                 1.3.4        ⬇ (1.85 MB)
+ locatexec                  0.1.1        ⬇ (70.31 kB)
+ nestcolor                  0.1.1       👷🏿‍♂️🔧 (GitHub: bcdf0c9)
+ pdftools                   3.3.3        ⬇ (7.21 MB)
+ qpdf                       1.3.2        ⬇ (4.80 MB)
+ random.cdisc.data          0.3.13.9052 👷🏻‍♂️🔧 (GitHub: 9ff4958)
+ rbibutils                  2.2.13
+ Rdpack                     2.4
+ rtables                    0.6.0.1     👷🏿‍♀️🔧 (GitHub: 91e30fc)
+ scda                       0.1.6.9000  👷🏿‍♀️🔧 (GitHub: 5b40d2c)
+ tablebook                  0.1.0       👷🔧 (GitHub: c842ee3)
+ tables            0.9.11 → 0.9.13      👷🏾‍♂️🔧 (GitHub: dc494a6)
+ tern                       0.7.10      👷🏽‍♀️🔧 (GitHub: e887629)
+ tfrmt                      0.0.3       👷🏼🔧 (GitHub: 9805e8d)
+ tidytlg                    0.0.1       👷🏻🔧 (GitHub: 1a0b1a3)
? Do you want to continue (Y/n)
dmurdoch commented 1 year ago

Thanks. That almost does what I want, but it actually installs the fake package, and that's not desirable. All I really want is to have the dependencies of the fake package installed. The fake package isn't on CRAN, so there's nothing to stop a user from having their own package with the same name. If that happens, then this install could wipe out their package, or perhaps be blocked by it.

What a "dry-run" option would do is to show the commands that correspond to the installs shown above. Then I could delete the one command corresponding to the fake package ('tablebook' in your run), and I'd get a script to install everything else.

I'll try to put this together.

gaborcsardi commented 1 year ago

What a "dry-run" option would do is to show the commands that correspond to the installs shown above.

A dry-run does not necessarily gives you commands that you can run, and indeed in the case of pak (and most R functions) that is difficult to do. A pak dry-run could print an installation plan, or pretend to do an installation without actually doing it.

Personally I don't think installing the fake package itself is terrible. If its name is unique enough, name clashes are unlikely.

But yeah, if you only want to install the dependencies, but not the package itself, that is basically https://github.com/r-lib/pak/issues/462

dmurdoch commented 1 year ago

Maybe "dry-run" was the wrong name. Here's code based on your first suggestion that does pretty much what I want:

# This produces a script to install any missing packages or update
# older ones

prop <- pkgdepends::new_pkg_installation_proposal(
  "RConsortium/rtrs-wg/tools/fakepkg",
  config = list(dependencies = TRUE)
)
prop$set_solve_policy("upgrade")
prop$solve()
deps <- prop$get_solution()$data
deps <- subset(deps, !directpkg & type != "installed")
cmd <- ifelse(deps$type == "standard", "install.packages", "remotes::install_github")
reason <- ifelse(deps$lib_status == "new", "# new install",
                 paste("# update from", deps$old_version, "to", deps$version))

script <- sprintf('%s("%s") \t%s', cmd, deps$ref, reason)
cat(script, sep = "\n")

It uses install.packages() and remotes::install_github() instead of pak::pkg_install() because I'm more familiar with the older functions, and I think my audience would be as well.

I'll close this now, as it really is close to a dup of #462.