kopia lustrzana https://github.com/jupyterhub/repo2docker
[MRG] Add support for installing different versions of R (#772)
[MRG] Add support for installing different versions of Rpull/797/head
commit
bfbec349c2
|
@ -212,7 +212,7 @@ If you only need to run things once during the build phase use :ref:`postBuild`.
|
||||||
|
|
||||||
Sometimes you want to specify the version of the runtime
|
Sometimes you want to specify the version of the runtime
|
||||||
(e.g. the version of Python or R),
|
(e.g. the version of Python or R),
|
||||||
but the environment specification format don't let you specify this information
|
but the environment specification format will not let you specify this information
|
||||||
(e.g. requirements.txt or install.R).
|
(e.g. requirements.txt or install.R).
|
||||||
For these cases, we have a special file, ``runtime.txt``.
|
For these cases, we have a special file, ``runtime.txt``.
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ For these cases, we have a special file, ``runtime.txt``.
|
||||||
|
|
||||||
``runtime.txt`` is only supported when used with environment specifications
|
``runtime.txt`` is only supported when used with environment specifications
|
||||||
that do not already support specifying the runtime
|
that do not already support specifying the runtime
|
||||||
(e.g. when using ``environment.yml`` for conda or ``Project.toml`` for Julia,
|
(when using ``environment.yml`` for conda or ``Project.toml`` for Julia,
|
||||||
``runtime.txt`` will be ignored).
|
``runtime.txt`` will be ignored).
|
||||||
|
|
||||||
To use python-2.7: add ``python-2.7`` in runtime.txt file.
|
To use python-2.7: add ``python-2.7`` in runtime.txt file.
|
||||||
|
@ -231,8 +231,11 @@ Python 2 installed. To see a full example repository, visit our
|
||||||
repo2docker uses R libraries pinned to a specific snapshot on
|
repo2docker uses R libraries pinned to a specific snapshot on
|
||||||
`MRAN <https://mran.microsoft.com/documents/rro/reproducibility>`_.
|
`MRAN <https://mran.microsoft.com/documents/rro/reproducibility>`_.
|
||||||
You need to have a ``runtime.txt`` file that is formatted as
|
You need to have a ``runtime.txt`` file that is formatted as
|
||||||
``r-<YYYY>-<MM>-<DD>``, where YYYY-MM-DD is a snapshot at MRAN that will be
|
``r-<RVERSION>-<YYYY>-<MM>-<DD>``, where YYYY-MM-DD is a snapshot at MRAN that will be
|
||||||
used for installing libraries.
|
used for installing libraries. You can set RVERSION to 3.4, 3.5 or 3.6 to select
|
||||||
|
the version of R you want to use. If you do not specify a R version the latest
|
||||||
|
released version will be used (currently R 3.6). You can also specify the exact
|
||||||
|
patch release you want to use for the 3.5 and 3.6 series.
|
||||||
|
|
||||||
To see an example R repository, visit our `R
|
To see an example R repository, visit our `R
|
||||||
example in binder-examples <https://github.com/binder-examples/r/blob/master/runtime.txt>`_.
|
example in binder-examples <https://github.com/binder-examples/r/blob/master/runtime.txt>`_.
|
||||||
|
|
|
@ -49,10 +49,11 @@ Julia versions 0.6.x and earlier are supported via a :ref:`REQUIRE <REQUIRE>` fi
|
||||||
R
|
R
|
||||||
~
|
~
|
||||||
|
|
||||||
Only R 3.4.4 is currently supported, which is installed via ``apt`` from the
|
The default version of R is currently R 3.6.1. You can select the version of
|
||||||
`ubuntu bionic repository <https://packages.ubuntu.com/bionic/r-base>`_.
|
R you want to use by specifying it in the :ref:`runtime.txt <runtime.txt>`
|
||||||
|
file.
|
||||||
|
|
||||||
|
We support R versions 3.4, 3.5 and 3.6.
|
||||||
|
|
||||||
|
|
||||||
Why is my repository is failing to build with ``ResolvePackageNotFound`` ?
|
Why is my repository is failing to build with ``ResolvePackageNotFound`` ?
|
||||||
|
@ -149,17 +150,17 @@ notebooks (among others).
|
||||||
the container, use the ``--volumes`` option instead. Similarly,
|
the container, use the ``--volumes`` option instead. Similarly,
|
||||||
for a fully customized user Dockerfile, this option is not
|
for a fully customized user Dockerfile, this option is not
|
||||||
guaranteed to work.
|
guaranteed to work.
|
||||||
|
|
||||||
|
|
||||||
Why is my R shiny app not launching?
|
Why is my R shiny app not launching?
|
||||||
----------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------
|
||||||
|
|
||||||
If you are trying to run an R shiny app using the ``/shiny/folder_containing_shiny``
|
If you are trying to run an R shiny app using the ``/shiny/folder_containing_shiny``
|
||||||
url option, but the launch returns "The application exited during initialization.",
|
url option, but the launch returns "The application exited during initialization.",
|
||||||
there might be something wrong with the specification of the app. One way of debugging
|
there might be something wrong with the specification of the app. One way of debugging
|
||||||
the app in the container is by running the ``rstudio`` url, open either the ui or
|
the app in the container is by running the ``rstudio`` url, open either the ui or
|
||||||
server file for the app, and run the app in the container rstudio. This way you can
|
server file for the app, and run the app in the container rstudio. This way you can
|
||||||
see the rstudio logs as it tries to initialise the shiny app. If you a missing a
|
see the rstudio logs as it tries to initialise the shiny app. If you a missing a
|
||||||
package or other dependency for the container, this will be obvious at this stage.
|
package or other dependency for the container, this will be obvious at this stage.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ import re
|
||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
from distutils.version import LooseVersion as V
|
||||||
|
|
||||||
from .python import PythonBuildPack
|
from .python import PythonBuildPack
|
||||||
|
|
||||||
|
|
||||||
|
@ -55,6 +57,41 @@ class RBuildPack(PythonBuildPack):
|
||||||
|
|
||||||
return self._runtime
|
return self._runtime
|
||||||
|
|
||||||
|
@property
|
||||||
|
def r_version(self):
|
||||||
|
"""Detect the R version for a given `runtime.txt`
|
||||||
|
|
||||||
|
Will return the version specified by the user or the current default
|
||||||
|
version.
|
||||||
|
"""
|
||||||
|
version_map = {
|
||||||
|
"3.4": "3.4",
|
||||||
|
"3.5": "3.5.3-1bionic",
|
||||||
|
"3.5.0": "3.5.0-1bionic",
|
||||||
|
"3.5.1": "3.5.1-2bionic",
|
||||||
|
"3.5.2": "3.5.2-1bionic",
|
||||||
|
"3.5.3": "3.5.3-1bionic",
|
||||||
|
"3.6": "3.6.1-3bionic",
|
||||||
|
"3.6.0": "3.6.0-2bionic",
|
||||||
|
"3.6.1": "3.6.1-3bionic",
|
||||||
|
}
|
||||||
|
# the default if nothing is specified
|
||||||
|
r_version = "3.6"
|
||||||
|
|
||||||
|
if not hasattr(self, "_r_version"):
|
||||||
|
parts = self.runtime.split("-")
|
||||||
|
if len(parts) == 5:
|
||||||
|
r_version = parts[1]
|
||||||
|
if r_version not in version_map:
|
||||||
|
raise ValueError(
|
||||||
|
"Version '{}' of R is not supported.".format(r_version)
|
||||||
|
)
|
||||||
|
|
||||||
|
# translate to the full version string
|
||||||
|
self._r_version = version_map.get(r_version)
|
||||||
|
|
||||||
|
return self._r_version
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def checkpoint_date(self):
|
def checkpoint_date(self):
|
||||||
"""
|
"""
|
||||||
|
@ -63,11 +100,14 @@ class RBuildPack(PythonBuildPack):
|
||||||
Returns '' if no date is specified
|
Returns '' if no date is specified
|
||||||
"""
|
"""
|
||||||
if not hasattr(self, "_checkpoint_date"):
|
if not hasattr(self, "_checkpoint_date"):
|
||||||
match = re.match(r"r-(\d\d\d\d)-(\d\d)-(\d\d)", self.runtime)
|
match = re.match(r"r-(\d.\d(.\d)?-)?(\d\d\d\d)-(\d\d)-(\d\d)", self.runtime)
|
||||||
if not match:
|
if not match:
|
||||||
self._checkpoint_date = False
|
self._checkpoint_date = False
|
||||||
else:
|
else:
|
||||||
self._checkpoint_date = datetime.date(*[int(s) for s in match.groups()])
|
# turn the last three groups of the match into a date
|
||||||
|
self._checkpoint_date = datetime.date(
|
||||||
|
*[int(s) for s in match.groups()[-3:]]
|
||||||
|
)
|
||||||
|
|
||||||
return self._checkpoint_date
|
return self._checkpoint_date
|
||||||
|
|
||||||
|
@ -127,20 +167,19 @@ class RBuildPack(PythonBuildPack):
|
||||||
We install a base version of R, and packages required for RStudio to
|
We install a base version of R, and packages required for RStudio to
|
||||||
be installed.
|
be installed.
|
||||||
"""
|
"""
|
||||||
return (
|
packages = [
|
||||||
super()
|
# For rstudio
|
||||||
.get_packages()
|
"psmisc",
|
||||||
.union(
|
"libapparmor1",
|
||||||
[
|
"sudo",
|
||||||
"r-base",
|
"lsb-release",
|
||||||
# For rstudio
|
]
|
||||||
"psmisc",
|
# For R 3.4 we use the default Ubuntu package, for other versions we
|
||||||
"libapparmor1",
|
# install from a different PPA
|
||||||
"sudo",
|
if V(self.r_version) < V("3.5"):
|
||||||
"lsb-release",
|
packages.append("r-base")
|
||||||
]
|
|
||||||
)
|
return super().get_packages().union(packages)
|
||||||
)
|
|
||||||
|
|
||||||
def get_build_scripts(self):
|
def get_build_scripts(self):
|
||||||
"""
|
"""
|
||||||
|
@ -180,7 +219,38 @@ class RBuildPack(PythonBuildPack):
|
||||||
self.checkpoint_date.isoformat()
|
self.checkpoint_date.isoformat()
|
||||||
)
|
)
|
||||||
|
|
||||||
scripts = [
|
scripts = []
|
||||||
|
# For R 3.4 we want to use the default Ubuntu package but otherwise
|
||||||
|
# we use the packages from a PPA
|
||||||
|
if V(self.r_version) >= V("3.5"):
|
||||||
|
scripts += [
|
||||||
|
(
|
||||||
|
"root",
|
||||||
|
r"""
|
||||||
|
echo "deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/" > /etc/apt/sources.list.d/r3.6-ubuntu.list
|
||||||
|
""",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"root",
|
||||||
|
r"""
|
||||||
|
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9
|
||||||
|
""",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"root",
|
||||||
|
r"""
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install --yes r-base={} && \
|
||||||
|
apt-get -qq purge && \
|
||||||
|
apt-get -qq clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
""".format(
|
||||||
|
self.r_version
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
scripts += [
|
||||||
(
|
(
|
||||||
"root",
|
"root",
|
||||||
r"""
|
r"""
|
||||||
|
@ -277,17 +347,32 @@ class RBuildPack(PythonBuildPack):
|
||||||
]
|
]
|
||||||
|
|
||||||
if "r" in self.stencila_contexts:
|
if "r" in self.stencila_contexts:
|
||||||
scripts += [
|
# new versions of R require a different way of installing bioconductor
|
||||||
(
|
if V(self.r_version) <= V("3.5"):
|
||||||
"${NB_USER}",
|
scripts += [
|
||||||
# Install and register stencila library
|
(
|
||||||
r"""
|
"${NB_USER}",
|
||||||
R --quiet -e "source('https://bioconductor.org/biocLite.R'); biocLite('graph')" && \
|
# Install and register stencila library
|
||||||
R --quiet -e "devtools::install_github('stencila/r', ref = '361bbf560f3f0561a8612349bca66cd8978f4f24')" && \
|
r"""
|
||||||
R --quiet -e "stencila::register()"
|
R --quiet -e "source('https://bioconductor.org/biocLite.R'); biocLite('graph')" && \
|
||||||
""",
|
R --quiet -e "devtools::install_github('stencila/r', ref = '361bbf560f3f0561a8612349bca66cd8978f4f24')" && \
|
||||||
)
|
R --quiet -e "stencila::register()"
|
||||||
]
|
""",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
else:
|
||||||
|
scripts += [
|
||||||
|
(
|
||||||
|
"${NB_USER}",
|
||||||
|
# Install and register stencila library
|
||||||
|
r"""
|
||||||
|
R --quiet -e "install.packages('BiocManager'); BiocManager::install(); BiocManager::install(c('graph'))" && \
|
||||||
|
R --quiet -e "devtools::install_github('stencila/r', ref = '361bbf560f3f0561a8612349bca66cd8978f4f24')" && \
|
||||||
|
R --quiet -e "stencila::register()"
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
return super().get_build_scripts() + scripts
|
return super().get_build_scripts() + scripts
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from repo2docker import buildpacks
|
||||||
|
|
||||||
|
|
||||||
|
def test_unsupported_version(tmpdir):
|
||||||
|
tmpdir.chdir()
|
||||||
|
|
||||||
|
with open("runtime.txt", "w") as f:
|
||||||
|
f.write("r-3.8-2019-01-01")
|
||||||
|
|
||||||
|
r = buildpacks.RBuildPack()
|
||||||
|
with pytest.raises(ValueError) as excinfo:
|
||||||
|
# access the property to trigger the exception
|
||||||
|
_ = r.r_version
|
||||||
|
# check the version is mentioned in the exception
|
||||||
|
assert "'3.8'" in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"runtime_version, expected", [("", "3.6"), ("3.6", "3.6"), ("3.5.1", "3.5")]
|
||||||
|
)
|
||||||
|
def test_version_specification(tmpdir, runtime_version, expected):
|
||||||
|
tmpdir.chdir()
|
||||||
|
|
||||||
|
with open("runtime.txt", "w") as f:
|
||||||
|
if runtime_version:
|
||||||
|
runtime_version += "-"
|
||||||
|
f.write(f"r-{runtime_version}2019-01-01")
|
||||||
|
|
||||||
|
r = buildpacks.RBuildPack()
|
||||||
|
assert r.r_version.startswith(expected)
|
||||||
|
|
||||||
|
|
||||||
|
def test_version_completion(tmpdir):
|
||||||
|
tmpdir.chdir()
|
||||||
|
|
||||||
|
with open("runtime.txt", "w") as f:
|
||||||
|
f.write(f"r-3.6-2019-01-01")
|
||||||
|
|
||||||
|
r = buildpacks.RBuildPack()
|
||||||
|
assert r.r_version == "3.6.1-3bionic"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"runtime, expected",
|
||||||
|
[
|
||||||
|
("r-2019-01-01", (2019, 1, 1)),
|
||||||
|
("r-3.6.1-2019-01-01", (2019, 1, 1)),
|
||||||
|
("r-3.5-2019-01-01", (2019, 1, 1)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_mran_date(tmpdir, runtime, expected):
|
||||||
|
tmpdir.chdir()
|
||||||
|
|
||||||
|
with open("runtime.txt", "w") as f:
|
||||||
|
f.write(runtime)
|
||||||
|
|
||||||
|
r = buildpacks.RBuildPack()
|
||||||
|
assert r.checkpoint_date == date(*expected)
|
||||||
|
|
||||||
|
|
||||||
|
def test_install_from_base(tmpdir):
|
||||||
|
# check that for R==3.4 we install from ubuntu
|
||||||
|
tmpdir.chdir()
|
||||||
|
|
||||||
|
with open("runtime.txt", "w") as f:
|
||||||
|
f.write("r-3.4-2019-01-02")
|
||||||
|
|
||||||
|
r = buildpacks.RBuildPack()
|
||||||
|
assert "r-base" in r.get_packages()
|
||||||
|
|
||||||
|
|
||||||
|
def test_install_from_ppa(tmpdir):
|
||||||
|
# check that for R>3.4 we don't install r-base from Ubuntu
|
||||||
|
tmpdir.chdir()
|
||||||
|
|
||||||
|
with open("runtime.txt", "w") as f:
|
||||||
|
f.write("r-3.5-2019-01-02")
|
||||||
|
|
||||||
|
r = buildpacks.RBuildPack()
|
||||||
|
assert "r-base" not in r.get_packages()
|
||||||
|
|
||||||
|
|
||||||
|
def test_custom_ppa(tmpdir):
|
||||||
|
tmpdir.chdir()
|
||||||
|
|
||||||
|
with open("runtime.txt", "w") as f:
|
||||||
|
f.write("r-3.5-2019-01-02")
|
||||||
|
|
||||||
|
r = buildpacks.RBuildPack()
|
||||||
|
|
||||||
|
scripts = r.get_build_scripts()
|
||||||
|
|
||||||
|
# check that at least one of the build scripts adds this new PPA
|
||||||
|
for user, script in scripts:
|
||||||
|
if "https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/" in script:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
assert False, "Should have added a new PPA"
|
||||||
|
|
||||||
|
# check that we install the right package
|
||||||
|
for user, script in scripts:
|
||||||
|
if "r-base=3.5" in script:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
assert False, "Should have installed base R"
|
Ładowanie…
Reference in New Issue