Rscript to use renv environment - r

How do I execute a command using RScript myfile.R so that it uses the renv environment of the project/directory it's in, NOT my default environment?

There are a couple ways:
Ensure your working directory is set to the root of your renv project, and that the renv project's auto-loader is active. (You can set up the auto-loader by calling renv::activate() from R in that project.)
In your script, explicitly call renv::load("/path/to/project") to load the requested project.
If neither of these methods suffice, please file an issue at https://github.com/rstudio/renv/issues.

I recently had a similar problem but the answer by #kevin-ushey was insufficient. Here's the background. I need to be able to run Rscript from any directory (Because I had several statistical models which were to be called from a Docker file, forcing a Docker file to have WORKDIR many times is just too cumbersome when you have long files with several Rscript calls. Moreover, some of these models are called several times in different bash files, making it cumbersome to cd to the directory before every Rscript call). We needed something akin to conda activate where any Rscript call would just use the activated 'renv environment' by default, regardless of what your working directory is. Here's a dummy example:
Install renv with install.packages('renv').
Create dummy folder with a dummy script containing the beepr library (just for the sake of the example) and initialize the renv environment:
mkdir ~/renv_test/
cd ~/renv_test/
echo "library(beepr); print('success')" >> test.R
Rscript -e "renv::init()"
Create a Docker image with the code below:
FROM rocker/r-base
ENV PROJ_ROOT='/usr/local/src/renv_test'
ENV RENV_DIR='/usr/local/.renv/'
COPY . $PROJ_ROOT
# Copy the projects renv infrastructure to RENV_DIR and remove all traces of renv from PROJ_ROOT
RUN mkdir -p $RENV_DIR/renv/ && \
cp $PROJ_ROOT/renv.lock $RENV_DIR && \
cp $PROJ_ROOT/renv/activate.R $RENV_DIR/renv/ && \
echo "source('renv/activate.R')" >> $RENV_DIR/.Rprofile && \
cd $RENV_DIR && \
Rscript -e "renv::restore()" && \
cd $PROJ_ROOT && Rscript -e "renv::deactivate()" && \
rm -rf renv/ renv.lock
# Set RENV_DIR's restore library as the default library
RUN echo $(cd $RENV_DIR && Rscript -e "cat(paste0('R_LIBS=', renv::paths\$library()), sep = '\n')") >> $HOME/.Renviron
# Run any script from any directory as if you had 'renv activated'
CMD Rscript $PROJ_ROOT/test.R
Here's a summary of the approach:
Copy the project to the docker image
Copy the renv infrastructure to a separate folder (here ~/.renv/) and restore the project there.
Eliminate all traces of renv from the project folder (this is so we don't mess up the path of the library if for some reason we execute a script from the root of this project).
Edit .Renviron so that it contains the restored library path in ~/.renv as the default library. This ensures that any new R session will use that library as the first option.
Execute any R scripts located in the project folder without having to cd or WORKDIR (docker) to the project folder.
If you build and run the previous Docker image, you should get a success statement even though we never cd to the project folder:
docker build -t renv_test .
docker run renv_test
[1] "success"

I believe a simpler way than the above answers:
Rscript -e 'renv::run("/path/to/myscript.R")'
It will pick up the renv environment from the base path. You can also specify the environment using the project parameter.

Related

Makefile "No such file or directory" when command is in PATH

I'm trying to write a Makefile in such a way that all of the steps in my recipe can call scripts a list of directories. As an example, I have node commands that are in a node_modules/.bin directory, but there could just as well be Python commands from a Python virtual environment as well, etc.
I have node installed, as well as TypeScript installed via npm in my /node_modules. Given my directory /tmp/test123, I wrote the following Makefile at /tmp/test123/Makefile as an example:
PATH = /tmp/test123/node_modules/.bin:$(PATH)
.PHONY: compile
compile:
env | grep "PATH"
tsc --help
However, if I try to run it, it outputs:
$ make compile
env | grep "PATH"
PATH=/tmp/test123/node_modules/.bin:... # the rest of my PATH
tsc --help
make: tsc: No such file or directory
make: *** [compile] Error 1
But if I change the last line to cd . && tsc --help, then compile runs successfully.
Why is make unable to find the tsc command in the PATH? Why do I need to run cd . && in front of it? Is there any better workaround than writing cd . && in front of all of my recipe steps?

External configuration of Project_Version in gpr file

With gnatpro 19.1, I'm trying to add gnathub to my project and am wondering how to set dynamically Project_Version as in:
package Dashboard is
for Project_Version use #git --describe --tags#; -- this needs to be updated.
end Dashboard;
I can't think of any simple way to do it.
A solution would be to use a Makefile that would configure a .gpr.in file but it seems contrived to change my whole buildchain just to add a version to the sonar config.
A simple, not automated solution, is to call the project with another switch:
gnathub -P Foo.gpr --plugins sonar-config,sonar-scanner\
--targs:sonar-scanner -Dsonar.projectVersion=$(git describe --tags)
But this is not really usable.
Similar question is to add the option -Dsonar.branch.name=$(git branch). AFAICT, the package Dashboard, as per the documentation has no Switch switch.
Is there any solution other than passing the extra arguments or forking gnatdashboard?
The best solution seems to reside in automating this configuration with a tool like Make.
For example, one can define the following Makefile:
# This target runs all the plugins listed
# in the section Dashboard.plugins of your project’s gpr
# sonar-config and sonar-scanner shall not be listed therein.
analyzes:
gnathub -P project
# This uses gnathub API to get the object dir where sonar-config file will be generated
OBJECT_DIR = $(shell gnathub -P project --exec object_dir.py 2>/dev/null | tail -n 1)
SONAR_PROPERTIES = $(OBJECT_DIR)/gnathub/sonar/sonar-project.properties
PROJECT_VERSION = $(shell git describe --tags)
BRANCH_NAME = $(shell git rev-parse --abbrev-ref HEAD)
# Uses gnathub to generate sonar properties file.
# Replaces the projectVersion and add branch name
# (notice that, on sonar, the branch name shall not be specified on your "master" branch)
$(SONAR_PROPERTIES): analyzes
gnathub -P project --plugins sonar-config --incremental
#sed -i "s/\(sonar.projectVersion = \).*/\1$(PROJECT_VERSION)/" $#
ifneq ($(BRANCH_NAME), master)
#echo "sonar.branch.name = $(BRANCH_NAME)" >> $#
endif
sonar: $(SONAR_PROPERTIES)
gnathub -P project --plugins sonar-scanner --incremental
.PHONY: sonar analyzes
Where object_dir.py is:
#!/usr/bin/env python
import GNAThub;
print (GNAThub.Project.object_dir());
Then:
$make sonar
Would run the analyzes and update them with the correct version and branch name (if necessary) to the SonarQube server.

Accessing global variables fails

I was trying to implement simple authentication in R that would store credentials outside of the source code under revision control. I'm aware of the approach using options() and getOption(), but using it would force me to remove project-level .Rprofile from revision control. I prefer to use another approach, based on exporting credentials' environment variables via .bashrc of an R-associated Linux user (ruser) and then reading these credentials in project-specific .Rprofile into global variables like this:
CB_API_KEY <<- Sys.getenv("CB_API_KEY")
However, accessing such global variables in R modules fails with message "object 'CB_API_KEY' not found". I suspect that the reason is that I source .Rprofile via separate calling R CMD BATCH in Makefile. Executing R modules, where I attempt to access these global variables, is done again via Makefile by separate call to Rscript. Therefore, it appears to me that global environment of the first R session is lost, hence access failures. I would appreciate your comments and advice on this issue.
UPDATE: The following is the contents of project-specific .Rprofile as well as the project's top-level and sub-project-level Makefile files, correspondingly.
.Rprofile:
# Execute global R profile first...
source("~/.Rprofile")
# ...then local project R setup
# Retrieve SRDA (SourceForge) credentials
SRDA_USER <<- Sys.getenv("SRDA_USER")
SRDA_PASS <<- Sys.getenv("SRDA_PASS")
# Retrieve CrunchBase API key
CB_API_KEY <<- Sys.getenv("CB_API_KEY")
# Another approach is to use options() and getOption(),
# but it requires removing this file from source control
options(SRDA_USER = "XXX", SRDA_PASS = "YYY", CB_API_KEY = "ZZZ")
Top-level Makefile:
# Major variable definitions
PROJECT="diss-floss"
HOME_DIR="~/diss-floss"
REPORT={$(PROJECT)-slides}
COLLECTION_DIR=import
PREPARATION_DIR=prepare
ANALYSIS_DIR=analysis
RESULTS_DIR=results
PRESENTATION_DIR=present
RSCRIPT=Rscript
# Targets and rules
all: rprofile collection preparation analysis results presentation
rprofile:
R CMD BATCH ./.Rprofile
collection:
cd $(COLLECTION_DIR) && $(MAKE)
preparation: collection
cd $(PREPARATION_DIR) && $(MAKE)
analysis: preparation
cd $(ANALYSIS_DIR) && $(MAKE)
results: analysis
cd $(RESULTS_DIR) && $(MAKE)
presentation: results
cd $(PRESENTATION_DIR) && $(MAKE)
## Phony targets and rules (for commands that do not produce files)
#.html
.PHONY: demo clean
# run demo presentation slides
demo: presentation
# knitr(Markdown) => HTML page
# HTML5 presentation via RStudio/RPubs or Slidify
# OR
# Shiny app
# remove intermediate files
clean:
rm -f tmp*.bz2 *.Rdata
Sub-project-level Makefile:
# Major variable definitions
RSCRIPT=Rscript
#RSCRIPT=R CMD BATCH
R_OPTS=--no-save --no-restore --verbose
#R_OUT=> outputFile.Rout 2>&1
# --no-save --no-restore --verbose myRfile.R > outputFile.Rout 2>&1
# Targets and rules
collection: importFLOSSmole \
importSourceForge \
importAngelList \
importCrunchBase
importFLOSSmole: getFLOSSmoleDataXML.R
$(RSCRIPT) $(R_OPTS) $<
importSourceForge: getSourceForgeData.R
$(RSCRIPT) $(R_OPTS) $<
importAngelList: getAngelListData.R
$(RSCRIPT) $(R_OPTS) $<
importCrunchBase: getCrunchBaseDataAPI.R
$(RSCRIPT) $(R_OPTS) $<
.PHONY: clean
# remove intermediate files
clean:
rm -f tmp*.bz2 *.Rdata .Rout
Directory structure is typical:
+ `ruser` home directory
|____+ project's home directory
|____ `import` sub-directory
|____ project's other sub-directories
Thank you!
If I've understood this correctly, you have a shell script named Makefile which calls R in batch mode (R CMD BATCH) to source your .Rprofile file, and then later runs another R session via Rscript, where it can't find the variable that you definine in your .Rprofile file.
You are correct in noticing that global variables aren't persisted between sessions.
However, unless you are calling Rscript with the --no-init-file or --vanilla arguments, the .Rprofile file should be sourced on startup.
Adding messages to your .Rprofile will let you know if and when it gets called.
OK, looks like I figured out how to solve this problem. I created another .Rprofile in the project's sub-directory (import), where processing requires authentication, and moved there code retrieving environment variables' values into R global variables. I tested it and haven't seen the previous error message (knocking on wood!). Additional benefit is that now the code is more separated into functional areas and, thus, is cleaner.
Lesson learned (and the reason of all this trouble):
Any R session sources .Rprofile file in any [current] directory where it runs (I wrongfully assumed that R session sources .Rprofile file only once, on the initial run, and subsequent R sessions cannot do that).

XCode 'Run Script' step to create directory and copy file is failing

I have the following custom script step during my build:
mkdir -p "${CONTENTS_FOLDER_PATH}/Frameworks"
cp "${SRCROOT}/testing.1.dylib" "${CONTENTS_FOLDER_PATH}/Frameworks"
The script runs successfully, but when I check the bundle the Frameworks directory does not exist.
Should not not work as I expect? (Frameworks folder created with the testing.1.dylib in it).
Edit: Added screenshot of the runscript.
How about trying the following:
dst=${CONFIGURATION_BUILD_DIR}/${CONTENTS_FOLDER_PATH}/Frameworks
mkdir -p "${dst}"
cp "..." "$dst"
(I found your example and adapted it as above to copy a dylib into the 'Frameworks' folder of my framework).

Zip command without including the compressed dir itself

Suppose the structure:
/foo/bar/
--file1
--file2
--file3
--folder1
--file4
--folder2
--file5
I want to run the unix zip utility, compressing the bar folder and all of it's files and subfolders, from foo folder, but not have the bar folder inside the zip, using only command line.
If I try to use the -j argument, it doesn't create the bar folder inside the zip as I want, but doesn't create folder1 and folder2. Doing -rj doesn't work.
(I know I can enter inside bar and do zip -r bar.zip . I want to know if it's possible to accomplish what $/foo/bar/ zip -r bar.zip . but doing it from $/foo).
You have to do cd /foo/bar then zip -r bar.zip ., however, you can group them with parentheses to run in a subshell:
# instead of: cd /foo/bar; zip -r bar.zip; cd -
( cd /foo/bar; zip -r bar.zip . )
The enclosed (paren-grouped) commands are run in a subshell and cd within it won't affect the outer shell session.
See sh manual.
Compound Commands
A compound command is one of the following:
(list) list is executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below).
Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes.
The return status is the exit status of list.
zip doesn't have a -C (change directory) command like tar does
you can do:
cd folder1 && zip -r ../bar.zip *
from within a command line shell
or you can use bsdtar which is a version of tar from libarchive that can create zips
bsdtar cf bar.zip --format zip -C folder1 .
(this creates a folder called ./ -- not sure a way around that)
I can't speak for the OP's reasoning. I was looking for this solution as well.
I am in the middle of coding a program that creates an .ods by building the internal xml files and zipping them together. They must be in the root dir of the archive, or you get an error when you try and run OOo.
I'm sure there is a dozen other ways to do this:
create a blank .ods file in OOo named blank.ods, extract to dir named blank, then try running:
cd blank && zip -r ../blank.ods *
The way I wrote mine, the shell closes after one command, so I don't need to navigate back to the original directory, if you do simply add && cd .. to the command line:
cd blank && zip -r ../blank.ods * && cd ..

Resources