UNPKG

4.94 kBPlain TextView Raw
1import { SoftwarePackage } from '@stencila/schema'
2
3import PackageGenerator from './PackageGenerator'
4import IUrlFetcher from './IUrlFetcher'
5
6/**
7 * A Dockerfile generator for R packages
8 */
9export default class RGenerator extends PackageGenerator {
10
11 /**
12 * Date used to pin the CRAN mirror used
13 */
14 date: string
15
16 constructor (urlFetcher: IUrlFetcher, pkg: SoftwarePackage, folder?: string) {
17 super(urlFetcher, pkg, folder)
18
19 // Default to yesterday's date (to ensure MRAN is available for the date)
20 // Set here as it is required in two methods below
21 let date = this.package.datePublished
22 if (!date) date = (new Date(Date.now() - 24 * 3600 * 1000)).toISOString().substring(0,10)
23 this.date = date
24 }
25
26 // Methods that override those in `Generator`.
27 // See that class for documentation on what each function does
28
29 applies (): boolean {
30 return this.package.runtimePlatform === 'R'
31 }
32
33 envVars (sysVersion: string): Array<[string, string]> {
34 return [
35 // Set the timezone to avoid warning from Sys.timezone()
36 // See https://github.com/rocker-org/rocker-versioned/issues/89
37 ['TZ', 'Etc/UTC']
38 ]
39 }
40
41 aptKeysCommand (sysVersion: string): string {
42 return 'apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 51716619E084DAB9'
43 }
44
45 aptRepos (base: string): Array<string> {
46 let version = this.baseVersionName(base)
47 // At time of writing, MRAN did not have an ubuntu:18.04(bionic) repo which supported R 3.4 (only bionic_3.5)
48 // See https://cran.microsoft.com/snapshot/2018-11-05/bin/linux/ubuntu/
49 // So append that to the deb line
50 if (version === 'bionic') version += '-cran35'
51 return [
52 `deb https://mran.microsoft.com/snapshot/${this.date}/bin/linux/ubuntu ${version}/`
53 ]
54 }
55
56 aptPackages (sysVersion: string): Array<string> {
57 let pkgs: Array<string> = [
58 'r-base'
59 ]
60
61 /**
62 * Recurse through `softwareRequirements` and find any deb packages
63 */
64 function find (pkg: any) {
65 if (pkg.runtimePlatform !== 'R' || !pkg.softwareRequirements) return
66 for (let subpkg of pkg.softwareRequirements) {
67 if (subpkg.runtimePlatform === 'deb') pkgs.push(subpkg.name || '')
68 else find(subpkg)
69 }
70 }
71 find(this.package)
72
73 return pkgs
74 }
75
76 stencilaInstall (sysVersion: string): string | undefined {
77 return `apt-get update \\
78 && apt-get install -y zlib1g-dev libxml2-dev pkg-config \\
79 && apt-get autoremove -y \\
80 && apt-get clean \\
81 && rm -rf /var/lib/apt/lists/* \\
82 && Rscript -e 'install.packages("devtools")' \\
83 && Rscript -e 'source("https://bioconductor.org/biocLite.R"); biocLite("graph")' \\
84 && Rscript -e 'devtools::install_github("r-lib/pkgbuild")' \\
85 && Rscript -e 'devtools::install_github("stencila/r")'`
86 }
87
88 installFiles (sysVersion: string): Array<[string, string]> {
89 // Copy user defined files if they exist
90 if (this.exists('install.R')) return [['install.R', 'install.R']]
91 if (this.exists('DESCRIPTION')) return [['DESCRIPTION', 'DESCRIPTION']]
92
93 // Generate a .DESCRIPTION with valid name to copy into image
94 const name = (this.package.name || 'unnamed').replace(/[^a-zA-Z0-9]/,'')
95 const pkgs = this.filterPackages('R').map(pkg => pkg.name)
96 let desc = `Package: ${name}
97Version: 1.0.0
98Date: ${this.date}
99Description: Generated by Dockter ${new Date().toISOString()}.
100 To stop Dockter generating this file and start editing it yourself, rename it to "DESCRIPTION".
101`
102 if (pkgs.length) desc += `Imports:\n ${pkgs.join(',\n ')}\n`
103
104 this.write('.DESCRIPTION', desc)
105 return [['.DESCRIPTION', 'DESCRIPTION']]
106 }
107
108 installCommand (sysVersion: string): string | undefined {
109 if (this.exists('install.R')) {
110 // Run the user supplied installation script
111 return `Rscript install.R`
112 } else if (this.exists('DESCRIPTION') || this.exists('.DESCRIPTION')) {
113 // To keep the Dockerfile as simple as possible, download and
114 // execute the installation-from-DESCRIPTION script.
115 return `bash -c "Rscript <(curl -sL https://unpkg.com/@stencila/dockter/src/install.R)"`
116 }
117 }
118
119 /**
120 * The files to copy into the Docker image
121 *
122 * Copies all `*.R` files to the container
123 */
124 projectFiles (): Array<[string, string]> {
125 const rfiles = this.glob('**/*.R')
126 return rfiles.map(file => [file, file]) as Array<[string, string]>
127 }
128
129 /**
130 * The command to execute in a container created from the Docker image
131 *
132 * If there is a top-level `main.R` or `cmd.R` then that will be used,
133 * otherwise, the first `*.R` files by alphabetical order will be used.
134 */
135 runCommand (): string | undefined {
136 const rfiles = this.glob('**/*.R')
137 if (rfiles.length === 0) return
138 let script
139 if (rfiles.includes('main.R')) script = 'main.R'
140 else if (rfiles.includes('cmd.R')) script = 'cmd.R'
141 else script = rfiles[0]
142 return `Rscript ${script}`
143 }
144}