littler icon indicating copy to clipboard operation
littler copied to clipboard

Feature Request: Add strict mode flag to fail on unavailable packages in `install.r`/`install2.r`

Open nick-youngblut opened this issue 2 months ago • 1 comments

Description

Current Behavior

When using install.r to install packages, if a package doesn't exist in any configured repository, the script only issues a warning and continues with exit status 0:

$ install.r BiocManager remotes nonexistentpackage
# ... installations proceed ...
Warning in install.packages :
  package 'nonexistentpackage' is not available for this version of R
# Exit code: 0 (success)

While install2.r has an --error flag, this only catches installation failures, not unavailable packages. A package can be unavailable (not in repos) but install2.r --error will still exit with status 0 because no installation was attempted.

Expected Behavior

There should be a flag (e.g., --strict or --check-availability) that:

  1. Checks if all requested packages exist in configured repositories before attempting installation
  2. Exits with non-zero status if any package is unavailable
  3. Provides clear error messaging listing which packages were not found

Use Case

In containerized environments (especially with rocker/r2u) and CI/CD pipelines, silent failures from typos or unavailable packages cause runtime errors that are hard to debug:

# This succeeds even with typo, failing later at runtime
RUN install.r Seurat SeurtObject  # typo in "SeuratObject"

Currently, explicit verification is required after each install.r call:

RUN install.r package1 package2 package3 \
    && R -e "if (!requireNamespace('package1', quietly=TRUE)) stop('package1 failed')" \
    && R -e "if (!requireNamespace('package2', quietly=TRUE)) stop('package2 failed')" \
    && R -e "if (!requireNamespace('package3', quietly=TRUE)) stop('package3 failed')"

Proposed Solution

Add --strict or --check-availability flag to validate package availability:

install2.r --strict package1 package2 nonexistentpackage
# Error: Package(s) not available in repositories: nonexistentpackage
# Available packages checked against: CRAN, BioConductor
# Exit code: 1

Implementation Sketch

if (opt$strict) {
    available_pkgs <- available.packages(repos = opt$repos)
    
    # Filter out local files and GitHub repos
    remote_pkgs <- opt$PACKAGES[!isMatchingFile(opt$PACKAGES) & !grepl("/", opt$PACKAGES)]
    
    missing <- remote_pkgs[!remote_pkgs %in% rownames(available_pkgs)]
    
    if (length(missing) > 0) {
        cat("Error: Package(s) not available in configured repositories:\n", file=stderr())
        cat(paste("  -", missing, collapse="\n"), "\n", file=stderr())
        cat("\nConfigured repos:", opt$repos, "\n", file=stderr())
        q(status = 1)
    }
}

Difference from Existing --error Flag

  • --error: Fails if install.packages() returns an error during installation
  • --strict (proposed): Fails if package doesn't exist in repos (pre-installation check)

Both flags could be used together for comprehensive validation.

Benefits

  • Fail-fast behavior in Docker builds
  • Clear error messages with package names
  • Catches typos immediately
  • No additional dependencies required
  • Backward compatible (opt-in flag)

nick-youngblut avatar Oct 24 '25 03:10 nick-youngblut

Workflows where I use the scripts often start fromt the other end, i.e. by accessing what available.packages() and/or CRAN_package_db() return but I had this on occassion. I would possibly rename the flag to --mustexist or something closer to the meaning of 'please fail if unavailable package asked for'. But I hit this too once or twice and it would be a possibly useful addtion to install2.r. (Whereas install.r may stay smaller/simpler...)

Feel free to sketch a PR. install2.r is already complicated because of the error handler and whatnot, but the added block you suggest may work. I look forward to maybe seeing some tests (illustrations really) and examples.

eddelbuettel avatar Oct 24 '25 12:10 eddelbuettel