#! /usr/local/bin/Rscript mailx <- function(subject = "", address, body = character(), cc, bcc, from, replyto, verbose = FALSE) { #if(missing(address)) stop("must specify 'address'") if(any(missing(address) | is.na(address))) stop("must specify 'address'") if(!nzchar(subject)) stop("'subject' is missing") args <- c("-s", shQuote(subject)) env <- character() ## For cc and bcc, use command line options -c and -b: ## -c address ## Send carbon copies to list of users. ## -b address ## Send blind carbon copies to list. List should be a ## comma-separated list of names. if(!missing(cc)) args <- c(args, "-c", shQuote(paste(cc, collapse = ","))) if(!missing(bcc)) args <- c(args, "-b", shQuote(paste(bcc, collapse = ","))) ## Argh. ## We really want to be able to specify the 'From' and 'Reply-to' ## fields in the messages. ## POSIX mailx ## ## has nothing for those. ## BSD mailx ## ## has command line option '-r' for the former, and env var REPLYTO ## for the latter. ## S-nail has command line option ## for the former, and env var replyto (as well as additional ## command line mechanisms) for the latter. ## Hence, for now use '-r' for 'From', and both env vars for ## 'Reply-To'. if(!missing(from)) args <- c(args, "-r", shQuote(from)) ## env <- c(env, sprintf("from=%s", shQuote(from))) if(!missing(replyto)) { env <- c(env, sprintf("replyto=%s", shQuote(replyto))) env <- c(env, sprintf("REPLYTO=%s", shQuote(replyto))) } address <- paste(shQuote(address), collapse = " ") filename <- sprintf("R_post_%s", format(Sys.time(), "%FT%T")) cat(body, file = filename, sep = "\n") ## ## To avoid reading the user's configuration files for general ## purpose use, the man page suggests using ## MAILRC=/dev/null mailx -n ## and create a configuration file for the script. ## if(verbose) message(sprintf("Sending email to %s", address)) ## ## This hard-wires mailx: we may prefer to use s-nail if available, ## or to allow to specify to command line MUA. ## status <- system2("mailx", c(args, address), env = env, stdin = filename, stdout = "", stderr = "") if(status == 0L) unlink(filename) else { message(sprintf("Sending email failed!\nThe unsent msg can be found in file %s.", sQuote(filename))) } invisible() } mailx_from_head_and_body_list <- function(x, verbose = FALSE) { h <- x$head mailx(h$Subject, h$To, body = x$body, from = "Prof Brian Ripley ", cc = h$CC, replyto = h$"Reply-To", verbose = verbose) } ## If we have a package p with problems for which we know that the ## maintainer will not fix these, either implicitly by never following ## up on reminders, or explicitly by telling us so, and the package has ## reverse dependencies, we can either provide NMUs, or need to escalate ## the issue by informing the maintainers of the reverse depends. ## The code below generates the necessary materials. CRAN_package_problem_escalation_message <- function(p, i = TRUE, d = Sys.Date() + 14, recursive = FALSE, collapse = FALSE) { d <- max(Sys.Date() + 14, as.Date("2022-01-06")) a <- available.packages() a <- a[startsWith(a[, "Repository"], getOption("repos")["CRAN"]), ] r <- tools::package_dependencies(p, a, reverse = TRUE, recursive = recursive) info <- tools:::CRAN_package_maintainers_info(c(p, unlist(r)), collapse = collapse) head <- info$head if(collapse) { head[startsWith(head, "Subject:")] <- sprintf("Subject: CRAN package %s and its reverse dependencies", p) } else { head$Subject <- sprintf("CRAN package %s and its reverse dependencies", p) } body <- c(info$body, "", sprintf("We have asked for an update fixing the problems in '%s' shown at", p), sprintf("https://www.stats.ox.ac.uk/pub/bdr/LENGTH1_self/%s.out", p), "and/or shown for the other packages at", "https://www.stats.ox.ac.uk/pub/bdr/LENGTH1/", if(i) "with no update from the maintainer thus far." else "and been informed that the maintainer will not be able to fix these.", "", strwrap(paste(sprintf("Thus, package '%s' is now scheduled for archival on %s,", p, d), "and archiving this will necessitate also archiving its", "strong reverse dependencies.")), "", "Please negotiate the necessary actions.", "", "The CRAN Team") list(head = head, body = body) } wrapper <- function() { args <- commandArgs(TRUE) if (length(args) != 1L) stop("needs one argument") m <- CRAN_package_problem_escalation_message(args, recursive = TRUE) mailx_from_head_and_body_list(m) } wrapper()