├── .Rbuildignore ├── .gitignore ├── .travis.yml ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── R ├── build_binder.R ├── internals.R ├── launch_binder.R └── utils-pipe.R ├── README-NOT.md ├── README.md ├── bindertools.Rproj └── man ├── CRAN_package.Rd ├── binder_installR.Rd ├── binder_runtime.Rd ├── build_binder.Rd ├── find_doc_libs.Rd ├── github_package.Rd ├── launch_binder.Rd └── pipe.Rd /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^LICENSE\.md$ 2 | ^\.travis\.yml$ 3 | ^toy_example$ 4 | ^.*\.Rproj$ 5 | ^\.Rproj\.user$ 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | toy_project.html -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # R for travis: see documentation at https://docs.travis-ci.com/user/languages/r 2 | 3 | language: R 4 | sudo: false 5 | cache: packages 6 | 7 | r_packages: 8 | - purrr 9 | - magrittr 10 | 11 | email: 12 | on_success: change 13 | on_failure: change 14 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: bindertools 2 | Title: Create requisite files and launch binder with mybinder.org 3 | Version: 0.0.0.9000 4 | Authors@R: c( 5 | person("Saras", "Windecker", role = c("aut", "cre"), email = "saras.windecker@gmail.com"), 6 | person("Felix", "Leung", role = "aut"), 7 | person("Steven", "Kambouris", role = "aut"), 8 | person("Miles", "McBain", role = "aut") 9 | ) 10 | Description: Computational reproducibility is a critical component of modern open science. Methods 11 | such as docker exist to containerise analyses, ensuring that operating systems and package versions 12 | are recorded and can be recreated in order to rerun analyses. Setting up dockerfiles, however, is 13 | a nontrivial task on top of a growing technical barrier to reproducible research. Binder is a 14 | easy interface to produce a virtual machine within which to rerun analyses without requiring 15 | installation or understanding of underlying containerisation principles. It does however still require 16 | researchers to search through their code to find packages and version of packages used in the project. 17 | This package seeks to make the bridge to using binder for analyses in R even simpler, by setting up 18 | the install.R file with all packages and version (both on CRAN and github) in one step. 19 | The binder can also be launched right from R, without needing to manually input repository information 20 | into the mybinder.org interface. 21 | License: MIT + file LICENSE 22 | Encoding: UTF-8 23 | LazyData: true 24 | RoxygenNote: 6.1.1 25 | Imports: 26 | purrr, 27 | curl, 28 | magrittr 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2018 2 | COPYRIGHT HOLDER: Saras Windecker 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2018 Saras Windecker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export("%>%") 4 | export(build_binder) 5 | export(launch_binder) 6 | importFrom(magrittr,"%>%") 7 | importFrom(purrr,map) 8 | importFrom(purrr,map_dfr) 9 | importFrom(utils,browseURL) 10 | importFrom(utils,globalVariables) 11 | importFrom(utils,packageDescription) 12 | -------------------------------------------------------------------------------- /R/build_binder.R: -------------------------------------------------------------------------------- 1 | #' Build binder files 2 | #' 3 | #' @description Builds the binder dockerfile and the install.R script. 4 | #' 5 | #' @param directory To build files in. Defaults to current directory. 6 | #' 7 | #' @return message confirming files created and suggesting next steps 8 | #' @export 9 | #' 10 | #' @examples build_binder() 11 | 12 | build_binder <- function (directory = '.') { 13 | 14 | # build dockerfile 15 | binder_runtime(directory) 16 | cat("Runtime file created.\n") 17 | 18 | # build install.R file 19 | binder_installR(directory) 20 | cat("install.R created.\nNext step: Commit and push to github!") 21 | 22 | } 23 | 24 | -------------------------------------------------------------------------------- /R/internals.R: -------------------------------------------------------------------------------- 1 | #' Creates install.R file 2 | #' 3 | #' @param directory To build files in. Defaults to current directory. 4 | #' @importFrom purrr map map_dfr walk2 5 | #' @importFrom utils globalVariables 6 | #' 7 | 8 | binder_installR <- function (directory = utils::globalVariables(c('.'))) { 9 | 10 | R_files <- dir(directory, 11 | full.names = TRUE, 12 | pattern = "\\.(rmd|r)$" , 13 | recursive = TRUE, 14 | ignore.case = TRUE) 15 | # if (length(R_files == 0)) stop('No R files found.') 16 | 17 | ## If there's a previous install.R file, exclude it. 18 | R_files <- R_files[!grepl("install.R", 19 | R_files, 20 | ignore.case = TRUE)] 21 | 22 | 23 | ## If we have R markdown files, inject it as a dependency. 24 | lib_list <- list() 25 | if (any(grepl(".rmd", R_files, ignore.case = TRUE))){ 26 | lib_list <- list("rmarkdown") 27 | } 28 | 29 | 30 | lib_list <- 31 | c(lib_list, 32 | purrr::map(R_files, readLines) %>% 33 | purrr::map(find_doc_libs) %>% 34 | unlist() %>% 35 | unique()) 36 | 37 | CRAN_packages <- purrr::map_dfr(lib_list, CRAN_package) %>% 38 | Filter(Negate(is.null), .) 39 | 40 | github_packages <- purrr::map(lib_list, github_package) %>% 41 | Filter(Negate(is.null), .) %>% 42 | as.character() 43 | 44 | if (length(CRAN_packages) != 0) { 45 | x_CRAN <- paste0("purrr::walk2(.x = ", 46 | deparse(CRAN_packages$package), 47 | ", .y = ", 48 | deparse(CRAN_packages$version), 49 | ", ~devtools::install_version(package = .x, version = .y))") 50 | } else { x_CRAN <- NULL } 51 | 52 | if (length(github_packages) != 0) { 53 | x_github <- paste0("devtools::install_github(c(", 54 | paste0("\"", github_packages, "\"", collapse = ", "), "))") 55 | } else { x_github <- NULL } 56 | 57 | defaults <- paste0("install.packages(c('purrr', 'devtools'))") 58 | 59 | # add to install.R file 60 | writeLines(paste0(defaults, '\n', 61 | x_CRAN, '\n', 62 | x_github), 63 | 'install.R') 64 | 65 | } 66 | 67 | #' Find out if package is on CRAN and get version 68 | #' 69 | #' @param package_name name of package to test 70 | #' @importFrom utils packageDescription 71 | #' 72 | CRAN_package <- function (package_name) { 73 | 74 | pd <- utils::packageDescription(package_name) 75 | 76 | if (is.null(pd$Repository)) { 77 | return (NULL) 78 | } 79 | 80 | if (!is.null(pd$Repository)) { 81 | return(list(package = package_name, 82 | version = pd$Version)) 83 | } 84 | 85 | } 86 | 87 | #' Find out if package is on github and get repo and commit SHA 88 | #' 89 | #' @param package_name name of package to test 90 | #' 91 | #' 92 | github_package <- function (package_name) { 93 | 94 | pd <- utils::packageDescription(package_name) 95 | 96 | if (is.null(pd$GithubRepo)) { 97 | return(NULL) 98 | } 99 | 100 | if (!is.null(pd$GithubRepo)) { 101 | return(paste0(pd$RemoteUsername, '/', pd$GithubRepo, '@', pd$RemoteSha)) 102 | } 103 | 104 | } 105 | 106 | #' Scan text to find packages 107 | #' 108 | #' @param doc text of file to scan 109 | #' @note from github.com/milesmcbain/deplearning 110 | #' @importFrom purrr map 111 | #' 112 | find_doc_libs <- function (doc) { 113 | 114 | # filter comments 115 | comments <- "^\\s*#" 116 | comment_lines <- grepl(pattern = comments, x = doc) 117 | doc <- doc[!comment_lines] 118 | 119 | # find dependencies 120 | patterns <- list( 121 | library = "(?<=library\\()\"*[a-zA-Z0-9.]+\"*(?=\\))", 122 | require = "(?<=require\\()\"*[a-zA-Z0-9.]+\"*(?=\\))", 123 | p_load = "(?<=p_load\\()\"*[a-zA-Z0-9.]+\"*[\\s*\\,\\s*\"*[a-zA-Z0-9.]+\"*]*(?=\\))", 124 | `::` = "[a-zA-Z0-9.]+(?=::)" 125 | ) 126 | 127 | match_pos <- purrr::map(patterns, ~regexec(., doc, perl = TRUE)) 128 | lib_matches <- purrr::map(match_pos, ~regmatches(doc, .)) %>% 129 | lapply(unlist) 130 | 131 | # post processing cleanup of matches 132 | final_matches <- 133 | lib_matches %>% 134 | purrr::map(~gsub(x = ., pattern = "\"", replacement ="")) %>% 135 | purrr::map(~strsplit(x = . , split = ",\\s*" )) %>% 136 | unlist() %>% 137 | unique() 138 | 139 | final_matches 140 | } 141 | 142 | ##' Create the binder runtime 143 | ##' 144 | ##' Writes the binder R runtime.txt to the user's disk current working directory. 145 | ##' 146 | ##' @title binder_runtime 147 | ##' @param directory the directory to write the binder docker file. 148 | ##' @return nothing, writes a file. 149 | binder_runtime <- function (directory) { 150 | 151 | runtime_date <- 152 | as.Date(Sys.time(), "UTC", format="%Y-%m-%d") 153 | 154 | x <- paste0("r-",runtime_date) 155 | 156 | writeLines(x, file.path(directory,'runtime.txt')) 157 | } 158 | -------------------------------------------------------------------------------- /R/launch_binder.R: -------------------------------------------------------------------------------- 1 | #' Launch binder for project at mybinder.org 2 | #' 3 | #' @description launches binder 4 | #' 5 | #' @param github_username For project repo 6 | #' @param repo_name Repository name 7 | #' @param branch_name Branch name, eg. 'master' 8 | #' 9 | #' @return opens URL for binder 10 | #' @importFrom utils browseURL 11 | #' @export 12 | #' 13 | 14 | launch_binder <- function (github_username, repo_name, branch_name) { 15 | 16 | # set up URL for mybinder.org 17 | URL <- paste0("http://beta.mybinder.org/v2/gh/", 18 | github_username, 19 | "/", 20 | repo_name, 21 | "/", 22 | branch_name, 23 | "?urlpath=rstudio") 24 | 25 | # launch binder and open URL 26 | utils::browseURL(url = URL) 27 | 28 | cat(URL) 29 | } 30 | -------------------------------------------------------------------------------- /R/utils-pipe.R: -------------------------------------------------------------------------------- 1 | #' Pipe operator 2 | #' 3 | #' See \code{magrittr::\link[magrittr]{\%>\%}} for details. 4 | #' 5 | #' @name %>% 6 | #' @rdname pipe 7 | #' @keywords internal 8 | #' @export 9 | #' @importFrom magrittr %>% 10 | #' @usage lhs \%>\% rhs 11 | NULL 12 | -------------------------------------------------------------------------------- /README-NOT.md: -------------------------------------------------------------------------------- 1 | # bindertools 2 | [![Project Status: Abandoned – Initial development has started, but there has not yet been a stable, usable release; the project has been abandoned and the author(s) do not intend on continuing development.](https://www.repostatus.org/badges/latest/abandoned.svg)](https://www.repostatus.org/#abandoned) 3 | 4 | This repo has been archived. For more information refer to [rOpenSci curation policy](https://devguide.ropensci.org/curationpolicy.html). 5 | 6 | For using Binder with R we suggest trying out [holepunch](https://karthik.github.io/holepunch/) 7 | 8 | ## Summary 9 | Computational reproducibility is a critical component of modern open science. Methods such as docker exist to containerise analyses, ensuring that operating systems and package versions are recorded and can be recreated in order to rerun analyses. Setting up dockerfiles, however, is a nontrivial task on top of a growing technical barrier to reproducible research. 10 | 11 | [binder](https://mybinder.org/) is a handy alternative, that: 12 | 1. builds a Docker image for your Github repo, and 13 | 2. hosts a live environment on [JupyterHub](https://jupyterhub.readthedocs.io/en/latest/) server, accessible via a reusable link. 14 | 15 | As an alternative to using docker itself, the benefit of binder is that it requires only an internet connection to use. The binder live repository allows anyone to replicate your research with the same computing environment and package versions as you used when you built it. Setting up binder, while straightforward, still requires researchers to search through their code to find package names, source, and versions used to create an install.R file that loads these into the virtual environment. 16 | 17 | `bindertools` is a little R helper that seeks to make the bridge to binder for analyses in R even simpler by setting up the install.R file with all packages and versions (both for CRAN and github packages) in one step. The online binder can also be launched right from R, without needing to manually input repository information into the mybinder.org interface. 18 | 19 | `bindertools` has two key functions: 20 | 21 | - `build_binder()` builds a runtime.txt file and an install.R file that contains code to install all required CRAN and Github packages mentioned in any .R and .Rmd file. 22 | - `launch_binder()` launches your live repository at mybinder.org in an RStudio session and also returns the html webpage that you can then send as is, or record in a github repo with a README button as follows: 23 | 24 | `[![Binder](http://mybinder.org/badge.svg)](COPY HTML LINK RETURNED BY launch_binder() HERE)` 25 | 26 | ## Installation 27 | 28 | You can install `bindertools` as follows: 29 | 30 | ``` r 31 | devtools::install_github("ropenscilabs/bindertools") 32 | ``` 33 | 34 | ## Example 35 | 36 | Let's say our project is contained in a local directory called `~/toy_project` that we want to reproduce in a live session on binder. 37 | 38 | In just three steps, we can do so with `bindertools`: 39 | 40 | 1. `build_binder(directory = "~/toy_project")`, which will create our runtime.txt and install.R files. 41 | 2. `git push` in the terminal or using the Git pane in RStudio to push the two added files to your Github repo. 42 | 3. run the following to launch: 43 | ```r 44 | launch_binder(github_username = 'my_username', 45 | repo_name = 'toy_project', 46 | branch_name = 'master') 47 | ``` 48 | 49 | Then in the browser you should see a live RStudio session populated with data same as `~/toy_project`. 50 | 51 | Access our toy project here: 52 | [![Binder](http://mybinder.org/badge.svg)](http://beta.mybinder.org/v2/gh/smwindecker/toy_project/master?urlpath=rstudio) 53 | 54 | ## More examples 55 | See also, the [github repo for binder](https://github.com/rocker-org/binder) and a [research compendium example](https://github.com/cboettig/noise-phenomena) using binder. 56 | 57 | ## Team members 58 | * `r emo::ji("cat")` [Saras Windecker](https://github.com/smwindecker) `r emo::ji("bird")` [\@smwindecker](https://twitter.com/smwindecker) 59 | * `r emo::ji("cat")` [Felix Leung](https://github.com/felixleungsc) `r emo::ji("bird")` [\@felixleungsc](https://twitter.com/felixleungsc) 60 | * `r emo::ji("cat")` [Steve Kambouris](https://github.com/stevekambouris) `r emo::ji("bird")` [\@steve_kambouris](https://twitter.com/steve_kambouris) 61 | * `r emo::ji("cat")` [Miles McBain](https://github.com/MilesMcBain) `r emo::ji("bird")` [\@MilesMcBain](https://twitter.com/MilesMcBain) 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bindertools 2 | 3 | [![Project Status: Abandoned](https://www.repostatus.org/badges/latest/abandoned.svg)](https://www.repostatus.org/#abandoned) 4 | 5 | This repository has been archived. The former README is now in [README-NOT.md](README-NOT.md). 6 | -------------------------------------------------------------------------------- /bindertools.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /man/CRAN_package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internals.R 3 | \name{CRAN_package} 4 | \alias{CRAN_package} 5 | \title{Find out if package is on CRAN and get version} 6 | \usage{ 7 | CRAN_package(package_name) 8 | } 9 | \arguments{ 10 | \item{package_name}{name of package to test} 11 | } 12 | \description{ 13 | Find out if package is on CRAN and get version 14 | } 15 | -------------------------------------------------------------------------------- /man/binder_installR.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internals.R 3 | \name{binder_installR} 4 | \alias{binder_installR} 5 | \title{Creates install.R file} 6 | \usage{ 7 | binder_installR(directory = utils::globalVariables(c("."))) 8 | } 9 | \arguments{ 10 | \item{directory}{To build files in. Defaults to current directory.} 11 | } 12 | \description{ 13 | Creates install.R file 14 | } 15 | -------------------------------------------------------------------------------- /man/binder_runtime.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internals.R 3 | \name{binder_runtime} 4 | \alias{binder_runtime} 5 | \title{binder_runtime} 6 | \usage{ 7 | binder_runtime(directory) 8 | } 9 | \arguments{ 10 | \item{directory}{the directory to write the binder docker file.} 11 | } 12 | \value{ 13 | nothing, writes a file. 14 | } 15 | \description{ 16 | Create the binder runtime 17 | } 18 | \details{ 19 | Writes the binder R runtime.txt to the user's disk current working directory. 20 | } 21 | -------------------------------------------------------------------------------- /man/build_binder.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/build_binder.R 3 | \name{build_binder} 4 | \alias{build_binder} 5 | \title{Build binder files} 6 | \usage{ 7 | build_binder(directory = ".") 8 | } 9 | \arguments{ 10 | \item{directory}{To build files in. Defaults to current directory.} 11 | } 12 | \value{ 13 | message confirming files created and suggesting next steps 14 | } 15 | \description{ 16 | Builds the binder dockerfile and the install.R script. 17 | } 18 | \examples{ 19 | build_binder() 20 | } 21 | -------------------------------------------------------------------------------- /man/find_doc_libs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internals.R 3 | \name{find_doc_libs} 4 | \alias{find_doc_libs} 5 | \title{Scan text to find packages} 6 | \usage{ 7 | find_doc_libs(doc) 8 | } 9 | \arguments{ 10 | \item{doc}{text of file to scan} 11 | } 12 | \description{ 13 | Scan text to find packages 14 | } 15 | \note{ 16 | from github.com/milesmcbain/deplearning 17 | } 18 | -------------------------------------------------------------------------------- /man/github_package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/internals.R 3 | \name{github_package} 4 | \alias{github_package} 5 | \title{Find out if package is on github and get repo and commit SHA} 6 | \usage{ 7 | github_package(package_name) 8 | } 9 | \arguments{ 10 | \item{package_name}{name of package to test} 11 | } 12 | \description{ 13 | Find out if package is on github and get repo and commit SHA 14 | } 15 | -------------------------------------------------------------------------------- /man/launch_binder.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/launch_binder.R 3 | \name{launch_binder} 4 | \alias{launch_binder} 5 | \title{Launch binder for project at mybinder.org} 6 | \usage{ 7 | launch_binder(github_username, repo_name, branch_name) 8 | } 9 | \arguments{ 10 | \item{github_username}{For project repo} 11 | 12 | \item{repo_name}{Repository name} 13 | 14 | \item{branch_name}{Branch name, eg. 'master'} 15 | } 16 | \value{ 17 | opens URL for binder 18 | } 19 | \description{ 20 | launches binder 21 | } 22 | -------------------------------------------------------------------------------- /man/pipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils-pipe.R 3 | \name{\%>\%} 4 | \alias{\%>\%} 5 | \title{Pipe operator} 6 | \usage{ 7 | lhs \%>\% rhs 8 | } 9 | \description{ 10 | See \code{magrittr::\link[magrittr]{\%>\%}} for details. 11 | } 12 | \keyword{internal} 13 | --------------------------------------------------------------------------------