From dd3fa631ddbec82a4ab11ec6948b9f4d20490540 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 14 Sep 2020 11:55:52 +0200 Subject: [PATCH 001/103] support R 4 --- repo2docker/buildpacks/r.py | 12 +++++++++--- tests/r/r4/install.R | 1 + tests/r/r4/runtime.txt | 1 + tests/r/r4/verify | 7 +++++++ 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 tests/r/r4/install.R create mode 100644 tests/r/r4/runtime.txt create mode 100755 tests/r/r4/verify diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index 0173a1e41..d3f9c07e2 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -78,6 +78,8 @@ def r_version(self): "3.6": "3.6.1-3bionic", "3.6.0": "3.6.0-2bionic", "3.6.1": "3.6.1-3bionic", + "4.0": "4.0.2-1.1804.0", + "4.0.2": "4.0.2-1.1804.0", } # the default if nothing is specified r_version = "3.6" @@ -238,13 +240,17 @@ def get_build_scripts(self): scripts = [] # For R 3.4 we want to use the default Ubuntu package but otherwise - # we use the packages from a PPA + # we use the packages from R's own repo if V(self.r_version) >= V("3.5"): + if V(self.r_version) >= V("4"): + vs = "40" + else: + vs = "35" scripts += [ ( "root", - r""" - echo "deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran35/" > /etc/apt/sources.list.d/r3.6-ubuntu.list + rf""" + echo "deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran{vs}/" > /etc/apt/sources.list.d/r-ubuntu.list """, ), # Use port 80 to talk to the keyserver to increase the chances diff --git a/tests/r/r4/install.R b/tests/r/r4/install.R new file mode 100644 index 000000000..df6d6a837 --- /dev/null +++ b/tests/r/r4/install.R @@ -0,0 +1 @@ +install.packages("ggplot2") diff --git a/tests/r/r4/runtime.txt b/tests/r/r4/runtime.txt new file mode 100644 index 000000000..e0b263062 --- /dev/null +++ b/tests/r/r4/runtime.txt @@ -0,0 +1 @@ +r-4.0-2020-07-07 diff --git a/tests/r/r4/verify b/tests/r/r4/verify new file mode 100755 index 000000000..6d28082b2 --- /dev/null +++ b/tests/r/r4/verify @@ -0,0 +1,7 @@ +#!/usr/bin/env Rscript +library('ggplot2') + +print(version) +if (version$major != "4") { + quit("yes", 1) +} From d90ede52395a87d3cfb35b5631314059412b7dc9 Mon Sep 17 00:00:00 2001 From: Kenan Erdogan Date: Wed, 8 Jul 2020 17:45:14 +0200 Subject: [PATCH 002/103] update docs for config dirs --- docs/source/usage.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/usage.rst b/docs/source/usage.rst index 4bcfa55f5..d3ec01392 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -90,8 +90,8 @@ Where to put configuration files * A folder named ``.binder/`` in the root of the repository. * The root directory of the repository. -`repo2docker` searches for these folders in order (``binder/``, ``.binder/``, -root). Only configuration files in the first identified folder are considered. +Having both ``binder/`` and ``.binder/`` folders is not allowed. +And if one of these folders exists, configuration files in that folder are considered only. Check the complete list of :ref:`configuration files ` supported by ``repo2docker`` to see how to configure the build process. From 526c914bbba9cd99c7b04dc1595e9516144ebe86 Mon Sep 17 00:00:00 2001 From: Kenan Erdogan Date: Wed, 8 Jul 2020 18:09:02 +0200 Subject: [PATCH 003/103] Update docs/source/usage.rst Co-authored-by: Simon Li --- docs/source/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/usage.rst b/docs/source/usage.rst index d3ec01392..6643038e8 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -91,7 +91,7 @@ Where to put configuration files * The root directory of the repository. Having both ``binder/`` and ``.binder/`` folders is not allowed. -And if one of these folders exists, configuration files in that folder are considered only. +If one of these folders exists, only configuration files in that folder are considered, configuration in the root directory will be ignored. Check the complete list of :ref:`configuration files ` supported by ``repo2docker`` to see how to configure the build process. From dc559b825a42b78c6a2d8d28316dbded1de84cdb Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 14 Oct 2020 23:03:26 -0400 Subject: [PATCH 004/103] add chown to COPY commands to reduce layer count --- repo2docker/buildpacks/base.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index e3a570f01..bd1de6fe9 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -100,7 +100,7 @@ {% if build_script_files -%} # If scripts required during build are present, copy them {% for src, dst in build_script_files|dictsort %} -COPY {{ src }} {{ dst }} +COPY --chown=${NB_USER}:${NB_USER} {{ src }} {{ dst }} {% endfor -%} {% endif -%} @@ -137,24 +137,16 @@ {% if preassemble_script_files -%} # If scripts required during build are present, copy them {% for src, dst in preassemble_script_files|dictsort %} -COPY src/{{ src }} ${REPO_DIR}/{{ dst }} +COPY --chown=${NB_USER}:${NB_USER} src/{{ src }} ${REPO_DIR}/{{ dst }} {% endfor -%} {% endif -%} -{% if preassemble_script_directives -%} -USER root -RUN chown -R ${NB_USER}:${NB_USER} ${REPO_DIR} -{% endif -%} - {% for sd in preassemble_script_directives -%} {{ sd }} {% endfor %} -# Copy and chown stuff. This doubles the size of the repo, because -# you can't actually copy as USER, only as root! Thanks, Docker! -USER root -COPY src/ ${REPO_DIR} -RUN chown -R ${NB_USER}:${NB_USER} ${REPO_DIR} +# Copy stuff. +COPY --chown=${NB_USER}:${NB_USER} src/ ${REPO_DIR} # Run assemble scripts! These will actually turn the specification # in the repository into an image. From 778c914c8c6823b776c1ddfe3b36ccab473e8438 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Thu, 15 Oct 2020 08:58:12 -0400 Subject: [PATCH 005/103] use jinja var for COPY chown --- repo2docker/buildpacks/base.py | 26 +++++++++++++++++--------- repo2docker/buildpacks/docker.py | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index bd1de6fe9..bc140f8a9 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -13,10 +13,11 @@ from traitlets import Dict +# Only use syntax features supported by Docker 17.09 TEMPLATE = r""" FROM buildpack-deps:bionic -# avoid prompts from apt +# Avoid prompts from apt ENV DEBIAN_FRONTEND=noninteractive # Set up locales properly @@ -100,12 +101,12 @@ {% if build_script_files -%} # If scripts required during build are present, copy them {% for src, dst in build_script_files|dictsort %} -COPY --chown=${NB_USER}:${NB_USER} {{ src }} {{ dst }} +COPY --chown={{ user }}:{{ user }} {{ src }} {{ dst }} {% endfor -%} {% endif -%} {% for sd in build_script_directives -%} -{{sd}} +{{ sd }} {% endfor %} # Allow target path repo is cloned to be configurable @@ -137,7 +138,7 @@ {% if preassemble_script_files -%} # If scripts required during build are present, copy them {% for src, dst in preassemble_script_files|dictsort %} -COPY --chown=${NB_USER}:${NB_USER} src/{{ src }} ${REPO_DIR}/{{ dst }} +COPY --chown={{ user }}:{{ user }} src/{{ src }} ${REPO_DIR}/{{ dst }} {% endfor -%} {% endif -%} @@ -146,7 +147,7 @@ {% endfor %} # Copy stuff. -COPY --chown=${NB_USER}:${NB_USER} src/ ${REPO_DIR} +COPY --chown={{ user }}:{{ user }} src/ ${REPO_DIR} # Run assemble scripts! These will actually turn the specification # in the repository into an image. @@ -195,6 +196,9 @@ os.path.dirname(os.path.abspath(__file__)), "repo2docker-entrypoint" ) +# Also used for the group +DEFAULT_NB_UID = 1000 + class BuildPack: """ @@ -499,10 +503,12 @@ def binder_path(self, path): def detect(self): return True - def render(self): + def render(self, build_args=None): """ Render BuildPack into Dockerfile """ + build_args = build_args or {} + t = jinja2.Template(TEMPLATE) build_script_directives = [] @@ -561,6 +567,8 @@ def render(self): post_build_scripts=self.get_post_build_scripts(), start_script=self.get_start_script(), appendix=self.appendix, + # For docker 17.09 `COPY --chown`, 19.03 would allow using $NBUSER + user=build_args.get("NB_UID", DEFAULT_NB_UID), ) @staticmethod @@ -604,7 +612,7 @@ def build( tarf = io.BytesIO() tar = tarfile.open(fileobj=tarf, mode="w") dockerfile_tarinfo = tarfile.TarInfo("Dockerfile") - dockerfile = self.render().encode("utf-8") + dockerfile = self.render(build_args).encode("utf-8") dockerfile_tarinfo.size = len(dockerfile) tar.addfile(dockerfile_tarinfo, io.BytesIO(dockerfile)) @@ -616,8 +624,8 @@ def _filter_tar(tar): # https://github.com/docker/docker-py/pull/1582 is related tar.uname = "" tar.gname = "" - tar.uid = int(build_args.get("NB_UID", 1000)) - tar.gid = int(build_args.get("NB_UID", 1000)) + tar.uid = int(build_args.get("NB_UID", DEFAULT_NB_UID)) + tar.gid = int(build_args.get("NB_UID", DEFAULT_NB_UID)) return tar for src in sorted(self.get_build_script_files()): diff --git a/repo2docker/buildpacks/docker.py b/repo2docker/buildpacks/docker.py index 037a05fa3..5a0981655 100644 --- a/repo2docker/buildpacks/docker.py +++ b/repo2docker/buildpacks/docker.py @@ -14,7 +14,7 @@ def detect(self): """Check if current repo should be built with the Docker BuildPack""" return os.path.exists(self.binder_path("Dockerfile")) - def render(self): + def render(self, build_args=None): """Render the Dockerfile using by reading it from the source repo""" Dockerfile = self.binder_path("Dockerfile") with open(Dockerfile) as f: From 8f714b48be258cb23f4c344c9fa255c2dad7beed Mon Sep 17 00:00:00 2001 From: Kyungdahm Yun Date: Sun, 18 Oct 2020 00:39:49 -0700 Subject: [PATCH 006/103] Ensure REPO_DIR owned by NB_USER --- repo2docker/buildpacks/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index bc140f8a9..3cb1ff2ef 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -113,6 +113,7 @@ ARG REPO_DIR=${HOME} ENV REPO_DIR ${REPO_DIR} WORKDIR ${REPO_DIR} +RUN chown ${NB_USER}:${NB_USER} ${REPO_DIR} # We want to allow two things: # 1. If there's a .local/bin directory in the repo, things there From 81513b06d62e67100e9729629cea18176c80c176 Mon Sep 17 00:00:00 2001 From: Kyungdahm Yun Date: Mon, 19 Oct 2020 21:49:09 -0700 Subject: [PATCH 007/103] Add test for writable permission on REPO_DIR --- tests/conda/repo-path/verify | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/conda/repo-path/verify b/tests/conda/repo-path/verify index 0d9381715..93f6fed67 100755 --- a/tests/conda/repo-path/verify +++ b/tests/conda/repo-path/verify @@ -13,5 +13,8 @@ assert sys.executable.startswith("/srv/conda/"), sys.executable assert os.path.exists("/srv/repo/verify") assert os.path.abspath(__file__) == "/srv/repo/verify" +# Repo should be writable +assert os.access("/srv/repo", os.W_OK) + # We should be able to import the package in environment.yml import numpy From 19c698e8d349aa9d8f16136a1b66325c5ea419a5 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 22 Oct 2020 07:46:20 +0200 Subject: [PATCH 008/103] Remove reference to `master` branch from CLI doc --- repo2docker/__main__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/repo2docker/__main__.py b/repo2docker/__main__.py index 907460d8c..a52a734a9 100644 --- a/repo2docker/__main__.py +++ b/repo2docker/__main__.py @@ -96,7 +96,8 @@ def get_argparser(): argparser.add_argument( "--ref", help=( - "If building a git url, which reference to check out. " "E.g., `master`." + "Reference to build instead of default reference. For example" + " branch name or commit for a Git repository." ), ) From 1ebb4b5d87ec819c156e4b351ea03e7bd0b5dcae Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 22 Oct 2020 08:04:29 +0200 Subject: [PATCH 009/103] Add explicit error message when `master` is specified Provide a specific error messaeg with a hint towards solution when a user specifies `master` as ref and we can't find it. --- repo2docker/contentproviders/git.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/repo2docker/contentproviders/git.py b/repo2docker/contentproviders/git.py index e203fa6b2..5c914860f 100644 --- a/repo2docker/contentproviders/git.py +++ b/repo2docker/contentproviders/git.py @@ -47,9 +47,18 @@ def fetch(self, spec, output_dir, yield_output=False): self.log.error( "Failed to check out ref %s", ref, extra=dict(phase="failed") ) - raise ValueError("Failed to check out ref {}".format(ref)) + if ref == "master": + msg = ( + "Failed to check out the 'master' branch. " + "Maybe the default branch is not named 'master' " + "for this repository.\n\nTry not explicitly " + "specifying `--ref`." + ) + else: + msg = "Failed to check out ref {}".format(ref) + raise ValueError(msg) # We don't need to explicitly checkout things as the reset will - # take of that. If the hash is resolved above, we should be + # take care of that. If the hash is resolved above, we should be # able to reset to it for line in execute_cmd( ["git", "reset", "--hard", hash], cwd=output_dir, capture=yield_output From e4b1779999157fc47214edc2116706884b2d7650 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 22 Oct 2020 15:29:16 +0200 Subject: [PATCH 010/103] mamba 0.6.1 --- repo2docker/buildpacks/conda/install-miniforge.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo2docker/buildpacks/conda/install-miniforge.bash b/repo2docker/buildpacks/conda/install-miniforge.bash index 071b33b3a..a117d2925 100755 --- a/repo2docker/buildpacks/conda/install-miniforge.bash +++ b/repo2docker/buildpacks/conda/install-miniforge.bash @@ -6,7 +6,7 @@ set -ex cd $(dirname $0) MINIFORGE_VERSION=4.8.2-1 -MAMBA_VERSION=0.5.1 +MAMBA_VERSION=0.6.1 # SHA256 for installers can be obtained from https://github.com/conda-forge/miniforge/releases SHA256SUM="4f897e503bd0edfb277524ca5b6a5b14ad818b3198c2f07a36858b7d88c928db" From 1bb92770eb663bf823a8cfd6d03d77f4a2578e19 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 22 Oct 2020 15:30:31 +0200 Subject: [PATCH 011/103] regression test for specifying channel name in enviornment.yml dependency this didn't work with mamba 0.5 --- tests/conda/channel-dep/environment.yml | 6 ++++++ tests/conda/channel-dep/verify | 2 ++ 2 files changed, 8 insertions(+) create mode 100644 tests/conda/channel-dep/environment.yml create mode 100755 tests/conda/channel-dep/verify diff --git a/tests/conda/channel-dep/environment.yml b/tests/conda/channel-dep/environment.yml new file mode 100644 index 000000000..ce7c15048 --- /dev/null +++ b/tests/conda/channel-dep/environment.yml @@ -0,0 +1,6 @@ +name: lume-model-server-demo +dependencies: + - python=3.7 + # specify a channel name in the dependency + # mamba < 0.6 doesn't handle this right + - bokeh::jupyter_bokeh==2.0.3 diff --git a/tests/conda/channel-dep/verify b/tests/conda/channel-dep/verify new file mode 100755 index 000000000..2fd3fc0e0 --- /dev/null +++ b/tests/conda/channel-dep/verify @@ -0,0 +1,2 @@ +#!/usr/bin/env python3 +import jupyter_bokeh From 4ca664320d122b4b029397b32af1b858a374e8a3 Mon Sep 17 00:00:00 2001 From: Kyungdahm Yun Date: Tue, 10 Nov 2020 11:16:42 -0800 Subject: [PATCH 012/103] Add Julia 1.5.3 support --- repo2docker/buildpacks/julia/julia_project.py | 1 + 1 file changed, 1 insertion(+) diff --git a/repo2docker/buildpacks/julia/julia_project.py b/repo2docker/buildpacks/julia/julia_project.py index 82c28c643..0d039aae7 100644 --- a/repo2docker/buildpacks/julia/julia_project.py +++ b/repo2docker/buildpacks/julia/julia_project.py @@ -32,6 +32,7 @@ class JuliaProjectTomlBuildPack(PythonBuildPack): "1.5.0", "1.5.1", "1.5.2", + "1.5.3", ] @property From edfca79371bd75810e7f3aff4f296a1610ad4d45 Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 11 Nov 2020 10:57:04 +0100 Subject: [PATCH 013/103] Remove support for stencila Changes in stencila mean it doesn't work anymore and it's not feasible to update nbstencilaproxy Instead, show only a warning about removed support for stencila --- .github/workflows/test.yml | 1 - docs/source/architecture.md | 7 +- docs/source/config_files.rst | 19 -- docs/source/howto/user_interface.rst | 17 +- repo2docker/buildpacks/base.py | 113 +--------- repo2docker/buildpacks/r.py | 38 +--- .../py/plain-python/bibliography.bibtex | 21 -- .../stencila-py/py/plain-python/manifest.xml | 6 - tests/stencila-py/py/plain-python/py.ipynb | 195 ---------------- .../py/plain-python/py.ipynb.jats.xml | 212 ------------------ tests/stencila-py/py/verify | 5 - .../pyjp/py-jupyter/bibliography.bibtex | 21 -- .../stencila-py/pyjp/py-jupyter/manifest.xml | 6 - .../pyjp/py-jupyter/py-jupyter.ipynb | 195 ---------------- .../pyjp/py-jupyter/py-jupyter.ipynb.jats.xml | 212 ------------------ tests/stencila-py/pyjp/verify | 5 - .../stencila-r/r-markdown/bibliography.bibtex | 13 -- tests/stencila-r/r-markdown/manifest.xml | 6 - tests/stencila-r/r-markdown/rmarkdown.Rmd | 57 ----- .../r-markdown/rmarkdown.Rmd.jats.xml | 141 ------------ tests/stencila-r/verify | 6 - 21 files changed, 18 insertions(+), 1278 deletions(-) delete mode 100644 tests/stencila-py/py/plain-python/bibliography.bibtex delete mode 100644 tests/stencila-py/py/plain-python/manifest.xml delete mode 100644 tests/stencila-py/py/plain-python/py.ipynb delete mode 100644 tests/stencila-py/py/plain-python/py.ipynb.jats.xml delete mode 100755 tests/stencila-py/py/verify delete mode 100644 tests/stencila-py/pyjp/py-jupyter/bibliography.bibtex delete mode 100644 tests/stencila-py/pyjp/py-jupyter/manifest.xml delete mode 100644 tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb delete mode 100644 tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml delete mode 100755 tests/stencila-py/pyjp/verify delete mode 100644 tests/stencila-r/r-markdown/bibliography.bibtex delete mode 100644 tests/stencila-r/r-markdown/manifest.xml delete mode 100644 tests/stencila-r/r-markdown/rmarkdown.Rmd delete mode 100644 tests/stencila-r/r-markdown/rmarkdown.Rmd.jats.xml delete mode 100755 tests/stencila-r/verify diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 385d82d0f..244e66c30 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,7 +74,6 @@ jobs: - nix - pipfile - r - - stencila-py - unit - venv include: diff --git a/docs/source/architecture.md b/docs/source/architecture.md index 4786bc19a..0d056ca98 100644 --- a/docs/source/architecture.md +++ b/docs/source/architecture.md @@ -44,10 +44,9 @@ It takes the following steps to determine this: repository' by returning `True`. Usually buildpacks look for presence of specific files (`requirements.txt`, `environment.yml`, `install.R`, `manifest.xml` etc) to determine if they can handle a repository or not. Buildpacks may also look into specific files to determine specifics of the - required environment, such as the Stencila integration which extracts the required language-specific - executions contexts from an XML file (see base `BuildPack`). More than one buildpack may use such - information, as properties can be inherited (e.g. the R buildpack uses the list of required Stencila - contexts to see if R must be installed). + required environment. + More than one buildpack may use such information, + as properties can be inherited. 3. If no `BuildPack` returns true, then repo2docker will use the default `BuildPack` (defined in `Repo2Docker.default_buildpack` traitlet). diff --git a/docs/source/config_files.rst b/docs/source/config_files.rst index 249b6e19a..4b71980cf 100644 --- a/docs/source/config_files.rst +++ b/docs/source/config_files.rst @@ -149,25 +149,6 @@ You also need to have a ``runtime.txt`` file that is formatted as used for your R installation. -.. _manifest.xml: - -``manifest.xml`` - Install Stencila -=================================== - -`Stencila `_ is an open source office suite for reproducible research. -It is powered by the open file format `Dar `_. - -If your repository contains a Stencila document, repo2docker detects it based on the file ``manifest.xml``. -The required `execution contexts `_ are extracted from a Dar article (i.e. -files named ``*.jats.xml``). - -You may also have a ``runtime.txt`` and/or an ``install.R`` to manually configure your R installation. - -To see example repositories, visit our -`Stencila with R `_ and -`Stencila with Python `_ examples. - - .. _postBuild: ``postBuild`` - Run code after installing the environment diff --git a/docs/source/howto/user_interface.rst b/docs/source/howto/user_interface.rst index 7aa175739..b79c1ba6e 100644 --- a/docs/source/howto/user_interface.rst +++ b/docs/source/howto/user_interface.rst @@ -112,18 +112,7 @@ http://mybinder.org/v2/gh/binder-examples/r/master?urlpath=shiny/bus-dashboard/ Stencila ======== -The Stencila user interface is automatically enabled if a Stencila document (i.e. -a file ``manifest.xml``) is detected. Stencila will be accessible by appending -``/stencila`` to the URL, like so: +.. note:: -.. code-block:: none - - http(s):///stencila - -The editor will open the Stencila document corresponding to the last ``manifest.xml`` -found in the file tree. If you want to open a different document, you can configure -the path in the URL parameter ``archive``: - -.. code-block:: none - - http(s):///stencila/?archive=other-dir + Stencila support has been removed due to changes in stencila making it incompatible. + Please `get in touch `__ if you would like to help restore stencila support. diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 3cb1ff2ef..7b403dda6 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -311,70 +311,16 @@ def get_build_script_files(self): """ return {} - @property - def stencila_manifest_dir(self): - """Find the stencila manifest dir if it exists""" - if hasattr(self, "_stencila_manifest_dir"): - return self._stencila_manifest_dir - - # look for a manifest.xml that suggests stencila could be used - # when we find one, stencila should be installed - # and set environment variables such that - # this file is located at: - # ${STENCILA_ARCHIVE_DIR}/${STENCILA_ARCHIVE}/manifest.xml - - self._stencila_manifest_dir = None + def _check_stencila(self): + """Find the stencila manifest dir if it exists + And warn about removed stencila support + """ for root, dirs, files in os.walk("."): if "manifest.xml" in files: - self.log.debug("Found a manifest.xml at %s", root) - self._stencila_manifest_dir = root.split(os.path.sep, 1)[1] - self.log.info( - "Using stencila manifest.xml in %s", self._stencila_manifest_dir + self.log.error( + f"Found a stencila manifest.xml at {root}. Stencila is no longer supported." ) - break - return self._stencila_manifest_dir - - @property - def stencila_contexts(self): - """Find the stencila manifest contexts from file path in manifest""" - if hasattr(self, "_stencila_contexts"): - return self._stencila_contexts - - # look at the content of the documents in the manifest - # to extract the required execution contexts - self._stencila_contexts = set() - - # get paths to the article files from manifest - files = [] - if self.stencila_manifest_dir: - manifest = ET.parse( - os.path.join(self.stencila_manifest_dir, "manifest.xml") - ) - documents = manifest.findall("./documents/document") - files = [ - os.path.join(self.stencila_manifest_dir, x.get("path")) - for x in documents - ] - - else: - return self._stencila_contexts - - for filename in files: - self.log.debug("Extracting contexts from %s", filename) - - # extract code languages from file - document = ET.parse(filename) - code_chunks = document.findall('.//code[@specific-use="source"]') - languages = [x.get("language") for x in code_chunks] - self._stencila_contexts.update(languages) - - self.log.info( - "Added executions contexts, now have %s", self._stencila_contexts - ) - break - - return self._stencila_contexts def get_build_scripts(self): """ @@ -553,6 +499,9 @@ def render(self, build_args=None): for k, v in self.get_build_script_files().items() } + # check if there's a stencila manifest, support for which has been removd + self._check_stencila() + return t.render( packages=sorted(self.get_packages()), path=self.get_path(), @@ -706,23 +655,7 @@ def get_build_scripts(self): def get_env(self): """Return env directives to be set after build""" - env = [] - if self.stencila_manifest_dir: - # manifest_dir is the path containing the manifest.xml - # archive_dir is the directory containing archive directories - # (one level up) default archive is the name of the directory - # in the archive_dir such that - # ${STENCILA_ARCHIVE_DIR}/${STENCILA_ARCHIVE}/manifest.xml - # exists. - - archive_dir, archive = os.path.split(self.stencila_manifest_dir) - env.extend( - [ - ("STENCILA_ARCHIVE_DIR", "${REPO_DIR}/" + archive_dir), - ("STENCILA_ARCHIVE", archive), - ] - ) - return env + return [] def detect(self): return True @@ -765,32 +698,6 @@ def get_preassemble_scripts(self): except FileNotFoundError: pass - if "py" in self.stencila_contexts: - scripts.extend( - [ - ( - "${NB_USER}", - r""" - ${KERNEL_PYTHON_PREFIX}/bin/pip install --no-cache https://github.com/stencila/py/archive/f1260796.tar.gz && \ - ${KERNEL_PYTHON_PREFIX}/bin/python -m stencila register - """, - ) - ] - ) - if self.stencila_manifest_dir: - scripts.extend( - [ - ( - "${NB_USER}", - r""" - ${NB_PYTHON_PREFIX}/bin/pip install --no-cache nbstencilaproxy==0.1.1 && \ - jupyter serverextension enable --sys-prefix --py nbstencilaproxy && \ - jupyter nbextension install --sys-prefix --py nbstencilaproxy && \ - jupyter nbextension enable --sys-prefix --py nbstencilaproxy - """, - ) - ] - ) return scripts def get_assemble_scripts(self): diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index 0173a1e41..30ce32457 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -25,8 +25,6 @@ class RBuildPack(PythonBuildPack): 2. A `DESCRIPTION` file signaling an R package - 3. A Stencila document (*.jats.xml) with R code chunks (i.e. language="r") - If there is no `runtime.txt`, then the MRAN snapshot is set to latest date that is guaranteed to exist across timezones. @@ -37,8 +35,7 @@ class RBuildPack(PythonBuildPack): - as dependencies in a `DESCRIPTION` file - - are needed by a specific tool, for example the package `stencila` is - installed and configured if a Stencila document is given. + - are needed by a specific tool The `r-base` package from Ubuntu apt repositories is used to install R itself, rather than any of the methods from https://cran.r-project.org/. @@ -129,9 +126,7 @@ def detect(self): return True description_R = "DESCRIPTION" - if ( - not self.binder_dir and os.path.exists(description_R) - ) or "r" in self.stencila_contexts: + if not self.binder_dir and os.path.exists(description_R): if not self.checkpoint_date: # no R snapshot date set through runtime.txt # set the R runtime to the latest date that is guaranteed to @@ -226,7 +221,6 @@ def get_build_scripts(self): (determined by MRAN) - IRKernel - nbrsessionproxy (to access RStudio via Jupyter Notebook) - - stencila R package (if Stencila document with R code chunks detected) We set the snapshot date used to install R libraries from based on the contents of runtime.txt. @@ -325,34 +319,6 @@ def get_build_scripts(self): ), ] - if "r" in self.stencila_contexts: - # new versions of R require a different way of installing bioconductor - if V(self.r_version) <= V("3.5"): - scripts += [ - ( - "${NB_USER}", - # Install and register stencila library - r""" - 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 def get_preassemble_script_files(self): diff --git a/tests/stencila-py/py/plain-python/bibliography.bibtex b/tests/stencila-py/py/plain-python/bibliography.bibtex deleted file mode 100644 index 643df1dd7..000000000 --- a/tests/stencila-py/py/plain-python/bibliography.bibtex +++ /dev/null @@ -1,21 +0,0 @@ -@article{kluyver2016jupyter, - title={Jupyter Notebooks-a publishing format for reproducible computational workflows.}, - author={Kluyver, Thomas and Ragan-Kelley, Benjamin and P{\'e}rez, Fernando and Granger, Brian E and Bussonnier, Matthias and Frederic, Jonathan and Kelley, Kyle and Hamrick, Jessica B and Grout, Jason and Corlay, Sylvain and others}, - journal={ELPUB}, - pages={87--90}, - year={2016} -} -@article{ragan2014jupyter, - title={The Jupyter/IPython architecture: a unified view of computational research, from interactive exploration to communication and publication.}, - author={Ragan-Kelley, M and Perez, F and Granger, B and Kluyver, T and Ivanov, P and Frederic, J and Bussonnier, M}, - journal={AGU Fall Meeting Abstracts}, - year={2014} -} -@article{perez2015project, - title={Project Jupyter: Computational narratives as the engine of collaborative data science}, - author={Perez, Fernando and Granger, Brian E}, - journal={Retrieved September}, - volume={11}, - pages={207}, - year={2015} -} diff --git a/tests/stencila-py/py/plain-python/manifest.xml b/tests/stencila-py/py/plain-python/manifest.xml deleted file mode 100644 index ef19f7d79..000000000 --- a/tests/stencila-py/py/plain-python/manifest.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/tests/stencila-py/py/plain-python/py.ipynb b/tests/stencila-py/py/plain-python/py.ipynb deleted file mode 100644 index 2a2f18352..000000000 --- a/tests/stencila-py/py/plain-python/py.ipynb +++ /dev/null @@ -1,195 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Introduction" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Jupyter notebooks ([@perez2015project; @kluyver2016jupyter; @ragan2014jupyter]) are one of the most popular platforms for doing reproducible research. Stencila supports importing of Jupyter Notebook `.ipynb` files. This allows you to work with collegues to refine a document for final publication while still retaining the code cells, and thus reprodubility of your the work. In the future we also plan to support exporting to `.ipynb` files. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Markdown cells\n", - "\n", - "Most standard Markdown should be supported by the importer including inline `code`, headings etc (although the Stencila user interface do not currently support rendering of some elements e.g. math and lists).\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Code cells\n", - "\n", - "Code cells in notebooks are imported without loss. Stencila's user interface currently differs from Jupyter in that code cells are executed on update while you are typing. This produces a very reactive user experience but is inappropriate for more compute intensive, longer running code cells. We are currently working on improving this to allowing users to decide to execute cells explicitly (e.g. using `Ctrl+Enter`)." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Hello this is Python 3.5 and it is Tue Feb 13 10:56:10 2018'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import sys\n", - "import time\n", - "'Hello this is Python %s.%s and it is %s' % (sys.version_info[0], sys.version_info[1], time.strftime('%c'))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Stencila also support Jupyter code cells that produce plots. The cell below produces a simple plot based on the example from [the Matplotlib website](https://matplotlib.org/examples/shapes_and_collections/scatter_demo.html). Try changing the code below (for example, the variable `N`)." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "N = 50\n", - "N = min(N, 1000) # Prevent generation of too many numbers :)\n", - "x = np.random.rand(N)\n", - "y = np.random.rand(N)\n", - "colors = np.random.rand(N)\n", - "area = np.pi * (15 * np.random.rand(N))**2 # 0 to 15 point radii\n", - "\n", - "plt.scatter(x, y, s=area, c=colors, alpha=0.5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We are currently working on supporting [Jupyter's magic commands](http://ipython.readthedocs.io/en/stable/interactive/magics.html) in Stencila via a bridge to Jupyter kernels." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Metadata\n", - "\n", - "To add some metadata about the document (such as authors, title, abstract and so on), In Jupyter, select `Edit -> Edit Notebook metadata` from the top menu. Add the title and abstract as JSON strings and authors and organisations metadata as [JSON arrays](https://www.w3schools.com/js/js_json_arrays.asp). Author `affiliation` identifiers (like `university-of-earth` below) must be unique and preferably use only lowercase characters and no spaces.\n", - " \n", - "For example," - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```\n", - " \"authors\": [\n", - " {\n", - " \"given-names\": \"Your first name goes here\",\n", - " \"surname\": \"Your last name goes here\",\n", - " \"email\": \"your.email@your-organisation\",\n", - " \"corresponding\": \"yes / no\",\n", - " \"affiliation\": \"university-of-earth\"\n", - " }\n", - " ],\n", - " \n", - " \"organisations\": [ \n", - " {\n", - " \"university-of-earth\": {\n", - " \"institution\": \"Your organisation name\",\n", - " \"city\": \"Your city\",\n", - " \"country\": \"Your country\" \n", - " }\n", - " ],\n", - "\n", - " \"title\": \"Your title goes here\",\n", - " \"abstract\": \"This is a paper about lots of different interesting things\",\n", - " \n", - " ```\n", - " \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Citations and references \n", - "\n", - "Stencila supports Pandoc style citations and reference lists within Jupyter notebook Markdown cells. Add a `bibliography` entry to the notebook's metadata which points to a file containing your list of references e.g.\n", - "\n", - "```json\n", - "\"bibliography\": \"my-bibliography.bibtex\"\n", - "```\n", - "\n", - "Then, within Markdown cells, you can insert citations inside square brackets and separated by semicolons. Each citation is represented using the `@` symbol followed by the citation identifier from the bibliography database e.g.\n", - "\n", - "```json\n", - "[@perez2015project; @kluyver2016jupyter]\n", - "```\n", - "\n", - "The [cite2c](https://github.com/takluyver/cite2c) Jupyter extension allows for easier, \"cite-while-you-write\" insertion of citations from a Zotero library. We're hoping to support conversion of cite2cstyle citations/references in the [future](https://github.com/stencila/convert/issues/14).\n" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "authors": [ - { - "given-names": "Aleksandra", - "surname": "Pawlik" - } - ], - "bibliography": "bibliography.bibtex", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.2" - }, - "title": "Jupyter and Stencila" - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tests/stencila-py/py/plain-python/py.ipynb.jats.xml b/tests/stencila-py/py/plain-python/py.ipynb.jats.xml deleted file mode 100644 index 7e6e1d10c..000000000 --- a/tests/stencila-py/py/plain-python/py.ipynb.jats.xml +++ /dev/null @@ -1,212 +0,0 @@ - - -
- - - - Jupyter and Stencila - - - - - Pawlik - Aleksandra - - - - -

An example of a Jupyter notebook converted into a JATS document for editing in Stencila.

-
-
-
- - - - Introduction -

Jupyter notebooks (13) are one of the most popular platforms for doing reproducible research. Stencila supports importing of Jupyter Notebook .ipynb files. This allows you to work with collegues to refine a document for final publication while still retaining the code cells, and thus reprodubility of your the work. In the future we also plan to support exporting to .ipynb files.

-
- - Markdown cells -

Most standard Markdown should be supported by the importer including inline code, headings etc (although the Stencila user interface do not currently support rendering of some elements e.g. math and lists).

-
- - Code cells -

Code cells in notebooks are imported without loss. Stencila’s user interface currently differs from Jupyter in that code cells are executed on update while you are typing. This produces a very reactive user experience but is inappropriate for more compute intensive, longer running code cells. We are currently working on improving this to allowing users to decide to execute cells explicitly (e.g. using Ctrl+Enter).

- - import sys -import time -'Hello this is Python %s.%s and it is %s' % (sys.version_info[0], sys.version_info[1], time.strftime('%c')) - {} - - - -

Stencila also support Jupyter code cells that produce plots. The cell below produces a simple plot based on the example from the Matplotlib website. Try changing the code below (for example, the variable N).

- - import numpy as np -import matplotlib.pyplot as plt - -N = 50 -N = min(N, 1000) # Prevent generation of too many numbers :) -x = np.random.rand(N) -y = np.random.rand(N) -colors = np.random.rand(N) -area = np.pi * (15 * np.random.rand(N))**2 # 0 to 15 point radii - -plt.scatter(x, y, s=area, c=colors, alpha=0.5) -plt.show() - {} - - - -

We are currently working on supporting Jupyter’s magic commands in Stencila via a bridge to Jupyter kernels.

-
- - Metadata -

To add some metadata about the document (such as authors, title, abstract and so on), In Jupyter, select Edit -> Edit Notebook metadata from the top menu. Add the title and abstract as JSON strings and authors and organisations metadata as JSON arrays. Author affiliation identifiers (like university-of-earth below) must be unique and preferably use only lowercase characters and no spaces.

-

For example,

- "authors": [ - { - "given-names": "Your first name goes here", - "surname": "Your last name goes here", - "email": "your.email@your-organisation", - "corresponding": "yes / no", - "affiliation": "university-of-earth" - } - ], - - "organisations": [ - { - "university-of-earth": { - "institution": "Your organisation name", - "city": "Your city", - "country": "Your country" - } - ], - - "title": "Your title goes here", - "abstract": "This is a paper about lots of different interesting things", - -
- - Citations and references -

Stencila supports Pandoc style citations and reference lists within Jupyter notebook Markdown cells. Add a bibliography entry to the notebook’s metadata which points to a file containing your list of references e.g.

- "bibliography": "my-bibliography.bibtex" -

Then, within Markdown cells, you can insert citations inside square brackets and separated by semicolons. Each citation is represented using the @ symbol followed by the citation identifier from the bibliography database e.g.

- [@perez2015project; @kluyver2016jupyter] -

The cite2c Jupyter extension allows for easier, “cite-while-you-write” insertion of citations from a Zotero library. We’re hoping to support conversion of cite2cstyle citations/references in the future.

-
- - - - - - - - Perez - Fernando - - - Granger - Brian E - - - Project jupyter: Computational narratives as the engine of collaborative data science - Retrieved September - 2015 - 11 - 207 - - - - - - - Kluyver - Thomas - - - Ragan-Kelley - Benjamin - - - Pérez - Fernando - - - Granger - Brian E - - - Bussonnier - Matthias - - - Frederic - Jonathan - - - Kelley - Kyle - - - Hamrick - Jessica B - - - Grout - Jason - - - Corlay - Sylvain - - - Others - - - Jupyter notebooks-a publishing format for reproducible computational workflows. - ELPUB - 2016 - 87 - - - - - - - Ragan-Kelley - M - - - Perez - F - - - Granger - B - - - Kluyver - T - - - Ivanov - P - - - Frederic - J - - - Bussonnier - M - - - The jupyter/ipython architecture: A unified view of computational research, from interactive exploration to communication and publication. - AGU Fall Meeting Abstracts - 2014 - - - - -
\ No newline at end of file diff --git a/tests/stencila-py/py/verify b/tests/stencila-py/py/verify deleted file mode 100755 index 788ef9da7..000000000 --- a/tests/stencila-py/py/verify +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -jupyter serverextension list 2>&1 | grep nbstencilaproxy -jupyter nbextension list 2>&1 | grep nbstencilaproxy -python3 -c "import stencila" diff --git a/tests/stencila-py/pyjp/py-jupyter/bibliography.bibtex b/tests/stencila-py/pyjp/py-jupyter/bibliography.bibtex deleted file mode 100644 index 643df1dd7..000000000 --- a/tests/stencila-py/pyjp/py-jupyter/bibliography.bibtex +++ /dev/null @@ -1,21 +0,0 @@ -@article{kluyver2016jupyter, - title={Jupyter Notebooks-a publishing format for reproducible computational workflows.}, - author={Kluyver, Thomas and Ragan-Kelley, Benjamin and P{\'e}rez, Fernando and Granger, Brian E and Bussonnier, Matthias and Frederic, Jonathan and Kelley, Kyle and Hamrick, Jessica B and Grout, Jason and Corlay, Sylvain and others}, - journal={ELPUB}, - pages={87--90}, - year={2016} -} -@article{ragan2014jupyter, - title={The Jupyter/IPython architecture: a unified view of computational research, from interactive exploration to communication and publication.}, - author={Ragan-Kelley, M and Perez, F and Granger, B and Kluyver, T and Ivanov, P and Frederic, J and Bussonnier, M}, - journal={AGU Fall Meeting Abstracts}, - year={2014} -} -@article{perez2015project, - title={Project Jupyter: Computational narratives as the engine of collaborative data science}, - author={Perez, Fernando and Granger, Brian E}, - journal={Retrieved September}, - volume={11}, - pages={207}, - year={2015} -} diff --git a/tests/stencila-py/pyjp/py-jupyter/manifest.xml b/tests/stencila-py/pyjp/py-jupyter/manifest.xml deleted file mode 100644 index 5bfbc6f8e..000000000 --- a/tests/stencila-py/pyjp/py-jupyter/manifest.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb b/tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb deleted file mode 100644 index 2a2f18352..000000000 --- a/tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb +++ /dev/null @@ -1,195 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Introduction" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Jupyter notebooks ([@perez2015project; @kluyver2016jupyter; @ragan2014jupyter]) are one of the most popular platforms for doing reproducible research. Stencila supports importing of Jupyter Notebook `.ipynb` files. This allows you to work with collegues to refine a document for final publication while still retaining the code cells, and thus reprodubility of your the work. In the future we also plan to support exporting to `.ipynb` files. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Markdown cells\n", - "\n", - "Most standard Markdown should be supported by the importer including inline `code`, headings etc (although the Stencila user interface do not currently support rendering of some elements e.g. math and lists).\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Code cells\n", - "\n", - "Code cells in notebooks are imported without loss. Stencila's user interface currently differs from Jupyter in that code cells are executed on update while you are typing. This produces a very reactive user experience but is inappropriate for more compute intensive, longer running code cells. We are currently working on improving this to allowing users to decide to execute cells explicitly (e.g. using `Ctrl+Enter`)." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Hello this is Python 3.5 and it is Tue Feb 13 10:56:10 2018'" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import sys\n", - "import time\n", - "'Hello this is Python %s.%s and it is %s' % (sys.version_info[0], sys.version_info[1], time.strftime('%c'))\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Stencila also support Jupyter code cells that produce plots. The cell below produces a simple plot based on the example from [the Matplotlib website](https://matplotlib.org/examples/shapes_and_collections/scatter_demo.html). Try changing the code below (for example, the variable `N`)." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "N = 50\n", - "N = min(N, 1000) # Prevent generation of too many numbers :)\n", - "x = np.random.rand(N)\n", - "y = np.random.rand(N)\n", - "colors = np.random.rand(N)\n", - "area = np.pi * (15 * np.random.rand(N))**2 # 0 to 15 point radii\n", - "\n", - "plt.scatter(x, y, s=area, c=colors, alpha=0.5)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We are currently working on supporting [Jupyter's magic commands](http://ipython.readthedocs.io/en/stable/interactive/magics.html) in Stencila via a bridge to Jupyter kernels." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Metadata\n", - "\n", - "To add some metadata about the document (such as authors, title, abstract and so on), In Jupyter, select `Edit -> Edit Notebook metadata` from the top menu. Add the title and abstract as JSON strings and authors and organisations metadata as [JSON arrays](https://www.w3schools.com/js/js_json_arrays.asp). Author `affiliation` identifiers (like `university-of-earth` below) must be unique and preferably use only lowercase characters and no spaces.\n", - " \n", - "For example," - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```\n", - " \"authors\": [\n", - " {\n", - " \"given-names\": \"Your first name goes here\",\n", - " \"surname\": \"Your last name goes here\",\n", - " \"email\": \"your.email@your-organisation\",\n", - " \"corresponding\": \"yes / no\",\n", - " \"affiliation\": \"university-of-earth\"\n", - " }\n", - " ],\n", - " \n", - " \"organisations\": [ \n", - " {\n", - " \"university-of-earth\": {\n", - " \"institution\": \"Your organisation name\",\n", - " \"city\": \"Your city\",\n", - " \"country\": \"Your country\" \n", - " }\n", - " ],\n", - "\n", - " \"title\": \"Your title goes here\",\n", - " \"abstract\": \"This is a paper about lots of different interesting things\",\n", - " \n", - " ```\n", - " \n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Citations and references \n", - "\n", - "Stencila supports Pandoc style citations and reference lists within Jupyter notebook Markdown cells. Add a `bibliography` entry to the notebook's metadata which points to a file containing your list of references e.g.\n", - "\n", - "```json\n", - "\"bibliography\": \"my-bibliography.bibtex\"\n", - "```\n", - "\n", - "Then, within Markdown cells, you can insert citations inside square brackets and separated by semicolons. Each citation is represented using the `@` symbol followed by the citation identifier from the bibliography database e.g.\n", - "\n", - "```json\n", - "[@perez2015project; @kluyver2016jupyter]\n", - "```\n", - "\n", - "The [cite2c](https://github.com/takluyver/cite2c) Jupyter extension allows for easier, \"cite-while-you-write\" insertion of citations from a Zotero library. We're hoping to support conversion of cite2cstyle citations/references in the [future](https://github.com/stencila/convert/issues/14).\n" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "authors": [ - { - "given-names": "Aleksandra", - "surname": "Pawlik" - } - ], - "bibliography": "bibliography.bibtex", - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.5.2" - }, - "title": "Jupyter and Stencila" - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml b/tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml deleted file mode 100644 index 38eac147e..000000000 --- a/tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml +++ /dev/null @@ -1,212 +0,0 @@ - - -
- - - - Jupyter and Stencila - - - - - Pawlik - Aleksandra - - - - -

An example of a Jupyter notebook converted into a JATS document for editing in Stencila.

-
-
-
- - - - Introduction -

Jupyter notebooks (13) are one of the most popular platforms for doing reproducible research. Stencila supports importing of Jupyter Notebook .ipynb files. This allows you to work with collegues to refine a document for final publication while still retaining the code cells, and thus reprodubility of your the work. In the future we also plan to support exporting to .ipynb files.

-
- - Markdown cells -

Most standard Markdown should be supported by the importer including inline code, headings etc (although the Stencila user interface do not currently support rendering of some elements e.g. math and lists).

-
- - Code cells -

Code cells in notebooks are imported without loss. Stencila’s user interface currently differs from Jupyter in that code cells are executed on update while you are typing. This produces a very reactive user experience but is inappropriate for more compute intensive, longer running code cells. We are currently working on improving this to allowing users to decide to execute cells explicitly (e.g. using Ctrl+Enter).

- - import sys -import time -'Hello this is Python %s.%s and it is %s' % (sys.version_info[0], sys.version_info[1], time.strftime('%c')) - {} - - - -

Stencila also support Jupyter code cells that produce plots. The cell below produces a simple plot based on the example from the Matplotlib website. Try changing the code below (for example, the variable N).

- - import numpy as np -import matplotlib.pyplot as plt - -N = 50 -N = min(N, 1000) # Prevent generation of too many numbers :) -x = np.random.rand(N) -y = np.random.rand(N) -colors = np.random.rand(N) -area = np.pi * (15 * np.random.rand(N))**2 # 0 to 15 point radii - -plt.scatter(x, y, s=area, c=colors, alpha=0.5) -plt.show() - {} - - - -

We are currently working on supporting Jupyter’s magic commands in Stencila via a bridge to Jupyter kernels.

-
- - Metadata -

To add some metadata about the document (such as authors, title, abstract and so on), In Jupyter, select Edit -> Edit Notebook metadata from the top menu. Add the title and abstract as JSON strings and authors and organisations metadata as JSON arrays. Author affiliation identifiers (like university-of-earth below) must be unique and preferably use only lowercase characters and no spaces.

-

For example,

- "authors": [ - { - "given-names": "Your first name goes here", - "surname": "Your last name goes here", - "email": "your.email@your-organisation", - "corresponding": "yes / no", - "affiliation": "university-of-earth" - } - ], - - "organisations": [ - { - "university-of-earth": { - "institution": "Your organisation name", - "city": "Your city", - "country": "Your country" - } - ], - - "title": "Your title goes here", - "abstract": "This is a paper about lots of different interesting things", - -
- - Citations and references -

Stencila supports Pandoc style citations and reference lists within Jupyter notebook Markdown cells. Add a bibliography entry to the notebook’s metadata which points to a file containing your list of references e.g.

- "bibliography": "my-bibliography.bibtex" -

Then, within Markdown cells, you can insert citations inside square brackets and separated by semicolons. Each citation is represented using the @ symbol followed by the citation identifier from the bibliography database e.g.

- [@perez2015project; @kluyver2016jupyter] -

The cite2c Jupyter extension allows for easier, “cite-while-you-write” insertion of citations from a Zotero library. We’re hoping to support conversion of cite2cstyle citations/references in the future.

-
- - - - - - - - Perez - Fernando - - - Granger - Brian E - - - Project jupyter: Computational narratives as the engine of collaborative data science - Retrieved September - 2015 - 11 - 207 - - - - - - - Kluyver - Thomas - - - Ragan-Kelley - Benjamin - - - Pérez - Fernando - - - Granger - Brian E - - - Bussonnier - Matthias - - - Frederic - Jonathan - - - Kelley - Kyle - - - Hamrick - Jessica B - - - Grout - Jason - - - Corlay - Sylvain - - - Others - - - Jupyter notebooks-a publishing format for reproducible computational workflows. - ELPUB - 2016 - 87 - - - - - - - Ragan-Kelley - M - - - Perez - F - - - Granger - B - - - Kluyver - T - - - Ivanov - P - - - Frederic - J - - - Bussonnier - M - - - The jupyter/ipython architecture: A unified view of computational research, from interactive exploration to communication and publication. - AGU Fall Meeting Abstracts - 2014 - - - - -
\ No newline at end of file diff --git a/tests/stencila-py/pyjp/verify b/tests/stencila-py/pyjp/verify deleted file mode 100755 index 218e75f42..000000000 --- a/tests/stencila-py/pyjp/verify +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -jupyter serverextension list 2>&1 | grep nbstencilaproxy -jupyter nbextension list 2>&1 | grep nbstencilaproxy -python3 -c "import stencila" 2>&1 | grep ModuleNotFoundError diff --git a/tests/stencila-r/r-markdown/bibliography.bibtex b/tests/stencila-r/r-markdown/bibliography.bibtex deleted file mode 100644 index bae49c051..000000000 --- a/tests/stencila-r/r-markdown/bibliography.bibtex +++ /dev/null @@ -1,13 +0,0 @@ -@article{baumer2014r, - title={R Markdown: Integrating a reproducible analysis tool into introductory statistics}, - author={Baumer, Ben and Cetinkaya-Rundel, Mine and Bray, Andrew and Loi, Linda and Horton, Nicholas J}, - journal={arXiv preprint arXiv:1402.1894}, - year={2014} -} - -@book{xie2016bookdown, - title={Bookdown: Authoring Books and Technical Documents with R Markdown}, - author={Xie, Yihui}, - year={2016}, - publisher={CRC Press} -} diff --git a/tests/stencila-r/r-markdown/manifest.xml b/tests/stencila-r/r-markdown/manifest.xml deleted file mode 100644 index ac9d29422..000000000 --- a/tests/stencila-r/r-markdown/manifest.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/tests/stencila-r/r-markdown/rmarkdown.Rmd b/tests/stencila-r/r-markdown/rmarkdown.Rmd deleted file mode 100644 index fb9f13bb6..000000000 --- a/tests/stencila-r/r-markdown/rmarkdown.Rmd +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: RMarkdown and Stencila -author: - - surname: Bentley - given-names: Nokome - affiliation: stencila - - surname: Pawlik - given-names: Aleksandra - - surname: Aufrieter - given-names: Michael - affiliation: substance - - surname: Buchtala - given-names: Oliver - affiliation: substance -organisations: - - id: stencila - name: Stencila - city: Kaikoura - country: New Zealand - - id: substance - name: Substance GmbH - city: Linz - country: Austria -abstract: | - Stencila currently supports import of RMarkdown documents. This allows use cases like allowing collaborators to work on the same document using a WYSIWYG editing environment, or for you to put the final touches on a paper before final submission to a journal. -bibliography: bibliography.bibtex ---- - -# Introduction - -[RMarkdown](https://rmarkdown.rstudio.com/) is a popular format for reproducible research using the R programming language ([@baumer2014r],[@xie2016bookdown]). Stencila is a able to import RMarkdown documents. For example, the source for this example document is available on [Github](https://github.com/stencila/stencila/blob/more-examples/data/r-markdown/rmarkdown.Rmd). Eventually, we'll also support exporting to RMarkdown, allowing WYSIWYG editing of RMarkdown documents in Stencila. - -# Code chunks - -In RMarkdown, code "chunks" are written using "fenced" code blocks. Stencila converts these code "chunks" into Stencila code "cells" like this one: - -```{r} -n <- 500 -``` - -Stencila cells behave like functions having zero or more named `inputs` (like function arguments) and an optional `output` (like a assigning a function's return value). A cell's inputs and outputs are determined by analyzing the cell's code. The cell above has no inputs but produces an output variable named `n`. Stencila's execution engine uses this information to make code cells reactive. For instance, the cell below has `n` as an input i.e it is dependent on the first cell. So when you update the `n` in the cell above, the following plot will update. - -```{r} -s <- min(1000, n) -x <- runif(s) -y <- x + runif(s) -z <- y + rnorm(s) -plot(x, y, cex=z*2, col=rainbow(length(z), alpha=0.5)[rank(x+z)], pch=16) -``` - -# Figures - -In RMarkdown, code chunks can have various options. A common use for options is to set the caption and dimensions of figures. Stencila converts code chunks with the `fig.cap` option into JATS `` elements with a ``. This allows the user to edit the figure caption and for automatic figure numbering and referencing (although that is not working 100% right now). Other options are placed in a comment at the top of the cell so that they are preserved (and eventually will be used to apply those options within the R execution context). - -```{r fig.width=7,fig.height=6,fig.cap="Figure title"} -hist(z, breaks=40, col=hsv(0.6, 0.9, 1), xlab="Value", main="") -``` diff --git a/tests/stencila-r/r-markdown/rmarkdown.Rmd.jats.xml b/tests/stencila-r/r-markdown/rmarkdown.Rmd.jats.xml deleted file mode 100644 index c7fc8e0e6..000000000 --- a/tests/stencila-r/r-markdown/rmarkdown.Rmd.jats.xml +++ /dev/null @@ -1,141 +0,0 @@ - - -
- - - - RMarkdown and Stencila - - - - - Bentley - Nokome - - - - - - Pawlik - Aleksandra - - - - - Aufrieter - Michael - - - - - - Buchtala - Oliver - - - - - - Stencila - Kaikoura - New Zealand - - - Substance GmbH - Linz - Austria - - -

Stencila currently supports import of RMarkdown documents. This allows use cases like allowing collaborators to work on the same document using a WYSIWYG editing environment, or for you to put the final touches on a paper before final submission to a journal.

-
-
-
- - - - Introduction -

RMarkdown is a popular format for reproducible research using the R programming language (1,2). Stencila is a able to import RMarkdown documents. For example, the source for this example document is available on Github. Eventually, we’ll also support exporting to RMarkdown, allowing WYSIWYG editing of RMarkdown documents in Stencila.

-
- - Code chunks -

In RMarkdown, code “chunks” are written using “fenced” code blocks. Stencila converts these code “chunks” into Stencila code “cells” like this one:

- - n <- 500 - {} - - - -

Stencila cells behave like functions having zero or more named inputs (like function arguments) and an optional output (like a assigning a function’s return value). A cell’s inputs and outputs are determined by analyzing the cell’s code. The cell above has no inputs but produces an output variable named n. Stencila’s execution engine uses this information to make code cells reactive. For instance, the cell below has n as an input i.e it is dependent on the first cell. So when you update the n in the cell above, the following plot will update.

- - s <- min(1000, n) -x <- runif(s) -y <- x + runif(s) -z <- y + rnorm(s) -plot(x, y, cex=z*2, col=rainbow(length(z), alpha=0.5)[rank(x+z)], pch=16) - {} - - - -
- - Figures -

In RMarkdown, code chunks can have various options. A common use for options is to set the caption and dimensions of figures. Stencila converts code chunks with the fig.cap option into JATS <fig fig-type="repro-fig"> elements with a <caption>. This allows the user to edit the figure caption and for automatic figure numbering and referencing (although that is not working 100% right now). Other options are placed in a comment at the top of the cell so that they are preserved (and eventually will be used to apply those options within the R execution context).

- - - Figure title - - - #: fig.width=7,fig.height=6 -hist(z, breaks=40, col=hsv(0.6, 0.9, 1), xlab="Value", main="") - {} - - -
- - - - - - - - Baumer - Ben - - - Cetinkaya-Rundel - Mine - - - Bray - Andrew - - - Loi - Linda - - - Horton - Nicholas J - - - R markdown: Integrating a reproducible analysis tool into introductory statistics - arXiv preprint arXiv:1402.1894 - 2014 - - - - - - - Xie - Yihui - - - Bookdown: Authoring books and technical documents with r markdown - CRC Press - 2016 - - - - -
\ No newline at end of file diff --git a/tests/stencila-r/verify b/tests/stencila-r/verify deleted file mode 100755 index 33db4a89b..000000000 --- a/tests/stencila-r/verify +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -jupyter serverextension list 2>&1 | grep nbstencilaproxy -jupyter nbextension list 2>&1 | grep nbstencilaproxy -python3 -c "import stencila" 2>&1 | grep ModuleNotFoundError -R -e "library('stencila');" From 53a9071e5e96f4cdaa67cf924a5e970d420ad5a4 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Fri, 6 Nov 2020 19:23:49 +0100 Subject: [PATCH 014/103] Update to node 14 --- repo2docker/buildpacks/base.py | 4 ++-- tests/base/node/README.md | 1 + tests/base/{node10 => node}/verify | 2 +- tests/base/node10/README.md | 1 - 4 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 tests/base/node/README.md rename tests/base/{node10 => node}/verify (84%) delete mode 100644 tests/base/node10/README.md diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 7b403dda6..bdb4290cb 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -57,8 +57,8 @@ RUN wget --quiet -O - https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \ DISTRO="bionic" && \ - echo "deb https://deb.nodesource.com/node_10.x $DISTRO main" >> /etc/apt/sources.list.d/nodesource.list && \ - echo "deb-src https://deb.nodesource.com/node_10.x $DISTRO main" >> /etc/apt/sources.list.d/nodesource.list + echo "deb https://deb.nodesource.com/node_14.x $DISTRO main" >> /etc/apt/sources.list.d/nodesource.list && \ + echo "deb-src https://deb.nodesource.com/node_14.x $DISTRO main" >> /etc/apt/sources.list.d/nodesource.list # Base package installs are not super interesting to users, so hide their outputs # If install fails for some reason, errors will still be printed diff --git a/tests/base/node/README.md b/tests/base/node/README.md new file mode 100644 index 000000000..9b21a5000 --- /dev/null +++ b/tests/base/node/README.md @@ -0,0 +1 @@ +Test that node 12 and npm 6 are installed and runnable. diff --git a/tests/base/node10/verify b/tests/base/node/verify similarity index 84% rename from tests/base/node10/verify rename to tests/base/node/verify index 46d877597..cfb35b403 100755 --- a/tests/base/node10/verify +++ b/tests/base/node/verify @@ -5,7 +5,7 @@ set -ex # check node system package and its version which node node --version -node --version | grep v10 +node --version | grep v14 which npm npm --version diff --git a/tests/base/node10/README.md b/tests/base/node10/README.md deleted file mode 100644 index a68e33a70..000000000 --- a/tests/base/node10/README.md +++ /dev/null @@ -1 +0,0 @@ -Test that node 10 and npm 6 are installed and runnable. From 3dd2b8652d392e99374452ed27b0ddb77fd6ec40 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Wed, 25 Nov 2020 11:29:20 +0100 Subject: [PATCH 015/103] Stream jupyter server logs to a file By streaming the logs also to a file users can access them from inside the container which helps them debug issues without having to ask an admin. --- repo2docker/buildpacks/repo2docker-entrypoint | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/repo2docker/buildpacks/repo2docker-entrypoint b/repo2docker/buildpacks/repo2docker-entrypoint index a9b08a5ca..9c2d0fc62 100755 --- a/repo2docker/buildpacks/repo2docker-entrypoint +++ b/repo2docker/buildpacks/repo2docker-entrypoint @@ -2,11 +2,19 @@ # lightest possible entrypoint that ensures that # we use a login shell to get a fully configured shell environment # (e.g. sourcing /etc/profile.d, ~/.bashrc, and friends) + +# Setup a file descriptor (FD) that is connected to a tee process which +# writes its input to $REPO_DIR/.jupyter-server-log.txt +# We later use this FD as a place to redirect the output of the actual +# command to. We can't add `tee` to the command directly as that will prevent +# the container from exiting when `docker stop` is run. +exec {log_fd}> >(exec tee $REPO_DIR/.jupyter-server-log.txt) + if [[ ! -z "${R2D_ENTRYPOINT:-}" ]]; then if [[ ! -x "$R2D_ENTRYPOINT" ]]; then chmod u+x "$R2D_ENTRYPOINT" fi - exec "$R2D_ENTRYPOINT" "$@" + exec "$R2D_ENTRYPOINT" "$@" >&"$log_fd" 2>&1 else - exec "$@" + exec "$@" >&"$log_fd" 2>&1 fi From 957571f7398be185d3da873698a829f1f6a11cbf Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 26 Nov 2020 07:27:06 +0100 Subject: [PATCH 016/103] Add StackOverflow link to comment Co-authored-by: Simon Li --- repo2docker/buildpacks/repo2docker-entrypoint | 1 + 1 file changed, 1 insertion(+) diff --git a/repo2docker/buildpacks/repo2docker-entrypoint b/repo2docker/buildpacks/repo2docker-entrypoint index 9c2d0fc62..ffc37fc0e 100755 --- a/repo2docker/buildpacks/repo2docker-entrypoint +++ b/repo2docker/buildpacks/repo2docker-entrypoint @@ -8,6 +8,7 @@ # We later use this FD as a place to redirect the output of the actual # command to. We can't add `tee` to the command directly as that will prevent # the container from exiting when `docker stop` is run. +# See https://stackoverflow.com/a/55678435 exec {log_fd}> >(exec tee $REPO_DIR/.jupyter-server-log.txt) if [[ ! -z "${R2D_ENTRYPOINT:-}" ]]; then From 044e112baecb8cec693ab87b0cfce22f130eea6b Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 26 Nov 2020 12:58:33 +0100 Subject: [PATCH 017/103] Explicitly close log output --- repo2docker/buildpacks/repo2docker-entrypoint | 3 +++ 1 file changed, 3 insertions(+) diff --git a/repo2docker/buildpacks/repo2docker-entrypoint b/repo2docker/buildpacks/repo2docker-entrypoint index ffc37fc0e..be263e25b 100755 --- a/repo2docker/buildpacks/repo2docker-entrypoint +++ b/repo2docker/buildpacks/repo2docker-entrypoint @@ -19,3 +19,6 @@ if [[ ! -z "${R2D_ENTRYPOINT:-}" ]]; then else exec "$@" >&"$log_fd" 2>&1 fi + +# Close the loggging output aggain +exec {log_fd}>&- From f2ed09bdfb1c760156e385ba046f6ade3753aed0 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 26 Nov 2020 14:53:31 +0100 Subject: [PATCH 018/103] Ensure output arrives on stderr --- repo2docker/buildpacks/repo2docker-entrypoint | 8 ++++---- tests/unit/test_env.py | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/repo2docker/buildpacks/repo2docker-entrypoint b/repo2docker/buildpacks/repo2docker-entrypoint index be263e25b..a1a2cac35 100755 --- a/repo2docker/buildpacks/repo2docker-entrypoint +++ b/repo2docker/buildpacks/repo2docker-entrypoint @@ -15,10 +15,10 @@ if [[ ! -z "${R2D_ENTRYPOINT:-}" ]]; then if [[ ! -x "$R2D_ENTRYPOINT" ]]; then chmod u+x "$R2D_ENTRYPOINT" fi - exec "$R2D_ENTRYPOINT" "$@" >&"$log_fd" 2>&1 + exec "$R2D_ENTRYPOINT" "$@" >&"$log_fd" else - exec "$@" >&"$log_fd" 2>&1 + exec "$@" >&"$log_fd" fi -# Close the loggging output aggain -exec {log_fd}>&- +# Close the logging output again +#exec {log_fd}>&- diff --git a/tests/unit/test_env.py b/tests/unit/test_env.py index 308f7e43f..d7de5fe67 100644 --- a/tests/unit/test_env.py +++ b/tests/unit/test_env.py @@ -58,6 +58,10 @@ def test_env(): # all docker output is returned by repo2docker on stderr # extract just the declare for better failure message formatting + # stdout should be empty + assert not result.stdout + + # stderr should contain lines of output declares = [x for x in result.stderr.split("\n") if x.startswith("declare")] assert 'declare -x FOO="{}"'.format(ts) in declares assert 'declare -x BAR="baz"' in declares From 6c1a2722b980c4f8ba43fd71bdf7278b364bddc0 Mon Sep 17 00:00:00 2001 From: Sylvain Corlay Date: Sun, 6 Dec 2020 08:33:48 +0100 Subject: [PATCH 019/103] Use mambaforge instead of miniforge --- repo2docker/buildpacks/conda/install-miniforge.bash | 12 +++--------- tests/conda/binder-dir/verify | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/repo2docker/buildpacks/conda/install-miniforge.bash b/repo2docker/buildpacks/conda/install-miniforge.bash index a117d2925..ab372c0cd 100755 --- a/repo2docker/buildpacks/conda/install-miniforge.bash +++ b/repo2docker/buildpacks/conda/install-miniforge.bash @@ -5,12 +5,11 @@ set -ex cd $(dirname $0) -MINIFORGE_VERSION=4.8.2-1 -MAMBA_VERSION=0.6.1 +MINIFORGE_VERSION=4.9.2-2 # SHA256 for installers can be obtained from https://github.com/conda-forge/miniforge/releases -SHA256SUM="4f897e503bd0edfb277524ca5b6a5b14ad818b3198c2f07a36858b7d88c928db" +SHA256SUM="7a7bfaff87680298304a97ba69bcf92f66c810995a7155a2918b99fafb8ca1dc" -URL="https://github.com/conda-forge/miniforge/releases/download/${MINIFORGE_VERSION}/Miniforge3-${MINIFORGE_VERSION}-Linux-x86_64.sh" +URL="https://github.com/conda-forge/miniforge/releases/download/${MINIFORGE_VERSION}/Mambaforge-${MINIFORGE_VERSION}-Linux-x86_64.sh" INSTALLER_PATH=/tmp/miniforge-installer.sh # make sure we don't do anything funky with user's $HOME @@ -42,11 +41,6 @@ echo 'update_dependencies: false' >> ${CONDA_DIR}/.condarc # avoid future changes to default channel_priority behavior conda config --system --set channel_priority "flexible" -# do all installation with mamba -time conda install -y mamba==${MAMBA_VERSION} -# switch back to conda -# ln -s $CONDA_DIR/bin/conda $CONDA_DIR/bin/mamba - echo "installing notebook env:" cat /tmp/environment.yml time mamba env create -p ${NB_PYTHON_PREFIX} -f /tmp/environment.yml diff --git a/tests/conda/binder-dir/verify b/tests/conda/binder-dir/verify index c11db13f4..b5e017b35 100755 --- a/tests/conda/binder-dir/verify +++ b/tests/conda/binder-dir/verify @@ -5,6 +5,6 @@ from subprocess import check_output assert sys.version_info[:2] == (3, 5), sys.version out = check_output(["conda", "--version"]).decode("utf8").strip() -assert out == "conda 4.8.2", out +assert out == "conda 4.9.2", out import numpy From d8a3d7fa0ac8b1ec151d473a86ca813535369a54 Mon Sep 17 00:00:00 2001 From: Sylvain Corlay Date: Tue, 8 Dec 2020 11:23:21 +0100 Subject: [PATCH 020/103] Update mamba to latest --- repo2docker/buildpacks/conda/install-miniforge.bash | 3 +++ 1 file changed, 3 insertions(+) diff --git a/repo2docker/buildpacks/conda/install-miniforge.bash b/repo2docker/buildpacks/conda/install-miniforge.bash index ab372c0cd..08ed01b30 100755 --- a/repo2docker/buildpacks/conda/install-miniforge.bash +++ b/repo2docker/buildpacks/conda/install-miniforge.bash @@ -6,6 +6,7 @@ set -ex cd $(dirname $0) MINIFORGE_VERSION=4.9.2-2 +MAMBA_VERSION=0.7.4 # SHA256 for installers can be obtained from https://github.com/conda-forge/miniforge/releases SHA256SUM="7a7bfaff87680298304a97ba69bcf92f66c810995a7155a2918b99fafb8ca1dc" @@ -41,6 +42,8 @@ echo 'update_dependencies: false' >> ${CONDA_DIR}/.condarc # avoid future changes to default channel_priority behavior conda config --system --set channel_priority "flexible" +time mamba install -y mamba==${MAMBA_VERSION} + echo "installing notebook env:" cat /tmp/environment.yml time mamba env create -p ${NB_PYTHON_PREFIX} -f /tmp/environment.yml From 5c4c2dc40e5759b4f94ee83bf2af55e62ed8822b Mon Sep 17 00:00:00 2001 From: Frederik Rietdijk Date: Sat, 28 Nov 2020 10:31:52 +0100 Subject: [PATCH 021/103] buildpacks/nix: disable sandboxing The version bump #915 broke the Nix buildpack in case one does a build. Nix 2.3 enables sandboxing by default. Building inside a Docker container, while Nix is having sandboxing enabled is not possible. Thus, sandbox = false should be set in /etc/nix/nix.conf. --- repo2docker/buildpacks/nix/__init__.py | 9 +++++++-- tests/nix/test-building/README.rst | 4 ++++ tests/nix/test-building/default.nix | 27 ++++++++++++++++++++++++++ tests/nix/test-building/verify | 3 +++ 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 tests/nix/test-building/README.rst create mode 100644 tests/nix/test-building/default.nix create mode 100755 tests/nix/test-building/verify diff --git a/repo2docker/buildpacks/nix/__init__.py b/repo2docker/buildpacks/nix/__init__.py index 7e43f7d10..b7ca105d7 100644 --- a/repo2docker/buildpacks/nix/__init__.py +++ b/repo2docker/buildpacks/nix/__init__.py @@ -23,17 +23,22 @@ def get_build_scripts(self): """ Return series of build-steps common to all nix repositories. Notice how only root privileges are needed for creating nix - directory. + directory and a nix.conf file. - create nix directory for user nix installation + - disable sandboxing because its unsupported inside a Docker container - install nix package manager for user + """ return super().get_build_scripts() + [ ( "root", """ mkdir -m 0755 /nix && \ - chown -R ${NB_USER}:${NB_USER} /nix /usr/local/bin/nix-shell-wrapper /home/${NB_USER} + chown -R ${NB_USER}:${NB_USER} /nix /usr/local/bin/nix-shell-wrapper /home/${NB_USER} && \ + mkdir -p /etc/nix && \ + touch /etc/nix/nix.conf && \ + echo "sandbox = false" >> /etc/nix/nix.conf """, ), ( diff --git a/tests/nix/test-building/README.rst b/tests/nix/test-building/README.rst new file mode 100644 index 000000000..5bcf53148 --- /dev/null +++ b/tests/nix/test-building/README.rst @@ -0,0 +1,4 @@ +Check that we can build +----------------------- + +Test that actual building instead of substituting (downloading an existing build) works. diff --git a/tests/nix/test-building/default.nix b/tests/nix/test-building/default.nix new file mode 100644 index 000000000..a746447c1 --- /dev/null +++ b/tests/nix/test-building/default.nix @@ -0,0 +1,27 @@ +let + # Pinning nixpkgs to specific release + # To get sha256 use "nix-prefetch-git --rev " + commitRev="5574b6a152b1b3ae5f93ba37c4ffd1981f62bf5a"; + nixpkgs = builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/${commitRev}.tar.gz"; + sha256 = "1pqdddp4aiz726c7qs1dwyfzixi14shp0mbzi1jhapl9hrajfsjg"; + }; + pkgs = import nixpkgs { config = { allowUnfree = true; }; }; + + # Test that we can actually build + test-build = pkgs.runCommand "test-build" { } '' + touch $out + ''; + +in +pkgs.mkShell { + buildInputs = with pkgs; [ + python36Packages.numpy + python36Packages.notebook + test-build + ]; + + shellHook = '' + export NIX_PATH="nixpkgs=${nixpkgs}:." + ''; +} \ No newline at end of file diff --git a/tests/nix/test-building/verify b/tests/nix/test-building/verify new file mode 100755 index 000000000..dbd34b2a1 --- /dev/null +++ b/tests/nix/test-building/verify @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +import numpy From 76444c2351731eb3cf0002c4b85d3dfd2d092cdf Mon Sep 17 00:00:00 2001 From: Frederik Rietdijk Date: Sat, 28 Nov 2020 10:32:15 +0100 Subject: [PATCH 022/103] buildpacks/nix: 2.3 -> 2.3.9 Update to latest version. --- repo2docker/buildpacks/nix/install-nix.bash | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repo2docker/buildpacks/nix/install-nix.bash b/repo2docker/buildpacks/nix/install-nix.bash index 0bfadfa15..1eb657966 100644 --- a/repo2docker/buildpacks/nix/install-nix.bash +++ b/repo2docker/buildpacks/nix/install-nix.bash @@ -2,8 +2,8 @@ # This downloads and installs a pinned version of nix set -ex -NIX_VERSION="2.3" -NIX_SHA256="e43f6947d1f302b6193302889e7800f3e3dd4a650b6f929c668c894884a02701" +NIX_VERSION="2.3.9" +NIX_SHA256="49763fd7fa06bcb712ced2f3f11afd275e3a4d7bc5ff0d6fd1d50a4c3ce7bbf4" # Do all our operations in /tmp, since we can't rely on current directory being writeable yet. cd /tmp From 58dca8be5dd0886bf1d489208f48b3cde3241f2b Mon Sep 17 00:00:00 2001 From: Simon Li Date: Mon, 4 Jan 2021 22:35:10 +0000 Subject: [PATCH 023/103] Weekly test of master to check for external failures Test failures can sometimes be quite hard to debug. By adding a weekly automated test run of the default branch we'll have a bit more information on whether a PR is failing due to a change in that PR or due to some external factor. --- .github/workflows/test.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 244e66c30..4e6de4dc2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,10 +7,13 @@ name: Continuous Integration -# Only trigger the workflow when pushing to master on: - - push - - pull_request + push: + pull_request: + schedule: + # Weekly test so we know if tests break for external reasons + # https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows#scheduled-events + - cron: '36 10 * * 0' # Global environment variables env: From c33840fa16f6f737ce31e906afda8514e13064f8 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 8 Jan 2021 23:29:37 +0000 Subject: [PATCH 024/103] Allow extra path in figshare URLs Copied from https://github.com/jupyterhub/binderhub/pull/1236/commits/1d818e90cabc46fab9e5b839362c2a4845055de5 --- repo2docker/contentproviders/figshare.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/repo2docker/contentproviders/figshare.py b/repo2docker/contentproviders/figshare.py index 43ec71faf..9ccaa0266 100644 --- a/repo2docker/contentproviders/figshare.py +++ b/repo2docker/contentproviders/figshare.py @@ -39,7 +39,7 @@ def __init__(self): } ] - url_regex = re.compile(r"(.*)/articles/([^/]+)/(\d+)(/)?(\d+)?") + url_regex = re.compile(r"(.*)/articles/([^/]+)/([^/]+)/(\d+)(/)?(\d+)?") def detect(self, doi, ref=None, extra_args=None): """Trigger this provider for things that resolve to a Figshare article""" @@ -53,8 +53,8 @@ def detect(self, doi, ref=None, extra_args=None): if any([url.startswith(s) for s in host["hostname"]]): match = self.url_regex.match(url) if match: - self.article_id = match.groups()[2] - self.article_version = match.groups()[4] + self.article_id = match.groups()[3] + self.article_version = match.groups()[5] if not self.article_version: self.article_version = "1" return { From b032ad7c4b1cf487fccad0796beda91f7e8bf1ff Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 8 Jan 2021 23:46:57 +0000 Subject: [PATCH 025/103] Can't just use '([^/]+)/' for the new figshare url This may break detection of non-code URLs --- repo2docker/contentproviders/figshare.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo2docker/contentproviders/figshare.py b/repo2docker/contentproviders/figshare.py index 9ccaa0266..0584f043c 100644 --- a/repo2docker/contentproviders/figshare.py +++ b/repo2docker/contentproviders/figshare.py @@ -39,7 +39,7 @@ def __init__(self): } ] - url_regex = re.compile(r"(.*)/articles/([^/]+)/([^/]+)/(\d+)(/)?(\d+)?") + url_regex = re.compile(r"(.*)/articles/(code/)?([^/]+)/(\d+)(/)?(\d+)?") def detect(self, doi, ref=None, extra_args=None): """Trigger this provider for things that resolve to a Figshare article""" From bcc97a2382712b84894de8bcbe6b6f6df6c1a7e3 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 8 Jan 2021 23:48:58 +0000 Subject: [PATCH 026/103] Bug in figshare? It looks like the new figshare redirects may drop the version suffix causing two tests to fail (see the test comments). --- tests/unit/contentproviders/test_figshare.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/unit/contentproviders/test_figshare.py b/tests/unit/contentproviders/test_figshare.py index 0e152ecf8..77f6ee35b 100644 --- a/tests/unit/contentproviders/test_figshare.py +++ b/tests/unit/contentproviders/test_figshare.py @@ -50,10 +50,16 @@ def test_content_id(link, expected): ( "https://doi.org/10.6084/m9.figshare.9782777.v1", {"host": test_fig.hosts[0], "article": "9782777", "version": "1"}, + # $ curl -sIL https://doi.org/10.6084/m9.figshare.9782777.v1 | grep location + # location: https://figshare.com/articles/Binder-ready_openSenseMap_Analysis/9782777/1 + # location: https://figshare.com/articles/code/Binder-ready_openSenseMap_Analysis/9782777 ), ( "https://doi.org/10.6084/m9.figshare.9782777.v3", {"host": test_fig.hosts[0], "article": "9782777", "version": "3"}, + # $ curl -sIL https://doi.org/10.6084/m9.figshare.9782777.v3 | grep location + # location: https://figshare.com/articles/Binder-ready_openSenseMap_Analysis/9782777/3 + # location: https://figshare.com/articles/code/Binder-ready_openSenseMap_Analysis/9782777 ), ( "https://figshare.com/articles/title/97827771234", From 5e6aa2aa9b862ceea63ffc30ba70be4e42f935d5 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sat, 9 Jan 2021 11:36:12 +0000 Subject: [PATCH 027/103] Mark failing figshare tests as xfail --- tests/unit/contentproviders/test_figshare.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/unit/contentproviders/test_figshare.py b/tests/unit/contentproviders/test_figshare.py index 77f6ee35b..597b80220 100644 --- a/tests/unit/contentproviders/test_figshare.py +++ b/tests/unit/contentproviders/test_figshare.py @@ -43,9 +43,13 @@ def test_content_id(link, expected): "10.6084/m9.figshare.9782777.v1", {"host": test_fig.hosts[0], "article": "9782777", "version": "1"}, ), - ( + pytest.param( "10.6084/m9.figshare.9782777.v2", {"host": test_fig.hosts[0], "article": "9782777", "version": "2"}, + # $ curl -sIL https://dx.doi.org/10.6084/m9.figshare.9782777.v2 | grep location + # location: https://figshare.com/articles/Binder-ready_openSenseMap_Analysis/9782777/2 + # location: https://figshare.com/articles/code/Binder-ready_openSenseMap_Analysis/9782777 + marks=pytest.mark.xfail(reason="Problem with figshare version redirects"), ), ( "https://doi.org/10.6084/m9.figshare.9782777.v1", @@ -54,12 +58,13 @@ def test_content_id(link, expected): # location: https://figshare.com/articles/Binder-ready_openSenseMap_Analysis/9782777/1 # location: https://figshare.com/articles/code/Binder-ready_openSenseMap_Analysis/9782777 ), - ( + pytest.param( "https://doi.org/10.6084/m9.figshare.9782777.v3", {"host": test_fig.hosts[0], "article": "9782777", "version": "3"}, # $ curl -sIL https://doi.org/10.6084/m9.figshare.9782777.v3 | grep location # location: https://figshare.com/articles/Binder-ready_openSenseMap_Analysis/9782777/3 # location: https://figshare.com/articles/code/Binder-ready_openSenseMap_Analysis/9782777 + marks=pytest.mark.xfail(reason="Problem with figshare version redirects"), ), ( "https://figshare.com/articles/title/97827771234", From e04a14a7ccb45a3e0d3ee242a123b71626067f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerhard=20Br=C3=A4unlich?= Date: Wed, 13 Jan 2021 13:32:17 +0100 Subject: [PATCH 028/103] Add entrypoint script which automatically propagates *_PROXY env vars to docker config --- Dockerfile | 5 +++++ docker/entrypoint | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100755 docker/entrypoint diff --git a/Dockerfile b/Dockerfile index 54f5a24e3..818bf8653 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,5 +30,10 @@ RUN pip3 install --no-cache-dir /tmp/wheelhouse/*.whl \ COPY ./docker/git-credential-env /usr/local/bin/git-credential-env RUN git config --system credential.helper env +# add entrypoint +COPY ./docker/entrypoint /usr/local/bin/entrypoint +RUN chmod +x /usr/local/bin/entrypoint +ENTRYPOINT ["/usr/local/bin/entrypoint"] + # Used for testing purpose in ports.py EXPOSE 52000 diff --git a/docker/entrypoint b/docker/entrypoint new file mode 100755 index 000000000..9400a6d9c --- /dev/null +++ b/docker/entrypoint @@ -0,0 +1,35 @@ +#!/bin/sh +set -e + +function write_config() { + # Checks for the environment variables *_PROXY. + # If at least one variables is found, it writes all found variables + # into a docker config file + docker_cfg="$1" + httpProxy="${HTTP_PROXY:-$http_proxy}" + httpsProxy="${HTTPS_PROXY:-$https_proxy}" + noProxy="${NO_PROXY:-$no_proxy}" + # If no proxy vars are set, do nothing + [ -z "$httpProxy" ] && [ -z "$httpsProxy" ] && [ -z "$noProxy" ] && return + [ -f "$1" ] && echo "$1 already exists. Not touching it. You are responsible for setting your proxy vars there yourself" >&2 && return + sep="" + mkdir -p "$(dirname $docker_cfg)" + cat < "$docker_cfg" +{ +"proxies": { + "default": { +EOF + [ -n "$httpProxy" ] && echo -ne "$sep"' "httpProxy": "'"$httpProxy"'"' >> "$docker_cfg" && sep=",\n" + [ -n "$httpsProxy" ] && echo -ne "$sep"' "httpsProxy": "'"$httpsProxy"'"' >> "$docker_cfg" && sep=",\n" + [ -n "$noProxy" ] && echo -ne "$sep"' "noProxy": "'"$noProxy"'"' >> "$docker_cfg" && sep=",\n" + cat <> "$docker_cfg" + + } +} +} +EOF +} + +write_config /root/.docker/config.json + +exec "$@" From ee7f7790d335583fc152ceb128f24b3b4a8c9d48 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 15 Jan 2021 16:11:27 +0000 Subject: [PATCH 029/103] figshare article types: code and dataset --- repo2docker/contentproviders/figshare.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/repo2docker/contentproviders/figshare.py b/repo2docker/contentproviders/figshare.py index 0584f043c..0ae29992a 100644 --- a/repo2docker/contentproviders/figshare.py +++ b/repo2docker/contentproviders/figshare.py @@ -39,7 +39,10 @@ def __init__(self): } ] - url_regex = re.compile(r"(.*)/articles/(code/)?([^/]+)/(\d+)(/)?(\d+)?") + # We may need to add other item types in future, see + # https://github.com/jupyterhub/repo2docker/pull/1001#issuecomment-760107436 + # for a list + url_regex = re.compile(r"(.*)/articles/(code/|dataset/)?([^/]+)/(\d+)(/)?(\d+)?") def detect(self, doi, ref=None, extra_args=None): """Trigger this provider for things that resolve to a Figshare article""" From 830a9c89c00ab53db3f4ccfae0cef942f4ce367b Mon Sep 17 00:00:00 2001 From: David Douard Date: Thu, 10 Dec 2020 18:55:14 +0100 Subject: [PATCH 030/103] Replace urllib by requests in contentproviders requests is globally simpler to use, and more and more people are more familiar with this later than urllib. --- dev-requirements.txt | 2 +- repo2docker/contentproviders/dataverse.py | 17 +- repo2docker/contentproviders/doi.py | 95 +++++--- repo2docker/contentproviders/figshare.py | 6 +- repo2docker/contentproviders/hydroshare.py | 4 +- repo2docker/contentproviders/zenodo.py | 6 +- setup.py | 1 + tests/unit/contentproviders/test_dataverse.py | 180 +++++++------- tests/unit/contentproviders/test_doi.py | 16 +- tests/unit/contentproviders/test_figshare.py | 132 +++++----- .../unit/contentproviders/test_hydroshare.py | 147 ++++++------ tests/unit/contentproviders/test_zenodo.py | 226 +++++++++--------- 12 files changed, 431 insertions(+), 401 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index bcecb7ba7..4b75d1bbc 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -5,4 +5,4 @@ pytest>=4.6 wheel pytest-cov pre-commit -requests +requests_mock diff --git a/repo2docker/contentproviders/dataverse.py b/repo2docker/contentproviders/dataverse.py index 7519d700a..66632f5e6 100644 --- a/repo2docker/contentproviders/dataverse.py +++ b/repo2docker/contentproviders/dataverse.py @@ -2,7 +2,6 @@ import json import shutil -from urllib.request import Request from urllib.parse import urlparse, urlunparse, parse_qs from .doi import DoiProvider @@ -56,7 +55,6 @@ def detect(self, doi, ref=None, extra_args=None): return query_args = parse_qs(parsed_url.query) - # Corner case handling if parsed_url.path.startswith("/file.xhtml"): # There's no way of getting file information using its persistentId, the only thing we can do is assume that doi @@ -75,8 +73,7 @@ def detect(self, doi, ref=None, extra_args=None): parsed_url._replace(path="/api/search", query=search_query) ) self.log.debug("Querying Dataverse: " + search_url) - resp = self.urlopen(search_url).read() - data = json.loads(resp.decode("utf-8"))["data"] + data = self.urlopen(search_url).json() if data["count_in_response"] != 1: self.log.debug( "Dataverse search query failed!\n - doi: {}\n - url: {}\n - resp: {}\n".format( @@ -101,14 +98,12 @@ def fetch(self, spec, output_dir, yield_output=False): host = spec["host"] yield "Fetching Dataverse record {}.\n".format(record_id) - req = Request( - "{}/api/datasets/:persistentId?persistentId={}".format( - host["url"], record_id - ), - headers={"accept": "application/json"}, + url = "{}/api/datasets/:persistentId?persistentId={}".format( + host["url"], record_id ) - resp = self.urlopen(req) - record = json.loads(resp.read().decode("utf-8"))["data"] + + resp = self.urlopen(url, headers={"accept": "application/json"}) + record = resp.json() for fobj in deep_get(record, "latestVersion.files"): file_url = "{}/api/access/datafile/{}".format( diff --git a/repo2docker/contentproviders/doi.py b/repo2docker/contentproviders/doi.py index 6ec2c8e17..c1941ba83 100644 --- a/repo2docker/contentproviders/doi.py +++ b/repo2docker/contentproviders/doi.py @@ -5,8 +5,8 @@ from os import makedirs from os import path -from urllib import request # urlopen, Request -from urllib.error import HTTPError +from requests import Session, HTTPError + from zipfile import ZipFile, is_zipfile from .base import ContentProvider @@ -18,7 +18,21 @@ class DoiProvider(ContentProvider): """Provide contents of a repository identified by a DOI and some helper functions.""" - def urlopen(self, req, headers=None): + def __init__(self): + super().__init__() + self.session = Session() + self.session.headers.update( + { + "user-agent": "repo2docker {}".format(__version__), + } + ) + + def _request(self, url, **kwargs): + return self.session.get(url, **kwargs) + + urlopen = _request + + def _urlopen(self, req, headers=None): """A urlopen() helper""" # someone passed a string, not a request if not isinstance(req, request.Request): @@ -38,7 +52,8 @@ def doi2url(self, doi): doi = normalize_doi(doi) try: - resp = self.urlopen("https://doi.org/{}".format(doi)) + resp = self._request("https://doi.org/{}".format(doi)) + resp.raise_for_status() # If the DOI doesn't resolve, just return URL except HTTPError: return doi @@ -53,38 +68,42 @@ def fetch_file(self, file_ref, host, output_dir, unzip=False): file_url = deep_get(file_ref, host["download"]) fname = deep_get(file_ref, host["filename"]) logging.debug("Downloading file {} as {}\n".format(file_url, fname)) - with self.urlopen(file_url) as src: + + yield "Requesting {}\n".format(file_url) + resp = self._request(file_url, stream=True) + resp.raise_for_status() + + if path.dirname(fname): + sub_dir = path.join(output_dir, path.dirname(fname)) + if not path.exists(sub_dir): + yield "Creating {}\n".format(sub_dir) + makedirs(sub_dir, exist_ok=True) + + dst_fname = path.join(output_dir, fname) + with open(dst_fname, "wb") as dst: + yield "Fetching {}\n".format(fname) + for chunk in resp.iter_content(chunk_size=None): + dst.write(chunk) + + if unzip and is_zipfile(dst_fname): + yield "Extracting {}\n".format(fname) + zfile = ZipFile(dst_fname) + zfile.extractall(path=output_dir) + zfile.close() + + # delete downloaded file ... + os.remove(dst_fname) + # ... and any directories we might have created, + # in which case sub_dir will be defined if path.dirname(fname): - sub_dir = path.join(output_dir, path.dirname(fname)) - if not path.exists(sub_dir): - yield "Creating {}\n".format(sub_dir) - makedirs(sub_dir, exist_ok=True) - - dst_fname = path.join(output_dir, fname) - with open(dst_fname, "wb") as dst: - yield "Fetching {}\n".format(fname) - shutil.copyfileobj(src, dst) - # first close the newly written file, then continue - # processing it - if unzip and is_zipfile(dst_fname): - yield "Extracting {}\n".format(fname) - zfile = ZipFile(dst_fname) - zfile.extractall(path=output_dir) - zfile.close() - - # delete downloaded file ... - os.remove(dst_fname) - # ... and any directories we might have created, - # in which case sub_dir will be defined - if path.dirname(fname): - shutil.rmtree(sub_dir) - - new_subdirs = os.listdir(output_dir) - # if there is only one new subdirectory move its contents - # to the top level directory - if len(new_subdirs) == 1: - d = new_subdirs[0] - copytree(path.join(output_dir, d), output_dir) - shutil.rmtree(path.join(output_dir, d)) - - yield "Fetched files: {}\n".format(os.listdir(output_dir)) + shutil.rmtree(sub_dir) + + new_subdirs = os.listdir(output_dir) + # if there is only one new subdirectory move its contents + # to the top level directory + if len(new_subdirs) == 1: + d = new_subdirs[0] + copytree(path.join(output_dir, d), output_dir) + shutil.rmtree(path.join(output_dir, d)) + + yield "Fetched files: {}\n".format(os.listdir(output_dir)) diff --git a/repo2docker/contentproviders/figshare.py b/repo2docker/contentproviders/figshare.py index 43ec71faf..0fa09719b 100644 --- a/repo2docker/contentproviders/figshare.py +++ b/repo2docker/contentproviders/figshare.py @@ -25,6 +25,7 @@ class Figshare(DoiProvider): """ def __init__(self): + super().__init__() self.hosts = [ { "hostname": [ @@ -74,13 +75,12 @@ def fetch(self, spec, output_dir, yield_output=False): yield "Fetching Figshare article {} in version {}.\n".format( article_id, article_version ) - req = Request( + resp = self.urlopen( "{}{}/versions/{}".format(host["api"], article_id, article_version), headers={"accept": "application/json"}, ) - resp = self.urlopen(req) - article = json.loads(resp.read().decode("utf-8")) + article = resp.json() files = deep_get(article, host["filepath"]) # only fetch files where is_link_only: False diff --git a/repo2docker/contentproviders/hydroshare.py b/repo2docker/contentproviders/hydroshare.py index 2531aa8bf..eac66e21c 100755 --- a/repo2docker/contentproviders/hydroshare.py +++ b/repo2docker/contentproviders/hydroshare.py @@ -16,9 +16,7 @@ class Hydroshare(DoiProvider): def _fetch_version(self, host): """Fetch resource modified date and convert to epoch""" - json_response = json.loads( - self.urlopen(host["version"].format(self.resource_id)).read() - ) + json_response = self.urlopen(host["version"].format(self.resource_id)).json() date = next( item for item in json_response["dates"] if item["type"] == "modified" )["start_date"] diff --git a/repo2docker/contentproviders/zenodo.py b/repo2docker/contentproviders/zenodo.py index 87e7967f4..68d3494dd 100644 --- a/repo2docker/contentproviders/zenodo.py +++ b/repo2docker/contentproviders/zenodo.py @@ -15,6 +15,7 @@ class Zenodo(DoiProvider): """Provide contents of a Zenodo deposit.""" def __init__(self): + super().__init__() # We need the hostname (url where records are), api url (for metadata), # filepath (path to files in metadata), filename (path to filename in # metadata), download (path to file download URL), and type (path to item type in metadata) @@ -55,13 +56,12 @@ def fetch(self, spec, output_dir, yield_output=False): host = spec["host"] yield "Fetching Zenodo record {}.\n".format(record_id) - req = Request( + resp = self.urlopen( "{}{}".format(host["api"], record_id), headers={"accept": "application/json"}, ) - resp = self.urlopen(req) - record = json.loads(resp.read().decode("utf-8")) + record = resp.json() is_software = deep_get(record, host["type"]).lower() == "software" files = deep_get(record, host["filepath"]) diff --git a/setup.py b/setup.py index 5084b486c..8bfe64ebc 100644 --- a/setup.py +++ b/setup.py @@ -52,6 +52,7 @@ def get_identifier(json): "python-json-logger", "escapism", "jinja2", + "requests", "ruamel.yaml>=0.15", "toml", "semver", diff --git a/tests/unit/contentproviders/test_dataverse.py b/tests/unit/contentproviders/test_dataverse.py index 76d396544..0e53e50aa 100644 --- a/tests/unit/contentproviders/test_dataverse.py +++ b/tests/unit/contentproviders/test_dataverse.py @@ -1,6 +1,7 @@ import json import os import pytest +import re from io import BytesIO from tempfile import TemporaryDirectory @@ -19,7 +20,7 @@ "doi:10.7910/DVN/6ZXAGT/3YRRYJ", "10.7910/DVN/6ZXAGT", "https://dataverse.harvard.edu/api/access/datafile/3323458", - "hdl:11529/10016", + "https://data.cimmyt.org/dataset.xhtml?persistentId=hdl:11529/10016", ], [ {"host": harvard_dv, "record": "doi:10.7910/DVN/6ZXAGT"}, @@ -27,57 +28,68 @@ ], ) ] -test_responses = { - "doi:10.7910/DVN/6ZXAGT/3YRRYJ": ( +doi_responses = { + "https://doi.org/10.7910/DVN/6ZXAGT/3YRRYJ": ( "https://dataverse.harvard.edu/file.xhtml" "?persistentId=doi:10.7910/DVN/6ZXAGT/3YRRYJ" ), - "doi:10.7910/DVN/6ZXAGT": ( + "https://doi.org/10.7910/DVN/6ZXAGT": ( "https://dataverse.harvard.edu/dataset.xhtml" "?persistentId=doi:10.7910/DVN/6ZXAGT" ), - "10.7910/DVN/6ZXAGT": ( - "https://dataverse.harvard.edu/dataset.xhtml" - "?persistentId=doi:10.7910/DVN/6ZXAGT" + "https://dataverse.harvard.edu/api/access/datafile/3323458": ( + "https://dataverse.harvard.edu/api/access/datafile/3323458" + ), + "https://doi.org/10.21105/joss.01277": ( + "https://joss.theoj.org/papers/10.21105/joss.01277" ), - "https://dataverse.harvard.edu/api/access/datafile/3323458": "https://dataverse.harvard.edu/api/access/datafile/3323458", - "hdl:11529/10016": "https://data.cimmyt.org/dataset.xhtml?persistentId=hdl:11529/10016", -} -test_search = { - "data": { - "count_in_response": 1, - "items": [{"dataset_persistent_id": "doi:10.7910/DVN/6ZXAGT"}], - } } @pytest.mark.parametrize("test_input, expected", test_hosts) -def test_detect_dataverse(test_input, expected): - def doi_resolver(url): - return test_responses.get(url) - - with patch.object(Dataverse, "urlopen") as fake_urlopen, patch.object( - Dataverse, "doi2url", side_effect=doi_resolver - ) as fake_doi2url: - fake_urlopen.return_value.read.return_value = json.dumps(test_search).encode() - # valid Dataverse DOIs trigger this content provider - assert Dataverse().detect(test_input[0]) == expected[0] - assert fake_doi2url.call_count == 2 # File, then dataset - assert Dataverse().detect(test_input[1]) == expected[0] - assert Dataverse().detect(test_input[2]) == expected[0] - # only two of the three calls above have to resolve a DOI - assert fake_urlopen.call_count == 1 - assert Dataverse().detect(test_input[3]) == expected[1] - - with patch.object(Dataverse, "urlopen") as fake_urlopen: - # Don't trigger the Dataverse content provider - assert Dataverse().detect("/some/path/here") is None - assert Dataverse().detect("https://example.com/path/here") is None - # don't handle DOIs that aren't from Dataverse - fake_urlopen.return_value.url = ( - "http://joss.theoj.org/papers/10.21105/joss.01277" - ) - assert Dataverse().detect("https://doi.org/10.21105/joss.01277") is None +def test_detect_dataverse(test_input, expected, requests_mock): + def doi_resolver(req, context): + resp = doi_responses.get(req.url) + # doi responses are redirects + if resp is not None: + context.status_code = 302 + context.headers["Location"] = resp + return resp + + requests_mock.get(re.compile("https://"), json=doi_resolver) + requests_mock.get( + "https://dataverse.harvard.edu/api/search?q=entityId:3323458&type=file", + json={ + "count_in_response": 1, + "items": [{"dataset_persistent_id": "doi:10.7910/DVN/6ZXAGT"}], + }, + ) + + assert requests_mock.call_count == 0 + # valid Dataverse DOIs trigger this content provider + assert Dataverse().detect(test_input[0]) == expected[0] + # 4: doi resolution (302), File, doi resolution (302), then dataset + assert requests_mock.call_count == 4 + requests_mock.reset_mock() + + assert Dataverse().detect(test_input[1]) == expected[0] + # 2: doi (302), dataset + assert requests_mock.call_count == 2 + requests_mock.reset_mock() + + assert Dataverse().detect(test_input[2]) == expected[0] + # 1: datafile (search dataverse for the file id) + assert requests_mock.call_count == 1 + requests_mock.reset_mock() + + assert Dataverse().detect(test_input[3]) == expected[1] + requests_mock.reset_mock() + + # Don't trigger the Dataverse content provider + assert Dataverse().detect("/some/path/here") is None + assert Dataverse().detect("https://example.com/path/here") is None + # don't handle DOIs that aren't from Dataverse + assert Dataverse().detect("https://doi.org/10.21105/joss.01277") is None @pytest.fixture @@ -95,50 +107,50 @@ def dv_files(tmpdir): return [f1, f2, f3] -def test_dataverse_fetch(dv_files): - mock_response_ds_query = BytesIO( - json.dumps( - { - "data": { - "latestVersion": { - "files": [ - {"dataFile": {"id": 1}, "label": "some-file.txt"}, - { - "dataFile": {"id": 2}, - "label": "some-other-file.txt", - "directoryLabel": "directory", - }, - { - "dataFile": {"id": 3}, - "label": "the-other-file.txt", - "directoryLabel": "directory/subdirectory", - }, - ] - } - } - } - ).encode("utf-8") - ) +def test_dataverse_fetch(dv_files, requests_mock): + mock_response = { + "latestVersion": { + "files": [ + {"dataFile": {"id": 1}, "label": "some-file.txt"}, + { + "dataFile": {"id": 2}, + "label": "some-other-file.txt", + "directoryLabel": "directory", + }, + { + "dataFile": {"id": 3}, + "label": "the-other-file.txt", + "directoryLabel": "directory/subdirectory", + }, + ] + } + } + spec = {"host": harvard_dv, "record": "doi:10.7910/DVN/6ZXAGT"} + def mock_filecontent(req, context): + file_no = int(req.url.split("/")[-1]) - 1 + return open(dv_files[file_no], "rb").read() + + requests_mock.get( + "https://dataverse.harvard.edu/api/datasets/" + ":persistentId?persistentId=doi:10.7910/DVN/6ZXAGT", + json=mock_response, + ) + requests_mock.get( + re.compile("https://dataverse.harvard.edu/api/access/datafile"), + content=mock_filecontent, + ) + dv = Dataverse() - def mock_urlopen(self, req): - if isinstance(req, Request): - return mock_response_ds_query - else: - file_no = int(req.split("/")[-1]) - 1 - return urlopen("file://{}".format(dv_files[file_no])) - - with patch.object(Dataverse, "urlopen", new=mock_urlopen): - with TemporaryDirectory() as d: - output = [] - for l in dv.fetch(spec, d): - output.append(l) - - unpacked_files = set(os.listdir(d)) - expected = set(["directory", "some-file.txt"]) - assert expected == unpacked_files - assert os.path.isfile( - os.path.join(d, "directory", "subdirectory", "the-other-file.txt") - ) + with TemporaryDirectory() as d: + output = [] + for l in dv.fetch(spec, d): + output.append(l) + unpacked_files = set(os.listdir(d)) + expected = set(["directory", "some-file.txt"]) + assert expected == unpacked_files + assert os.path.isfile( + os.path.join(d, "directory", "subdirectory", "the-other-file.txt") + ) diff --git a/tests/unit/contentproviders/test_doi.py b/tests/unit/contentproviders/test_doi.py index 16d321e67..dbe391604 100644 --- a/tests/unit/contentproviders/test_doi.py +++ b/tests/unit/contentproviders/test_doi.py @@ -11,6 +11,7 @@ from repo2docker.contentproviders.doi import DoiProvider from repo2docker.contentproviders.base import ContentProviderException +from repo2docker import __version__ def test_content_id(): @@ -18,20 +19,15 @@ def test_content_id(): assert doi.content_id is None -def fake_urlopen(req): - print(req) - return req.headers - - -@patch("urllib.request.urlopen", fake_urlopen) -def test_url_headers(): +def test_url_headers(requests_mock): + requests_mock.get("https://mybinder.org", text="resp") doi = DoiProvider() headers = {"test1": "value1", "Test2": "value2"} result = doi.urlopen("https://mybinder.org", headers=headers) - assert "Test1" in result - assert "Test2" in result - assert len(result) is 3 # User-agent is also set + assert "test1" in result.request.headers + assert "Test2" in result.request.headers + assert result.request.headers["User-Agent"] == "repo2docker {}".format(__version__) def test_unresolving_doi(): diff --git a/tests/unit/contentproviders/test_figshare.py b/tests/unit/contentproviders/test_figshare.py index 0e152ecf8..3377d642f 100644 --- a/tests/unit/contentproviders/test_figshare.py +++ b/tests/unit/contentproviders/test_figshare.py @@ -22,12 +22,17 @@ @pytest.mark.parametrize("link,expected", test_content_ids) -def test_content_id(link, expected): - with patch.object(Figshare, "urlopen") as fake_urlopen: - fake_urlopen.return_value.url = link - fig = Figshare() - fig.detect("10.6084/m9.figshare.9782777") - assert fig.content_id == expected +def test_content_id(link, expected, requests_mock): + def mocked_get(req, context): + if req.url.startswith("https://doi.org"): + context.status_code = 302 + context.headers["Location"] = link + return link + + requests_mock.get(re.compile("https://"), text=mocked_get) + fig = Figshare() + fig.detect("10.6084/m9.figshare.9782777") + assert fig.content_id == expected test_fig = Figshare() @@ -103,76 +108,73 @@ def figshare_archive(prefix="a_directory"): yield zfile.name -def test_fetch_zip(): +def test_fetch_zip(requests_mock): # see test_zenodo.py/test_fetch_software with figshare_archive() as fig_path: - mock_response = BytesIO( - json.dumps( + mock_response = { + "files": [ { - "files": [ - { - "name": "afake.zip", - "is_link_only": False, - "download_url": "file://{}".format(fig_path), - } - ] + "name": "afake.zip", + "is_link_only": False, + "download_url": "file://{}".format(fig_path), } - ).encode("utf-8") + ] + } + requests_mock.get( + "https://api.figshare.com/v2/articles/123456/versions/42", + json=mock_response, + ) + requests_mock.get( + "file://{}".format(fig_path), content=open(fig_path, "rb").read() ) - def mock_urlopen(self, req): - if isinstance(req, Request): - return mock_response - else: - return urlopen(req) - - with patch.object(Figshare, "urlopen", new=mock_urlopen): - with TemporaryDirectory() as d: - output = [] - for l in test_fig.fetch(test_spec, d): - output.append(l) + # with patch.object(Figshare, "urlopen", new=mock_urlopen): + with TemporaryDirectory() as d: + output = [] + for l in test_fig.fetch(test_spec, d): + output.append(l) - unpacked_files = set(os.listdir(d)) - expected = set(["some-other-file.txt", "some-file.txt"]) - assert expected == unpacked_files + unpacked_files = set(os.listdir(d)) + expected = set(["some-other-file.txt", "some-file.txt"]) + assert expected == unpacked_files -def test_fetch_data(): +def test_fetch_data(requests_mock): with figshare_archive() as a_path: with figshare_archive() as b_path: - mock_response = BytesIO( - json.dumps( + mock_response = { + "files": [ + { + "name": "afake.file", + "download_url": "file://{}".format(a_path), + "is_link_only": False, + }, { - "files": [ - { - "name": "afake.file", - "download_url": "file://{}".format(a_path), - "is_link_only": False, - }, - { - "name": "bfake.data", - "download_url": "file://{}".format(b_path), - "is_link_only": False, - }, - {"name": "cfake.link", "is_link_only": True}, - ] - } - ).encode("utf-8") + "name": "bfake.data", + "download_url": "file://{}".format(b_path), + "is_link_only": False, + }, + {"name": "cfake.link", "is_link_only": True}, + ] + } + + requests_mock.get( + "https://api.figshare.com/v2/articles/123456/versions/42", + json=mock_response, + ) + requests_mock.get( + "file://{}".format(a_path), content=open(a_path, "rb").read() + ) + requests_mock.get( + "file://{}".format(b_path), content=open(b_path, "rb").read() ) - def mock_urlopen(self, req): - if isinstance(req, Request): - return mock_response - else: - return urlopen(req) - - with patch.object(Figshare, "urlopen", new=mock_urlopen): - with TemporaryDirectory() as d: - output = [] - for l in test_fig.fetch(test_spec, d): - output.append(l) - - unpacked_files = set(os.listdir(d)) - # ZIP files shouldn't have been unpacked - expected = {"bfake.data", "afake.file"} - assert expected == unpacked_files + with TemporaryDirectory() as d: + output = [] + for l in test_fig.fetch(test_spec, d): + output.append(l) + + unpacked_files = set(os.listdir(d)) + # ZIP files shouldn't have been unpacked + expected = {"bfake.data", "afake.file"} + assert expected == unpacked_files diff --git a/tests/unit/contentproviders/test_hydroshare.py b/tests/unit/contentproviders/test_hydroshare.py index d662dbff5..4af7d1579 100755 --- a/tests/unit/contentproviders/test_hydroshare.py +++ b/tests/unit/contentproviders/test_hydroshare.py @@ -5,87 +5,98 @@ from tempfile import TemporaryDirectory, NamedTemporaryFile from unittest.mock import patch from zipfile import ZipFile +import re from repo2docker.contentproviders import Hydroshare from repo2docker.contentproviders.base import ContentProviderException -def test_content_id(): - with patch.object(Hydroshare, "urlopen") as fake_urlopen: - fake_urlopen.return_value.url = ( - "https://www.hydroshare.org/resource/b8f6eae9d89241cf8b5904033460af61" - ) +doi_responses = { + "https://doi.org/10.4211/hs.b8f6eae9d89241cf8b5904033460af61": ( + "https://www.hydroshare.org/resource/b8f6eae9d89241cf8b5904033460af61" + ), + "https://doi.org/10.21105/joss.01277": ( + "https://joss.theoj.org/papers/10.21105/joss.01277" + ), +} - def read(): - return '{"dates": [{"type": "modified", "start_date": "2019-09-25T16:09:17.006152Z"}]}' - fake_urlopen.return_value.read = read - hydro = Hydroshare() +def doi_resolver(req, context): + resp = doi_responses.get(req.url) + # doi responses are redirects + if resp is not None: + context.status_code = 302 + context.headers["Location"] = resp + return resp - hydro.detect("10.4211/hs.b8f6eae9d89241cf8b5904033460af61") - assert hydro.content_id == "b8f6eae9d89241cf8b5904033460af61.v1569427757" +hydroshare_data = { + "dates": [{"type": "modified", "start_date": "2019-09-25T16:09:17.006152Z"}] +} -def test_detect_hydroshare(): - with patch.object(Hydroshare, "urlopen") as fake_urlopen: - fake_urlopen.return_value.url = ( - "https://www.hydroshare.org/resource/b8f6eae9d89241cf8b5904033460af61" - ) - def read(): - return '{"dates": [{"type": "modified", "start_date": "2019-09-25T16:09:17.006152Z"}]}' - - fake_urlopen.return_value.read = read - # valid Hydroshare DOIs trigger this content provider - expected = { - "host": { - "hostname": [ - "https://www.hydroshare.org/resource/", - "http://www.hydroshare.org/resource/", - ], - "django_irods": "https://www.hydroshare.org/django_irods/download/bags/", - "version": "https://www.hydroshare.org/hsapi/resource/{}/scimeta/elements", - }, - "resource": "b8f6eae9d89241cf8b5904033460af61", - "version": "1569427757", - } - assert ( - Hydroshare().detect( - "https://www.hydroshare.org/resource/b8f6eae9d89241cf8b5904033460af61" - ) - == expected - ) - # assert a call to urlopen was called to fetch version - assert fake_urlopen.call_count == 1 - assert ( - Hydroshare().detect("10.4211/hs.b8f6eae9d89241cf8b5904033460af61") - == expected - ) - # assert 2 more calls were made, one to resolve the DOI and another to fetch the version - assert fake_urlopen.call_count == 3 - assert ( - Hydroshare().detect( - "https://doi.org/10.4211/hs.b8f6eae9d89241cf8b5904033460af61" - ) - == expected - ) - # assert 2 more calls were made, one to resolve the DOI and another to fetch the version - assert fake_urlopen.call_count == 5 - - with patch.object(Hydroshare, "urlopen") as fake_urlopen: - # Don't trigger the Hydroshare content provider - assert Hydroshare().detect("/some/path/here") is None - assert Hydroshare().detect("https://example.com/path/here") is None - # don't handle DOIs that aren't from Hydroshare - fake_urlopen.return_value.url = ( - "http://joss.theoj.org/papers/10.21105/joss.01277" - ) +def test_content_id(requests_mock): - def read(): - return '{"dates": [{"type": "modified", "start_date": "2019-09-25T16:09:17.006152Z"}]}' + requests_mock.get(re.compile("https://"), json=hydroshare_data) + requests_mock.get(re.compile("https://doi.org"), json=doi_resolver) - fake_urlopen.return_value.read = read - assert Hydroshare().detect("https://doi.org/10.21105/joss.01277") is None + hydro = Hydroshare() + + hydro.detect("10.4211/hs.b8f6eae9d89241cf8b5904033460af61") + assert hydro.content_id == "b8f6eae9d89241cf8b5904033460af61.v1569427757" + + +def test_detect_hydroshare(requests_mock): + requests_mock.get(re.compile("https://"), json=hydroshare_data) + requests_mock.get(re.compile("https://doi.org"), json=doi_resolver) + + # valid Hydroshare DOIs trigger this content provider + expected = { + "host": { + "hostname": [ + "https://www.hydroshare.org/resource/", + "http://www.hydroshare.org/resource/", + ], + "django_irods": "https://www.hydroshare.org/django_irods/download/bags/", + "version": "https://www.hydroshare.org/hsapi/resource/{}/scimeta/elements", + }, + "resource": "b8f6eae9d89241cf8b5904033460af61", + "version": "1569427757", + } + + assert ( + Hydroshare().detect( + "https://www.hydroshare.org/resource/b8f6eae9d89241cf8b5904033460af61" + ) + == expected + ) + # assert a call to urlopen was called to fetch version + assert requests_mock.call_count == 1 + requests_mock.reset_mock() + + assert ( + Hydroshare().detect("10.4211/hs.b8f6eae9d89241cf8b5904033460af61") == expected + ) + # assert 3 calls were made, 2 to resolve the DOI (302 + 200) and another to fetch the version + assert requests_mock.call_count == 3 + requests_mock.reset_mock() + + assert ( + Hydroshare().detect( + "https://doi.org/10.4211/hs.b8f6eae9d89241cf8b5904033460af61" + ) + == expected + ) + # assert 3 more calls were made, 2 to resolve the DOI and another to fetch the version + assert requests_mock.call_count == 3 + requests_mock.reset_mock() + + # Don't trigger the Hydroshare content provider + assert Hydroshare().detect("/some/path/here") is None + assert Hydroshare().detect("https://example.com/path/here") is None + + # don't handle DOIs that aren't from Hydroshare + assert Hydroshare().detect("https://doi.org/10.21105/joss.01277") is None @contextmanager diff --git a/tests/unit/contentproviders/test_zenodo.py b/tests/unit/contentproviders/test_zenodo.py index 61dcbfbf3..fa8b30fa8 100644 --- a/tests/unit/contentproviders/test_zenodo.py +++ b/tests/unit/contentproviders/test_zenodo.py @@ -1,6 +1,7 @@ import json import os import pytest +import re from contextlib import contextmanager from io import BytesIO @@ -11,14 +12,30 @@ from repo2docker.contentproviders import Zenodo +doi_responses = { + "https://doi.org/10.5281/zenodo.3232985": ("https://zenodo.org/record/3232985"), + "https://doi.org/10.22002/d1.1235": ("https://data.caltech.edu/records/1235"), + "https://doi.org/10.21105/joss.01277": ( + "https://joss.theoj.org/papers/10.21105/joss.01277" + ), +} -def test_content_id(): - with patch.object(Zenodo, "urlopen") as fake_urlopen: - fake_urlopen.return_value.url = "https://zenodo.org/record/3232985" - zen = Zenodo() - zen.detect("10.5281/zenodo.3232985") - assert zen.content_id == "3232985" +def doi_resolver(req, context): + resp = doi_responses.get(req.url) + # doi responses are redirects + if resp is not None: + context.status_code = 302 + context.headers["Location"] = resp + return resp + + +def test_content_id(requests_mock): + requests_mock.get(re.compile("https://"), json=doi_resolver) + + zen = Zenodo() + zen.detect("10.5281/zenodo.3232985") + assert zen.content_id == "3232985" test_zen = Zenodo() @@ -43,25 +60,22 @@ def test_content_id(): @pytest.mark.parametrize("test_input,expected", test_hosts) -def test_detect_zenodo(test_input, expected): - with patch.object(Zenodo, "urlopen") as fake_urlopen: - fake_urlopen.return_value.url = test_input[0] - # valid Zenodo DOIs trigger this content provider - assert Zenodo().detect(test_input[0]) == expected - assert Zenodo().detect(test_input[1]) == expected - assert Zenodo().detect(test_input[2]) == expected - # only two of the three calls above have to resolve a DOI - assert fake_urlopen.call_count == 2 - - with patch.object(Zenodo, "urlopen") as fake_urlopen: - # Don't trigger the Zenodo content provider - assert Zenodo().detect("/some/path/here") is None - assert Zenodo().detect("https://example.com/path/here") is None - # don't handle DOIs that aren't from Zenodo - fake_urlopen.return_value.url = ( - "http://joss.theoj.org/papers/10.21105/joss.01277" - ) - assert Zenodo().detect("https://doi.org/10.21105/joss.01277") is None +def test_detect_zenodo(test_input, expected, requests_mock): + requests_mock.get(re.compile("https://"), json=doi_resolver) + # valid Zenodo DOIs trigger this content provider + assert Zenodo().detect(test_input[0]) == expected + assert Zenodo().detect(test_input[1]) == expected + assert Zenodo().detect(test_input[2]) == expected + # only two of the three calls above have to resolve a DOI (2 req per doi resolution) + assert requests_mock.call_count == 4 + requests_mock.reset_mock() + + # Don't trigger the Zenodo content provider + assert Zenodo().detect("/some/path/here") is None + assert Zenodo().detect("https://example.com/path/here") is None + + # don't handle DOIs that aren't from Zenodo + assert Zenodo().detect("https://doi.org/10.21105/joss.01277") is None @contextmanager @@ -74,120 +88,102 @@ def zenodo_archive(prefix="a_directory"): yield zfile.name -def test_fetch_software_from_github_archive(): +def test_fetch_software_from_github_archive(requests_mock): # we "fetch" a local ZIP file to simulate a Zenodo record created from a # GitHub repository via the Zenodo-GitHub integration with zenodo_archive() as zen_path: - mock_response = BytesIO( - json.dumps( + mock_response = { + "files": [ { - "files": [ - { - "filename": "some_dir/afake.zip", - "links": {"download": "file://{}".format(zen_path)}, - } - ], - "metadata": {"upload_type": "software"}, + "filename": "some_dir/afake.zip", + "links": {"download": "file://{}".format(zen_path)}, } - ).encode("utf-8") + ], + "metadata": {"upload_type": "software"}, + } + requests_mock.get("https://zenodo.org/api/records/1234", json=mock_response) + requests_mock.get( + "file://{}".format(zen_path), content=open(zen_path, "rb").read() ) - def mock_urlopen(self, req): - if isinstance(req, Request): - return mock_response - else: - return urlopen(req) - - with patch.object(Zenodo, "urlopen", new=mock_urlopen): - zen = Zenodo() - spec = {"host": test_zen.hosts[0], "record": "1234"} + zen = Zenodo() + spec = {"host": test_zen.hosts[0], "record": "1234"} - with TemporaryDirectory() as d: - output = [] - for l in zen.fetch(spec, d): - output.append(l) + with TemporaryDirectory() as d: + output = [] + for l in zen.fetch(spec, d): + output.append(l) - unpacked_files = set(os.listdir(d)) - expected = set(["some-other-file.txt", "some-file.txt"]) - assert expected == unpacked_files + unpacked_files = set(os.listdir(d)) + expected = set(["some-other-file.txt", "some-file.txt"]) + assert expected == unpacked_files -def test_fetch_software(): +def test_fetch_software(requests_mock): # we "fetch" a local ZIP file to simulate a Zenodo software record with a # ZIP file in it with zenodo_archive() as zen_path: - mock_response = BytesIO( - json.dumps( + mock_response = { + "files": [ { - "files": [ - { - # this is the difference to the GitHub generated one, - # the ZIP file isn't in a directory - "filename": "afake.zip", - "links": {"download": "file://{}".format(zen_path)}, - } - ], - "metadata": {"upload_type": "software"}, + # this is the difference to the GitHub generated one, + # the ZIP file isn't in a directory + "filename": "afake.zip", + "links": {"download": "file://{}".format(zen_path)}, } - ).encode("utf-8") + ], + "metadata": {"upload_type": "software"}, + } + requests_mock.get("https://zenodo.org/api/records/1234", json=mock_response) + requests_mock.get( + "file://{}".format(zen_path), content=open(zen_path, "rb").read() ) - def mock_urlopen(self, req): - if isinstance(req, Request): - return mock_response - else: - return urlopen(req) - - with patch.object(Zenodo, "urlopen", new=mock_urlopen): - with TemporaryDirectory() as d: - zen = Zenodo() - spec = spec = {"host": test_zen.hosts[0], "record": "1234"} - output = [] - for l in zen.fetch(spec, d): - output.append(l) + with TemporaryDirectory() as d: + zen = Zenodo() + spec = spec = {"host": test_zen.hosts[0], "record": "1234"} + output = [] + for l in zen.fetch(spec, d): + output.append(l) - unpacked_files = set(os.listdir(d)) - expected = set(["some-other-file.txt", "some-file.txt"]) - assert expected == unpacked_files + unpacked_files = set(os.listdir(d)) + expected = set(["some-other-file.txt", "some-file.txt"]) + assert expected == unpacked_files -def test_fetch_data(): +def test_fetch_data(requests_mock): # we "fetch" a local ZIP file to simulate a Zenodo data record with zenodo_archive() as a_zen_path: with zenodo_archive() as b_zen_path: - mock_response = BytesIO( - json.dumps( + mock_response = { + "files": [ + { + "filename": "afake.zip", + "links": {"download": "file://{}".format(a_zen_path)}, + }, { - "files": [ - { - "filename": "afake.zip", - "links": {"download": "file://{}".format(a_zen_path)}, - }, - { - "filename": "bfake.zip", - "links": {"download": "file://{}".format(b_zen_path)}, - }, - ], - "metadata": {"upload_type": "data"}, - } - ).encode("utf-8") + "filename": "bfake.zip", + "links": {"download": "file://{}".format(b_zen_path)}, + }, + ], + "metadata": {"upload_type": "data"}, + } + requests_mock.get("https://zenodo.org/api/records/1234", json=mock_response) + requests_mock.get( + "file://{}".format(a_zen_path), content=open(a_zen_path, "rb").read() + ) + requests_mock.get( + "file://{}".format(b_zen_path), content=open(b_zen_path, "rb").read() ) - def mock_urlopen(self, req): - if isinstance(req, Request): - return mock_response - else: - return urlopen(req) - - with patch.object(Zenodo, "urlopen", new=mock_urlopen): - with TemporaryDirectory() as d: - zen = Zenodo() - spec = {"host": test_zen.hosts[0], "record": "1234"} - output = [] - for l in zen.fetch(spec, d): - output.append(l) - - unpacked_files = set(os.listdir(d)) - # ZIP files shouldn't have been unpacked - expected = {"bfake.zip", "afake.zip"} - assert expected == unpacked_files + with TemporaryDirectory() as d: + zen = Zenodo() + spec = {"host": test_zen.hosts[0], "record": "1234"} + output = [] + for l in zen.fetch(spec, d): + output.append(l) + + unpacked_files = set(os.listdir(d)) + # ZIP files shouldn't have been unpacked + expected = {"bfake.zip", "afake.zip"} + assert expected == unpacked_files From 983607fed792060ea72d179f16b7f875a91c1eb9 Mon Sep 17 00:00:00 2001 From: David Douard Date: Tue, 24 Nov 2020 18:19:42 +0100 Subject: [PATCH 031/103] Fix the docker image name generation make sure all the parts that constitute the generated docker image name are escaped, otherwise the docker building process can fail (with a rather hard to understand error). Before this fix, the `provider.content_id` was not escaped. --- repo2docker/app.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) mode change 100755 => 100644 repo2docker/app.py diff --git a/repo2docker/app.py b/repo2docker/app.py old mode 100755 new mode 100644 index 937e74793..1ffd2218b --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -401,20 +401,19 @@ def fetch(self, url, ref, checkout_path): self.log.info(log_line, extra=dict(phase="fetching")) if not self.output_image_spec: - self.output_image_spec = ( - "r2d" + escapism.escape(self.repo, escape_char="-").lower() - ) + image_spec = "r2d" + self.repo # if we are building from a subdirectory include that in the # image name so we can tell builds from different sub-directories # apart. if self.subdir: - self.output_image_spec += escapism.escape( - self.subdir, escape_char="-" - ).lower() + image_spec += self.subdir if picked_content_provider.content_id is not None: - self.output_image_spec += picked_content_provider.content_id + image_spec += picked_content_provider.content_id else: - self.output_image_spec += str(int(time.time())) + image_spec += str(int(time.time())) + self.output_image_spec = escapism.escape( + image_spec, escape_char="-" + ).lower() def json_excepthook(self, etype, evalue, traceback): """Called on an uncaught exception when using json logging From e54c24ce2ee540805d6e4f74a355da995679fa3c Mon Sep 17 00:00:00 2001 From: David Douard Date: Tue, 24 Nov 2020 18:30:08 +0100 Subject: [PATCH 032/103] Add support for the SWHID content provider This content provider allows to retrieve the content from a Software Heritage (SWH) persistent identifier (SWHID). Typical usage: repo2docker swh:1:rev:94dca98c006b80309704c717b5d83dff3c1fa3a0 It uses the SWH public vault API to retrieve the content of the given directory. Most of the times, this will not need an authentication token to bypass the rate-limiting of the SWH API. Without authentication, one should be allowed to retrieve one directory content per minute. If this is not enought, then the user must use authenticated calls to the SWH API. For this, a new `swh_token` config item has been added to the Repo2Docker application class. To use authentication: repo2docker --config cfg.json swh:1:rev:94dca98c006b80309704c717b5d83dff3c1fa3a0 with the swh_token config option being defined in the cfg.json config file. --- repo2docker/app.py | 17 +++ repo2docker/contentproviders/__init__.py | 1 + repo2docker/contentproviders/swhid.py | 113 ++++++++++++++++ setup.py | 1 + tests/unit/contentproviders/test_swhid.py | 157 ++++++++++++++++++++++ 5 files changed, 289 insertions(+) mode change 100644 => 100755 repo2docker/app.py create mode 100644 repo2docker/contentproviders/swhid.py create mode 100644 tests/unit/contentproviders/test_swhid.py diff --git a/repo2docker/app.py b/repo2docker/app.py old mode 100644 new mode 100755 index 1ffd2218b..5b553712e --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -148,6 +148,7 @@ def _default_log_level(self): contentproviders.Figshare, contentproviders.Dataverse, contentproviders.Hydroshare, + contentproviders.Swhid, contentproviders.Mercurial, contentproviders.Git, ], @@ -269,6 +270,18 @@ def _user_name_default(self): allow_none=True, ) + swh_token = Unicode( + None, + help=""" + Token to use authenticated SWH API access. + + If unset, default to unauthenticated (limited) usage of the Software + Heritage API. + """, + config=True, + allow_none=True, + ) + cleanup_checkout = Bool( False, help=""" @@ -395,6 +408,10 @@ def fetch(self, url, ref, checkout_path): "No matching content provider found for " "{url}.".format(url=url) ) + swh_token = self.config.get("swh_token", self.swh_token) + if swh_token and isinstance(picked_content_provider, contentproviders.Swhid): + picked_content_provider.set_auth_token(swh_token) + for log_line in picked_content_provider.fetch( spec, checkout_path, yield_output=self.json_logs ): diff --git a/repo2docker/contentproviders/__init__.py b/repo2docker/contentproviders/__init__.py index ae0b8c27c..6398c233e 100755 --- a/repo2docker/contentproviders/__init__.py +++ b/repo2docker/contentproviders/__init__.py @@ -5,3 +5,4 @@ from .dataverse import Dataverse from .hydroshare import Hydroshare from .mercurial import Mercurial +from .swhid import Swhid diff --git a/repo2docker/contentproviders/swhid.py b/repo2docker/contentproviders/swhid.py new file mode 100644 index 000000000..e20501770 --- /dev/null +++ b/repo2docker/contentproviders/swhid.py @@ -0,0 +1,113 @@ +import io +import os +import shutil +import tarfile +import time +import re + +from os import path + +import requests + +from .base import ContentProvider +from ..utils import copytree +from .. import __version__ + + +def parse_swhid(swhid): + swhid_regexp = r"^swh:(?P\d+):(?Pori|cnt|rev|dir|snp|rel):(?P[0-9a-f]{40})$" + # only parse/check the of the swhid + # see https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html + m = re.match(swhid_regexp, swhid.split(";")[0]) + if m: + return m.groupdict() + + +class Swhid(ContentProvider): + """Provide contents of a repository identified by a SWHID.""" + + retry_delay = 5 + + def __init__(self): + self.swhid = None + self.base_url = "https://archive.softwareheritage.org/api/1" + self.session = requests.Session() + self.session.headers.update( + { + "user-agent": "repo2docker {}".format(__version__), + } + ) + + def set_auth_token(self, token): + header = {"Authorization": "Bearer {}".format(token)} + self.session.headers.update(header) + + def _request(self, url, method="GET"): + if not url.endswith("/"): + url = url + "/" + + for retries in range(3): + try: + resp = self.session.request(method, url) + if resp.ok: + break + except requests.ConnectionError: + time.sleep(self.retry_delay) + + return resp + + @property + def content_id(self): + """The SWHID record ID used for content retrival""" + return self.swhid + + def detect(self, swhid, ref=None, extra_args=None): + swhid_dict = parse_swhid(swhid) + + if ( + swhid_dict + and swhid_dict["type"] in ("dir", "rev") + and swhid_dict["version"] == "1" + ): + return {"swhid": swhid, "swhid_obj": swhid_dict} + + def fetch_directory(self, dir_hash, output_dir): + url = "{}/vault/directory/{}/".format(self.base_url, dir_hash) + yield "Fetching directory {} from {}\n".format(dir_hash, url) + resp = self._request(url, "POST") + receipt = resp.json() + status = receipt["status"] + assert status != "failed", receipt + while status not in ("failed", "done"): + time.sleep(self.retry_delay) + resp = self._request(url) + status = resp.json()["status"] + if status == "failed": + yield "Error preparing the directory for download" + raise Exception() + resp = self._request(resp.json()["fetch_url"]) + archive = tarfile.open(fileobj=io.BytesIO(resp.content)) + archive.extractall(path=output_dir) + # the output_dir should have only one subdir named after the dir_hash + # move its content one level up + copytree(path.join(output_dir, dir_hash), output_dir) + shutil.rmtree(path.join(output_dir, dir_hash)) + yield "Fetched files: {}\n".format(os.listdir(output_dir)) + + def fetch(self, spec, output_dir, yield_output=False): + swhid = spec["swhid"] + swhid_obj = spec["swhid_obj"] + + if swhid_obj["type"] == "rev": + # need to get the directory for this revision + sha1git = swhid_obj["hash"] + url = "{}/revision/{}/".format(self.base_url, sha1git) + yield "Fetching revision {} from {}\n".format(sha1git, url) + resp = self._request(url) + assert resp.ok, (resp.content, self.session.headers) + directory = resp.json()["directory"] + self.swhid = "swh:1:dir:{}".format(directory) + yield from self.fetch_directory(directory, output_dir) + elif swhid_obj["type"] == "dir": + self.swhid = swhid + yield from self.fetch_directory(swhid_obj["hash"], output_dir) diff --git a/setup.py b/setup.py index 8bfe64ebc..dab829d70 100644 --- a/setup.py +++ b/setup.py @@ -56,6 +56,7 @@ def get_identifier(json): "ruamel.yaml>=0.15", "toml", "semver", + "requests", ], python_requires=">=3.6", author="Project Jupyter Contributors", diff --git a/tests/unit/contentproviders/test_swhid.py b/tests/unit/contentproviders/test_swhid.py new file mode 100644 index 000000000..953218e3c --- /dev/null +++ b/tests/unit/contentproviders/test_swhid.py @@ -0,0 +1,157 @@ +import json +import os +import io +import tarfile +import shutil +import re +import urllib +import pytest +import tempfile +import logging +import requests_mock + +from os import makedirs +from os.path import join +from unittest.mock import patch, MagicMock, mock_open +from zipfile import ZipFile + +from repo2docker.contentproviders.swhid import Swhid, parse_swhid +from repo2docker.contentproviders.base import ContentProviderException + + +# this is a slightly stripped down copy of swh.model.cli.swhid_of_dir(). +# We do not use this later to prevent having to depend on swh.model[cli] +def swhid_of_dir(path): + object = Directory.from_disk(path=path).get_data() + return swhid(DIRECTORY, object) + + +def test_content_id(): + swhid = Swhid() + assert swhid.content_id is None + + +swhids_ok = [ + "swh:1:dir:" + "0" * 40, + "swh:1:rev:" + "0" * 40, +] +swhids_invalid = [ + "swh:1:dir:" + "0" * 39, + "swh:2:dir:" + "0" * 40, + "swh:1:rev:" + "0" * 41, + "swh:1:cnt:" + "0" * 40, + "swh:1:ori:" + "0" * 40, + "swh:1:rel:" + "0" * 40, + "swh:1:snp:" + "0" * 40, +] + +detect_values = [ + (swhid, {"swhid": swhid, "swhid_obj": parse_swhid(swhid)}) for swhid in swhids_ok +] + [(swhid, None) for swhid in swhids_invalid] + + +@pytest.mark.parametrize("swhid, expected", detect_values) +def test_detect(swhid, expected): + provider = Swhid() + assert provider.detect(swhid) == expected + + +def fake_urlopen(req): + print(req) + return req.headers + + +def test_unresolving_swhid(): + provider = Swhid() + + # swhid = "0" * 40 + # assert provider.swhid2url(swhid) is swhid + + +NULLID = "0" * 40 + + +@pytest.fixture +def gen_tarfile(tmpdir): + rootdir = join(tmpdir, "tmp") + makedirs(rootdir) + with open(join(rootdir, "file1.txt"), "wb") as fobj: + fobj.write(b"Some content\n") + + # this directory hash can be computed using the swh.model package, but we do + # nto want to depend on this later to limit dependencies and because it + # does not support python 3.6; + dirhash = "89a3bd29a2c5ae0b1465febbe5df09730a8576fe" + buf = io.BytesIO() + tarf = tarfile.open(name=dirhash, fileobj=buf, mode="w") + tarf.add(rootdir, arcname=dirhash) + tarf.close() + shutil.rmtree(rootdir) + return dirhash, buf.getvalue() + + +def mocked_provider(tmpdir, dirhash, tarfile_buf): + provider = Swhid() + adapter = requests_mock.Adapter() + provider.base_url = "mock://api/1" + provider.retry_delay = 0.1 + provider.session.mount("mock://", adapter) + + adapter.register_uri( + "GET", + "mock://api/1/revision/{}/".format(NULLID), + json={ + "author": {"fullname": "John Doe "}, + "directory": dirhash, + }, + ) + adapter.register_uri( + "POST", + "mock://api/1/vault/directory/{}/".format(dirhash), + json={ + "fetch_url": "mock://api/1/vault/directory/{}/raw/".format(dirhash), + "status": "new", + }, + ) + adapter.register_uri( + "GET", + "mock://api/1/vault/directory/{}/".format(dirhash), + [ + { + "json": { + "fetch_url": "mock://api/1/vault/directory/{}/raw/".format(dirhash), + "status": "pending", + } + }, + { + "json": { + "fetch_url": "mock://api/1/vault/directory/{}/raw/".format(dirhash), + "status": "done", + } + }, + ], + ) + adapter.register_uri( + "GET", + "mock://api/1/vault/directory/{}/raw/".format(dirhash), + content=tarfile_buf, + ) + return provider + + +def test_fetch_revision(tmpdir, gen_tarfile): + dir_id, tarfile_buf = gen_tarfile + provider = mocked_provider(tmpdir, dir_id, tarfile_buf) + swhid = "swh:1:rev:" + NULLID + for log in provider.fetch(provider.detect(swhid), tmpdir): + print(log) + assert provider.content_id == "swh:1:dir:" + dir_id + + +def test_fetch_directory(tmpdir, gen_tarfile): + dir_id, tarfile_buf = gen_tarfile + provider = mocked_provider(tmpdir, dir_id, tarfile_buf) + swhid = "swh:1:dir:" + dir_id + for log in provider.fetch(provider.detect(swhid), tmpdir): + print(log) + assert provider.content_id == swhid From 5f26710d0108b5751bd846731caab80c5c5b03f6 Mon Sep 17 00:00:00 2001 From: David Douard Date: Thu, 26 Nov 2020 15:21:06 +0100 Subject: [PATCH 033/103] Mention the SWHID source-repository format in the usage documentation --- docs/source/usage.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/source/usage.rst b/docs/source/usage.rst index 6643038e8..bdac9b835 100644 --- a/docs/source/usage.rst +++ b/docs/source/usage.rst @@ -14,6 +14,8 @@ Using ``repo2docker`` follows :ref:`specification`. repo2docker is called with the URL of a Git repository, a `DOI `_ from Zenodo or Figshare, a `Handle `_ or DOI from a Dataverse installation, +a `SWHID`_ of a directory of a revision archived in the +`Software Heritage Archive `_, or a path to a local directory. It then performs these steps: @@ -36,7 +38,8 @@ repo2docker is called with this command:: where ```` is: * a URL of a Git repository (``https://github.com/binder-examples/requirements``), - * a Zenodo DOI (``10.5281/zenodo.1211089``), or + * a Zenodo DOI (``10.5281/zenodo.1211089``), + * a SWHID_ (``swh:1:rev:999dd06c7f679a2714dfe5199bdca09522a29649``), or * a path to a local directory (``a/local/directory``) of the source repository you want to build. @@ -132,3 +135,4 @@ Command line API .. _Pytudes: https://github.com/norvig/pytudes +.. _SWHID: https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html From 98795a9e22cc30f7612a01dcbf15db389438abc1 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Wed, 20 Jan 2021 09:07:44 +0100 Subject: [PATCH 034/103] Add workflow to push a release to pypi This adds a new GH Action workflow that will build and upload a new package to pypi.org. The release instructions are updated to use calendar versioning. Outdated instructions related to the change log have been removed. --- .github/workflows/release.yml | 32 ++++++++++++ docs/source/contributing/contributing.md | 4 +- docs/source/contributing/tasks.md | 65 ++++-------------------- 3 files changed, 43 insertions(+), 58 deletions(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..c29e6d6e4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,32 @@ +name: Create a release on pypi.org + +on: push + +jobs: + build-n-publish: + runs-on: ubuntu-18.04 + steps: + - uses: actions/checkout@v2 + + - name: "Setup Python 3.8" + uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: "Install dependencies" + run: | + pip install --upgrade setuptools pip + pip install --upgrade -r dev-requirements.txt + pip freeze + + - name: "Build distribution archives" + run: | + python setup.py sdist bdist_wheel + + # This step is only run when a new tag is pushed + # all previous steps always run in order to exercise them + - name: Publish distribution to PyPI + if: startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@master + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/docs/source/contributing/contributing.md b/docs/source/contributing/contributing.md index c7903e54d..5e0a0075c 100644 --- a/docs/source/contributing/contributing.md +++ b/docs/source/contributing/contributing.md @@ -61,10 +61,11 @@ This outlines the process for getting changes to the repo2docker project merged. 3. Make edits in [your fork](https://help.github.com/en/articles/fork-a-repo) of the [repo2docker repository](https://github.com/jupyterhub/repo2docker). 4. Make a [pull request](https://help.github.com/en/articles/about-pull-requests). Read the [next section](#guidelines-to-getting-a-pull-request-merged) for guidelines for both reviewers and contributors on merging a PR. -5. Edit [the changelog](./../../changelog) by appending your feature / bug fix to the development version. 6. Wait for a community member to merge your changes. Remember that **someone else must merge your pull request**. That goes for new contributors and long term maintainers alike. + Because `master` is continuously deployed to mybinder.org it is essential + that `master` is always in a deployable state. 7. (optional) Deploy a new version of repo2docker to mybinder.org by [following these steps](http://mybinder-sre.readthedocs.io/en/latest/deployment/how.html) ## Guidelines to getting a Pull Request merged @@ -84,7 +85,6 @@ These are not hard rules to be enforced by 🚓 but they are suggestions written This makes it easier to find all changes since the last deployment `git log --merges --pretty=format:"%h %<(10,trunc)%an %<(15)%ar %s" ..` and your PR easier to review. * **Make it clear when your PR is ready for review.** Prefix the title of your pull request (PR) with `[MRG]` if the contribution is complete and should be subjected to a detailed review. -* **Enter your changes into the [changelog](./../../changelog)** in `docs/source/changelog.rst`. * **Use commit messages to describe _why_ you are proposing the changes you are proposing.** * **Try to not rush changes** (the definition of rush depends on how big your changes are). Remember that everyone in the repo2docker team is a volunteer and we can not (nor would we want to) control their time or interests. diff --git a/docs/source/contributing/tasks.md b/docs/source/contributing/tasks.md index a1f6a34d0..b633d3229 100644 --- a/docs/source/contributing/tasks.md +++ b/docs/source/contributing/tasks.md @@ -113,41 +113,35 @@ added and why. If you fix a bug or add new functionality consider adding a new test to prevent the bug from coming back/the feature breaking in the future. - ## Creating a Release -We try to make a release of repo2docker every few months if possible. - -We follow [semantic versioning](https://semver.org/). +We make a release of whatever is on `master` every month. We uses "calendar versioning". +Monthly releases give users a predictable pattern for when releases are going to +happen and prevents locking up improvements for fixes for long periods of time. A new release will automatically be created when a new git tag is created -and pushed to the repository (using -[Travis CI](https://github.com/jupyterhub/repo2docker/blob/master/.travis.yml#L52)). +and pushed to the repository. To create a new release, follow these steps: -### Confirm that the changelog is ready - -[The changelog](https://github.com/jupyterhub/repo2docker/blob/master/docs/source/changelog.rst) -should reflect all significant enhancements and fixes to repo2docker and -its documentation. In addition, ensure that the correct version is displayed -at the top, and create a new `dev` section if needed. - ### Create a new tag and push it First, tag a new release locally: ```bash -V=0.7.0; git tag -am "release $V" $V +V=YYYY.MM.0; git tag -am "release $V" $V ``` +> If you need to make a second (or third) release in a month increment the +> trailing 0 of the version to 1 (or 2). + Then push this change up to the master repository ``` git push origin --tags ``` -Travis should automatically run the tests and, if they pass, create a +GitHub Actions should create a new release on the [repo2docker PyPI](https://pypi.org/project/jupyter-repo2docker/). Once this has completed, make sure that the new version has been updated. @@ -159,51 +153,10 @@ release on the [GitHub repository releases page](https://github.com/jupyterhub/r * Click "Draft a new release" * Choose a tag version using the same tag you just created above * The release name is simply the tag version -* The description is [a link to the Changelog](https://github.com/jupyterhub/repo2docker/blob/master/docs/source/changelog.rst), - ideally with an anchor to the latest release. * Finally, click "Publish release" That's it! -## Update the change log - -To add your change to the change log, find the relevant Feature/Bug -fix/API change section for the next release near the top of the file; -then add one or two sentences as a new bullet point about your -changes. Include the pull request or issue number between square -brackets at the end. - -Some details: - -- versioning follows the x.y.z, major.minor.bugfix numbering - -- bug fixes go into the next bugfix release. If there isn't any, you - can create a new section (see point below). Don't worry if you're - not sure about that, and think it should go into a next major or - minor release: an admin will let you know, or move the change later - to the appropriate section - -- API changes should preferably go into the next major release, unless - they are backward compatible (for example, a deprecated function - keyword): then they can go into the next minor release. For release - with major release 0, non-backward compatible breaking changes are - also fine for the next minor release. - -- new features should go into the next minor release. - -- if there is no section for the appropriate release, you can add one: - - follow the versioning scheme, by simply increasing the relevant - number for one of the major /minor/bugfix numbers, appropriate for - your change (see the above bullet points); add the release - section. Then add three subsections: new features, api changes, and - bug fixes. Leave out the sections that are not appropriate for the - newlye added release section. - -Release candidate versions in the change log are only temporary, and -should be superseded by either a next release candidate, or the final -release for that version (bugfix version 0). - ## Keeping the Pipfile and requirements files up to date From 61a0f3d7d615c5f01aa1f462678b38cef7eaa242 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Wed, 20 Jan 2021 09:26:28 +0100 Subject: [PATCH 035/103] Update comment about travis steps We no longer run tests on the code so we would not spot missing files or problems with the wheel we build. --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9467a64c4..8bef05ac8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,8 +7,7 @@ services: python: - 3.8 install: -# Make a wheel and install it to test to catch possible -# issues with releases +# Make a wheel and install it - pip install --upgrade setuptools pip - pip install -r dev-requirements.txt - python setup.py bdist_wheel From e048d5441e54a202e237778d081492f2a7b18eef Mon Sep 17 00:00:00 2001 From: Tim Head Date: Wed, 20 Jan 2021 09:27:22 +0100 Subject: [PATCH 036/103] Remove travis infrastructure for uploading releases to pypi.org --- .travis.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8bef05ac8..f0d16c8a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,13 +33,3 @@ after_failure: echo " pre-commit run" echo "or after-the-fact on already committed files with" echo " pre-commit run --all-files" - -deploy: - provider: pypi - username: mybinderteam - distributions: sdist bdist_wheel - on: - tags: true - repo: jupyterhub/repo2docker - password: - secure: ZkJTcI6fVkh2yRB0UVwSPVvGtfade7sQDZ6BjQR5bHRZuBLFq4/nxmn88BIPc6uYEHB6hxxfr9RbyP7ZnyUVUoTiyRfDM8kQe0RvFUVxRj7brZZFMYt6OTMiPUgWvyDqYIdVx+D5qgFgLxnQtUiZ0iqvPgQ+9Jn5SxZuuovrARpaTavlmKo4Vw63Ks/3zV61YeehvELFxU2Ibjy5ujMo/R119KZ7G3Z1w0IyJyVZQ9WaG1VXLO1LjFifpCcjMawaTJ9TmD5BOdF4IAIlP2QlB9N+v2xxuEGy7Mc9FwAH6M8kNqmjhe/ayj83vEMmlkxhE66unqiFJkSXzH1Rh8ythOy9s9qiDgeZeW/rYYLrzVNl9aMHicidV4PKEzobwXS/u8c/wx0fsAMNPcHY+O/8+hFLwy4ZLHusNiyCjPv5sVOq7yM/EKCjkod71bzFOvnnCZ70S0pxrR1nwEKo3x8qaK7l9aw0raSgWyrp6VHzEI4zPfBvP+R2ZYCUqBBj7rrpEl2C02AMKEkTh7Xm5LpaAFCBBMUwTATg7hECmB232vO+C6CLED+ONmGost5jOgsGwIun6viM5eXhP1tCeC5XeAtYozo/9UUIc5yHZL/8YtnmfCxvSi7p6h4qgLDCviKTH3bJCsRjF1ktkTiWAT+wL/SywsRz5D9tjV4b70neM8I= From edc5d20c54e346e84c16fd587fd93cad9e82b5b6 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Wed, 20 Jan 2021 15:27:42 +0100 Subject: [PATCH 037/103] Remove changelog reference --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1a02fa1a5..f1a583d59 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,4 +23,4 @@ There are a few other pages to highlight: Its a good place to understand _why_ the team have made the decisions that they have along the way! * We absolutely encourage discussion around refactoring, updating or extending repo2docker, but please make sure that you've understood this page before opening an issue to discuss the change you'd like to propose. * [Common developer tasks and how-tos](https://repo2docker.readthedocs.io/en/latest/contributing/tasks.html) - * Some notes on running tests, buildpack dependencies, creating a release, updating the changelog and keeping the pip files up to date. + * Some notes on running tests, buildpack dependencies, creating a release, and keeping the pip files up to date. From c7578d926e79fc53364bd7f7225237cf3af3498b Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 21 Jan 2021 16:45:27 +0100 Subject: [PATCH 038/103] Fix typo Co-authored-by: Chris Holdgraf --- docs/source/contributing/tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/contributing/tasks.md b/docs/source/contributing/tasks.md index b633d3229..780033c7f 100644 --- a/docs/source/contributing/tasks.md +++ b/docs/source/contributing/tasks.md @@ -115,7 +115,7 @@ test to prevent the bug from coming back/the feature breaking in the future. ## Creating a Release -We make a release of whatever is on `master` every month. We uses "calendar versioning". +We make a release of whatever is on `master` every month. We use "calendar versioning". Monthly releases give users a predictable pattern for when releases are going to happen and prevents locking up improvements for fixes for long periods of time. From d5eaf445ce84ff69eac595c0e892b1476b3c83f4 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Sat, 23 Jan 2021 10:23:13 +0100 Subject: [PATCH 039/103] Remove travis CI configuration All tests have been migrated to other CI providers so we can turn travis off completely. --- .travis.yml | 35 ----------------------------------- 1 file changed, 35 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index f0d16c8a3..000000000 --- a/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -dist: xenial -os: linux -language: python -cache: pip -services: -- docker -python: -- 3.8 -install: -# Make a wheel and install it -- pip install --upgrade setuptools pip -- pip install -r dev-requirements.txt -- python setup.py bdist_wheel -- pip install dist/*.whl -- pip freeze -script: -- | - # run autoformat - if [[ "$REPO_TYPE" == "lint" ]]; then - pre-commit run --all-files - fi -after_success: -- pip install codecov -- pushd tests && codecov && popd - -after_failure: -- | - # point to auto-lint-fix - echo "You can install pre-commit hooks to automatically run formatting" - echo "on each commit with:" - echo " pre-commit install" - echo "or you can run by hand on staged files with" - echo " pre-commit run" - echo "or after-the-fact on already committed files with" - echo " pre-commit run --all-files" From 2c3e7dc57c916a0bae2f05972780f4b513262b21 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Sat, 23 Jan 2021 10:25:52 +0100 Subject: [PATCH 040/103] Run release workflow on push and pull_request --- .github/workflows/release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c29e6d6e4..d8a5f30e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,8 @@ name: Create a release on pypi.org -on: push +on: + push: + pull_request: jobs: build-n-publish: From 6145f463a86bc387d01fd74c64f6389d02d2eba9 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 5 Nov 2020 22:00:47 +0100 Subject: [PATCH 041/103] Experiment with different install mechanism to get coverage --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e6de4dc2..340e54092 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -106,7 +106,8 @@ jobs: pip install --upgrade setuptools pip wheel pip install --upgrade -r dev-requirements.txt python setup.py bdist_wheel - pip install dist/*.whl + #pip install dist/*.whl + pip install -e. pip freeze # hg-evolve pinned to 9.2 because hg-evolve dropped support for # hg 4.5, installed with apt in Ubuntu 18.04 From b96412bd338917f1f919fe4f7c349b8f34e0cbda Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 5 Nov 2020 22:14:30 +0100 Subject: [PATCH 042/103] Specify coverage rule to combine results We can specify a rule to map different source files from which coverage stats were collected to the same source file in our repository. --- .coveragerc | 8 ++++++++ .github/workflows/test.yml | 3 +-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.coveragerc b/.coveragerc index f2a1e5cea..62e14303e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,3 +1,11 @@ [run] # this file comes from versioneer and we don't test it omit = */_version.py + +[paths] +# This tells coverage how to combine results together or said differently +# which files at different paths are actually the same file +# documented at https://coverage.readthedocs.io/en/latest/config.html#paths +source = + repo2docker/ + /opt/hostedtoolcache/Python/*/site-packages/repo2docker diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 340e54092..4e6de4dc2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -106,8 +106,7 @@ jobs: pip install --upgrade setuptools pip wheel pip install --upgrade -r dev-requirements.txt python setup.py bdist_wheel - #pip install dist/*.whl - pip install -e. + pip install dist/*.whl pip freeze # hg-evolve pinned to 9.2 because hg-evolve dropped support for # hg 4.5, installed with apt in Ubuntu 18.04 From d56deb02dd4fa6804b39636713587fbdef3b3a7e Mon Sep 17 00:00:00 2001 From: Tim Head Date: Fri, 6 Nov 2020 07:44:53 +0100 Subject: [PATCH 043/103] Changeg source file aliasing --- .coveragerc | 9 +++++++-- .github/workflows/test.yml | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.coveragerc b/.coveragerc index 62e14303e..937d60694 100644 --- a/.coveragerc +++ b/.coveragerc @@ -6,6 +6,11 @@ omit = */_version.py # This tells coverage how to combine results together or said differently # which files at different paths are actually the same file # documented at https://coverage.readthedocs.io/en/latest/config.html#paths +# Yes, we list repo2docker twice here. This allows you to install repo2docker +# with `pip install -e.` for local development and from the wheel (as done on +# CI) and get `repo2docker/foo.py` as paths in the coverage report source = - repo2docker/ - /opt/hostedtoolcache/Python/*/site-packages/repo2docker + repo2docker + repo2docker + ../repo2docker + */site-packages/repo2docker diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e6de4dc2..d5a1eddb8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -114,7 +114,10 @@ jobs: - name: "Run tests" run: | - pytest --durations 10 --cov repo2docker -v tests/${{ matrix.repo_type }} + cd tests + pytest --durations 10 --cov repo2docker -v ${{ matrix.repo_type }} # Action Repo: https://github.com/codecov/codecov-action - uses: codecov/codecov-action@v1 + with: + directory: ./tests From 79325be491301e2049ebf3a9793dc80d84400b2b Mon Sep 17 00:00:00 2001 From: Tim Head Date: Fri, 6 Nov 2020 09:22:05 +0100 Subject: [PATCH 044/103] Set working directory for codecov action --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d5a1eddb8..ceb6ea1a9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -119,5 +119,6 @@ jobs: # Action Repo: https://github.com/codecov/codecov-action - uses: codecov/codecov-action@v1 + working-directory: ./tests with: directory: ./tests From da6e08aec08188368e3c61295f29789e4aa3f31f Mon Sep 17 00:00:00 2001 From: Tim Head Date: Fri, 6 Nov 2020 09:48:39 +0100 Subject: [PATCH 045/103] Use Python codecov package instead of GH action We can't set the working directory of the GH Action which means we need to install codecov ourselves and run it in the `tests/` sub-directory. --- .github/workflows/test.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ceb6ea1a9..424cf37b3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -117,8 +117,7 @@ jobs: cd tests pytest --durations 10 --cov repo2docker -v ${{ matrix.repo_type }} - # Action Repo: https://github.com/codecov/codecov-action - - uses: codecov/codecov-action@v1 - working-directory: ./tests - with: - directory: ./tests + - name: "Upload code coverage stats" + run: | + pip install codecov + pushd tests && codecov && popd From 3e6f99cefe624c73771463407bf885d918f77dc2 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Fri, 6 Nov 2020 11:11:32 +0100 Subject: [PATCH 046/103] Fix/rewrite paths for codecov filename matching --- .codecov.yml | 6 ++++-- .github/workflows/test.yml | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 62be777bf..8836913fb 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,5 +1,7 @@ -# show coverage in CI status, not as a comment. +# show coverage in CI status, not as a comment. comment: off +fixes: + - "*/site-packages/::" coverage: status: project: @@ -7,4 +9,4 @@ coverage: target: auto patch: default: - target: 20% + target: 20% diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 424cf37b3..5362a4977 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -120,4 +120,5 @@ jobs: - name: "Upload code coverage stats" run: | pip install codecov - pushd tests && codecov && popd + pushd tests && codecov && cat + cat /home/runner/work/repo2docker/repo2docker/tests/coverage.xml From 6e372b5bc9f2de04253124d7fbd281dcfca0e142 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 28 Jan 2021 09:10:41 +0100 Subject: [PATCH 047/103] Extreme measures --- repo2docker/app.py | 12 ++++++++++++ repo2docker/buildpacks/repo2docker-entrypoint | 6 +++--- tests/unit/test_env.py | 9 +++++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/repo2docker/app.py b/repo2docker/app.py index 5b553712e..8a804905c 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -605,12 +605,24 @@ def wait_for_container(self, container): try: for line in container.logs(stream=True): self.log.info(line.decode("utf-8"), extra=dict(phase="running")) + finally: container.reload() if container.status == "running": self.log.info("Stopping container...\n", extra=dict(phase="running")) container.kill() exit_code = container.attrs["State"]["ExitCode"] + + container.wait() + + self.log.info( + "Container finished running.\n".upper(), extra=dict(phase="running") + ) + # are there more logs? Let's send them back too + late_logs = container.logs().decode("utf-8") + for line in late_logs.split("\n"): + self.log.info(line + "\n", extra=dict(phase="running")) + container.remove() if exit_code: sys.exit(exit_code) diff --git a/repo2docker/buildpacks/repo2docker-entrypoint b/repo2docker/buildpacks/repo2docker-entrypoint index a1a2cac35..cc1404d5f 100755 --- a/repo2docker/buildpacks/repo2docker-entrypoint +++ b/repo2docker/buildpacks/repo2docker-entrypoint @@ -15,10 +15,10 @@ if [[ ! -z "${R2D_ENTRYPOINT:-}" ]]; then if [[ ! -x "$R2D_ENTRYPOINT" ]]; then chmod u+x "$R2D_ENTRYPOINT" fi - exec "$R2D_ENTRYPOINT" "$@" >&"$log_fd" + exec "$R2D_ENTRYPOINT" "$@" 2>&1 >&"$log_fd" else - exec "$@" >&"$log_fd" + exec "$@" 2>&1 >&"$log_fd" fi # Close the logging output again -#exec {log_fd}>&- +exec {log_fd}>&- diff --git a/tests/unit/test_env.py b/tests/unit/test_env.py index d7de5fe67..4602210f6 100644 --- a/tests/unit/test_env.py +++ b/tests/unit/test_env.py @@ -42,13 +42,15 @@ def test_env(): # value "--env", "SPAM_2=", - "--", + # "--", tmpdir, "/bin/bash", "-c", # Docker exports all passed env variables, so we can # just look at exported variables. - "export", + "export; sleep 1", + # "export; echo TIMDONE", + # "export", ], universal_newlines=True, stdout=subprocess.PIPE, @@ -61,6 +63,9 @@ def test_env(): # stdout should be empty assert not result.stdout + print(result.stderr.split("\n")) + # assert False + # stderr should contain lines of output declares = [x for x in result.stderr.split("\n") if x.startswith("declare")] assert 'declare -x FOO="{}"'.format(ts) in declares From 0885204a5d1ed0af61fed58f8769a50b1c4569b5 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Thu, 11 Feb 2021 21:58:21 +0100 Subject: [PATCH 048/103] fix dataverse regression introduced in last release --- repo2docker/contentproviders/dataverse.py | 4 +-- tests/unit/contentproviders/test_dataverse.py | 36 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/repo2docker/contentproviders/dataverse.py b/repo2docker/contentproviders/dataverse.py index 66632f5e6..a1c57caa9 100644 --- a/repo2docker/contentproviders/dataverse.py +++ b/repo2docker/contentproviders/dataverse.py @@ -73,7 +73,7 @@ def detect(self, doi, ref=None, extra_args=None): parsed_url._replace(path="/api/search", query=search_query) ) self.log.debug("Querying Dataverse: " + search_url) - data = self.urlopen(search_url).json() + data = self.urlopen(search_url).json()["data"] if data["count_in_response"] != 1: self.log.debug( "Dataverse search query failed!\n - doi: {}\n - url: {}\n - resp: {}\n".format( @@ -103,7 +103,7 @@ def fetch(self, spec, output_dir, yield_output=False): ) resp = self.urlopen(url, headers={"accept": "application/json"}) - record = resp.json() + record = resp.json()["data"] for fobj in deep_get(record, "latestVersion.files"): file_url = "{}/api/access/datafile/{}".format( diff --git a/tests/unit/contentproviders/test_dataverse.py b/tests/unit/contentproviders/test_dataverse.py index 0e53e50aa..3d912a7b9 100644 --- a/tests/unit/contentproviders/test_dataverse.py +++ b/tests/unit/contentproviders/test_dataverse.py @@ -60,8 +60,10 @@ def doi_resolver(req, context): requests_mock.get( "https://dataverse.harvard.edu/api/search?q=entityId:3323458&type=file", json={ - "count_in_response": 1, - "items": [{"dataset_persistent_id": "doi:10.7910/DVN/6ZXAGT"}], + "data": { + "count_in_response": 1, + "items": [{"dataset_persistent_id": "doi:10.7910/DVN/6ZXAGT"}], + } }, ) @@ -109,20 +111,22 @@ def dv_files(tmpdir): def test_dataverse_fetch(dv_files, requests_mock): mock_response = { - "latestVersion": { - "files": [ - {"dataFile": {"id": 1}, "label": "some-file.txt"}, - { - "dataFile": {"id": 2}, - "label": "some-other-file.txt", - "directoryLabel": "directory", - }, - { - "dataFile": {"id": 3}, - "label": "the-other-file.txt", - "directoryLabel": "directory/subdirectory", - }, - ] + "data": { + "latestVersion": { + "files": [ + {"dataFile": {"id": 1}, "label": "some-file.txt"}, + { + "dataFile": {"id": 2}, + "label": "some-other-file.txt", + "directoryLabel": "directory", + }, + { + "dataFile": {"id": 3}, + "label": "the-other-file.txt", + "directoryLabel": "directory/subdirectory", + }, + ] + } } } From bcce6e8a1ee17d0c05ad5cd67e8fd648bc850106 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Mon, 4 Jan 2021 15:27:59 +0100 Subject: [PATCH 049/103] Update to JupyterLab 3.0 --- .../conda/environment.py-2.7.frozen.yml | 32 ++--- .../conda/environment.py-3.6.frozen.yml | 116 +----------------- .../buildpacks/conda/environment.py-3.6.yml | 8 +- repo2docker/buildpacks/conda/environment.yml | 6 +- .../buildpacks/conda/install-miniforge.bash | 9 +- 5 files changed, 25 insertions(+), 146 deletions(-) diff --git a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml index 53dd6916a..62ad1c4ea 100644 --- a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-2.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2020-07-25 17:13:39 UTC +# Frozen on 2021-01-04 14:15:02 UTC name: r2d channels: - conda-forge @@ -7,11 +7,11 @@ channels: - conda-forge/label/broken dependencies: - _libgcc_mutex=0.1=conda_forge - - _openmp_mutex=4.5=0_gnu + - _openmp_mutex=4.5=1_gnu - backports=1.0=py_2 - backports.shutil_get_terminal_size=1.0.0=py_3 - backports_abc=0.5=py_1 - - ca-certificates=2020.6.20=hecda079_0 + - ca-certificates=2020.12.5=ha878542_0 - certifi=2019.11.28=py27h8c360ce_1 - configparser=3.7.3=py27h8c360ce_2 - decorator=4.4.2=py_0 @@ -23,20 +23,20 @@ dependencies: - ipython_genutils=0.2.0=py_1 - jupyter_client=5.3.4=py27_1 - jupyter_core=4.6.3=py27h8c360ce_1 - - ld_impl_linux-64=2.34=h53a641e_7 + - ld_impl_linux-64=2.35.1=hea4e1c9_1 - libffi=3.2.1=he1b5a44_1007 - - libgcc-ng=9.2.0=h24d8f2e_2 - - libgomp=9.2.0=h24d8f2e_2 + - libgcc-ng=9.3.0=h5dbcf3e_17 + - libgomp=9.3.0=h5dbcf3e_17 - libsodium=1.0.17=h516909a_0 - - libstdcxx-ng=9.2.0=hdf63c60_2 - - ncurses=6.2=he1b5a44_1 - - openssl=1.1.1g=h516909a_0 + - libstdcxx-ng=9.3.0=h2ae2ef3_17 + - ncurses=6.2=h58526e2_4 + - openssl=1.1.1i=h7f98852_0 - pathlib2=2.3.5=py27h8c360ce_1 - - pexpect=4.8.0=py27h8c360ce_1 + - pexpect=4.8.0=pyh9f0ad1d_2 - pickleshare=0.7.5=py27h8c360ce_1001 - pip=20.1.1=pyh9f0ad1d_0 - prompt_toolkit=1.0.15=py_1 - - ptyprocess=0.6.0=py_1001 + - ptyprocess=0.7.0=pyhd3deb0d_0 - pygments=2.5.2=py_0 - python=2.7.15=h5a48372_1011_cpython - python-dateutil=2.8.1=py_0 @@ -46,15 +46,15 @@ dependencies: - scandir=1.10.0=py27hdf8410d_1 - setuptools=44.0.0=py27_0 - simplegeneric=0.8.1=py_1 - - singledispatch=3.4.0.3=py27_1000 + - singledispatch=3.4.0.3=pyh9f0ad1d_1001 - six=1.15.0=pyh9f0ad1d_0 - - sqlite=3.32.3=hcee41ef_1 - - tk=8.6.10=hed695b0_0 + - sqlite=3.34.0=h74cdb3f_0 + - tk=8.6.10=h21135ba_1 - tornado=5.1.1=py27h14c3975_1000 - traitlets=4.3.3=py27h8c360ce_1 - wcwidth=0.1.9=pyh9f0ad1d_0 - - wheel=0.34.2=py_1 + - wheel=0.36.2=pyhd3deb0d_0 - zeromq=4.3.2=he1b5a44_2 - - zlib=1.2.11=h516909a_1006 + - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml index 1766bd043..bef23e722 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml @@ -1,116 +1,2 @@ # AUTO GENERATED FROM environment.py-3.6.yml, DO NOT MANUALLY MODIFY -# Frozen on 2020-07-25 17:14:29 UTC -name: r2d -channels: - - conda-forge - - defaults - - conda-forge/label/broken -dependencies: - - _libgcc_mutex=0.1=conda_forge - - _openmp_mutex=4.5=0_gnu - - alembic=1.4.2=pyh9f0ad1d_0 - - async_generator=1.10=py_0 - - attrs=19.3.0=py_0 - - backcall=0.2.0=pyh9f0ad1d_0 - - bleach=3.1.5=pyh9f0ad1d_0 - - blinker=1.4=py_1 - - brotlipy=0.7.0=py36h8c4c3a4_1000 - - ca-certificates=2020.6.20=hecda079_0 - - certifi=2020.6.20=py36h9f0ad1d_0 - - certipy=0.1.3=py_0 - - cffi=1.14.0=py36hd463f26_0 - - chardet=3.0.4=py36h9f0ad1d_1006 - - cryptography=3.0=py36h45558ae_0 - - decorator=4.4.2=py_0 - - defusedxml=0.6.0=py_0 - - entrypoints=0.3=py36h9f0ad1d_1001 - - idna=2.10=pyh9f0ad1d_0 - - importlib-metadata=1.7.0=py36h9f0ad1d_0 - - importlib_metadata=1.7.0=0 - - ipykernel=5.3.4=py36h95af2a2_0 - - ipython=7.16.1=py36h95af2a2_0 - - ipython_genutils=0.2.0=py_1 - - ipywidgets=7.5.1=py_0 - - jedi=0.17.2=py36h9f0ad1d_0 - - jinja2=2.11.2=pyh9f0ad1d_0 - - json5=0.9.4=pyh9f0ad1d_0 - - jsonschema=3.2.0=py36h9f0ad1d_1 - - jupyter_client=6.1.6=py_0 - - jupyter_core=4.6.3=py36h9f0ad1d_1 - - jupyter_telemetry=0.0.5=py_0 - - jupyterhub-base=1.1.0=py36_2 - - jupyterhub-singleuser=1.1.0=py36_2 - - jupyterlab=2.2.0=py_0 - - jupyterlab_server=1.2.0=py_0 - - krb5=1.17.1=hfafb76e_1 - - ld_impl_linux-64=2.34=h53a641e_7 - - libcurl=7.71.1=hcdd3856_3 - - libedit=3.1.20191231=h46ee950_1 - - libffi=3.2.1=he1b5a44_1007 - - libgcc-ng=9.2.0=h24d8f2e_2 - - libgomp=9.2.0=h24d8f2e_2 - - libsodium=1.0.17=h516909a_0 - - libssh2=1.9.0=hab1572f_4 - - libstdcxx-ng=9.2.0=hdf63c60_2 - - mako=1.1.0=py_0 - - markupsafe=1.1.1=py36h8c4c3a4_1 - - mistune=0.8.4=py36h8c4c3a4_1001 - - nbconvert=5.6.1=py36h9f0ad1d_1 - - nbformat=5.0.7=py_0 - - nbresuse=0.3.3=py_0 - - ncurses=6.2=he1b5a44_1 - - notebook=6.0.3=py36h9f0ad1d_1 - - nteract_on_jupyter=2.1.3=py_0 - - oauthlib=3.0.1=py_0 - - openssl=1.1.1g=h516909a_0 - - packaging=20.4=pyh9f0ad1d_0 - - pamela=1.0.0=py_0 - - pandoc=2.10.1=h516909a_0 - - pandocfilters=1.4.2=py_1 - - parso=0.7.1=pyh9f0ad1d_0 - - pexpect=4.8.0=py36h9f0ad1d_1 - - pickleshare=0.7.5=py36h9f0ad1d_1001 - - pip=20.1.1=py_1 - - prometheus_client=0.8.0=pyh9f0ad1d_0 - - prompt-toolkit=3.0.5=py_1 - - psutil=5.7.2=py36h8c4c3a4_0 - - ptyprocess=0.6.0=py_1001 - - pycparser=2.20=pyh9f0ad1d_2 - - pycurl=7.43.0.5=py36h2e6c563_2 - - pygments=2.6.1=py_0 - - pyjwt=1.7.1=py_0 - - pyopenssl=19.1.0=py_1 - - pyparsing=2.4.7=pyh9f0ad1d_0 - - pyrsistent=0.16.0=py36h8c4c3a4_0 - - pysocks=1.7.1=py36h9f0ad1d_1 - - python=3.6.11=h425cb1d_0_cpython - - python-dateutil=2.8.1=py_0 - - python-editor=1.0.4=py_0 - - python-json-logger=0.1.11=py_0 - - python_abi=3.6=1_cp36m - - pyzmq=19.0.1=py36h9947dbf_0 - - readline=8.0=he28a2e2_2 - - requests=2.24.0=pyh9f0ad1d_0 - - ruamel.yaml=0.16.6=py36h8c4c3a4_1 - - ruamel.yaml.clib=0.2.0=py36h8c4c3a4_1 - - send2trash=1.5.0=py_0 - - setuptools=49.2.0=py36h9f0ad1d_0 - - six=1.15.0=pyh9f0ad1d_0 - - sqlalchemy=1.3.18=py36h8c4c3a4_0 - - sqlite=3.32.3=hcee41ef_1 - - terminado=0.8.3=py36h9f0ad1d_1 - - testpath=0.4.4=py_0 - - tk=8.6.10=hed695b0_0 - - tornado=6.0.4=py36h8c4c3a4_1 - - traitlets=4.3.3=py36h9f0ad1d_1 - - urllib3=1.25.10=py_0 - - wcwidth=0.2.5=pyh9f0ad1d_0 - - webencodings=0.5.1=py_1 - - wheel=0.34.2=py_1 - - widgetsnbextension=3.5.1=py36h9f0ad1d_1 - - xz=5.2.5=h516909a_1 - - zeromq=4.3.2=he1b5a44_2 - - zipp=3.1.0=py_0 - - zlib=1.2.11=h516909a_1006 -prefix: /opt/conda/envs/r2d - +# Frozen on 2021-01-04 14:16:04 UTC diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.yml b/repo2docker/buildpacks/conda/environment.py-3.6.yml index 060efd06f..cb1a637fc 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.yml @@ -1,11 +1,11 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2020-07-25 17:14:29 UTC +# Generated on 2021-01-04 14:16:04 UTC dependencies: - python=3.6.* -- ipywidgets==7.5.1 -- jupyterlab==2.2.0 +- ipywidgets==7.6.0 +- jupyter-resource-usage==0.5.1 +- jupyterlab==3.0.0 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 -- nbresuse==0.3.3 - notebook==6.0.3 - nteract_on_jupyter==2.1.3 diff --git a/repo2docker/buildpacks/conda/environment.yml b/repo2docker/buildpacks/conda/environment.yml index 1d6c74917..9a627f712 100644 --- a/repo2docker/buildpacks/conda/environment.yml +++ b/repo2docker/buildpacks/conda/environment.yml @@ -1,9 +1,9 @@ dependencies: - python=3.7 - - ipywidgets==7.5.1 - - jupyterlab==2.2.0 + - ipywidgets==7.6.0 + - jupyter-resource-usage==0.5.1 + - jupyterlab==3.0.0 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 - - nbresuse==0.3.3 - notebook==6.0.3 - nteract_on_jupyter==2.1.3 diff --git a/repo2docker/buildpacks/conda/install-miniforge.bash b/repo2docker/buildpacks/conda/install-miniforge.bash index 08ed01b30..5eab7707d 100755 --- a/repo2docker/buildpacks/conda/install-miniforge.bash +++ b/repo2docker/buildpacks/conda/install-miniforge.bash @@ -50,15 +50,8 @@ time mamba env create -p ${NB_PYTHON_PREFIX} -f /tmp/environment.yml # Install jupyter-offline-notebook to allow users to download notebooks # after the server connection has been lost -# This will install and enable the extension for jupyter notebook +# This will install and enable the extension for the classic notebook and JupyterLab time ${NB_PYTHON_PREFIX}/bin/python -m pip install jupyter-offlinenotebook==0.1.0 -# and this installs it for lab. Keep going if the lab version is incompatible -# with the extension. -# Don't minimize build as it may fail, possibly due to excessive resource usage -# https://discourse.jupyter.org/t/tip-binder-jupyterlab-extension/6022 -# https://discourse.jupyter.org/t/jupyter-lab-build-hangs-and-then-fails-at-webpack-config-webpack-prod-minimize-config-js/6017/3 -time ${NB_PYTHON_PREFIX}/bin/jupyter labextension install --no-build jupyter-offlinenotebook && \ -time $NB_PYTHON_PREFIX/bin/jupyter lab build --minimize=False || \ true # empty conda history file, From bd08989717ba1252fb0c454cb2a10a0083f5ef54 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Mon, 4 Jan 2021 21:28:28 +0100 Subject: [PATCH 050/103] Add jupyter-offlinenotebook --- .../buildpacks/conda/environment.frozen.yml | 175 ++++++++++-------- .../conda/environment.py-2.7.frozen.yml | 2 +- .../conda/environment.py-3.6.frozen.yml | 134 +++++++++++++- .../buildpacks/conda/environment.py-3.6.yml | 5 +- .../conda/environment.py-3.7.frozen.yml | 175 ++++++++++-------- .../buildpacks/conda/environment.py-3.7.yml | 11 +- .../conda/environment.py-3.8.frozen.yml | 174 +++++++++-------- .../buildpacks/conda/environment.py-3.8.yml | 11 +- repo2docker/buildpacks/conda/environment.yml | 3 +- 9 files changed, 435 insertions(+), 255 deletions(-) diff --git a/repo2docker/buildpacks/conda/environment.frozen.yml b/repo2docker/buildpacks/conda/environment.frozen.yml index 95355ea67..109bd458a 100644 --- a/repo2docker/buildpacks/conda/environment.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2020-07-25 17:16:14 UTC +# Frozen on 2021-01-04 20:18:06 UTC name: r2d channels: - conda-forge @@ -7,110 +7,125 @@ channels: - conda-forge/label/broken dependencies: - _libgcc_mutex=0.1=conda_forge - - _openmp_mutex=4.5=0_gnu - - alembic=1.4.2=pyh9f0ad1d_0 + - _openmp_mutex=4.5=1_gnu + - alembic=1.4.3=pyh9f0ad1d_0 + - anyio=2.0.2=py37h89c1867_3 + - argon2-cffi=20.1.0=py37h4abf009_2 - async_generator=1.10=py_0 - - attrs=19.3.0=py_0 + - attrs=20.3.0=pyhd3deb0d_0 + - babel=2.9.0=pyhd3deb0d_0 - backcall=0.2.0=pyh9f0ad1d_0 - - bleach=3.1.5=pyh9f0ad1d_0 + - backports=1.0=py_2 + - backports.functools_lru_cache=1.6.1=py_0 + - bleach=3.2.1=pyh9f0ad1d_0 - blinker=1.4=py_1 - - brotlipy=0.7.0=py37h8f50634_1000 - - ca-certificates=2020.6.20=hecda079_0 - - certifi=2020.6.20=py37hc8dfbb8_0 + - brotlipy=0.7.0=py37hb5d75c8_1001 + - c-ares=1.17.1=h36c2ea0_0 + - ca-certificates=2020.12.5=ha878542_0 + - certifi=2020.12.5=py37h89c1867_0 - certipy=0.1.3=py_0 - - cffi=1.14.0=py37hd463f26_0 - - chardet=3.0.4=py37hc8dfbb8_1006 - - cryptography=3.0=py37hb09aad4_0 + - cffi=1.14.4=py37hc58025e_1 + - chardet=4.0.0=py37h89c1867_0 + - cryptography=3.3.1=py37h7f0c10b_0 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - - entrypoints=0.3=py37hc8dfbb8_1001 + - entrypoints=0.3=pyhd8ed1ab_1003 - idna=2.10=pyh9f0ad1d_0 - - importlib-metadata=1.7.0=py37hc8dfbb8_0 - - importlib_metadata=1.7.0=0 - - ipykernel=5.3.4=py37h43977f1_0 - - ipython=7.16.1=py37h43977f1_0 + - importlib-metadata=3.3.0=py37h89c1867_2 + - importlib_metadata=3.3.0=hd8ed1ab_2 + - ipykernel=5.4.2=py37h888b3d9_0 + - ipython=7.19.0=py37h888b3d9_0 - ipython_genutils=0.2.0=py_1 - - ipywidgets=7.5.1=py_0 - - jedi=0.17.2=py37hc8dfbb8_0 + - ipywidgets=7.6.0=pyhd3deb0d_0 + - jedi=0.18.0=py37h89c1867_1 - jinja2=2.11.2=pyh9f0ad1d_0 - - json5=0.9.4=pyh9f0ad1d_0 - - jsonschema=3.2.0=py37hc8dfbb8_1 - - jupyter_client=6.1.6=py_0 - - jupyter_core=4.6.3=py37hc8dfbb8_1 - - jupyter_telemetry=0.0.5=py_0 - - jupyterhub-base=1.1.0=py37_2 - - jupyterhub-singleuser=1.1.0=py37_2 - - jupyterlab=2.2.0=py_0 - - jupyterlab_server=1.2.0=py_0 - - krb5=1.17.1=hfafb76e_1 - - ld_impl_linux-64=2.34=h53a641e_7 - - libcurl=7.71.1=hcdd3856_3 - - libedit=3.1.20191231=h46ee950_1 - - libffi=3.2.1=he1b5a44_1007 - - libgcc-ng=9.2.0=h24d8f2e_2 - - libgomp=9.2.0=h24d8f2e_2 - - libsodium=1.0.17=h516909a_0 - - libssh2=1.9.0=hab1572f_4 - - libstdcxx-ng=9.2.0=hdf63c60_2 - - mako=1.1.0=py_0 - - markupsafe=1.1.1=py37h8f50634_1 - - mistune=0.8.4=py37h8f50634_1001 + - json5=0.9.5=pyh9f0ad1d_0 + - jsonschema=3.2.0=py_2 + - jupyter-offlinenotebook=0.1.0=pyh8c360ce_0 + - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 + - jupyter_client=6.1.7=py_0 + - jupyter_core=4.7.0=py37h89c1867_0 + - jupyter_server=1.1.3=py37h89c1867_1 + - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 + - jupyterhub-base=1.1.0=py37hc8dfbb8_5 + - jupyterhub-singleuser=1.1.0=py37hc8dfbb8_5 + - jupyterlab=3.0.0=pyhd8ed1ab_0 + - jupyterlab_server=2.0.0=pyhd8ed1ab_0 + - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 + - krb5=1.17.2=h926e7f8_0 + - ld_impl_linux-64=2.35.1=hea4e1c9_1 + - libcurl=7.71.1=hcdd3856_8 + - libedit=3.1.20191231=he28a2e2_2 + - libev=4.33=h516909a_1 + - libffi=3.3=h58526e2_2 + - libgcc-ng=9.3.0=h5dbcf3e_17 + - libgomp=9.3.0=h5dbcf3e_17 + - libnghttp2=1.41.0=h8cfc5f6_2 + - libsodium=1.0.18=h36c2ea0_1 + - libssh2=1.9.0=hab1572f_5 + - libstdcxx-ng=9.3.0=h2ae2ef3_17 + - mako=1.1.3=pyh9f0ad1d_0 + - markupsafe=1.1.1=py37hb5d75c8_2 + - mistune=0.8.4=py37h4abf009_1002 + - nbclassic=0.2.5=pyhd8ed1ab_0 - nbconvert=5.6.1=py37hc8dfbb8_1 - - nbformat=5.0.7=py_0 - - nbresuse=0.3.3=py_0 - - ncurses=6.2=he1b5a44_1 - - notebook=6.0.3=py37hc8dfbb8_1 + - nbformat=5.0.8=py_0 + - ncurses=6.2=h58526e2_4 + - notebook=6.1.6=py37h89c1867_0 - nteract_on_jupyter=2.1.3=py_0 - oauthlib=3.0.1=py_0 - - openssl=1.1.1g=h516909a_0 - - packaging=20.4=pyh9f0ad1d_0 + - openssl=1.1.1i=h7f98852_0 + - packaging=20.8=pyhd3deb0d_0 - pamela=1.0.0=py_0 - - pandoc=2.10.1=h516909a_0 + - pandoc=2.11.3.2=h7f98852_0 - pandocfilters=1.4.2=py_1 - - parso=0.7.1=pyh9f0ad1d_0 - - pexpect=4.8.0=py37hc8dfbb8_1 - - pickleshare=0.7.5=py37hc8dfbb8_1001 - - pip=20.1.1=py_1 - - prometheus_client=0.8.0=pyh9f0ad1d_0 - - prompt-toolkit=3.0.5=py_1 - - psutil=5.7.2=py37h8f50634_0 - - ptyprocess=0.6.0=py_1001 + - parso=0.8.1=pyhd8ed1ab_0 + - pexpect=4.8.0=pyh9f0ad1d_2 + - pickleshare=0.7.5=py_1003 + - pip=20.3.3=pyhd8ed1ab_0 + - prometheus_client=0.9.0=pyhd3deb0d_0 + - prompt-toolkit=3.0.8=pyha770c72_0 + - psutil=5.8.0=py37h5e8e339_0 + - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - - pycurl=7.43.0.5=py37hce7685b_2 - - pygments=2.6.1=py_0 + - pycurl=7.43.0.6=py37h21fb010_0 + - pygments=2.7.3=pyhd8ed1ab_0 - pyjwt=1.7.1=py_0 - - pyopenssl=19.1.0=py_1 + - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - - pyrsistent=0.16.0=py37h8f50634_0 - - pysocks=1.7.1=py37hc8dfbb8_1 - - python=3.7.8=h6f2ec95_0_cpython + - pyrsistent=0.17.3=py37h4abf009_1 + - pysocks=1.7.1=py37he5f6b98_2 + - python=3.7.9=hffdb5ce_0_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - - python-json-logger=0.1.11=py_0 + - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.7=1_cp37m - - pyzmq=19.0.1=py37hac76be4_0 + - pytz=2020.5=pyhd8ed1ab_0 + - pyzmq=20.0.0=py37h5a562af_1 - readline=8.0=he28a2e2_2 - - requests=2.24.0=pyh9f0ad1d_0 - - ruamel.yaml=0.16.6=py37h8f50634_1 - - ruamel.yaml.clib=0.2.0=py37h8f50634_1 + - requests=2.25.1=pyhd3deb0d_0 + - ruamel.yaml=0.16.12=py37h4abf009_1 + - ruamel.yaml.clib=0.2.2=py37h4abf009_1 - send2trash=1.5.0=py_0 - - setuptools=49.2.0=py37hc8dfbb8_0 + - setuptools=49.6.0=py37he5f6b98_2 - six=1.15.0=pyh9f0ad1d_0 - - sqlalchemy=1.3.18=py37h8f50634_0 - - sqlite=3.32.3=hcee41ef_1 - - terminado=0.8.3=py37hc8dfbb8_1 + - sniffio=1.2.0=py37h89c1867_0 + - sqlalchemy=1.3.22=py37h5e8e339_0 + - sqlite=3.34.0=h74cdb3f_0 + - terminado=0.9.1=py37h89c1867_1 - testpath=0.4.4=py_0 - - tk=8.6.10=hed695b0_0 - - tornado=6.0.4=py37h8f50634_1 - - traitlets=4.3.3=py37hc8dfbb8_1 - - urllib3=1.25.10=py_0 - - wcwidth=0.2.5=pyh9f0ad1d_0 + - tk=8.6.10=h21135ba_1 + - tornado=6.1=py37h4abf009_0 + - traitlets=5.0.5=py_0 + - typing_extensions=3.7.4.3=py_0 + - urllib3=1.26.2=pyhd8ed1ab_0 + - wcwidth=0.2.5=pyh9f0ad1d_2 - webencodings=0.5.1=py_1 - - wheel=0.34.2=py_1 - - widgetsnbextension=3.5.1=py37hc8dfbb8_1 + - wheel=0.36.2=pyhd3deb0d_0 + - widgetsnbextension=3.5.1=py37h89c1867_4 - xz=5.2.5=h516909a_1 - - zeromq=4.3.2=he1b5a44_2 - - zipp=3.1.0=py_0 - - zlib=1.2.11=h516909a_1006 + - zeromq=4.3.3=h58526e2_3 + - zipp=3.4.0=py_0 + - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d diff --git a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml index 62ad1c4ea..2803fa30b 100644 --- a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-2.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-04 14:15:02 UTC +# Frozen on 2021-01-04 20:15:02 UTC name: r2d channels: - conda-forge diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml index bef23e722..d04044b12 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml @@ -1,2 +1,134 @@ # AUTO GENERATED FROM environment.py-3.6.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-04 14:16:04 UTC +# Frozen on 2021-01-04 20:16:04 UTC +name: r2d +channels: + - conda-forge + - defaults + - conda-forge/label/broken +dependencies: + - _libgcc_mutex=0.1=conda_forge + - _openmp_mutex=4.5=1_gnu + - alembic=1.4.3=pyh9f0ad1d_0 + - anyio=2.0.2=py36h5fab9bb_3 + - argon2-cffi=20.1.0=py36h1d69622_2 + - async_generator=1.10=py_0 + - attrs=20.3.0=pyhd3deb0d_0 + - babel=2.9.0=pyhd3deb0d_0 + - backcall=0.2.0=pyh9f0ad1d_0 + - backports=1.0=py_2 + - backports.functools_lru_cache=1.6.1=py_0 + - bleach=3.2.1=pyh9f0ad1d_0 + - blinker=1.4=py_1 + - brotlipy=0.7.0=py36he6145b8_1001 + - c-ares=1.17.1=h36c2ea0_0 + - ca-certificates=2020.12.5=ha878542_0 + - certifi=2020.12.5=py36h5fab9bb_0 + - certipy=0.1.3=py_0 + - cffi=1.14.4=py36hc120d54_1 + - chardet=4.0.0=py36h5fab9bb_0 + - contextvars=2.4=py_0 + - cryptography=3.3.1=py36h0a59100_0 + - dataclasses=0.7=pyhe4b4509_6 + - decorator=4.4.2=py_0 + - defusedxml=0.6.0=py_0 + - entrypoints=0.3=pyhd8ed1ab_1003 + - idna=2.10=pyh9f0ad1d_0 + - immutables=0.14=py36h8c4c3a4_1 + - importlib-metadata=3.3.0=py36h5fab9bb_2 + - importlib_metadata=3.3.0=hd8ed1ab_2 + - ipykernel=5.4.2=py36he448a4c_0 + - ipython=7.16.1=py36h95af2a2_0 + - ipython_genutils=0.2.0=py_1 + - ipywidgets=7.6.0=pyhd3deb0d_0 + - jedi=0.18.0=py36h5fab9bb_1 + - jinja2=2.11.2=pyh9f0ad1d_0 + - json5=0.9.5=pyh9f0ad1d_0 + - jsonschema=3.2.0=py_2 + - jupyter-offlinenotebook=0.1.0=pyh8c360ce_0 + - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 + - jupyter_client=6.1.7=py_0 + - jupyter_core=4.7.0=py36h5fab9bb_0 + - jupyter_server=1.1.3=py36h5fab9bb_1 + - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 + - jupyterhub-base=1.1.0=py36h9f0ad1d_5 + - jupyterhub-singleuser=1.1.0=py36h9f0ad1d_5 + - jupyterlab=3.0.0=pyhd8ed1ab_0 + - jupyterlab_server=2.0.0=pyhd8ed1ab_0 + - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 + - krb5=1.17.2=h926e7f8_0 + - ld_impl_linux-64=2.35.1=hea4e1c9_1 + - libcurl=7.71.1=hcdd3856_8 + - libedit=3.1.20191231=he28a2e2_2 + - libev=4.33=h516909a_1 + - libffi=3.3=h58526e2_2 + - libgcc-ng=9.3.0=h5dbcf3e_17 + - libgomp=9.3.0=h5dbcf3e_17 + - libnghttp2=1.41.0=h8cfc5f6_2 + - libsodium=1.0.18=h36c2ea0_1 + - libssh2=1.9.0=hab1572f_5 + - libstdcxx-ng=9.3.0=h2ae2ef3_17 + - mako=1.1.3=pyh9f0ad1d_0 + - markupsafe=1.1.1=py36he6145b8_2 + - mistune=0.8.4=py36h1d69622_1002 + - nbclassic=0.2.5=pyhd8ed1ab_0 + - nbconvert=5.6.1=py36h9f0ad1d_1 + - nbformat=5.0.8=py_0 + - ncurses=6.2=h58526e2_4 + - notebook=6.1.6=py36h5fab9bb_0 + - nteract_on_jupyter=2.1.3=py_0 + - oauthlib=3.0.1=py_0 + - openssl=1.1.1i=h7f98852_0 + - packaging=20.8=pyhd3deb0d_0 + - pamela=1.0.0=py_0 + - pandoc=2.11.3.2=h7f98852_0 + - pandocfilters=1.4.2=py_1 + - parso=0.8.1=pyhd8ed1ab_0 + - pexpect=4.8.0=pyh9f0ad1d_2 + - pickleshare=0.7.5=py_1003 + - pip=20.3.3=pyhd8ed1ab_0 + - prometheus_client=0.9.0=pyhd3deb0d_0 + - prompt-toolkit=3.0.8=pyha770c72_0 + - psutil=5.8.0=py36h8f6f2f9_0 + - ptyprocess=0.7.0=pyhd3deb0d_0 + - pycparser=2.20=pyh9f0ad1d_2 + - pycurl=7.43.0.6=py36h5be270d_0 + - pygments=2.7.3=pyhd8ed1ab_0 + - pyjwt=1.7.1=py_0 + - pyopenssl=20.0.1=pyhd8ed1ab_0 + - pyparsing=2.4.7=pyh9f0ad1d_0 + - pyrsistent=0.17.3=py36h1d69622_1 + - pysocks=1.7.1=py36h9880bd3_2 + - python=3.6.12=hffdb5ce_0_cpython + - python-dateutil=2.8.1=py_0 + - python-editor=1.0.4=py_0 + - python-json-logger=2.0.1=pyh9f0ad1d_0 + - python_abi=3.6=1_cp36m + - pytz=2020.5=pyhd8ed1ab_0 + - pyzmq=20.0.0=py36h2b1bd32_1 + - readline=8.0=he28a2e2_2 + - requests=2.25.1=pyhd3deb0d_0 + - ruamel.yaml=0.16.12=py36h1d69622_1 + - ruamel.yaml.clib=0.2.2=py36h1d69622_1 + - send2trash=1.5.0=py_0 + - setuptools=49.6.0=py36h9880bd3_2 + - six=1.15.0=pyh9f0ad1d_0 + - sniffio=1.2.0=py36h5fab9bb_0 + - sqlalchemy=1.3.22=py36h8f6f2f9_0 + - sqlite=3.34.0=h74cdb3f_0 + - terminado=0.9.1=py36h5fab9bb_1 + - testpath=0.4.4=py_0 + - tk=8.6.10=h21135ba_1 + - tornado=6.1=py36h1d69622_0 + - traitlets=4.3.3=py36h9f0ad1d_1 + - typing_extensions=3.7.4.3=py_0 + - urllib3=1.26.2=pyhd8ed1ab_0 + - wcwidth=0.2.5=pyh9f0ad1d_2 + - webencodings=0.5.1=py_1 + - wheel=0.36.2=pyhd3deb0d_0 + - widgetsnbextension=3.5.1=py36h5fab9bb_4 + - xz=5.2.5=h516909a_1 + - zeromq=4.3.3=h58526e2_3 + - zipp=3.4.0=py_0 + - zlib=1.2.11=h516909a_1010 +prefix: /opt/conda/envs/r2d + diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.yml b/repo2docker/buildpacks/conda/environment.py-3.6.yml index cb1a637fc..8a49333a3 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.yml @@ -1,11 +1,12 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-01-04 14:16:04 UTC +# Generated on 2021-01-04 20:16:04 UTC dependencies: - python=3.6.* - ipywidgets==7.6.0 +- jupyter-offlinenotebook==0.1.0 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.0 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 -- notebook==6.0.3 +- notebook==6.1.6 - nteract_on_jupyter==2.1.3 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml index 95355ea67..109bd458a 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2020-07-25 17:16:14 UTC +# Frozen on 2021-01-04 20:18:06 UTC name: r2d channels: - conda-forge @@ -7,110 +7,125 @@ channels: - conda-forge/label/broken dependencies: - _libgcc_mutex=0.1=conda_forge - - _openmp_mutex=4.5=0_gnu - - alembic=1.4.2=pyh9f0ad1d_0 + - _openmp_mutex=4.5=1_gnu + - alembic=1.4.3=pyh9f0ad1d_0 + - anyio=2.0.2=py37h89c1867_3 + - argon2-cffi=20.1.0=py37h4abf009_2 - async_generator=1.10=py_0 - - attrs=19.3.0=py_0 + - attrs=20.3.0=pyhd3deb0d_0 + - babel=2.9.0=pyhd3deb0d_0 - backcall=0.2.0=pyh9f0ad1d_0 - - bleach=3.1.5=pyh9f0ad1d_0 + - backports=1.0=py_2 + - backports.functools_lru_cache=1.6.1=py_0 + - bleach=3.2.1=pyh9f0ad1d_0 - blinker=1.4=py_1 - - brotlipy=0.7.0=py37h8f50634_1000 - - ca-certificates=2020.6.20=hecda079_0 - - certifi=2020.6.20=py37hc8dfbb8_0 + - brotlipy=0.7.0=py37hb5d75c8_1001 + - c-ares=1.17.1=h36c2ea0_0 + - ca-certificates=2020.12.5=ha878542_0 + - certifi=2020.12.5=py37h89c1867_0 - certipy=0.1.3=py_0 - - cffi=1.14.0=py37hd463f26_0 - - chardet=3.0.4=py37hc8dfbb8_1006 - - cryptography=3.0=py37hb09aad4_0 + - cffi=1.14.4=py37hc58025e_1 + - chardet=4.0.0=py37h89c1867_0 + - cryptography=3.3.1=py37h7f0c10b_0 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - - entrypoints=0.3=py37hc8dfbb8_1001 + - entrypoints=0.3=pyhd8ed1ab_1003 - idna=2.10=pyh9f0ad1d_0 - - importlib-metadata=1.7.0=py37hc8dfbb8_0 - - importlib_metadata=1.7.0=0 - - ipykernel=5.3.4=py37h43977f1_0 - - ipython=7.16.1=py37h43977f1_0 + - importlib-metadata=3.3.0=py37h89c1867_2 + - importlib_metadata=3.3.0=hd8ed1ab_2 + - ipykernel=5.4.2=py37h888b3d9_0 + - ipython=7.19.0=py37h888b3d9_0 - ipython_genutils=0.2.0=py_1 - - ipywidgets=7.5.1=py_0 - - jedi=0.17.2=py37hc8dfbb8_0 + - ipywidgets=7.6.0=pyhd3deb0d_0 + - jedi=0.18.0=py37h89c1867_1 - jinja2=2.11.2=pyh9f0ad1d_0 - - json5=0.9.4=pyh9f0ad1d_0 - - jsonschema=3.2.0=py37hc8dfbb8_1 - - jupyter_client=6.1.6=py_0 - - jupyter_core=4.6.3=py37hc8dfbb8_1 - - jupyter_telemetry=0.0.5=py_0 - - jupyterhub-base=1.1.0=py37_2 - - jupyterhub-singleuser=1.1.0=py37_2 - - jupyterlab=2.2.0=py_0 - - jupyterlab_server=1.2.0=py_0 - - krb5=1.17.1=hfafb76e_1 - - ld_impl_linux-64=2.34=h53a641e_7 - - libcurl=7.71.1=hcdd3856_3 - - libedit=3.1.20191231=h46ee950_1 - - libffi=3.2.1=he1b5a44_1007 - - libgcc-ng=9.2.0=h24d8f2e_2 - - libgomp=9.2.0=h24d8f2e_2 - - libsodium=1.0.17=h516909a_0 - - libssh2=1.9.0=hab1572f_4 - - libstdcxx-ng=9.2.0=hdf63c60_2 - - mako=1.1.0=py_0 - - markupsafe=1.1.1=py37h8f50634_1 - - mistune=0.8.4=py37h8f50634_1001 + - json5=0.9.5=pyh9f0ad1d_0 + - jsonschema=3.2.0=py_2 + - jupyter-offlinenotebook=0.1.0=pyh8c360ce_0 + - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 + - jupyter_client=6.1.7=py_0 + - jupyter_core=4.7.0=py37h89c1867_0 + - jupyter_server=1.1.3=py37h89c1867_1 + - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 + - jupyterhub-base=1.1.0=py37hc8dfbb8_5 + - jupyterhub-singleuser=1.1.0=py37hc8dfbb8_5 + - jupyterlab=3.0.0=pyhd8ed1ab_0 + - jupyterlab_server=2.0.0=pyhd8ed1ab_0 + - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 + - krb5=1.17.2=h926e7f8_0 + - ld_impl_linux-64=2.35.1=hea4e1c9_1 + - libcurl=7.71.1=hcdd3856_8 + - libedit=3.1.20191231=he28a2e2_2 + - libev=4.33=h516909a_1 + - libffi=3.3=h58526e2_2 + - libgcc-ng=9.3.0=h5dbcf3e_17 + - libgomp=9.3.0=h5dbcf3e_17 + - libnghttp2=1.41.0=h8cfc5f6_2 + - libsodium=1.0.18=h36c2ea0_1 + - libssh2=1.9.0=hab1572f_5 + - libstdcxx-ng=9.3.0=h2ae2ef3_17 + - mako=1.1.3=pyh9f0ad1d_0 + - markupsafe=1.1.1=py37hb5d75c8_2 + - mistune=0.8.4=py37h4abf009_1002 + - nbclassic=0.2.5=pyhd8ed1ab_0 - nbconvert=5.6.1=py37hc8dfbb8_1 - - nbformat=5.0.7=py_0 - - nbresuse=0.3.3=py_0 - - ncurses=6.2=he1b5a44_1 - - notebook=6.0.3=py37hc8dfbb8_1 + - nbformat=5.0.8=py_0 + - ncurses=6.2=h58526e2_4 + - notebook=6.1.6=py37h89c1867_0 - nteract_on_jupyter=2.1.3=py_0 - oauthlib=3.0.1=py_0 - - openssl=1.1.1g=h516909a_0 - - packaging=20.4=pyh9f0ad1d_0 + - openssl=1.1.1i=h7f98852_0 + - packaging=20.8=pyhd3deb0d_0 - pamela=1.0.0=py_0 - - pandoc=2.10.1=h516909a_0 + - pandoc=2.11.3.2=h7f98852_0 - pandocfilters=1.4.2=py_1 - - parso=0.7.1=pyh9f0ad1d_0 - - pexpect=4.8.0=py37hc8dfbb8_1 - - pickleshare=0.7.5=py37hc8dfbb8_1001 - - pip=20.1.1=py_1 - - prometheus_client=0.8.0=pyh9f0ad1d_0 - - prompt-toolkit=3.0.5=py_1 - - psutil=5.7.2=py37h8f50634_0 - - ptyprocess=0.6.0=py_1001 + - parso=0.8.1=pyhd8ed1ab_0 + - pexpect=4.8.0=pyh9f0ad1d_2 + - pickleshare=0.7.5=py_1003 + - pip=20.3.3=pyhd8ed1ab_0 + - prometheus_client=0.9.0=pyhd3deb0d_0 + - prompt-toolkit=3.0.8=pyha770c72_0 + - psutil=5.8.0=py37h5e8e339_0 + - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - - pycurl=7.43.0.5=py37hce7685b_2 - - pygments=2.6.1=py_0 + - pycurl=7.43.0.6=py37h21fb010_0 + - pygments=2.7.3=pyhd8ed1ab_0 - pyjwt=1.7.1=py_0 - - pyopenssl=19.1.0=py_1 + - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - - pyrsistent=0.16.0=py37h8f50634_0 - - pysocks=1.7.1=py37hc8dfbb8_1 - - python=3.7.8=h6f2ec95_0_cpython + - pyrsistent=0.17.3=py37h4abf009_1 + - pysocks=1.7.1=py37he5f6b98_2 + - python=3.7.9=hffdb5ce_0_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - - python-json-logger=0.1.11=py_0 + - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.7=1_cp37m - - pyzmq=19.0.1=py37hac76be4_0 + - pytz=2020.5=pyhd8ed1ab_0 + - pyzmq=20.0.0=py37h5a562af_1 - readline=8.0=he28a2e2_2 - - requests=2.24.0=pyh9f0ad1d_0 - - ruamel.yaml=0.16.6=py37h8f50634_1 - - ruamel.yaml.clib=0.2.0=py37h8f50634_1 + - requests=2.25.1=pyhd3deb0d_0 + - ruamel.yaml=0.16.12=py37h4abf009_1 + - ruamel.yaml.clib=0.2.2=py37h4abf009_1 - send2trash=1.5.0=py_0 - - setuptools=49.2.0=py37hc8dfbb8_0 + - setuptools=49.6.0=py37he5f6b98_2 - six=1.15.0=pyh9f0ad1d_0 - - sqlalchemy=1.3.18=py37h8f50634_0 - - sqlite=3.32.3=hcee41ef_1 - - terminado=0.8.3=py37hc8dfbb8_1 + - sniffio=1.2.0=py37h89c1867_0 + - sqlalchemy=1.3.22=py37h5e8e339_0 + - sqlite=3.34.0=h74cdb3f_0 + - terminado=0.9.1=py37h89c1867_1 - testpath=0.4.4=py_0 - - tk=8.6.10=hed695b0_0 - - tornado=6.0.4=py37h8f50634_1 - - traitlets=4.3.3=py37hc8dfbb8_1 - - urllib3=1.25.10=py_0 - - wcwidth=0.2.5=pyh9f0ad1d_0 + - tk=8.6.10=h21135ba_1 + - tornado=6.1=py37h4abf009_0 + - traitlets=5.0.5=py_0 + - typing_extensions=3.7.4.3=py_0 + - urllib3=1.26.2=pyhd8ed1ab_0 + - wcwidth=0.2.5=pyh9f0ad1d_2 - webencodings=0.5.1=py_1 - - wheel=0.34.2=py_1 - - widgetsnbextension=3.5.1=py37hc8dfbb8_1 + - wheel=0.36.2=pyhd3deb0d_0 + - widgetsnbextension=3.5.1=py37h89c1867_4 - xz=5.2.5=h516909a_1 - - zeromq=4.3.2=he1b5a44_2 - - zipp=3.1.0=py_0 - - zlib=1.2.11=h516909a_1006 + - zeromq=4.3.3=h58526e2_3 + - zipp=3.4.0=py_0 + - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.yml b/repo2docker/buildpacks/conda/environment.py-3.7.yml index a5929ecc4..a41f45611 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.yml @@ -1,11 +1,12 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2020-07-25 17:16:14 UTC +# Generated on 2021-01-04 20:18:06 UTC dependencies: - python=3.7.* -- ipywidgets==7.5.1 -- jupyterlab==2.2.0 +- ipywidgets==7.6.0 +- jupyter-offlinenotebook==0.1.0 +- jupyter-resource-usage==0.5.1 +- jupyterlab==3.0.0 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 -- nbresuse==0.3.3 -- notebook==6.0.3 +- notebook==6.1.6 - nteract_on_jupyter==2.1.3 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml index 33e713436..82ecff765 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.8.yml, DO NOT MANUALLY MODIFY -# Frozen on 2020-07-25 17:17:57 UTC +# Frozen on 2021-01-04 20:20:02 UTC name: r2d channels: - conda-forge @@ -7,110 +7,124 @@ channels: - conda-forge/label/broken dependencies: - _libgcc_mutex=0.1=conda_forge - - _openmp_mutex=4.5=0_gnu - - alembic=1.4.2=pyh9f0ad1d_0 + - _openmp_mutex=4.5=1_gnu + - alembic=1.4.3=pyh9f0ad1d_0 + - anyio=2.0.2=py38h578d9bd_3 + - argon2-cffi=20.1.0=py38h25fe258_2 - async_generator=1.10=py_0 - - attrs=19.3.0=py_0 + - attrs=20.3.0=pyhd3deb0d_0 + - babel=2.9.0=pyhd3deb0d_0 - backcall=0.2.0=pyh9f0ad1d_0 - - bleach=3.1.5=pyh9f0ad1d_0 + - backports=1.0=py_2 + - backports.functools_lru_cache=1.6.1=py_0 + - bleach=3.2.1=pyh9f0ad1d_0 - blinker=1.4=py_1 - - brotlipy=0.7.0=py38h1e0a361_1000 - - ca-certificates=2020.6.20=hecda079_0 - - certifi=2020.6.20=py38h32f6830_0 + - brotlipy=0.7.0=py38h8df0ef7_1001 + - c-ares=1.17.1=h36c2ea0_0 + - ca-certificates=2020.12.5=ha878542_0 + - certifi=2020.12.5=py38h578d9bd_0 - certipy=0.1.3=py_0 - - cffi=1.14.0=py38hd463f26_0 - - chardet=3.0.4=py38h32f6830_1006 - - cryptography=3.0=py38h766eaa4_0 + - cffi=1.14.4=py38ha65f79e_1 + - chardet=4.0.0=py38h578d9bd_0 + - cryptography=3.3.1=py38h2b97feb_0 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - - entrypoints=0.3=py38h32f6830_1001 + - entrypoints=0.3=pyhd8ed1ab_1003 - idna=2.10=pyh9f0ad1d_0 - - importlib-metadata=1.7.0=py38h32f6830_0 - - importlib_metadata=1.7.0=0 - - ipykernel=5.3.4=py38h23f93f0_0 - - ipython=7.16.1=py38h23f93f0_0 + - importlib-metadata=3.3.0=py38h578d9bd_2 + - importlib_metadata=3.3.0=hd8ed1ab_2 + - ipykernel=5.4.2=py38h81c977d_0 + - ipython=7.19.0=py38h81c977d_0 - ipython_genutils=0.2.0=py_1 - - ipywidgets=7.5.1=py_0 - - jedi=0.17.2=py38h32f6830_0 + - ipywidgets=7.6.0=pyhd3deb0d_0 + - jedi=0.18.0=py38h578d9bd_1 - jinja2=2.11.2=pyh9f0ad1d_0 - - json5=0.9.4=pyh9f0ad1d_0 - - jsonschema=3.2.0=py38h32f6830_1 - - jupyter_client=6.1.6=py_0 - - jupyter_core=4.6.3=py38h32f6830_1 - - jupyter_telemetry=0.0.5=py_0 - - jupyterhub-base=1.1.0=py38_2 - - jupyterhub-singleuser=1.1.0=py38_2 - - jupyterlab=2.2.0=py_0 - - jupyterlab_server=1.2.0=py_0 - - krb5=1.17.1=hfafb76e_1 - - ld_impl_linux-64=2.34=h53a641e_7 - - libcurl=7.71.1=hcdd3856_3 - - libedit=3.1.20191231=h46ee950_1 - - libffi=3.2.1=he1b5a44_1007 - - libgcc-ng=9.2.0=h24d8f2e_2 - - libgomp=9.2.0=h24d8f2e_2 - - libsodium=1.0.17=h516909a_0 - - libssh2=1.9.0=hab1572f_4 - - libstdcxx-ng=9.2.0=hdf63c60_2 - - mako=1.1.0=py_0 - - markupsafe=1.1.1=py38h1e0a361_1 - - mistune=0.8.4=py38h1e0a361_1001 + - json5=0.9.5=pyh9f0ad1d_0 + - jsonschema=3.2.0=py_2 + - jupyter-offlinenotebook=0.1.0=pyh8c360ce_0 + - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 + - jupyter_client=6.1.7=py_0 + - jupyter_core=4.7.0=py38h578d9bd_0 + - jupyter_server=1.1.3=py38h578d9bd_1 + - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 + - jupyterhub-base=1.1.0=py38h32f6830_5 + - jupyterhub-singleuser=1.1.0=py38h32f6830_5 + - jupyterlab=3.0.0=pyhd8ed1ab_0 + - jupyterlab_server=2.0.0=pyhd8ed1ab_0 + - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 + - krb5=1.17.2=h926e7f8_0 + - ld_impl_linux-64=2.35.1=hea4e1c9_1 + - libcurl=7.71.1=hcdd3856_8 + - libedit=3.1.20191231=he28a2e2_2 + - libev=4.33=h516909a_1 + - libffi=3.3=h58526e2_2 + - libgcc-ng=9.3.0=h5dbcf3e_17 + - libgomp=9.3.0=h5dbcf3e_17 + - libnghttp2=1.41.0=h8cfc5f6_2 + - libsodium=1.0.18=h36c2ea0_1 + - libssh2=1.9.0=hab1572f_5 + - libstdcxx-ng=9.3.0=h2ae2ef3_17 + - mako=1.1.3=pyh9f0ad1d_0 + - markupsafe=1.1.1=py38h8df0ef7_2 + - mistune=0.8.4=py38h25fe258_1002 + - nbclassic=0.2.5=pyhd8ed1ab_0 - nbconvert=5.6.1=py38h32f6830_1 - - nbformat=5.0.7=py_0 - - nbresuse=0.3.3=py_0 - - ncurses=6.2=he1b5a44_1 - - notebook=6.0.3=py38h32f6830_1 + - nbformat=5.0.8=py_0 + - ncurses=6.2=h58526e2_4 + - notebook=6.1.6=py38h578d9bd_0 - nteract_on_jupyter=2.1.3=py_0 - oauthlib=3.0.1=py_0 - - openssl=1.1.1g=h516909a_0 - - packaging=20.4=pyh9f0ad1d_0 + - openssl=1.1.1i=h7f98852_0 + - packaging=20.8=pyhd3deb0d_0 - pamela=1.0.0=py_0 - - pandoc=2.10.1=h516909a_0 + - pandoc=2.11.3.2=h7f98852_0 - pandocfilters=1.4.2=py_1 - - parso=0.7.1=pyh9f0ad1d_0 - - pexpect=4.8.0=py38h32f6830_1 - - pickleshare=0.7.5=py38h32f6830_1001 - - pip=20.1.1=py_1 - - prometheus_client=0.8.0=pyh9f0ad1d_0 - - prompt-toolkit=3.0.5=py_1 - - psutil=5.7.2=py38h1e0a361_0 - - ptyprocess=0.6.0=py_1001 + - parso=0.8.1=pyhd8ed1ab_0 + - pexpect=4.8.0=pyh9f0ad1d_2 + - pickleshare=0.7.5=py_1003 + - pip=20.3.3=pyhd8ed1ab_0 + - prometheus_client=0.9.0=pyhd3deb0d_0 + - prompt-toolkit=3.0.8=pyha770c72_0 + - psutil=5.8.0=py38h497a2fe_0 + - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - - pycurl=7.43.0.5=py38h4400d41_2 - - pygments=2.6.1=py_0 + - pycurl=7.43.0.6=py38hd8bd627_0 + - pygments=2.7.3=pyhd8ed1ab_0 - pyjwt=1.7.1=py_0 - - pyopenssl=19.1.0=py_1 + - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - - pyrsistent=0.16.0=py38h1e0a361_0 - - pysocks=1.7.1=py38h32f6830_1 - - python=3.8.5=h425cb1d_1_cpython + - pyrsistent=0.17.3=py38h25fe258_1 + - pysocks=1.7.1=py38h924ce5b_2 + - python=3.8.6=hffdb5ce_4_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - - python-json-logger=0.1.11=py_0 + - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.8=1_cp38 - - pyzmq=19.0.1=py38ha71036d_0 + - pytz=2020.5=pyhd8ed1ab_0 + - pyzmq=20.0.0=py38h1d1b12f_1 - readline=8.0=he28a2e2_2 - - requests=2.24.0=pyh9f0ad1d_0 - - ruamel.yaml=0.16.6=py38h1e0a361_1 - - ruamel.yaml.clib=0.2.0=py38h1e0a361_1 + - requests=2.25.1=pyhd3deb0d_0 + - ruamel.yaml=0.16.12=py38h25fe258_1 + - ruamel.yaml.clib=0.2.2=py38h25fe258_1 - send2trash=1.5.0=py_0 - - setuptools=49.2.0=py38h32f6830_0 + - setuptools=49.6.0=py38h924ce5b_2 - six=1.15.0=pyh9f0ad1d_0 - - sqlalchemy=1.3.18=py38h1e0a361_0 - - sqlite=3.32.3=hcee41ef_1 - - terminado=0.8.3=py38h32f6830_1 + - sniffio=1.2.0=py38h578d9bd_0 + - sqlalchemy=1.3.22=py38h497a2fe_0 + - sqlite=3.34.0=h74cdb3f_0 + - terminado=0.9.1=py38h32f6830_1 - testpath=0.4.4=py_0 - - tk=8.6.10=hed695b0_0 - - tornado=6.0.4=py38h1e0a361_1 - - traitlets=4.3.3=py38h32f6830_1 - - urllib3=1.25.10=py_0 - - wcwidth=0.2.5=pyh9f0ad1d_0 + - tk=8.6.10=h21135ba_1 + - tornado=6.1=py38h25fe258_0 + - traitlets=5.0.5=py_0 + - urllib3=1.26.2=pyhd8ed1ab_0 + - wcwidth=0.2.5=pyh9f0ad1d_2 - webencodings=0.5.1=py_1 - - wheel=0.34.2=py_1 - - widgetsnbextension=3.5.1=py38h32f6830_1 + - wheel=0.36.2=pyhd3deb0d_0 + - widgetsnbextension=3.5.1=py38h578d9bd_4 - xz=5.2.5=h516909a_1 - - zeromq=4.3.2=he1b5a44_2 - - zipp=3.1.0=py_0 - - zlib=1.2.11=h516909a_1006 + - zeromq=4.3.3=h58526e2_3 + - zipp=3.4.0=py_0 + - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.yml b/repo2docker/buildpacks/conda/environment.py-3.8.yml index d6bc4274c..e2965eff2 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.yml @@ -1,11 +1,12 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2020-07-25 17:17:57 UTC +# Generated on 2021-01-04 20:20:02 UTC dependencies: - python=3.8.* -- ipywidgets==7.5.1 -- jupyterlab==2.2.0 +- ipywidgets==7.6.0 +- jupyter-offlinenotebook==0.1.0 +- jupyter-resource-usage==0.5.1 +- jupyterlab==3.0.0 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 -- nbresuse==0.3.3 -- notebook==6.0.3 +- notebook==6.1.6 - nteract_on_jupyter==2.1.3 diff --git a/repo2docker/buildpacks/conda/environment.yml b/repo2docker/buildpacks/conda/environment.yml index 9a627f712..67314af9c 100644 --- a/repo2docker/buildpacks/conda/environment.yml +++ b/repo2docker/buildpacks/conda/environment.yml @@ -1,9 +1,10 @@ dependencies: - python=3.7 - ipywidgets==7.6.0 + - jupyter-offlinenotebook==0.1.0 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.0 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 - - notebook==6.0.3 + - notebook==6.1.6 - nteract_on_jupyter==2.1.3 From e0432d37e7ec15bdde81183cc6fe38e90752a2e7 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Wed, 13 Jan 2021 13:17:15 +0100 Subject: [PATCH 051/103] Update to jupyter-offlinenotebook==0.2.0 --- .../buildpacks/conda/environment.frozen.yml | 60 +++++++++--------- .../conda/environment.py-2.7.frozen.yml | 2 +- .../conda/environment.py-3.6.frozen.yml | 62 +++++++++---------- .../buildpacks/conda/environment.py-3.6.yml | 4 +- .../conda/environment.py-3.7.frozen.yml | 60 +++++++++--------- .../buildpacks/conda/environment.py-3.7.yml | 4 +- .../conda/environment.py-3.8.frozen.yml | 60 +++++++++--------- .../buildpacks/conda/environment.py-3.8.yml | 4 +- repo2docker/buildpacks/conda/environment.yml | 2 +- .../buildpacks/conda/install-miniforge.bash | 6 -- 10 files changed, 129 insertions(+), 135 deletions(-) diff --git a/repo2docker/buildpacks/conda/environment.frozen.yml b/repo2docker/buildpacks/conda/environment.frozen.yml index 109bd458a..dc2085cff 100644 --- a/repo2docker/buildpacks/conda/environment.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-04 20:18:06 UTC +# Frozen on 2021-01-13 12:05:35 UTC name: r2d channels: - conda-forge @@ -9,8 +9,8 @@ dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - alembic=1.4.3=pyh9f0ad1d_0 - - anyio=2.0.2=py37h89c1867_3 - - argon2-cffi=20.1.0=py37h4abf009_2 + - anyio=2.0.2=py37h89c1867_4 + - argon2-cffi=20.1.0=py37h5e8e339_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 - babel=2.9.0=pyhd3deb0d_0 @@ -22,35 +22,35 @@ dependencies: - brotlipy=0.7.0=py37hb5d75c8_1001 - c-ares=1.17.1=h36c2ea0_0 - ca-certificates=2020.12.5=ha878542_0 - - certifi=2020.12.5=py37h89c1867_0 + - certifi=2020.12.5=py37h89c1867_1 - certipy=0.1.3=py_0 - cffi=1.14.4=py37hc58025e_1 - - chardet=4.0.0=py37h89c1867_0 - - cryptography=3.3.1=py37h7f0c10b_0 + - chardet=4.0.0=py37h89c1867_1 + - cryptography=3.3.1=py37h7f0c10b_1 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - entrypoints=0.3=pyhd8ed1ab_1003 - idna=2.10=pyh9f0ad1d_0 - - importlib-metadata=3.3.0=py37h89c1867_2 - - importlib_metadata=3.3.0=hd8ed1ab_2 + - importlib-metadata=3.4.0=py37h89c1867_0 + - importlib_metadata=3.4.0=hd8ed1ab_0 - ipykernel=5.4.2=py37h888b3d9_0 - ipython=7.19.0=py37h888b3d9_0 - ipython_genutils=0.2.0=py_1 - ipywidgets=7.6.0=pyhd3deb0d_0 - - jedi=0.18.0=py37h89c1867_1 + - jedi=0.18.0=py37h89c1867_2 - jinja2=2.11.2=pyh9f0ad1d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - - jupyter-offlinenotebook=0.1.0=pyh8c360ce_0 + - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - - jupyter_client=6.1.7=py_0 + - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.0=py37h89c1867_0 - - jupyter_server=1.1.3=py37h89c1867_1 + - jupyter_server=1.2.1=py37h89c1867_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py37hc8dfbb8_5 - jupyterhub-singleuser=1.1.0=py37hc8dfbb8_5 - jupyterlab=3.0.0=pyhd8ed1ab_0 - - jupyterlab_server=2.0.0=pyhd8ed1ab_0 + - jupyterlab_server=2.1.2=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - ld_impl_linux-64=2.35.1=hea4e1c9_1 @@ -65,9 +65,9 @@ dependencies: - libssh2=1.9.0=hab1572f_5 - libstdcxx-ng=9.3.0=h2ae2ef3_17 - mako=1.1.3=pyh9f0ad1d_0 - - markupsafe=1.1.1=py37hb5d75c8_2 - - mistune=0.8.4=py37h4abf009_1002 - - nbclassic=0.2.5=pyhd8ed1ab_0 + - markupsafe=1.1.1=py37h5e8e339_3 + - mistune=0.8.4=py37h5e8e339_1003 + - nbclassic=0.2.6=pyhd8ed1ab_0 - nbconvert=5.6.1=py37hc8dfbb8_1 - nbformat=5.0.8=py_0 - ncurses=6.2=h58526e2_4 @@ -84,38 +84,38 @@ dependencies: - pickleshare=0.7.5=py_1003 - pip=20.3.3=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - - prompt-toolkit=3.0.8=pyha770c72_0 - - psutil=5.8.0=py37h5e8e339_0 + - prompt-toolkit=3.0.10=pyha770c72_0 + - psutil=5.8.0=py37h5e8e339_1 - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - - pycurl=7.43.0.6=py37h21fb010_0 + - pycurl=7.43.0.6=py37h88a64d2_1 - pygments=2.7.3=pyhd8ed1ab_0 - - pyjwt=1.7.1=py_0 + - pyjwt=2.0.0=pyhd8ed1ab_0 - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - - pyrsistent=0.17.3=py37h4abf009_1 - - pysocks=1.7.1=py37he5f6b98_2 + - pyrsistent=0.17.3=py37h5e8e339_2 + - pysocks=1.7.1=py37h89c1867_3 - python=3.7.9=hffdb5ce_0_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.7=1_cp37m - pytz=2020.5=pyhd8ed1ab_0 - - pyzmq=20.0.0=py37h5a562af_1 + - pyzmq=20.0.0=py37h499b945_1 - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - - ruamel.yaml=0.16.12=py37h4abf009_1 - - ruamel.yaml.clib=0.2.2=py37h4abf009_1 + - ruamel.yaml=0.16.12=py37h5e8e339_2 + - ruamel.yaml.clib=0.2.2=py37h5e8e339_2 - send2trash=1.5.0=py_0 - - setuptools=49.6.0=py37he5f6b98_2 + - setuptools=49.6.0=py37h89c1867_3 - six=1.15.0=pyh9f0ad1d_0 - - sniffio=1.2.0=py37h89c1867_0 - - sqlalchemy=1.3.22=py37h5e8e339_0 + - sniffio=1.2.0=py37h89c1867_1 + - sqlalchemy=1.3.22=py37h5e8e339_1 - sqlite=3.34.0=h74cdb3f_0 - - terminado=0.9.1=py37h89c1867_1 + - terminado=0.9.2=py37h89c1867_0 - testpath=0.4.4=py_0 - tk=8.6.10=h21135ba_1 - - tornado=6.1=py37h4abf009_0 + - tornado=6.1=py37h5e8e339_1 - traitlets=5.0.5=py_0 - typing_extensions=3.7.4.3=py_0 - urllib3=1.26.2=pyhd8ed1ab_0 diff --git a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml index 2803fa30b..b6982a7d1 100644 --- a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-2.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-04 20:15:02 UTC +# Frozen on 2021-01-13 12:00:52 UTC name: r2d channels: - conda-forge diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml index d04044b12..64d0b710d 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.6.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-04 20:16:04 UTC +# Frozen on 2021-01-13 12:02:30 UTC name: r2d channels: - conda-forge @@ -9,8 +9,8 @@ dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - alembic=1.4.3=pyh9f0ad1d_0 - - anyio=2.0.2=py36h5fab9bb_3 - - argon2-cffi=20.1.0=py36h1d69622_2 + - anyio=2.0.2=py36h5fab9bb_4 + - argon2-cffi=20.1.0=py36h8f6f2f9_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 - babel=2.9.0=pyhd3deb0d_0 @@ -22,38 +22,38 @@ dependencies: - brotlipy=0.7.0=py36he6145b8_1001 - c-ares=1.17.1=h36c2ea0_0 - ca-certificates=2020.12.5=ha878542_0 - - certifi=2020.12.5=py36h5fab9bb_0 + - certifi=2020.12.5=py36h5fab9bb_1 - certipy=0.1.3=py_0 - cffi=1.14.4=py36hc120d54_1 - - chardet=4.0.0=py36h5fab9bb_0 + - chardet=4.0.0=py36h5fab9bb_1 - contextvars=2.4=py_0 - - cryptography=3.3.1=py36h0a59100_0 + - cryptography=3.3.1=py36h0a59100_1 - dataclasses=0.7=pyhe4b4509_6 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - entrypoints=0.3=pyhd8ed1ab_1003 - idna=2.10=pyh9f0ad1d_0 - - immutables=0.14=py36h8c4c3a4_1 - - importlib-metadata=3.3.0=py36h5fab9bb_2 - - importlib_metadata=3.3.0=hd8ed1ab_2 + - immutables=0.14=py36h8f6f2f9_1 + - importlib-metadata=3.4.0=py36h5fab9bb_0 + - importlib_metadata=3.4.0=hd8ed1ab_0 - ipykernel=5.4.2=py36he448a4c_0 - ipython=7.16.1=py36h95af2a2_0 - ipython_genutils=0.2.0=py_1 - ipywidgets=7.6.0=pyhd3deb0d_0 - - jedi=0.18.0=py36h5fab9bb_1 + - jedi=0.18.0=py36h5fab9bb_2 - jinja2=2.11.2=pyh9f0ad1d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - - jupyter-offlinenotebook=0.1.0=pyh8c360ce_0 + - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - - jupyter_client=6.1.7=py_0 + - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.0=py36h5fab9bb_0 - - jupyter_server=1.1.3=py36h5fab9bb_1 + - jupyter_server=1.2.1=py36h5fab9bb_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py36h9f0ad1d_5 - jupyterhub-singleuser=1.1.0=py36h9f0ad1d_5 - jupyterlab=3.0.0=pyhd8ed1ab_0 - - jupyterlab_server=2.0.0=pyhd8ed1ab_0 + - jupyterlab_server=2.1.2=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - ld_impl_linux-64=2.35.1=hea4e1c9_1 @@ -68,9 +68,9 @@ dependencies: - libssh2=1.9.0=hab1572f_5 - libstdcxx-ng=9.3.0=h2ae2ef3_17 - mako=1.1.3=pyh9f0ad1d_0 - - markupsafe=1.1.1=py36he6145b8_2 - - mistune=0.8.4=py36h1d69622_1002 - - nbclassic=0.2.5=pyhd8ed1ab_0 + - markupsafe=1.1.1=py36h8f6f2f9_3 + - mistune=0.8.4=py36h8f6f2f9_1003 + - nbclassic=0.2.6=pyhd8ed1ab_0 - nbconvert=5.6.1=py36h9f0ad1d_1 - nbformat=5.0.8=py_0 - ncurses=6.2=h58526e2_4 @@ -87,38 +87,38 @@ dependencies: - pickleshare=0.7.5=py_1003 - pip=20.3.3=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - - prompt-toolkit=3.0.8=pyha770c72_0 - - psutil=5.8.0=py36h8f6f2f9_0 + - prompt-toolkit=3.0.10=pyha770c72_0 + - psutil=5.8.0=py36h8f6f2f9_1 - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - - pycurl=7.43.0.6=py36h5be270d_0 + - pycurl=7.43.0.6=py36h66a4f8d_1 - pygments=2.7.3=pyhd8ed1ab_0 - - pyjwt=1.7.1=py_0 + - pyjwt=2.0.0=pyhd8ed1ab_0 - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - - pyrsistent=0.17.3=py36h1d69622_1 - - pysocks=1.7.1=py36h9880bd3_2 + - pyrsistent=0.17.3=py36h8f6f2f9_2 + - pysocks=1.7.1=py36h5fab9bb_3 - python=3.6.12=hffdb5ce_0_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.6=1_cp36m - pytz=2020.5=pyhd8ed1ab_0 - - pyzmq=20.0.0=py36h2b1bd32_1 + - pyzmq=20.0.0=py36h81c33ee_1 - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - - ruamel.yaml=0.16.12=py36h1d69622_1 - - ruamel.yaml.clib=0.2.2=py36h1d69622_1 + - ruamel.yaml=0.16.12=py36h8f6f2f9_2 + - ruamel.yaml.clib=0.2.2=py36h8f6f2f9_2 - send2trash=1.5.0=py_0 - - setuptools=49.6.0=py36h9880bd3_2 + - setuptools=49.6.0=py36h5fab9bb_3 - six=1.15.0=pyh9f0ad1d_0 - - sniffio=1.2.0=py36h5fab9bb_0 - - sqlalchemy=1.3.22=py36h8f6f2f9_0 + - sniffio=1.2.0=py36h5fab9bb_1 + - sqlalchemy=1.3.22=py36h8f6f2f9_1 - sqlite=3.34.0=h74cdb3f_0 - - terminado=0.9.1=py36h5fab9bb_1 + - terminado=0.9.2=py36h5fab9bb_0 - testpath=0.4.4=py_0 - tk=8.6.10=h21135ba_1 - - tornado=6.1=py36h1d69622_0 + - tornado=6.1=py36h8f6f2f9_1 - traitlets=4.3.3=py36h9f0ad1d_1 - typing_extensions=3.7.4.3=py_0 - urllib3=1.26.2=pyhd8ed1ab_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.yml b/repo2docker/buildpacks/conda/environment.py-3.6.yml index 8a49333a3..a81defc27 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.yml @@ -1,9 +1,9 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-01-04 20:16:04 UTC +# Generated on 2021-01-13 12:02:30 UTC dependencies: - python=3.6.* - ipywidgets==7.6.0 -- jupyter-offlinenotebook==0.1.0 +- jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.0 - jupyterhub-singleuser==1.1.0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml index 109bd458a..dc2085cff 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-04 20:18:06 UTC +# Frozen on 2021-01-13 12:05:35 UTC name: r2d channels: - conda-forge @@ -9,8 +9,8 @@ dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - alembic=1.4.3=pyh9f0ad1d_0 - - anyio=2.0.2=py37h89c1867_3 - - argon2-cffi=20.1.0=py37h4abf009_2 + - anyio=2.0.2=py37h89c1867_4 + - argon2-cffi=20.1.0=py37h5e8e339_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 - babel=2.9.0=pyhd3deb0d_0 @@ -22,35 +22,35 @@ dependencies: - brotlipy=0.7.0=py37hb5d75c8_1001 - c-ares=1.17.1=h36c2ea0_0 - ca-certificates=2020.12.5=ha878542_0 - - certifi=2020.12.5=py37h89c1867_0 + - certifi=2020.12.5=py37h89c1867_1 - certipy=0.1.3=py_0 - cffi=1.14.4=py37hc58025e_1 - - chardet=4.0.0=py37h89c1867_0 - - cryptography=3.3.1=py37h7f0c10b_0 + - chardet=4.0.0=py37h89c1867_1 + - cryptography=3.3.1=py37h7f0c10b_1 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - entrypoints=0.3=pyhd8ed1ab_1003 - idna=2.10=pyh9f0ad1d_0 - - importlib-metadata=3.3.0=py37h89c1867_2 - - importlib_metadata=3.3.0=hd8ed1ab_2 + - importlib-metadata=3.4.0=py37h89c1867_0 + - importlib_metadata=3.4.0=hd8ed1ab_0 - ipykernel=5.4.2=py37h888b3d9_0 - ipython=7.19.0=py37h888b3d9_0 - ipython_genutils=0.2.0=py_1 - ipywidgets=7.6.0=pyhd3deb0d_0 - - jedi=0.18.0=py37h89c1867_1 + - jedi=0.18.0=py37h89c1867_2 - jinja2=2.11.2=pyh9f0ad1d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - - jupyter-offlinenotebook=0.1.0=pyh8c360ce_0 + - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - - jupyter_client=6.1.7=py_0 + - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.0=py37h89c1867_0 - - jupyter_server=1.1.3=py37h89c1867_1 + - jupyter_server=1.2.1=py37h89c1867_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py37hc8dfbb8_5 - jupyterhub-singleuser=1.1.0=py37hc8dfbb8_5 - jupyterlab=3.0.0=pyhd8ed1ab_0 - - jupyterlab_server=2.0.0=pyhd8ed1ab_0 + - jupyterlab_server=2.1.2=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - ld_impl_linux-64=2.35.1=hea4e1c9_1 @@ -65,9 +65,9 @@ dependencies: - libssh2=1.9.0=hab1572f_5 - libstdcxx-ng=9.3.0=h2ae2ef3_17 - mako=1.1.3=pyh9f0ad1d_0 - - markupsafe=1.1.1=py37hb5d75c8_2 - - mistune=0.8.4=py37h4abf009_1002 - - nbclassic=0.2.5=pyhd8ed1ab_0 + - markupsafe=1.1.1=py37h5e8e339_3 + - mistune=0.8.4=py37h5e8e339_1003 + - nbclassic=0.2.6=pyhd8ed1ab_0 - nbconvert=5.6.1=py37hc8dfbb8_1 - nbformat=5.0.8=py_0 - ncurses=6.2=h58526e2_4 @@ -84,38 +84,38 @@ dependencies: - pickleshare=0.7.5=py_1003 - pip=20.3.3=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - - prompt-toolkit=3.0.8=pyha770c72_0 - - psutil=5.8.0=py37h5e8e339_0 + - prompt-toolkit=3.0.10=pyha770c72_0 + - psutil=5.8.0=py37h5e8e339_1 - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - - pycurl=7.43.0.6=py37h21fb010_0 + - pycurl=7.43.0.6=py37h88a64d2_1 - pygments=2.7.3=pyhd8ed1ab_0 - - pyjwt=1.7.1=py_0 + - pyjwt=2.0.0=pyhd8ed1ab_0 - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - - pyrsistent=0.17.3=py37h4abf009_1 - - pysocks=1.7.1=py37he5f6b98_2 + - pyrsistent=0.17.3=py37h5e8e339_2 + - pysocks=1.7.1=py37h89c1867_3 - python=3.7.9=hffdb5ce_0_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.7=1_cp37m - pytz=2020.5=pyhd8ed1ab_0 - - pyzmq=20.0.0=py37h5a562af_1 + - pyzmq=20.0.0=py37h499b945_1 - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - - ruamel.yaml=0.16.12=py37h4abf009_1 - - ruamel.yaml.clib=0.2.2=py37h4abf009_1 + - ruamel.yaml=0.16.12=py37h5e8e339_2 + - ruamel.yaml.clib=0.2.2=py37h5e8e339_2 - send2trash=1.5.0=py_0 - - setuptools=49.6.0=py37he5f6b98_2 + - setuptools=49.6.0=py37h89c1867_3 - six=1.15.0=pyh9f0ad1d_0 - - sniffio=1.2.0=py37h89c1867_0 - - sqlalchemy=1.3.22=py37h5e8e339_0 + - sniffio=1.2.0=py37h89c1867_1 + - sqlalchemy=1.3.22=py37h5e8e339_1 - sqlite=3.34.0=h74cdb3f_0 - - terminado=0.9.1=py37h89c1867_1 + - terminado=0.9.2=py37h89c1867_0 - testpath=0.4.4=py_0 - tk=8.6.10=h21135ba_1 - - tornado=6.1=py37h4abf009_0 + - tornado=6.1=py37h5e8e339_1 - traitlets=5.0.5=py_0 - typing_extensions=3.7.4.3=py_0 - urllib3=1.26.2=pyhd8ed1ab_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.yml b/repo2docker/buildpacks/conda/environment.py-3.7.yml index a41f45611..d09ad5e39 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.yml @@ -1,9 +1,9 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-01-04 20:18:06 UTC +# Generated on 2021-01-13 12:05:35 UTC dependencies: - python=3.7.* - ipywidgets==7.6.0 -- jupyter-offlinenotebook==0.1.0 +- jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.0 - jupyterhub-singleuser==1.1.0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml index 82ecff765..59cf868cf 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.8.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-04 20:20:02 UTC +# Frozen on 2021-01-13 12:09:06 UTC name: r2d channels: - conda-forge @@ -9,8 +9,8 @@ dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - alembic=1.4.3=pyh9f0ad1d_0 - - anyio=2.0.2=py38h578d9bd_3 - - argon2-cffi=20.1.0=py38h25fe258_2 + - anyio=2.0.2=py38h578d9bd_4 + - argon2-cffi=20.1.0=py38h497a2fe_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 - babel=2.9.0=pyhd3deb0d_0 @@ -22,35 +22,35 @@ dependencies: - brotlipy=0.7.0=py38h8df0ef7_1001 - c-ares=1.17.1=h36c2ea0_0 - ca-certificates=2020.12.5=ha878542_0 - - certifi=2020.12.5=py38h578d9bd_0 + - certifi=2020.12.5=py38h578d9bd_1 - certipy=0.1.3=py_0 - cffi=1.14.4=py38ha65f79e_1 - - chardet=4.0.0=py38h578d9bd_0 - - cryptography=3.3.1=py38h2b97feb_0 + - chardet=4.0.0=py38h578d9bd_1 + - cryptography=3.3.1=py38h2b97feb_1 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - entrypoints=0.3=pyhd8ed1ab_1003 - idna=2.10=pyh9f0ad1d_0 - - importlib-metadata=3.3.0=py38h578d9bd_2 - - importlib_metadata=3.3.0=hd8ed1ab_2 + - importlib-metadata=3.4.0=py38h578d9bd_0 + - importlib_metadata=3.4.0=hd8ed1ab_0 - ipykernel=5.4.2=py38h81c977d_0 - ipython=7.19.0=py38h81c977d_0 - ipython_genutils=0.2.0=py_1 - ipywidgets=7.6.0=pyhd3deb0d_0 - - jedi=0.18.0=py38h578d9bd_1 + - jedi=0.18.0=py38h578d9bd_2 - jinja2=2.11.2=pyh9f0ad1d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - - jupyter-offlinenotebook=0.1.0=pyh8c360ce_0 + - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - - jupyter_client=6.1.7=py_0 + - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.0=py38h578d9bd_0 - - jupyter_server=1.1.3=py38h578d9bd_1 + - jupyter_server=1.2.1=py38h578d9bd_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py38h32f6830_5 - jupyterhub-singleuser=1.1.0=py38h32f6830_5 - jupyterlab=3.0.0=pyhd8ed1ab_0 - - jupyterlab_server=2.0.0=pyhd8ed1ab_0 + - jupyterlab_server=2.1.2=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - ld_impl_linux-64=2.35.1=hea4e1c9_1 @@ -65,9 +65,9 @@ dependencies: - libssh2=1.9.0=hab1572f_5 - libstdcxx-ng=9.3.0=h2ae2ef3_17 - mako=1.1.3=pyh9f0ad1d_0 - - markupsafe=1.1.1=py38h8df0ef7_2 - - mistune=0.8.4=py38h25fe258_1002 - - nbclassic=0.2.5=pyhd8ed1ab_0 + - markupsafe=1.1.1=py38h497a2fe_3 + - mistune=0.8.4=py38h497a2fe_1003 + - nbclassic=0.2.6=pyhd8ed1ab_0 - nbconvert=5.6.1=py38h32f6830_1 - nbformat=5.0.8=py_0 - ncurses=6.2=h58526e2_4 @@ -84,38 +84,38 @@ dependencies: - pickleshare=0.7.5=py_1003 - pip=20.3.3=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - - prompt-toolkit=3.0.8=pyha770c72_0 - - psutil=5.8.0=py38h497a2fe_0 + - prompt-toolkit=3.0.10=pyha770c72_0 + - psutil=5.8.0=py38h497a2fe_1 - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - - pycurl=7.43.0.6=py38hd8bd627_0 + - pycurl=7.43.0.6=py38h996a351_1 - pygments=2.7.3=pyhd8ed1ab_0 - - pyjwt=1.7.1=py_0 + - pyjwt=2.0.0=pyhd8ed1ab_0 - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - - pyrsistent=0.17.3=py38h25fe258_1 - - pysocks=1.7.1=py38h924ce5b_2 + - pyrsistent=0.17.3=py38h497a2fe_2 + - pysocks=1.7.1=py38h578d9bd_3 - python=3.8.6=hffdb5ce_4_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.8=1_cp38 - pytz=2020.5=pyhd8ed1ab_0 - - pyzmq=20.0.0=py38h1d1b12f_1 + - pyzmq=20.0.0=py38h3d7ac18_1 - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - - ruamel.yaml=0.16.12=py38h25fe258_1 - - ruamel.yaml.clib=0.2.2=py38h25fe258_1 + - ruamel.yaml=0.16.12=py38h497a2fe_2 + - ruamel.yaml.clib=0.2.2=py38h497a2fe_2 - send2trash=1.5.0=py_0 - - setuptools=49.6.0=py38h924ce5b_2 + - setuptools=49.6.0=py38h578d9bd_3 - six=1.15.0=pyh9f0ad1d_0 - - sniffio=1.2.0=py38h578d9bd_0 - - sqlalchemy=1.3.22=py38h497a2fe_0 + - sniffio=1.2.0=py38h578d9bd_1 + - sqlalchemy=1.3.22=py38h497a2fe_1 - sqlite=3.34.0=h74cdb3f_0 - - terminado=0.9.1=py38h32f6830_1 + - terminado=0.9.2=py38h578d9bd_0 - testpath=0.4.4=py_0 - tk=8.6.10=h21135ba_1 - - tornado=6.1=py38h25fe258_0 + - tornado=6.1=py38h497a2fe_1 - traitlets=5.0.5=py_0 - urllib3=1.26.2=pyhd8ed1ab_0 - wcwidth=0.2.5=pyh9f0ad1d_2 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.yml b/repo2docker/buildpacks/conda/environment.py-3.8.yml index e2965eff2..95849a67f 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.yml @@ -1,9 +1,9 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-01-04 20:20:02 UTC +# Generated on 2021-01-13 12:09:06 UTC dependencies: - python=3.8.* - ipywidgets==7.6.0 -- jupyter-offlinenotebook==0.1.0 +- jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.0 - jupyterhub-singleuser==1.1.0 diff --git a/repo2docker/buildpacks/conda/environment.yml b/repo2docker/buildpacks/conda/environment.yml index 67314af9c..0b12b5284 100644 --- a/repo2docker/buildpacks/conda/environment.yml +++ b/repo2docker/buildpacks/conda/environment.yml @@ -1,7 +1,7 @@ dependencies: - python=3.7 - ipywidgets==7.6.0 - - jupyter-offlinenotebook==0.1.0 + - jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.0 - jupyterhub-singleuser==1.1.0 diff --git a/repo2docker/buildpacks/conda/install-miniforge.bash b/repo2docker/buildpacks/conda/install-miniforge.bash index 5eab7707d..5d3c5d9b9 100755 --- a/repo2docker/buildpacks/conda/install-miniforge.bash +++ b/repo2docker/buildpacks/conda/install-miniforge.bash @@ -48,12 +48,6 @@ echo "installing notebook env:" cat /tmp/environment.yml time mamba env create -p ${NB_PYTHON_PREFIX} -f /tmp/environment.yml -# Install jupyter-offline-notebook to allow users to download notebooks -# after the server connection has been lost -# This will install and enable the extension for the classic notebook and JupyterLab -time ${NB_PYTHON_PREFIX}/bin/python -m pip install jupyter-offlinenotebook==0.1.0 -true - # empty conda history file, # which seems to result in some effective pinning of packages in the initial env, # which we don't intend. From 37c7f5a379f35d5e1a1b3a07f5aff4c706ae40c6 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Wed, 13 Jan 2021 13:36:28 +0100 Subject: [PATCH 052/103] Update to jupyterlab==3.0.4 --- repo2docker/buildpacks/conda/environment.frozen.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml | 2 +- repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-3.6.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-3.7.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-3.8.yml | 4 ++-- repo2docker/buildpacks/conda/environment.yml | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/repo2docker/buildpacks/conda/environment.frozen.yml b/repo2docker/buildpacks/conda/environment.frozen.yml index dc2085cff..6d10aaf0e 100644 --- a/repo2docker/buildpacks/conda/environment.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-13 12:05:35 UTC +# Frozen on 2021-01-13 12:22:30 UTC name: r2d channels: - conda-forge @@ -49,7 +49,7 @@ dependencies: - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py37hc8dfbb8_5 - jupyterhub-singleuser=1.1.0=py37hc8dfbb8_5 - - jupyterlab=3.0.0=pyhd8ed1ab_0 + - jupyterlab=3.0.4=pyhd8ed1ab_0 - jupyterlab_server=2.1.2=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 diff --git a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml index b6982a7d1..2805f6291 100644 --- a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-2.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-13 12:00:52 UTC +# Frozen on 2021-01-13 12:17:48 UTC name: r2d channels: - conda-forge diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml index 64d0b710d..113368647 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.6.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-13 12:02:30 UTC +# Frozen on 2021-01-13 12:19:21 UTC name: r2d channels: - conda-forge @@ -52,7 +52,7 @@ dependencies: - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py36h9f0ad1d_5 - jupyterhub-singleuser=1.1.0=py36h9f0ad1d_5 - - jupyterlab=3.0.0=pyhd8ed1ab_0 + - jupyterlab=3.0.4=pyhd8ed1ab_0 - jupyterlab_server=2.1.2=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.yml b/repo2docker/buildpacks/conda/environment.py-3.6.yml index a81defc27..9bfbf22b9 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.yml @@ -1,11 +1,11 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-01-13 12:02:30 UTC +# Generated on 2021-01-13 12:19:21 UTC dependencies: - python=3.6.* - ipywidgets==7.6.0 - jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 -- jupyterlab==3.0.0 +- jupyterlab==3.0.4 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 - notebook==6.1.6 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml index dc2085cff..6d10aaf0e 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-13 12:05:35 UTC +# Frozen on 2021-01-13 12:22:30 UTC name: r2d channels: - conda-forge @@ -49,7 +49,7 @@ dependencies: - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py37hc8dfbb8_5 - jupyterhub-singleuser=1.1.0=py37hc8dfbb8_5 - - jupyterlab=3.0.0=pyhd8ed1ab_0 + - jupyterlab=3.0.4=pyhd8ed1ab_0 - jupyterlab_server=2.1.2=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.yml b/repo2docker/buildpacks/conda/environment.py-3.7.yml index d09ad5e39..7cb0d451f 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.yml @@ -1,11 +1,11 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-01-13 12:05:35 UTC +# Generated on 2021-01-13 12:22:30 UTC dependencies: - python=3.7.* - ipywidgets==7.6.0 - jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 -- jupyterlab==3.0.0 +- jupyterlab==3.0.4 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 - notebook==6.1.6 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml index 59cf868cf..38bef09ac 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.8.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-13 12:09:06 UTC +# Frozen on 2021-01-13 12:26:04 UTC name: r2d channels: - conda-forge @@ -49,7 +49,7 @@ dependencies: - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py38h32f6830_5 - jupyterhub-singleuser=1.1.0=py38h32f6830_5 - - jupyterlab=3.0.0=pyhd8ed1ab_0 + - jupyterlab=3.0.4=pyhd8ed1ab_0 - jupyterlab_server=2.1.2=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.yml b/repo2docker/buildpacks/conda/environment.py-3.8.yml index 95849a67f..19408d961 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.yml @@ -1,11 +1,11 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-01-13 12:09:06 UTC +# Generated on 2021-01-13 12:26:04 UTC dependencies: - python=3.8.* - ipywidgets==7.6.0 - jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 -- jupyterlab==3.0.0 +- jupyterlab==3.0.4 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 - notebook==6.1.6 diff --git a/repo2docker/buildpacks/conda/environment.yml b/repo2docker/buildpacks/conda/environment.yml index 0b12b5284..60c6addf4 100644 --- a/repo2docker/buildpacks/conda/environment.yml +++ b/repo2docker/buildpacks/conda/environment.yml @@ -3,7 +3,7 @@ dependencies: - ipywidgets==7.6.0 - jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 - - jupyterlab==3.0.0 + - jupyterlab==3.0.4 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 - notebook==6.1.6 From 0ca6b4388a1a13782524a8da82bb77f6463ee5ef Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Tue, 16 Feb 2021 15:22:26 +0100 Subject: [PATCH 053/103] Update to jupyterlab==3.0.7 and ipywidgets=7.6.3 --- .../buildpacks/conda/environment.frozen.yml | 66 ++++++++--------- .../conda/environment.py-2.7.frozen.yml | 10 +-- .../conda/environment.py-3.6.frozen.yml | 70 +++++++++---------- .../buildpacks/conda/environment.py-3.6.yml | 6 +- .../conda/environment.py-3.7.frozen.yml | 66 ++++++++--------- .../buildpacks/conda/environment.py-3.7.yml | 6 +- .../conda/environment.py-3.8.frozen.yml | 66 ++++++++--------- .../buildpacks/conda/environment.py-3.8.yml | 6 +- repo2docker/buildpacks/conda/environment.yml | 4 +- 9 files changed, 150 insertions(+), 150 deletions(-) diff --git a/repo2docker/buildpacks/conda/environment.frozen.yml b/repo2docker/buildpacks/conda/environment.frozen.yml index 6d10aaf0e..13f5d987e 100644 --- a/repo2docker/buildpacks/conda/environment.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-13 12:22:30 UTC +# Frozen on 2021-02-16 14:16:19 UTC name: r2d channels: - conda-forge @@ -8,8 +8,8 @@ channels: dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - - alembic=1.4.3=pyh9f0ad1d_0 - - anyio=2.0.2=py37h89c1867_4 + - alembic=1.5.4=pyhd8ed1ab_0 + - anyio=2.1.0=py37h89c1867_0 - argon2-cffi=20.1.0=py37h5e8e339_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 @@ -17,16 +17,16 @@ dependencies: - backcall=0.2.0=pyh9f0ad1d_0 - backports=1.0=py_2 - backports.functools_lru_cache=1.6.1=py_0 - - bleach=3.2.1=pyh9f0ad1d_0 + - bleach=3.3.0=pyh44b312d_0 - blinker=1.4=py_1 - - brotlipy=0.7.0=py37hb5d75c8_1001 + - brotlipy=0.7.0=py37h5e8e339_1001 - c-ares=1.17.1=h36c2ea0_0 - ca-certificates=2020.12.5=ha878542_0 - certifi=2020.12.5=py37h89c1867_1 - certipy=0.1.3=py_0 - - cffi=1.14.4=py37hc58025e_1 + - cffi=1.14.5=py37hc58025e_0 - chardet=4.0.0=py37h89c1867_1 - - cryptography=3.3.1=py37h7f0c10b_1 + - cryptography=3.4.4=py37hf1a17b8_0 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - entrypoints=0.3=pyhd8ed1ab_1003 @@ -34,74 +34,74 @@ dependencies: - importlib-metadata=3.4.0=py37h89c1867_0 - importlib_metadata=3.4.0=hd8ed1ab_0 - ipykernel=5.4.2=py37h888b3d9_0 - - ipython=7.19.0=py37h888b3d9_0 + - ipython=7.20.0=py37h888b3d9_2 - ipython_genutils=0.2.0=py_1 - - ipywidgets=7.6.0=pyhd3deb0d_0 + - ipywidgets=7.6.3=pyhd3deb0d_0 - jedi=0.18.0=py37h89c1867_2 - - jinja2=2.11.2=pyh9f0ad1d_0 + - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - - jupyter_core=4.7.0=py37h89c1867_0 - - jupyter_server=1.2.1=py37h89c1867_0 + - jupyter_core=4.7.1=py37h89c1867_0 + - jupyter_server=1.3.0=py37h89c1867_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py37hc8dfbb8_5 - jupyterhub-singleuser=1.1.0=py37hc8dfbb8_5 - - jupyterlab=3.0.4=pyhd8ed1ab_0 - - jupyterlab_server=2.1.2=pyhd8ed1ab_0 + - jupyterlab=3.0.7=pyhd8ed1ab_0 + - jupyterlab_server=2.2.0=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - - ld_impl_linux-64=2.35.1=hea4e1c9_1 + - ld_impl_linux-64=2.35.1=hea4e1c9_2 - libcurl=7.71.1=hcdd3856_8 - libedit=3.1.20191231=he28a2e2_2 - libev=4.33=h516909a_1 - libffi=3.3=h58526e2_2 - - libgcc-ng=9.3.0=h5dbcf3e_17 - - libgomp=9.3.0=h5dbcf3e_17 - - libnghttp2=1.41.0=h8cfc5f6_2 + - libgcc-ng=9.3.0=h2828fa1_18 + - libgomp=9.3.0=h2828fa1_18 + - libnghttp2=1.43.0=h812cca2_0 - libsodium=1.0.18=h36c2ea0_1 - libssh2=1.9.0=hab1572f_5 - - libstdcxx-ng=9.3.0=h2ae2ef3_17 - - mako=1.1.3=pyh9f0ad1d_0 + - libstdcxx-ng=9.3.0=h6de172a_18 + - mako=1.1.4=pyh44b312d_0 - markupsafe=1.1.1=py37h5e8e339_3 - mistune=0.8.4=py37h5e8e339_1003 - nbclassic=0.2.6=pyhd8ed1ab_0 - nbconvert=5.6.1=py37hc8dfbb8_1 - - nbformat=5.0.8=py_0 + - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - notebook=6.1.6=py37h89c1867_0 - nteract_on_jupyter=2.1.3=py_0 - oauthlib=3.0.1=py_0 - openssl=1.1.1i=h7f98852_0 - - packaging=20.8=pyhd3deb0d_0 + - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - - pandoc=2.11.3.2=h7f98852_0 + - pandoc=2.11.4=h7f98852_0 - pandocfilters=1.4.2=py_1 - parso=0.8.1=pyhd8ed1ab_0 - pexpect=4.8.0=pyh9f0ad1d_2 - pickleshare=0.7.5=py_1003 - - pip=20.3.3=pyhd8ed1ab_0 + - pip=21.0.1=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - - prompt-toolkit=3.0.10=pyha770c72_0 + - prompt-toolkit=3.0.16=pyha770c72_0 - psutil=5.8.0=py37h5e8e339_1 - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - pycurl=7.43.0.6=py37h88a64d2_1 - - pygments=2.7.3=pyhd8ed1ab_0 - - pyjwt=2.0.0=pyhd8ed1ab_0 + - pygments=2.8.0=pyhd8ed1ab_0 + - pyjwt=2.0.1=pyhd8ed1ab_0 - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - pyrsistent=0.17.3=py37h5e8e339_2 - pysocks=1.7.1=py37h89c1867_3 - - python=3.7.9=hffdb5ce_0_cpython + - python=3.7.9=hffdb5ce_100_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.7=1_cp37m - - pytz=2020.5=pyhd8ed1ab_0 - - pyzmq=20.0.0=py37h499b945_1 + - pytz=2021.1=pyhd8ed1ab_0 + - pyzmq=22.0.3=py37h499b945_0 - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py37h5e8e339_2 @@ -110,7 +110,7 @@ dependencies: - setuptools=49.6.0=py37h89c1867_3 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py37h89c1867_1 - - sqlalchemy=1.3.22=py37h5e8e339_1 + - sqlalchemy=1.3.23=py37h5e8e339_0 - sqlite=3.34.0=h74cdb3f_0 - terminado=0.9.2=py37h89c1867_0 - testpath=0.4.4=py_0 @@ -118,13 +118,13 @@ dependencies: - tornado=6.1=py37h5e8e339_1 - traitlets=5.0.5=py_0 - typing_extensions=3.7.4.3=py_0 - - urllib3=1.26.2=pyhd8ed1ab_0 + - urllib3=1.26.3=pyhd8ed1ab_0 - wcwidth=0.2.5=pyh9f0ad1d_2 - webencodings=0.5.1=py_1 - wheel=0.36.2=pyhd3deb0d_0 - widgetsnbextension=3.5.1=py37h89c1867_4 - xz=5.2.5=h516909a_1 - - zeromq=4.3.3=h58526e2_3 + - zeromq=4.3.4=h9c3ff4c_0 - zipp=3.4.0=py_0 - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d diff --git a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml index 2805f6291..95884d580 100644 --- a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-2.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-13 12:17:48 UTC +# Frozen on 2021-02-16 14:11:39 UTC name: r2d channels: - conda-forge @@ -23,12 +23,12 @@ dependencies: - ipython_genutils=0.2.0=py_1 - jupyter_client=5.3.4=py27_1 - jupyter_core=4.6.3=py27h8c360ce_1 - - ld_impl_linux-64=2.35.1=hea4e1c9_1 + - ld_impl_linux-64=2.35.1=hea4e1c9_2 - libffi=3.2.1=he1b5a44_1007 - - libgcc-ng=9.3.0=h5dbcf3e_17 - - libgomp=9.3.0=h5dbcf3e_17 + - libgcc-ng=9.3.0=h2828fa1_18 + - libgomp=9.3.0=h2828fa1_18 - libsodium=1.0.17=h516909a_0 - - libstdcxx-ng=9.3.0=h2ae2ef3_17 + - libstdcxx-ng=9.3.0=h6de172a_18 - ncurses=6.2=h58526e2_4 - openssl=1.1.1i=h7f98852_0 - pathlib2=2.3.5=py27h8c360ce_1 diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml index 113368647..76115c2c8 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.6.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-13 12:19:21 UTC +# Frozen on 2021-02-16 14:13:18 UTC name: r2d channels: - conda-forge @@ -8,8 +8,8 @@ channels: dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - - alembic=1.4.3=pyh9f0ad1d_0 - - anyio=2.0.2=py36h5fab9bb_4 + - alembic=1.5.4=pyhd8ed1ab_0 + - anyio=2.1.0=py36h5fab9bb_0 - argon2-cffi=20.1.0=py36h8f6f2f9_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 @@ -17,83 +17,83 @@ dependencies: - backcall=0.2.0=pyh9f0ad1d_0 - backports=1.0=py_2 - backports.functools_lru_cache=1.6.1=py_0 - - bleach=3.2.1=pyh9f0ad1d_0 + - bleach=3.3.0=pyh44b312d_0 - blinker=1.4=py_1 - - brotlipy=0.7.0=py36he6145b8_1001 + - brotlipy=0.7.0=py36h8f6f2f9_1001 - c-ares=1.17.1=h36c2ea0_0 - ca-certificates=2020.12.5=ha878542_0 - certifi=2020.12.5=py36h5fab9bb_1 - certipy=0.1.3=py_0 - - cffi=1.14.4=py36hc120d54_1 + - cffi=1.14.5=py36hc120d54_0 - chardet=4.0.0=py36h5fab9bb_1 - contextvars=2.4=py_0 - - cryptography=3.3.1=py36h0a59100_1 + - cryptography=3.4.4=py36hc39840e_0 - dataclasses=0.7=pyhe4b4509_6 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - entrypoints=0.3=pyhd8ed1ab_1003 - idna=2.10=pyh9f0ad1d_0 - - immutables=0.14=py36h8f6f2f9_1 + - immutables=0.15=py36h8f6f2f9_0 - importlib-metadata=3.4.0=py36h5fab9bb_0 - importlib_metadata=3.4.0=hd8ed1ab_0 - ipykernel=5.4.2=py36he448a4c_0 - - ipython=7.16.1=py36h95af2a2_0 + - ipython=7.16.1=py36he448a4c_2 - ipython_genutils=0.2.0=py_1 - - ipywidgets=7.6.0=pyhd3deb0d_0 - - jedi=0.18.0=py36h5fab9bb_2 - - jinja2=2.11.2=pyh9f0ad1d_0 + - ipywidgets=7.6.3=pyhd3deb0d_0 + - jedi=0.17.2=py36h5fab9bb_1 + - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - - jupyter_core=4.7.0=py36h5fab9bb_0 - - jupyter_server=1.2.1=py36h5fab9bb_0 + - jupyter_core=4.7.1=py36h5fab9bb_0 + - jupyter_server=1.3.0=py36h5fab9bb_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py36h9f0ad1d_5 - jupyterhub-singleuser=1.1.0=py36h9f0ad1d_5 - - jupyterlab=3.0.4=pyhd8ed1ab_0 - - jupyterlab_server=2.1.2=pyhd8ed1ab_0 + - jupyterlab=3.0.7=pyhd8ed1ab_0 + - jupyterlab_server=2.2.0=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - - ld_impl_linux-64=2.35.1=hea4e1c9_1 + - ld_impl_linux-64=2.35.1=hea4e1c9_2 - libcurl=7.71.1=hcdd3856_8 - libedit=3.1.20191231=he28a2e2_2 - libev=4.33=h516909a_1 - libffi=3.3=h58526e2_2 - - libgcc-ng=9.3.0=h5dbcf3e_17 - - libgomp=9.3.0=h5dbcf3e_17 - - libnghttp2=1.41.0=h8cfc5f6_2 + - libgcc-ng=9.3.0=h2828fa1_18 + - libgomp=9.3.0=h2828fa1_18 + - libnghttp2=1.43.0=h812cca2_0 - libsodium=1.0.18=h36c2ea0_1 - libssh2=1.9.0=hab1572f_5 - - libstdcxx-ng=9.3.0=h2ae2ef3_17 - - mako=1.1.3=pyh9f0ad1d_0 + - libstdcxx-ng=9.3.0=h6de172a_18 + - mako=1.1.4=pyh44b312d_0 - markupsafe=1.1.1=py36h8f6f2f9_3 - mistune=0.8.4=py36h8f6f2f9_1003 - nbclassic=0.2.6=pyhd8ed1ab_0 - nbconvert=5.6.1=py36h9f0ad1d_1 - - nbformat=5.0.8=py_0 + - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - notebook=6.1.6=py36h5fab9bb_0 - nteract_on_jupyter=2.1.3=py_0 - oauthlib=3.0.1=py_0 - openssl=1.1.1i=h7f98852_0 - - packaging=20.8=pyhd3deb0d_0 + - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - - pandoc=2.11.3.2=h7f98852_0 + - pandoc=2.11.4=h7f98852_0 - pandocfilters=1.4.2=py_1 - - parso=0.8.1=pyhd8ed1ab_0 + - parso=0.7.1=pyh9f0ad1d_0 - pexpect=4.8.0=pyh9f0ad1d_2 - pickleshare=0.7.5=py_1003 - - pip=20.3.3=pyhd8ed1ab_0 + - pip=21.0.1=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - - prompt-toolkit=3.0.10=pyha770c72_0 + - prompt-toolkit=3.0.16=pyha770c72_0 - psutil=5.8.0=py36h8f6f2f9_1 - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - pycurl=7.43.0.6=py36h66a4f8d_1 - - pygments=2.7.3=pyhd8ed1ab_0 - - pyjwt=2.0.0=pyhd8ed1ab_0 + - pygments=2.8.0=pyhd8ed1ab_0 + - pyjwt=2.0.1=pyhd8ed1ab_0 - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - pyrsistent=0.17.3=py36h8f6f2f9_2 @@ -103,8 +103,8 @@ dependencies: - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.6=1_cp36m - - pytz=2020.5=pyhd8ed1ab_0 - - pyzmq=20.0.0=py36h81c33ee_1 + - pytz=2021.1=pyhd8ed1ab_0 + - pyzmq=22.0.3=py36h81c33ee_0 - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py36h8f6f2f9_2 @@ -113,7 +113,7 @@ dependencies: - setuptools=49.6.0=py36h5fab9bb_3 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py36h5fab9bb_1 - - sqlalchemy=1.3.22=py36h8f6f2f9_1 + - sqlalchemy=1.3.23=py36h8f6f2f9_0 - sqlite=3.34.0=h74cdb3f_0 - terminado=0.9.2=py36h5fab9bb_0 - testpath=0.4.4=py_0 @@ -121,13 +121,13 @@ dependencies: - tornado=6.1=py36h8f6f2f9_1 - traitlets=4.3.3=py36h9f0ad1d_1 - typing_extensions=3.7.4.3=py_0 - - urllib3=1.26.2=pyhd8ed1ab_0 + - urllib3=1.26.3=pyhd8ed1ab_0 - wcwidth=0.2.5=pyh9f0ad1d_2 - webencodings=0.5.1=py_1 - wheel=0.36.2=pyhd3deb0d_0 - widgetsnbextension=3.5.1=py36h5fab9bb_4 - xz=5.2.5=h516909a_1 - - zeromq=4.3.3=h58526e2_3 + - zeromq=4.3.4=h9c3ff4c_0 - zipp=3.4.0=py_0 - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.yml b/repo2docker/buildpacks/conda/environment.py-3.6.yml index 9bfbf22b9..e6faf2913 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.yml @@ -1,11 +1,11 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-01-13 12:19:21 UTC +# Generated on 2021-02-16 14:13:18 UTC dependencies: - python=3.6.* -- ipywidgets==7.6.0 +- ipywidgets==7.6.3 - jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 -- jupyterlab==3.0.4 +- jupyterlab==3.0.7 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 - notebook==6.1.6 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml index 6d10aaf0e..13f5d987e 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-13 12:22:30 UTC +# Frozen on 2021-02-16 14:16:19 UTC name: r2d channels: - conda-forge @@ -8,8 +8,8 @@ channels: dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - - alembic=1.4.3=pyh9f0ad1d_0 - - anyio=2.0.2=py37h89c1867_4 + - alembic=1.5.4=pyhd8ed1ab_0 + - anyio=2.1.0=py37h89c1867_0 - argon2-cffi=20.1.0=py37h5e8e339_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 @@ -17,16 +17,16 @@ dependencies: - backcall=0.2.0=pyh9f0ad1d_0 - backports=1.0=py_2 - backports.functools_lru_cache=1.6.1=py_0 - - bleach=3.2.1=pyh9f0ad1d_0 + - bleach=3.3.0=pyh44b312d_0 - blinker=1.4=py_1 - - brotlipy=0.7.0=py37hb5d75c8_1001 + - brotlipy=0.7.0=py37h5e8e339_1001 - c-ares=1.17.1=h36c2ea0_0 - ca-certificates=2020.12.5=ha878542_0 - certifi=2020.12.5=py37h89c1867_1 - certipy=0.1.3=py_0 - - cffi=1.14.4=py37hc58025e_1 + - cffi=1.14.5=py37hc58025e_0 - chardet=4.0.0=py37h89c1867_1 - - cryptography=3.3.1=py37h7f0c10b_1 + - cryptography=3.4.4=py37hf1a17b8_0 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - entrypoints=0.3=pyhd8ed1ab_1003 @@ -34,74 +34,74 @@ dependencies: - importlib-metadata=3.4.0=py37h89c1867_0 - importlib_metadata=3.4.0=hd8ed1ab_0 - ipykernel=5.4.2=py37h888b3d9_0 - - ipython=7.19.0=py37h888b3d9_0 + - ipython=7.20.0=py37h888b3d9_2 - ipython_genutils=0.2.0=py_1 - - ipywidgets=7.6.0=pyhd3deb0d_0 + - ipywidgets=7.6.3=pyhd3deb0d_0 - jedi=0.18.0=py37h89c1867_2 - - jinja2=2.11.2=pyh9f0ad1d_0 + - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - - jupyter_core=4.7.0=py37h89c1867_0 - - jupyter_server=1.2.1=py37h89c1867_0 + - jupyter_core=4.7.1=py37h89c1867_0 + - jupyter_server=1.3.0=py37h89c1867_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py37hc8dfbb8_5 - jupyterhub-singleuser=1.1.0=py37hc8dfbb8_5 - - jupyterlab=3.0.4=pyhd8ed1ab_0 - - jupyterlab_server=2.1.2=pyhd8ed1ab_0 + - jupyterlab=3.0.7=pyhd8ed1ab_0 + - jupyterlab_server=2.2.0=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - - ld_impl_linux-64=2.35.1=hea4e1c9_1 + - ld_impl_linux-64=2.35.1=hea4e1c9_2 - libcurl=7.71.1=hcdd3856_8 - libedit=3.1.20191231=he28a2e2_2 - libev=4.33=h516909a_1 - libffi=3.3=h58526e2_2 - - libgcc-ng=9.3.0=h5dbcf3e_17 - - libgomp=9.3.0=h5dbcf3e_17 - - libnghttp2=1.41.0=h8cfc5f6_2 + - libgcc-ng=9.3.0=h2828fa1_18 + - libgomp=9.3.0=h2828fa1_18 + - libnghttp2=1.43.0=h812cca2_0 - libsodium=1.0.18=h36c2ea0_1 - libssh2=1.9.0=hab1572f_5 - - libstdcxx-ng=9.3.0=h2ae2ef3_17 - - mako=1.1.3=pyh9f0ad1d_0 + - libstdcxx-ng=9.3.0=h6de172a_18 + - mako=1.1.4=pyh44b312d_0 - markupsafe=1.1.1=py37h5e8e339_3 - mistune=0.8.4=py37h5e8e339_1003 - nbclassic=0.2.6=pyhd8ed1ab_0 - nbconvert=5.6.1=py37hc8dfbb8_1 - - nbformat=5.0.8=py_0 + - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - notebook=6.1.6=py37h89c1867_0 - nteract_on_jupyter=2.1.3=py_0 - oauthlib=3.0.1=py_0 - openssl=1.1.1i=h7f98852_0 - - packaging=20.8=pyhd3deb0d_0 + - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - - pandoc=2.11.3.2=h7f98852_0 + - pandoc=2.11.4=h7f98852_0 - pandocfilters=1.4.2=py_1 - parso=0.8.1=pyhd8ed1ab_0 - pexpect=4.8.0=pyh9f0ad1d_2 - pickleshare=0.7.5=py_1003 - - pip=20.3.3=pyhd8ed1ab_0 + - pip=21.0.1=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - - prompt-toolkit=3.0.10=pyha770c72_0 + - prompt-toolkit=3.0.16=pyha770c72_0 - psutil=5.8.0=py37h5e8e339_1 - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - pycurl=7.43.0.6=py37h88a64d2_1 - - pygments=2.7.3=pyhd8ed1ab_0 - - pyjwt=2.0.0=pyhd8ed1ab_0 + - pygments=2.8.0=pyhd8ed1ab_0 + - pyjwt=2.0.1=pyhd8ed1ab_0 - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - pyrsistent=0.17.3=py37h5e8e339_2 - pysocks=1.7.1=py37h89c1867_3 - - python=3.7.9=hffdb5ce_0_cpython + - python=3.7.9=hffdb5ce_100_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.7=1_cp37m - - pytz=2020.5=pyhd8ed1ab_0 - - pyzmq=20.0.0=py37h499b945_1 + - pytz=2021.1=pyhd8ed1ab_0 + - pyzmq=22.0.3=py37h499b945_0 - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py37h5e8e339_2 @@ -110,7 +110,7 @@ dependencies: - setuptools=49.6.0=py37h89c1867_3 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py37h89c1867_1 - - sqlalchemy=1.3.22=py37h5e8e339_1 + - sqlalchemy=1.3.23=py37h5e8e339_0 - sqlite=3.34.0=h74cdb3f_0 - terminado=0.9.2=py37h89c1867_0 - testpath=0.4.4=py_0 @@ -118,13 +118,13 @@ dependencies: - tornado=6.1=py37h5e8e339_1 - traitlets=5.0.5=py_0 - typing_extensions=3.7.4.3=py_0 - - urllib3=1.26.2=pyhd8ed1ab_0 + - urllib3=1.26.3=pyhd8ed1ab_0 - wcwidth=0.2.5=pyh9f0ad1d_2 - webencodings=0.5.1=py_1 - wheel=0.36.2=pyhd3deb0d_0 - widgetsnbextension=3.5.1=py37h89c1867_4 - xz=5.2.5=h516909a_1 - - zeromq=4.3.3=h58526e2_3 + - zeromq=4.3.4=h9c3ff4c_0 - zipp=3.4.0=py_0 - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.yml b/repo2docker/buildpacks/conda/environment.py-3.7.yml index 7cb0d451f..8be2e8035 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.yml @@ -1,11 +1,11 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-01-13 12:22:30 UTC +# Generated on 2021-02-16 14:16:19 UTC dependencies: - python=3.7.* -- ipywidgets==7.6.0 +- ipywidgets==7.6.3 - jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 -- jupyterlab==3.0.4 +- jupyterlab==3.0.7 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 - notebook==6.1.6 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml index 38bef09ac..00f6a86c2 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.8.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-01-13 12:26:04 UTC +# Frozen on 2021-02-16 14:19:12 UTC name: r2d channels: - conda-forge @@ -8,8 +8,8 @@ channels: dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - - alembic=1.4.3=pyh9f0ad1d_0 - - anyio=2.0.2=py38h578d9bd_4 + - alembic=1.5.4=pyhd8ed1ab_0 + - anyio=2.1.0=py38h578d9bd_0 - argon2-cffi=20.1.0=py38h497a2fe_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 @@ -17,16 +17,16 @@ dependencies: - backcall=0.2.0=pyh9f0ad1d_0 - backports=1.0=py_2 - backports.functools_lru_cache=1.6.1=py_0 - - bleach=3.2.1=pyh9f0ad1d_0 + - bleach=3.3.0=pyh44b312d_0 - blinker=1.4=py_1 - - brotlipy=0.7.0=py38h8df0ef7_1001 + - brotlipy=0.7.0=py38h497a2fe_1001 - c-ares=1.17.1=h36c2ea0_0 - ca-certificates=2020.12.5=ha878542_0 - certifi=2020.12.5=py38h578d9bd_1 - certipy=0.1.3=py_0 - - cffi=1.14.4=py38ha65f79e_1 + - cffi=1.14.5=py38ha65f79e_0 - chardet=4.0.0=py38h578d9bd_1 - - cryptography=3.3.1=py38h2b97feb_1 + - cryptography=3.4.4=py38h3e25421_0 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - entrypoints=0.3=pyhd8ed1ab_1003 @@ -34,74 +34,74 @@ dependencies: - importlib-metadata=3.4.0=py38h578d9bd_0 - importlib_metadata=3.4.0=hd8ed1ab_0 - ipykernel=5.4.2=py38h81c977d_0 - - ipython=7.19.0=py38h81c977d_0 + - ipython=7.20.0=py38h81c977d_2 - ipython_genutils=0.2.0=py_1 - - ipywidgets=7.6.0=pyhd3deb0d_0 + - ipywidgets=7.6.3=pyhd3deb0d_0 - jedi=0.18.0=py38h578d9bd_2 - - jinja2=2.11.2=pyh9f0ad1d_0 + - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - - jupyter_core=4.7.0=py38h578d9bd_0 - - jupyter_server=1.2.1=py38h578d9bd_0 + - jupyter_core=4.7.1=py38h578d9bd_0 + - jupyter_server=1.3.0=py38h578d9bd_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - jupyterhub-base=1.1.0=py38h32f6830_5 - jupyterhub-singleuser=1.1.0=py38h32f6830_5 - - jupyterlab=3.0.4=pyhd8ed1ab_0 - - jupyterlab_server=2.1.2=pyhd8ed1ab_0 + - jupyterlab=3.0.7=pyhd8ed1ab_0 + - jupyterlab_server=2.2.0=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - - ld_impl_linux-64=2.35.1=hea4e1c9_1 + - ld_impl_linux-64=2.35.1=hea4e1c9_2 - libcurl=7.71.1=hcdd3856_8 - libedit=3.1.20191231=he28a2e2_2 - libev=4.33=h516909a_1 - libffi=3.3=h58526e2_2 - - libgcc-ng=9.3.0=h5dbcf3e_17 - - libgomp=9.3.0=h5dbcf3e_17 - - libnghttp2=1.41.0=h8cfc5f6_2 + - libgcc-ng=9.3.0=h2828fa1_18 + - libgomp=9.3.0=h2828fa1_18 + - libnghttp2=1.43.0=h812cca2_0 - libsodium=1.0.18=h36c2ea0_1 - libssh2=1.9.0=hab1572f_5 - - libstdcxx-ng=9.3.0=h2ae2ef3_17 - - mako=1.1.3=pyh9f0ad1d_0 + - libstdcxx-ng=9.3.0=h6de172a_18 + - mako=1.1.4=pyh44b312d_0 - markupsafe=1.1.1=py38h497a2fe_3 - mistune=0.8.4=py38h497a2fe_1003 - nbclassic=0.2.6=pyhd8ed1ab_0 - nbconvert=5.6.1=py38h32f6830_1 - - nbformat=5.0.8=py_0 + - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - notebook=6.1.6=py38h578d9bd_0 - nteract_on_jupyter=2.1.3=py_0 - oauthlib=3.0.1=py_0 - openssl=1.1.1i=h7f98852_0 - - packaging=20.8=pyhd3deb0d_0 + - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - - pandoc=2.11.3.2=h7f98852_0 + - pandoc=2.11.4=h7f98852_0 - pandocfilters=1.4.2=py_1 - parso=0.8.1=pyhd8ed1ab_0 - pexpect=4.8.0=pyh9f0ad1d_2 - pickleshare=0.7.5=py_1003 - - pip=20.3.3=pyhd8ed1ab_0 + - pip=21.0.1=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - - prompt-toolkit=3.0.10=pyha770c72_0 + - prompt-toolkit=3.0.16=pyha770c72_0 - psutil=5.8.0=py38h497a2fe_1 - ptyprocess=0.7.0=pyhd3deb0d_0 - pycparser=2.20=pyh9f0ad1d_2 - pycurl=7.43.0.6=py38h996a351_1 - - pygments=2.7.3=pyhd8ed1ab_0 - - pyjwt=2.0.0=pyhd8ed1ab_0 + - pygments=2.8.0=pyhd8ed1ab_0 + - pyjwt=2.0.1=pyhd8ed1ab_0 - pyopenssl=20.0.1=pyhd8ed1ab_0 - pyparsing=2.4.7=pyh9f0ad1d_0 - pyrsistent=0.17.3=py38h497a2fe_2 - pysocks=1.7.1=py38h578d9bd_3 - - python=3.8.6=hffdb5ce_4_cpython + - python=3.8.6=hffdb5ce_5_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.8=1_cp38 - - pytz=2020.5=pyhd8ed1ab_0 - - pyzmq=20.0.0=py38h3d7ac18_1 + - pytz=2021.1=pyhd8ed1ab_0 + - pyzmq=22.0.3=py38h3d7ac18_0 - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py38h497a2fe_2 @@ -110,20 +110,20 @@ dependencies: - setuptools=49.6.0=py38h578d9bd_3 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py38h578d9bd_1 - - sqlalchemy=1.3.22=py38h497a2fe_1 + - sqlalchemy=1.3.23=py38h497a2fe_0 - sqlite=3.34.0=h74cdb3f_0 - terminado=0.9.2=py38h578d9bd_0 - testpath=0.4.4=py_0 - tk=8.6.10=h21135ba_1 - tornado=6.1=py38h497a2fe_1 - traitlets=5.0.5=py_0 - - urllib3=1.26.2=pyhd8ed1ab_0 + - urllib3=1.26.3=pyhd8ed1ab_0 - wcwidth=0.2.5=pyh9f0ad1d_2 - webencodings=0.5.1=py_1 - wheel=0.36.2=pyhd3deb0d_0 - widgetsnbextension=3.5.1=py38h578d9bd_4 - xz=5.2.5=h516909a_1 - - zeromq=4.3.3=h58526e2_3 + - zeromq=4.3.4=h9c3ff4c_0 - zipp=3.4.0=py_0 - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.yml b/repo2docker/buildpacks/conda/environment.py-3.8.yml index 19408d961..c1b5db2b2 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.yml @@ -1,11 +1,11 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-01-13 12:26:04 UTC +# Generated on 2021-02-16 14:19:12 UTC dependencies: - python=3.8.* -- ipywidgets==7.6.0 +- ipywidgets==7.6.3 - jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 -- jupyterlab==3.0.4 +- jupyterlab==3.0.7 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 - notebook==6.1.6 diff --git a/repo2docker/buildpacks/conda/environment.yml b/repo2docker/buildpacks/conda/environment.yml index 60c6addf4..a035b9145 100644 --- a/repo2docker/buildpacks/conda/environment.yml +++ b/repo2docker/buildpacks/conda/environment.yml @@ -1,9 +1,9 @@ dependencies: - python=3.7 - - ipywidgets==7.6.0 + - ipywidgets==7.6.3 - jupyter-offlinenotebook==0.2.0 - jupyter-resource-usage==0.5.1 - - jupyterlab==3.0.4 + - jupyterlab==3.0.7 - jupyterhub-singleuser==1.1.0 - nbconvert==5.6.1 - notebook==6.1.6 From 4c061eebbb30db45f4d6686793c797dcbff1c361 Mon Sep 17 00:00:00 2001 From: Jeremy Tuloup Date: Tue, 16 Feb 2021 15:55:27 +0100 Subject: [PATCH 054/103] Update to jupyter-offlinenotebook=0.2.1 --- repo2docker/buildpacks/conda/environment.frozen.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml | 2 +- repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-3.6.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-3.7.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml | 4 ++-- repo2docker/buildpacks/conda/environment.py-3.8.yml | 4 ++-- repo2docker/buildpacks/conda/environment.yml | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/repo2docker/buildpacks/conda/environment.frozen.yml b/repo2docker/buildpacks/conda/environment.frozen.yml index 13f5d987e..bd4a68b93 100644 --- a/repo2docker/buildpacks/conda/environment.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-16 14:16:19 UTC +# Frozen on 2021-02-16 14:46:08 UTC name: r2d channels: - conda-forge @@ -41,7 +41,7 @@ dependencies: - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 + - jupyter-offlinenotebook=0.2.1=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.1=py37h89c1867_0 diff --git a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml index 95884d580..83c42bcf7 100644 --- a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-2.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-16 14:11:39 UTC +# Frozen on 2021-02-16 14:41:11 UTC name: r2d channels: - conda-forge diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml index 76115c2c8..bb87d163f 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.6.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-16 14:13:18 UTC +# Frozen on 2021-02-16 14:42:56 UTC name: r2d channels: - conda-forge @@ -44,7 +44,7 @@ dependencies: - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 + - jupyter-offlinenotebook=0.2.1=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.1=py36h5fab9bb_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.yml b/repo2docker/buildpacks/conda/environment.py-3.6.yml index e6faf2913..e21901c4e 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.yml @@ -1,9 +1,9 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-02-16 14:13:18 UTC +# Generated on 2021-02-16 14:42:56 UTC dependencies: - python=3.6.* - ipywidgets==7.6.3 -- jupyter-offlinenotebook==0.2.0 +- jupyter-offlinenotebook==0.2.1 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.7 - jupyterhub-singleuser==1.1.0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml index 13f5d987e..bd4a68b93 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-16 14:16:19 UTC +# Frozen on 2021-02-16 14:46:08 UTC name: r2d channels: - conda-forge @@ -41,7 +41,7 @@ dependencies: - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 + - jupyter-offlinenotebook=0.2.1=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.1=py37h89c1867_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.yml b/repo2docker/buildpacks/conda/environment.py-3.7.yml index 8be2e8035..62b74054a 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.yml @@ -1,9 +1,9 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-02-16 14:16:19 UTC +# Generated on 2021-02-16 14:46:08 UTC dependencies: - python=3.7.* - ipywidgets==7.6.3 -- jupyter-offlinenotebook==0.2.0 +- jupyter-offlinenotebook==0.2.1 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.7 - jupyterhub-singleuser==1.1.0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml index 00f6a86c2..df2eafe12 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.8.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-16 14:19:12 UTC +# Frozen on 2021-02-16 14:48:09 UTC name: r2d channels: - conda-forge @@ -41,7 +41,7 @@ dependencies: - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - jsonschema=3.2.0=py_2 - - jupyter-offlinenotebook=0.2.0=pyhd8ed1ab_0 + - jupyter-offlinenotebook=0.2.1=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.1=py38h578d9bd_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.yml b/repo2docker/buildpacks/conda/environment.py-3.8.yml index c1b5db2b2..b9e2cbb6a 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.yml @@ -1,9 +1,9 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-02-16 14:19:12 UTC +# Generated on 2021-02-16 14:48:09 UTC dependencies: - python=3.8.* - ipywidgets==7.6.3 -- jupyter-offlinenotebook==0.2.0 +- jupyter-offlinenotebook==0.2.1 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.7 - jupyterhub-singleuser==1.1.0 diff --git a/repo2docker/buildpacks/conda/environment.yml b/repo2docker/buildpacks/conda/environment.yml index a035b9145..55b5e6967 100644 --- a/repo2docker/buildpacks/conda/environment.yml +++ b/repo2docker/buildpacks/conda/environment.yml @@ -1,7 +1,7 @@ dependencies: - python=3.7 - ipywidgets==7.6.3 - - jupyter-offlinenotebook==0.2.0 + - jupyter-offlinenotebook==0.2.1 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.7 - jupyterhub-singleuser==1.1.0 From 586d8d4269597de18e136413f98a47c8a9e067d9 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 16 Feb 2021 16:22:51 +0100 Subject: [PATCH 055/103] bump python in docs tests --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 45dcb5ead..925feea25 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,7 +4,7 @@ jobs: build_docs: # This is the base environment that Circle will use docker: - - image: circleci/python:3.6-stretch + - image: circleci/python:3.8-buster steps: # Get our data and merge with upstream - run: sudo apt-get update From ebf640cb84bb7fad32be5332452f8ee93f04a3a7 Mon Sep 17 00:00:00 2001 From: Min RK Date: Wed, 17 Feb 2021 12:08:43 +0100 Subject: [PATCH 056/103] implement entrypoint in Python - make sure to set PYTHONUNBUFFERED - implement tee with subprocess readlines - forward all relevant signals from entrypoint to 'true' command - add monitor process to ensure child shuts down if parent does (maybe not useful in docker?) --- repo2docker/buildpacks/base.py | 1 + repo2docker/buildpacks/repo2docker-entrypoint | 174 +++++++++++++++--- tests/unit/test_env.py | 21 +-- 3 files changed, 160 insertions(+), 36 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index bdb4290cb..6dfa2220f 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -181,6 +181,7 @@ {% endif -%} # Add entrypoint +ENV PYTHONUNBUFFERED=1 COPY /repo2docker-entrypoint /usr/local/bin/repo2docker-entrypoint ENTRYPOINT ["/usr/local/bin/repo2docker-entrypoint"] diff --git a/repo2docker/buildpacks/repo2docker-entrypoint b/repo2docker/buildpacks/repo2docker-entrypoint index cc1404d5f..d4c7631f8 100755 --- a/repo2docker/buildpacks/repo2docker-entrypoint +++ b/repo2docker/buildpacks/repo2docker-entrypoint @@ -1,24 +1,150 @@ -#!/bin/bash -l -# lightest possible entrypoint that ensures that -# we use a login shell to get a fully configured shell environment -# (e.g. sourcing /etc/profile.d, ~/.bashrc, and friends) - -# Setup a file descriptor (FD) that is connected to a tee process which -# writes its input to $REPO_DIR/.jupyter-server-log.txt -# We later use this FD as a place to redirect the output of the actual -# command to. We can't add `tee` to the command directly as that will prevent -# the container from exiting when `docker stop` is run. -# See https://stackoverflow.com/a/55678435 -exec {log_fd}> >(exec tee $REPO_DIR/.jupyter-server-log.txt) - -if [[ ! -z "${R2D_ENTRYPOINT:-}" ]]; then - if [[ ! -x "$R2D_ENTRYPOINT" ]]; then - chmod u+x "$R2D_ENTRYPOINT" - fi - exec "$R2D_ENTRYPOINT" "$@" 2>&1 >&"$log_fd" -else - exec "$@" 2>&1 >&"$log_fd" -fi - -# Close the logging output again -exec {log_fd}>&- +#!/usr/bin/env python3 + +# goals: +# - load environment variables from a login shell (bash -l) +# - preserve signal handling of subprocess (kill -TERM and friends) +# - tee output to a log file + +import json +import os +import signal +import subprocess +import sys +import time + + +def get_login_env(): + """Instantiate a login shell to retrieve environment variables + + Serialize with Python to ensure proper escapes + """ + p = subprocess.run( + [ + "bash", + "-l", + "-c", + "python3 -c 'import os, json; print(json.dumps(dict(os.environ)))'", + ], + stdout=subprocess.PIPE, + ) + if p.returncode: + print("Error getting login env") + return {} + + last_line = p.stdout.splitlines()[-1] + try: + return json.loads(last_line) + except Exception as e: + print(f"Error getting login env: {e}", file=sys.stderr) + return {} + + +def monitor_parent(parent_pid, child_pgid): + """Monitor parent_pid and shutdown child_pgid if parent goes away first""" + while True: + try: + os.kill(parent_pid, 0) + except ProcessLookupError: + # parent is gone, likely by SIGKILL + # send SIGKILL to child process group + try: + os.killpg(child_pgid, signal.SIGKILL) + except (ProcessLookupError, PermissionError): + # ignore if the child is already gone + pass + return + else: + time.sleep(1) + + +# signals to be forwarded to the child +SIGNALS = [ + signal.SIGHUP, + signal.SIGINT, + # signal.SIGKILL, + signal.SIGQUIT, + signal.SIGTERM, + signal.SIGUSR1, + signal.SIGUSR2, + signal.SIGWINCH, +] + + +def main(): + + # load login shell environment + login_env = get_login_env() + env = os.environ.copy() + env.update(login_env) + + # open log file to send output + log_file = open( + os.path.join(os.environ.get("REPO_DIR", "."), ".jupyter-server-log.txt"), + "a", + ) + + child = subprocess.Popen( + sys.argv[1:], + bufsize=1, + env=env, + start_new_session=True, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) + child_pgid = os.getpgid(child.pid) + + # if parent is forcefully shutdown, + # make sure child shuts down immediately as well + parent_pid = os.getpid() + + monitor_pid = os.fork() + if monitor_pid == 0: + # child process, sibling of 'real' command + # avoid receiving signals sent to parent + os.setpgrp() + # terminate child if parent goes away, + # e.g. in ungraceful KILL not relayed to children + monitor_parent(parent_pid, child_pgid) + return + + # hook up ~all signals so that every signal the parent gets, + # the children also get + + def relay_signal(sig, frame): + """Relay a signal to children""" + print(f"Forwarding signal {sig} to {child_pgid}") + os.killpg(child_pgid, sig) + + # question: maybe use all valid_signals() except a few, e.g. SIGCHLD? + # rather than opt-in list + for signum in SIGNALS: + signal.signal(signum, relay_signal) + + # tee output from child to both our stdout and the log file + def tee(chunk): + for f in [sys.stdout, log_file]: + f.write(chunk) + f.flush() + + while child.poll() is None: + tee(child.stdout.readline()) + + # flush the rest + chunk = child.stdout.read() + while chunk: + tee(chunk) + chunk = child.stdout.read() + + # child exited, cleanup monitor + try: + os.kill(monitor_pid, signal.SIGKILL) + except ProcessLookupError: + pass + + # preserve returncode + sys.exit(child.returncode) + + +if __name__ == "__main__": + main() diff --git a/tests/unit/test_env.py b/tests/unit/test_env.py index 4602210f6..ee01b3615 100644 --- a/tests/unit/test_env.py +++ b/tests/unit/test_env.py @@ -3,12 +3,13 @@ """ import os import subprocess +import sys import tempfile import time from getpass import getuser -def test_env(): +def test_env(capfd): """ Validate that you can define environment variables @@ -42,20 +43,19 @@ def test_env(): # value "--env", "SPAM_2=", - # "--", tmpdir, + "--", "/bin/bash", "-c", # Docker exports all passed env variables, so we can # just look at exported variables. - "export; sleep 1", - # "export; echo TIMDONE", - # "export", + "export", ], - universal_newlines=True, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, ) + captured = capfd.readouterr() + print(captured.out, end="") + print(captured.err, file=sys.stderr, end="") + assert result.returncode == 0 # all docker output is returned by repo2docker on stderr @@ -63,11 +63,8 @@ def test_env(): # stdout should be empty assert not result.stdout - print(result.stderr.split("\n")) - # assert False - # stderr should contain lines of output - declares = [x for x in result.stderr.split("\n") if x.startswith("declare")] + declares = [x for x in captured.err.splitlines() if x.startswith("declare")] assert 'declare -x FOO="{}"'.format(ts) in declares assert 'declare -x BAR="baz"' in declares assert 'declare -x SPAM="eggs"' in declares From a06991de500b1263c3459fb9fd8c71c7496ed169 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Feb 2021 09:46:42 +0100 Subject: [PATCH 057/103] entrypoint needs to run on py35 --- repo2docker/buildpacks/repo2docker-entrypoint | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/repo2docker/buildpacks/repo2docker-entrypoint b/repo2docker/buildpacks/repo2docker-entrypoint index d4c7631f8..90645c1ee 100755 --- a/repo2docker/buildpacks/repo2docker-entrypoint +++ b/repo2docker/buildpacks/repo2docker-entrypoint @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +# note: must run on Python >= 3.5 # goals: # - load environment variables from a login shell (bash -l) @@ -35,7 +36,7 @@ def get_login_env(): try: return json.loads(last_line) except Exception as e: - print(f"Error getting login env: {e}", file=sys.stderr) + print("Error getting login env: {e}".format(e=e), file=sys.stderr) return {} @@ -113,7 +114,11 @@ def main(): def relay_signal(sig, frame): """Relay a signal to children""" - print(f"Forwarding signal {sig} to {child_pgid}") + print( + "Forwarding signal {sig} to {child_pgid}".format( + sig=sig, child_pgid=child_pgid + ) + ) os.killpg(child_pgid, sig) # question: maybe use all valid_signals() except a few, e.g. SIGCHLD? From 5a32cc4ad896828ac58c106cec5ac59921556632 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 16 Feb 2021 15:11:23 +0100 Subject: [PATCH 058/103] refreeze with mamba, add 3.9 to freeze list --- repo2docker/buildpacks/conda/freeze.py | 28 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/repo2docker/buildpacks/conda/freeze.py b/repo2docker/buildpacks/conda/freeze.py index 04758bad3..eaa02e3e6 100755 --- a/repo2docker/buildpacks/conda/freeze.py +++ b/repo2docker/buildpacks/conda/freeze.py @@ -19,10 +19,11 @@ from ruamel.yaml import YAML -# Docker image version can be different than conda version, -# since miniconda3 docker images seem to lag conda releases. -MINICONDA_DOCKER_VERSION = "4.7.12" -CONDA_VERSION = "4.7.12" +DOCKER_IMAGE = "condaforge/mambaforge:4.9.2-5" +# set mamba and/or conda versions +# if needed to differ from what's in the image +MAMBA_VERSION = "" +CONDA_VERSION = "" HERE = pathlib.Path(os.path.dirname(os.path.abspath(__file__))) @@ -40,8 +41,8 @@ def freeze(env_file, frozen_file): By running in docker: - conda env create - conda env export + mamba env create + mamba env export Result will be stored in frozen_file """ @@ -70,20 +71,25 @@ def freeze(env_file, frozen_file): "--rm", "-v" f"{HERE}:/r2d", "-it", - f"continuumio/miniconda3:{MINICONDA_DOCKER_VERSION}", + DOCKER_IMAGE, "sh", "-c", "; ".join( [ "set -ex", - f"conda install -yq -S conda={CONDA_VERSION}", "conda config --add channels conda-forge", + f"mamba install -yq -S mamba={MAMBA_VERSION}" + if MAMBA_VERSION + else "true", + f"mamba install -yq -S conda={CONDA_VERSION}" + if CONDA_VERSION + else "true", "conda config --system --set auto_update_conda false", - f"conda env create -v -f /r2d/{env_file.relative_to(HERE)} -n r2d", + f"mamba env create -v -f /r2d/{env_file.relative_to(HERE)} -n r2d", # add conda-forge broken channel as lowest priority in case # any of our frozen packages are marked as broken after freezing "conda config --append channels conda-forge/label/broken", - f"conda env export -n r2d >> /r2d/{frozen_file.relative_to(HERE)}", + f"mamba env export -n r2d >> /r2d/{frozen_file.relative_to(HERE)}", ] ), ] @@ -119,7 +125,7 @@ def set_python(py_env_file, py): if __name__ == "__main__": # allow specifying which Pythons to update on argv - pys = sys.argv[1:] or ("2.7", "3.6", "3.7", "3.8") + pys = sys.argv[1:] or ("2.7", "3.6", "3.7", "3.8", "3.9") default_py = "3.7" for py in pys: env_file = pathlib.Path(str(ENV_FILE_T).format(py=py)) From 67cc5f7a75599035d593389a93b96fcf3f0bee53 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Feb 2021 12:01:23 +0100 Subject: [PATCH 059/103] bump notebook, nbconvert, jupyterhub - jupyterhub-singleuser==1.3.0 - nbconvert==6.0.7 - notebook==6.2.0 --- repo2docker/buildpacks/conda/environment.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/repo2docker/buildpacks/conda/environment.yml b/repo2docker/buildpacks/conda/environment.yml index 55b5e6967..ccb88e10d 100644 --- a/repo2docker/buildpacks/conda/environment.yml +++ b/repo2docker/buildpacks/conda/environment.yml @@ -4,7 +4,7 @@ dependencies: - jupyter-offlinenotebook==0.2.1 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.7 - - jupyterhub-singleuser==1.1.0 - - nbconvert==5.6.1 - - notebook==6.1.6 + - jupyterhub-singleuser==1.3.0 + - nbconvert==6.0.7 + - notebook==6.2.0 - nteract_on_jupyter==2.1.3 From 0f848f7855f5dfdd6b318a80d7d54aee9fa97614 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Feb 2021 14:34:07 +0100 Subject: [PATCH 060/103] handle $R2D_ENTRYPOINT in new entrypoint --- repo2docker/buildpacks/repo2docker-entrypoint | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/repo2docker/buildpacks/repo2docker-entrypoint b/repo2docker/buildpacks/repo2docker-entrypoint index 90645c1ee..1ff4dd615 100755 --- a/repo2docker/buildpacks/repo2docker-entrypoint +++ b/repo2docker/buildpacks/repo2docker-entrypoint @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# note: must run on Python >= 3.5 +# note: must run on Python >= 3.5, which mainly means no f-strings # goals: # - load environment variables from a login shell (bash -l) @@ -84,8 +84,13 @@ def main(): "a", ) + command = sys.argv[1:] + r2d_entrypoint = os.environ.get("R2D_ENTRYPOINT") + if r2d_entrypoint: + command.insert(0, r2d_entrypoint) + child = subprocess.Popen( - sys.argv[1:], + command, bufsize=1, env=env, start_new_session=True, From 751ff6d4c3fd8f70e8ba391db180fbac52858c4d Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Feb 2021 14:52:05 +0100 Subject: [PATCH 061/103] bump docker action to v2 - tags/repository are merged - set context --- .github/workflows/docker.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 79f862fa9..22a01bfdf 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -15,10 +15,10 @@ jobs: # https://github.com/docker/build-push-action - name: Build Docker image - uses: docker/build-push-action@v1 + uses: docker/build-push-action@v2 with: - repository: jupyter/repo2docker - tags: pr + context: . + tags: jupyter/repo2docker:pr push: false - name: Run repo2docker Docker image From b66caf6f65c2d56f5d5b8dc303a18dbf82d2f1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerhard=20Br=C3=A4unlich?= Date: Thu, 18 Feb 2021 16:37:51 +0100 Subject: [PATCH 062/103] buildpacks.r: dont use apt-key directly to respect *_proxy env vars --- repo2docker/buildpacks/r.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index 23ba30ab6..2df72982c 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -247,12 +247,12 @@ def get_build_scripts(self): echo "deb https://cloud.r-project.org/bin/linux/ubuntu bionic-cran{vs}/" > /etc/apt/sources.list.d/r-ubuntu.list """, ), - # Use port 80 to talk to the keyserver to increase the chances + # Dont use apt-key directly, as gpg does not always respect *_proxy vars. This increase the chances # of being able to reach it from behind a firewall ( "root", r""" - apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys E298A3A825C0D65DFD57CBB651716619E084DAB9 + wget --quiet -O - 'https://keyserver.ubuntu.com/pks/lookup?op=get&search=0xe298a3a825c0d65dfd57cbb651716619e084dab9' | apt-key add - """, ), ( From fc6ea2d53bc1a6b25bc400553ec4911ce4d11b68 Mon Sep 17 00:00:00 2001 From: Kyungdahm Yun Date: Fri, 18 Dec 2020 22:01:33 -0800 Subject: [PATCH 063/103] Fetch available Julia versions from versions.json --- repo2docker/buildpacks/julia/julia_project.py | 43 ++++++++----------- repo2docker/buildpacks/julia/semver.py | 3 +- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/repo2docker/buildpacks/julia/julia_project.py b/repo2docker/buildpacks/julia/julia_project.py index 0d039aae7..2ea2a2b13 100644 --- a/repo2docker/buildpacks/julia/julia_project.py +++ b/repo2docker/buildpacks/julia/julia_project.py @@ -1,8 +1,10 @@ """Generates a Dockerfile based on an input matrix for Julia""" +import functools import os +import requests import toml from ..python import PythonBuildPack -from .semver import find_semver_match +from .semver import find_semver_match, semver class JuliaProjectTomlBuildPack(PythonBuildPack): @@ -13,32 +15,24 @@ class JuliaProjectTomlBuildPack(PythonBuildPack): # ALL EXISTING JULIA VERSIONS # Note that these must remain ordered, in order for the find_semver_match() # function to behave correctly. - all_julias = [ - "0.7.0", - "1.0.0", - "1.0.1", - "1.0.2", - "1.0.3", - "1.0.4", - "1.0.5", - "1.1.0", - "1.1.1", - "1.2.0", - "1.3.0", - "1.3.1", - "1.4.0", - "1.4.1", - "1.4.2", - "1.5.0", - "1.5.1", - "1.5.2", - "1.5.3", - ] + @property + @functools.lru_cache(maxsize=1) + def all_julias(self): + json = requests.get( + "https://julialang-s3.julialang.org/bin/versions.json" + ).json() + vers = [semver.VersionInfo.parse(v) for v in json.keys()] + # filter out pre-release versions not supported by find_semver_match() + filtered_vers = [v for v in vers if v.prerelease is None] + # properly sort list of VersionInfo + sorted_vers = sorted( + filtered_vers, key=functools.cmp_to_key(semver.VersionInfo.compare) + ) + # return list of semver string + return [str(v) for v in sorted_vers] @property def julia_version(self): - default_julia_version = self.all_julias[-1] - if os.path.exists(self.binder_path("JuliaProject.toml")): project_toml = toml.load(self.binder_path("JuliaProject.toml")) else: @@ -54,6 +48,7 @@ def julia_version(self): if _julia_version is not None: return _julia_version + default_julia_version = self.all_julias[-1] return default_julia_version def get_build_env(self): diff --git a/repo2docker/buildpacks/julia/semver.py b/repo2docker/buildpacks/julia/semver.py index accd31922..da9bf7c77 100644 --- a/repo2docker/buildpacks/julia/semver.py +++ b/repo2docker/buildpacks/julia/semver.py @@ -113,7 +113,8 @@ def match(self, v): while len(v) < 3: v = v + (0,) v_str = ".".join(map(str, v)) - return semver.match(v_str, self.constraint_str) + v_ver = semver.VersionInfo.parse(v_str) + return semver.VersionInfo.match(v_ver, self.constraint_str) def __eq__(self, rhs): return self.constraint_str == rhs.constraint_str From 80837821ad973a454ce777a5f3da9ebdcba66bfa Mon Sep 17 00:00:00 2001 From: Kyungdahm Yun Date: Fri, 19 Feb 2021 09:09:53 -0800 Subject: [PATCH 064/103] Catch errors on fetching Julia versions --- repo2docker/buildpacks/julia/julia_project.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/repo2docker/buildpacks/julia/julia_project.py b/repo2docker/buildpacks/julia/julia_project.py index 2ea2a2b13..b2a136abc 100644 --- a/repo2docker/buildpacks/julia/julia_project.py +++ b/repo2docker/buildpacks/julia/julia_project.py @@ -18,9 +18,12 @@ class JuliaProjectTomlBuildPack(PythonBuildPack): @property @functools.lru_cache(maxsize=1) def all_julias(self): - json = requests.get( - "https://julialang-s3.julialang.org/bin/versions.json" - ).json() + try: + json = requests.get( + "https://julialang-s3.julialang.org/bin/versions.json" + ).json() + except Exception as e: + raise RuntimeError("Failed to fetch available Julia versions: {e}") vers = [semver.VersionInfo.parse(v) for v in json.keys()] # filter out pre-release versions not supported by find_semver_match() filtered_vers = [v for v in vers if v.prerelease is None] From 27a0ed870c84c46f0e26471c3b2a6799efe10dcc Mon Sep 17 00:00:00 2001 From: Kyungdahm Yun Date: Fri, 19 Feb 2021 09:43:23 -0800 Subject: [PATCH 065/103] Manually specify default compat version 1.5 --- repo2docker/buildpacks/julia/julia_project.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/repo2docker/buildpacks/julia/julia_project.py b/repo2docker/buildpacks/julia/julia_project.py index b2a136abc..0e9f9daa4 100644 --- a/repo2docker/buildpacks/julia/julia_project.py +++ b/repo2docker/buildpacks/julia/julia_project.py @@ -41,18 +41,18 @@ def julia_version(self): else: project_toml = toml.load(self.binder_path("Project.toml")) - if "compat" in project_toml: - if "julia" in project_toml["compat"]: - julia_version_str = project_toml["compat"]["julia"] - - # For Project.toml files, install the latest julia version that - # satisfies the given semver. - _julia_version = find_semver_match(julia_version_str, self.all_julias) - if _julia_version is not None: - return _julia_version - - default_julia_version = self.all_julias[-1] - return default_julia_version + try: + # For Project.toml files, install the latest julia version that + # satisfies the given semver. + compat = project_toml["compat"]["julia"] + except: + # Default version which needs to be manually updated with new major.minor releases + compat = "1.5" + + match = find_semver_match(compat, self.all_julias) + if match is None: + raise RuntimeError("Failed to find a matching Julia version: {compat}") + return match def get_build_env(self): """Get additional environment settings for Julia and Jupyter From f24cbef5f1cf3ea58dfa63c15b72c427e42329db Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 19 Feb 2021 19:24:00 +0000 Subject: [PATCH 066/103] setup.py: Remove duplicate requests from install_requires --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index dab829d70..8bfe64ebc 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,6 @@ def get_identifier(json): "ruamel.yaml>=0.15", "toml", "semver", - "requests", ], python_requires=">=3.6", author="Project Jupyter Contributors", From 23c1be9d1c54e704e93b85163f1b05aeff7b66a7 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 19 Feb 2021 19:24:45 +0000 Subject: [PATCH 067/103] setup.py: install_requires in alphabetical order --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 8bfe64ebc..af0611497 100644 --- a/setup.py +++ b/setup.py @@ -48,14 +48,14 @@ def get_identifier(json): version=versioneer.get_version(), install_requires=[ "docker", - "traitlets", - "python-json-logger", "escapism", "jinja2", + "python-json-logger", "requests", "ruamel.yaml>=0.15", - "toml", "semver", + "toml", + "traitlets", ], python_requires=">=3.6", author="Project Jupyter Contributors", From 246d99c2a580377364d9eeb85f20438a6c8b2d64 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Feb 2021 14:20:56 +0100 Subject: [PATCH 068/103] refreeze adds 3.9 environment --- .../buildpacks/conda/environment.frozen.yml | 54 +++---- .../conda/environment.py-2.7.frozen.yml | 35 +++-- .../conda/environment.py-3.6.frozen.yml | 60 ++++---- .../buildpacks/conda/environment.py-3.6.yml | 8 +- .../conda/environment.py-3.7.frozen.yml | 54 +++---- .../buildpacks/conda/environment.py-3.7.yml | 8 +- .../conda/environment.py-3.8.frozen.yml | 48 ++++--- .../buildpacks/conda/environment.py-3.8.yml | 8 +- .../conda/environment.py-3.9.frozen.yml | 133 ++++++++++++++++++ .../buildpacks/conda/environment.py-3.9.yml | 12 ++ 10 files changed, 285 insertions(+), 135 deletions(-) create mode 100644 repo2docker/buildpacks/conda/environment.py-3.9.frozen.yml create mode 100644 repo2docker/buildpacks/conda/environment.py-3.9.yml diff --git a/repo2docker/buildpacks/conda/environment.frozen.yml b/repo2docker/buildpacks/conda/environment.frozen.yml index bd4a68b93..82aa231f3 100644 --- a/repo2docker/buildpacks/conda/environment.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-16 14:46:08 UTC +# Frozen on 2021-02-22 10:03:32 UTC name: r2d channels: - conda-forge @@ -8,9 +8,9 @@ channels: dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - - alembic=1.5.4=pyhd8ed1ab_0 + - alembic=1.5.5=pyhd8ed1ab_0 - anyio=2.1.0=py37h89c1867_0 - - argon2-cffi=20.1.0=py37h5e8e339_2 + - argon2-cffi=20.1.0=py37h8f50634_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 - babel=2.9.0=pyhd3deb0d_0 @@ -19,9 +19,9 @@ dependencies: - backports.functools_lru_cache=1.6.1=py_0 - bleach=3.3.0=pyh44b312d_0 - blinker=1.4=py_1 - - brotlipy=0.7.0=py37h5e8e339_1001 + - brotlipy=0.7.0=py37hb5d75c8_1001 - c-ares=1.17.1=h36c2ea0_0 - - ca-certificates=2020.12.5=ha878542_0 + - ca-certificates=2021.1.19=h06a4308_0 - certifi=2020.12.5=py37h89c1867_1 - certipy=0.1.3=py_0 - cffi=1.14.5=py37hc58025e_0 @@ -29,28 +29,29 @@ dependencies: - cryptography=3.4.4=py37hf1a17b8_0 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - - entrypoints=0.3=pyhd8ed1ab_1003 + - entrypoints=0.3=py37hc8dfbb8_1002 - idna=2.10=pyh9f0ad1d_0 - importlib-metadata=3.4.0=py37h89c1867_0 - importlib_metadata=3.4.0=hd8ed1ab_0 - - ipykernel=5.4.2=py37h888b3d9_0 + - ipykernel=5.5.0=py37h888b3d9_1 - ipython=7.20.0=py37h888b3d9_2 - ipython_genutils=0.2.0=py_1 - ipywidgets=7.6.3=pyhd3deb0d_0 - jedi=0.18.0=py37h89c1867_2 - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - - jsonschema=3.2.0=py_2 + - jsonschema=3.2.0=py37hc8dfbb8_1 - jupyter-offlinenotebook=0.2.1=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.1=py37h89c1867_0 - - jupyter_server=1.3.0=py37h89c1867_0 + - jupyter_server=1.4.0=py37h89c1867_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - - jupyterhub-base=1.1.0=py37hc8dfbb8_5 - - jupyterhub-singleuser=1.1.0=py37hc8dfbb8_5 + - jupyterhub-base=1.3.0=py37h89c1867_0 + - jupyterhub-singleuser=1.3.0=py37h89c1867_0 - jupyterlab=3.0.7=pyhd8ed1ab_0 - - jupyterlab_server=2.2.0=pyhd8ed1ab_0 + - jupyterlab_pygments=0.1.2=pyh9f0ad1d_0 + - jupyterlab_server=2.3.0=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - ld_impl_linux-64=2.35.1=hea4e1c9_2 @@ -61,27 +62,29 @@ dependencies: - libgcc-ng=9.3.0=h2828fa1_18 - libgomp=9.3.0=h2828fa1_18 - libnghttp2=1.43.0=h812cca2_0 - - libsodium=1.0.18=h36c2ea0_1 + - libsodium=1.0.18=h516909a_1 - libssh2=1.9.0=hab1572f_5 - libstdcxx-ng=9.3.0=h6de172a_18 - mako=1.1.4=pyh44b312d_0 - markupsafe=1.1.1=py37h5e8e339_3 - mistune=0.8.4=py37h5e8e339_1003 - nbclassic=0.2.6=pyhd8ed1ab_0 - - nbconvert=5.6.1=py37hc8dfbb8_1 + - nbclient=0.5.2=pyhd8ed1ab_0 + - nbconvert=6.0.7=py37h89c1867_3 - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - - notebook=6.1.6=py37h89c1867_0 + - nest-asyncio=1.5.1=pyhd3eb1b0_0 + - notebook=6.2.0=py37h89c1867_0 - nteract_on_jupyter=2.1.3=py_0 - - oauthlib=3.0.1=py_0 - - openssl=1.1.1i=h7f98852_0 + - oauthlib=3.1.0=py_0 + - openssl=1.1.1j=h7f98852_0 - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - pandoc=2.11.4=h7f98852_0 - - pandocfilters=1.4.2=py_1 + - pandocfilters=1.4.3=py37h06a4308_1 - parso=0.8.1=pyhd8ed1ab_0 - - pexpect=4.8.0=pyh9f0ad1d_2 - - pickleshare=0.7.5=py_1003 + - pexpect=4.8.0=py37hc8dfbb8_1 + - pickleshare=0.7.5=py37hc8dfbb8_1002 - pip=21.0.1=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - prompt-toolkit=3.0.16=pyha770c72_0 @@ -95,26 +98,26 @@ dependencies: - pyparsing=2.4.7=pyh9f0ad1d_0 - pyrsistent=0.17.3=py37h5e8e339_2 - pysocks=1.7.1=py37h89c1867_3 - - python=3.7.9=hffdb5ce_100_cpython + - python=3.7.10=hffdb5ce_100_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.7=1_cp37m - pytz=2021.1=pyhd8ed1ab_0 - pyzmq=22.0.3=py37h499b945_0 - - readline=8.0=he28a2e2_2 + - readline=8.1=h27cfd23_0 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py37h5e8e339_2 - ruamel.yaml.clib=0.2.2=py37h5e8e339_2 - send2trash=1.5.0=py_0 - - setuptools=49.6.0=py37h89c1867_3 + - setuptools=52.0.0=py37h06a4308_0 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py37h89c1867_1 - sqlalchemy=1.3.23=py37h5e8e339_0 - sqlite=3.34.0=h74cdb3f_0 - terminado=0.9.2=py37h89c1867_0 - testpath=0.4.4=py_0 - - tk=8.6.10=h21135ba_1 + - tk=8.6.10=hed695b0_1 - tornado=6.1=py37h5e8e339_1 - traitlets=5.0.5=py_0 - typing_extensions=3.7.4.3=py_0 @@ -122,10 +125,9 @@ dependencies: - wcwidth=0.2.5=pyh9f0ad1d_2 - webencodings=0.5.1=py_1 - wheel=0.36.2=pyhd3deb0d_0 - - widgetsnbextension=3.5.1=py37h89c1867_4 + - widgetsnbextension=3.5.1=py37hc8dfbb8_4 - xz=5.2.5=h516909a_1 - zeromq=4.3.4=h9c3ff4c_0 - zipp=3.4.0=py_0 - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d - diff --git a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml index 83c42bcf7..791560641 100644 --- a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-2.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-16 14:41:11 UTC +# Frozen on 2021-02-22 10:00:47 UTC name: r2d channels: - conda-forge @@ -8,48 +8,46 @@ channels: dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - - backports=1.0=py_2 - - backports.shutil_get_terminal_size=1.0.0=py_3 - - backports_abc=0.5=py_1 - - ca-certificates=2020.12.5=ha878542_0 - - certifi=2019.11.28=py27h8c360ce_1 - - configparser=3.7.3=py27h8c360ce_2 + - backports=1.0=py27_1 + - backports.shutil_get_terminal_size=1.0.0=py27_1 + - backports_abc=0.5=py27_0 + - ca-certificates=2021.1.19=h06a4308_0 + - certifi=2020.6.20=pyhd3eb1b0_3 + - configparser=4.0.2=py27_0 - decorator=4.4.2=py_0 - entrypoints=0.3=py27h8c360ce_1001 - enum34=1.1.10=py27h8c360ce_1 - futures=3.3.0=py27h8c360ce_1 - ipykernel=4.8.2=py27_0 - ipython=5.8.0=py27_1 - - ipython_genutils=0.2.0=py_1 + - ipython_genutils=0.2.0=py27_0 - jupyter_client=5.3.4=py27_1 - jupyter_core=4.6.3=py27h8c360ce_1 - - ld_impl_linux-64=2.35.1=hea4e1c9_2 - - libffi=3.2.1=he1b5a44_1007 + - libffi=3.3=h58526e2_2 - libgcc-ng=9.3.0=h2828fa1_18 - libgomp=9.3.0=h2828fa1_18 - libsodium=1.0.17=h516909a_0 - libstdcxx-ng=9.3.0=h6de172a_18 - ncurses=6.2=h58526e2_4 - - openssl=1.1.1i=h7f98852_0 - pathlib2=2.3.5=py27h8c360ce_1 - - pexpect=4.8.0=pyh9f0ad1d_2 + - pexpect=4.8.0=py27h8c360ce_1 - pickleshare=0.7.5=py27h8c360ce_1001 - pip=20.1.1=pyh9f0ad1d_0 - - prompt_toolkit=1.0.15=py_1 + - prompt_toolkit=1.0.15=py27_0 - ptyprocess=0.7.0=pyhd3deb0d_0 - pygments=2.5.2=py_0 - - python=2.7.15=h5a48372_1011_cpython + - python=2.7.18=h15b4118_1 - python-dateutil=2.8.1=py_0 - python_abi=2.7=1_cp27mu - pyzmq=19.0.0=py27h76efe43_1 - - readline=8.0=he28a2e2_2 + - readline=8.1=h27cfd23_0 - scandir=1.10.0=py27hdf8410d_1 - setuptools=44.0.0=py27_0 - - simplegeneric=0.8.1=py_1 - - singledispatch=3.4.0.3=pyh9f0ad1d_1001 + - simplegeneric=0.8.1=py27_0 + - singledispatch=3.6.0=pyh44b312d_0 - six=1.15.0=pyh9f0ad1d_0 - sqlite=3.34.0=h74cdb3f_0 - - tk=8.6.10=h21135ba_1 + - tk=8.6.10=hed695b0_1 - tornado=5.1.1=py27h14c3975_1000 - traitlets=4.3.3=py27h8c360ce_1 - wcwidth=0.1.9=pyh9f0ad1d_0 @@ -57,4 +55,3 @@ dependencies: - zeromq=4.3.2=he1b5a44_2 - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d - diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml index bb87d163f..7b4aebd34 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.6.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-16 14:42:56 UTC +# Frozen on 2021-02-22 10:01:26 UTC name: r2d channels: - conda-forge @@ -8,52 +8,53 @@ channels: dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - - alembic=1.5.4=pyhd8ed1ab_0 + - alembic=1.5.5=pyhd8ed1ab_0 - anyio=2.1.0=py36h5fab9bb_0 - argon2-cffi=20.1.0=py36h8f6f2f9_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 - babel=2.9.0=pyhd3deb0d_0 - backcall=0.2.0=pyh9f0ad1d_0 - - backports=1.0=py_2 + - backports=1.0=py36_1 - backports.functools_lru_cache=1.6.1=py_0 - bleach=3.3.0=pyh44b312d_0 - blinker=1.4=py_1 - - brotlipy=0.7.0=py36h8f6f2f9_1001 + - brotlipy=0.7.0=py36he6145b8_1001 - c-ares=1.17.1=h36c2ea0_0 - - ca-certificates=2020.12.5=ha878542_0 + - ca-certificates=2021.1.19=h06a4308_0 - certifi=2020.12.5=py36h5fab9bb_1 - certipy=0.1.3=py_0 - cffi=1.14.5=py36hc120d54_0 - chardet=4.0.0=py36h5fab9bb_1 - contextvars=2.4=py_0 - cryptography=3.4.4=py36hc39840e_0 - - dataclasses=0.7=pyhe4b4509_6 + - dataclasses=0.7=py36_0 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - - entrypoints=0.3=pyhd8ed1ab_1003 + - entrypoints=0.3=py36h9f0ad1d_1002 - idna=2.10=pyh9f0ad1d_0 - immutables=0.15=py36h8f6f2f9_0 - importlib-metadata=3.4.0=py36h5fab9bb_0 - importlib_metadata=3.4.0=hd8ed1ab_0 - - ipykernel=5.4.2=py36he448a4c_0 + - ipykernel=5.5.0=py36he448a4c_1 - ipython=7.16.1=py36he448a4c_2 - - ipython_genutils=0.2.0=py_1 + - ipython_genutils=0.2.0=py36_0 - ipywidgets=7.6.3=pyhd3deb0d_0 - - jedi=0.17.2=py36h5fab9bb_1 + - jedi=0.17.2=py36h9f0ad1d_1 - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - - jsonschema=3.2.0=py_2 + - jsonschema=3.2.0=py36h9f0ad1d_1 - jupyter-offlinenotebook=0.2.1=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.1=py36h5fab9bb_0 - - jupyter_server=1.3.0=py36h5fab9bb_0 + - jupyter_server=1.4.0=py36h5fab9bb_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - - jupyterhub-base=1.1.0=py36h9f0ad1d_5 - - jupyterhub-singleuser=1.1.0=py36h9f0ad1d_5 + - jupyterhub-base=1.3.0=py36h5fab9bb_0 + - jupyterhub-singleuser=1.3.0=py36h5fab9bb_0 - jupyterlab=3.0.7=pyhd8ed1ab_0 - - jupyterlab_server=2.2.0=pyhd8ed1ab_0 + - jupyterlab_pygments=0.1.2=pyh9f0ad1d_0 + - jupyterlab_server=2.3.0=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - ld_impl_linux-64=2.35.1=hea4e1c9_2 @@ -64,27 +65,29 @@ dependencies: - libgcc-ng=9.3.0=h2828fa1_18 - libgomp=9.3.0=h2828fa1_18 - libnghttp2=1.43.0=h812cca2_0 - - libsodium=1.0.18=h36c2ea0_1 + - libsodium=1.0.18=h516909a_1 - libssh2=1.9.0=hab1572f_5 - libstdcxx-ng=9.3.0=h6de172a_18 - mako=1.1.4=pyh44b312d_0 - markupsafe=1.1.1=py36h8f6f2f9_3 - mistune=0.8.4=py36h8f6f2f9_1003 - nbclassic=0.2.6=pyhd8ed1ab_0 - - nbconvert=5.6.1=py36h9f0ad1d_1 + - nbclient=0.5.2=pyhd8ed1ab_0 + - nbconvert=6.0.7=py36h5fab9bb_3 - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - - notebook=6.1.6=py36h5fab9bb_0 + - nest-asyncio=1.5.1=pyhd3eb1b0_0 + - notebook=6.2.0=py36h5fab9bb_0 - nteract_on_jupyter=2.1.3=py_0 - - oauthlib=3.0.1=py_0 - - openssl=1.1.1i=h7f98852_0 + - oauthlib=3.1.0=py_0 + - openssl=1.1.1j=h7f98852_0 - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - pandoc=2.11.4=h7f98852_0 - - pandocfilters=1.4.2=py_1 + - pandocfilters=1.4.3=py36h06a4308_1 - parso=0.7.1=pyh9f0ad1d_0 - - pexpect=4.8.0=pyh9f0ad1d_2 - - pickleshare=0.7.5=py_1003 + - pexpect=4.8.0=py36h9f0ad1d_1 + - pickleshare=0.7.5=py36h9f0ad1d_1002 - pip=21.0.1=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - prompt-toolkit=3.0.16=pyha770c72_0 @@ -98,26 +101,26 @@ dependencies: - pyparsing=2.4.7=pyh9f0ad1d_0 - pyrsistent=0.17.3=py36h8f6f2f9_2 - pysocks=1.7.1=py36h5fab9bb_3 - - python=3.6.12=hffdb5ce_0_cpython + - python=3.6.13=hffdb5ce_0_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.6=1_cp36m - pytz=2021.1=pyhd8ed1ab_0 - pyzmq=22.0.3=py36h81c33ee_0 - - readline=8.0=he28a2e2_2 + - readline=8.1=h27cfd23_0 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py36h8f6f2f9_2 - ruamel.yaml.clib=0.2.2=py36h8f6f2f9_2 - send2trash=1.5.0=py_0 - - setuptools=49.6.0=py36h5fab9bb_3 + - setuptools=52.0.0=py36h06a4308_0 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py36h5fab9bb_1 - sqlalchemy=1.3.23=py36h8f6f2f9_0 - sqlite=3.34.0=h74cdb3f_0 - terminado=0.9.2=py36h5fab9bb_0 - testpath=0.4.4=py_0 - - tk=8.6.10=h21135ba_1 + - tk=8.6.10=hed695b0_1 - tornado=6.1=py36h8f6f2f9_1 - traitlets=4.3.3=py36h9f0ad1d_1 - typing_extensions=3.7.4.3=py_0 @@ -125,10 +128,9 @@ dependencies: - wcwidth=0.2.5=pyh9f0ad1d_2 - webencodings=0.5.1=py_1 - wheel=0.36.2=pyhd3deb0d_0 - - widgetsnbextension=3.5.1=py36h5fab9bb_4 + - widgetsnbextension=3.5.1=py36h9f0ad1d_4 - xz=5.2.5=h516909a_1 - zeromq=4.3.4=h9c3ff4c_0 - zipp=3.4.0=py_0 - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d - diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.yml b/repo2docker/buildpacks/conda/environment.py-3.6.yml index e21901c4e..87c3a2aaa 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.yml @@ -1,12 +1,12 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-02-16 14:42:56 UTC +# Generated on 2021-02-22 10:01:26 UTC dependencies: - python=3.6.* - ipywidgets==7.6.3 - jupyter-offlinenotebook==0.2.1 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.7 -- jupyterhub-singleuser==1.1.0 -- nbconvert==5.6.1 -- notebook==6.1.6 +- jupyterhub-singleuser==1.3.0 +- nbconvert==6.0.7 +- notebook==6.2.0 - nteract_on_jupyter==2.1.3 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml index bd4a68b93..82aa231f3 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-16 14:46:08 UTC +# Frozen on 2021-02-22 10:03:32 UTC name: r2d channels: - conda-forge @@ -8,9 +8,9 @@ channels: dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - - alembic=1.5.4=pyhd8ed1ab_0 + - alembic=1.5.5=pyhd8ed1ab_0 - anyio=2.1.0=py37h89c1867_0 - - argon2-cffi=20.1.0=py37h5e8e339_2 + - argon2-cffi=20.1.0=py37h8f50634_2 - async_generator=1.10=py_0 - attrs=20.3.0=pyhd3deb0d_0 - babel=2.9.0=pyhd3deb0d_0 @@ -19,9 +19,9 @@ dependencies: - backports.functools_lru_cache=1.6.1=py_0 - bleach=3.3.0=pyh44b312d_0 - blinker=1.4=py_1 - - brotlipy=0.7.0=py37h5e8e339_1001 + - brotlipy=0.7.0=py37hb5d75c8_1001 - c-ares=1.17.1=h36c2ea0_0 - - ca-certificates=2020.12.5=ha878542_0 + - ca-certificates=2021.1.19=h06a4308_0 - certifi=2020.12.5=py37h89c1867_1 - certipy=0.1.3=py_0 - cffi=1.14.5=py37hc58025e_0 @@ -29,28 +29,29 @@ dependencies: - cryptography=3.4.4=py37hf1a17b8_0 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - - entrypoints=0.3=pyhd8ed1ab_1003 + - entrypoints=0.3=py37hc8dfbb8_1002 - idna=2.10=pyh9f0ad1d_0 - importlib-metadata=3.4.0=py37h89c1867_0 - importlib_metadata=3.4.0=hd8ed1ab_0 - - ipykernel=5.4.2=py37h888b3d9_0 + - ipykernel=5.5.0=py37h888b3d9_1 - ipython=7.20.0=py37h888b3d9_2 - ipython_genutils=0.2.0=py_1 - ipywidgets=7.6.3=pyhd3deb0d_0 - jedi=0.18.0=py37h89c1867_2 - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - - jsonschema=3.2.0=py_2 + - jsonschema=3.2.0=py37hc8dfbb8_1 - jupyter-offlinenotebook=0.2.1=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.1=py37h89c1867_0 - - jupyter_server=1.3.0=py37h89c1867_0 + - jupyter_server=1.4.0=py37h89c1867_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - - jupyterhub-base=1.1.0=py37hc8dfbb8_5 - - jupyterhub-singleuser=1.1.0=py37hc8dfbb8_5 + - jupyterhub-base=1.3.0=py37h89c1867_0 + - jupyterhub-singleuser=1.3.0=py37h89c1867_0 - jupyterlab=3.0.7=pyhd8ed1ab_0 - - jupyterlab_server=2.2.0=pyhd8ed1ab_0 + - jupyterlab_pygments=0.1.2=pyh9f0ad1d_0 + - jupyterlab_server=2.3.0=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - ld_impl_linux-64=2.35.1=hea4e1c9_2 @@ -61,27 +62,29 @@ dependencies: - libgcc-ng=9.3.0=h2828fa1_18 - libgomp=9.3.0=h2828fa1_18 - libnghttp2=1.43.0=h812cca2_0 - - libsodium=1.0.18=h36c2ea0_1 + - libsodium=1.0.18=h516909a_1 - libssh2=1.9.0=hab1572f_5 - libstdcxx-ng=9.3.0=h6de172a_18 - mako=1.1.4=pyh44b312d_0 - markupsafe=1.1.1=py37h5e8e339_3 - mistune=0.8.4=py37h5e8e339_1003 - nbclassic=0.2.6=pyhd8ed1ab_0 - - nbconvert=5.6.1=py37hc8dfbb8_1 + - nbclient=0.5.2=pyhd8ed1ab_0 + - nbconvert=6.0.7=py37h89c1867_3 - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - - notebook=6.1.6=py37h89c1867_0 + - nest-asyncio=1.5.1=pyhd3eb1b0_0 + - notebook=6.2.0=py37h89c1867_0 - nteract_on_jupyter=2.1.3=py_0 - - oauthlib=3.0.1=py_0 - - openssl=1.1.1i=h7f98852_0 + - oauthlib=3.1.0=py_0 + - openssl=1.1.1j=h7f98852_0 - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - pandoc=2.11.4=h7f98852_0 - - pandocfilters=1.4.2=py_1 + - pandocfilters=1.4.3=py37h06a4308_1 - parso=0.8.1=pyhd8ed1ab_0 - - pexpect=4.8.0=pyh9f0ad1d_2 - - pickleshare=0.7.5=py_1003 + - pexpect=4.8.0=py37hc8dfbb8_1 + - pickleshare=0.7.5=py37hc8dfbb8_1002 - pip=21.0.1=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - prompt-toolkit=3.0.16=pyha770c72_0 @@ -95,26 +98,26 @@ dependencies: - pyparsing=2.4.7=pyh9f0ad1d_0 - pyrsistent=0.17.3=py37h5e8e339_2 - pysocks=1.7.1=py37h89c1867_3 - - python=3.7.9=hffdb5ce_100_cpython + - python=3.7.10=hffdb5ce_100_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.7=1_cp37m - pytz=2021.1=pyhd8ed1ab_0 - pyzmq=22.0.3=py37h499b945_0 - - readline=8.0=he28a2e2_2 + - readline=8.1=h27cfd23_0 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py37h5e8e339_2 - ruamel.yaml.clib=0.2.2=py37h5e8e339_2 - send2trash=1.5.0=py_0 - - setuptools=49.6.0=py37h89c1867_3 + - setuptools=52.0.0=py37h06a4308_0 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py37h89c1867_1 - sqlalchemy=1.3.23=py37h5e8e339_0 - sqlite=3.34.0=h74cdb3f_0 - terminado=0.9.2=py37h89c1867_0 - testpath=0.4.4=py_0 - - tk=8.6.10=h21135ba_1 + - tk=8.6.10=hed695b0_1 - tornado=6.1=py37h5e8e339_1 - traitlets=5.0.5=py_0 - typing_extensions=3.7.4.3=py_0 @@ -122,10 +125,9 @@ dependencies: - wcwidth=0.2.5=pyh9f0ad1d_2 - webencodings=0.5.1=py_1 - wheel=0.36.2=pyhd3deb0d_0 - - widgetsnbextension=3.5.1=py37h89c1867_4 + - widgetsnbextension=3.5.1=py37hc8dfbb8_4 - xz=5.2.5=h516909a_1 - zeromq=4.3.4=h9c3ff4c_0 - zipp=3.4.0=py_0 - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d - diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.yml b/repo2docker/buildpacks/conda/environment.py-3.7.yml index 62b74054a..094549717 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.yml @@ -1,12 +1,12 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-02-16 14:46:08 UTC +# Generated on 2021-02-22 10:03:32 UTC dependencies: - python=3.7.* - ipywidgets==7.6.3 - jupyter-offlinenotebook==0.2.1 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.7 -- jupyterhub-singleuser==1.1.0 -- nbconvert==5.6.1 -- notebook==6.1.6 +- jupyterhub-singleuser==1.3.0 +- nbconvert==6.0.7 +- notebook==6.2.0 - nteract_on_jupyter==2.1.3 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml index df2eafe12..54c832c00 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.8.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-16 14:48:09 UTC +# Frozen on 2021-02-22 10:05:25 UTC name: r2d channels: - conda-forge @@ -8,7 +8,7 @@ channels: dependencies: - _libgcc_mutex=0.1=conda_forge - _openmp_mutex=4.5=1_gnu - - alembic=1.5.4=pyhd8ed1ab_0 + - alembic=1.5.5=pyhd8ed1ab_0 - anyio=2.1.0=py38h578d9bd_0 - argon2-cffi=20.1.0=py38h497a2fe_2 - async_generator=1.10=py_0 @@ -21,7 +21,7 @@ dependencies: - blinker=1.4=py_1 - brotlipy=0.7.0=py38h497a2fe_1001 - c-ares=1.17.1=h36c2ea0_0 - - ca-certificates=2020.12.5=ha878542_0 + - ca-certificates=2021.1.19=h06a4308_0 - certifi=2020.12.5=py38h578d9bd_1 - certipy=0.1.3=py_0 - cffi=1.14.5=py38ha65f79e_0 @@ -29,28 +29,29 @@ dependencies: - cryptography=3.4.4=py38h3e25421_0 - decorator=4.4.2=py_0 - defusedxml=0.6.0=py_0 - - entrypoints=0.3=pyhd8ed1ab_1003 + - entrypoints=0.3=py38h32f6830_1002 - idna=2.10=pyh9f0ad1d_0 - importlib-metadata=3.4.0=py38h578d9bd_0 - importlib_metadata=3.4.0=hd8ed1ab_0 - - ipykernel=5.4.2=py38h81c977d_0 + - ipykernel=5.5.0=py38h81c977d_1 - ipython=7.20.0=py38h81c977d_2 - ipython_genutils=0.2.0=py_1 - ipywidgets=7.6.3=pyhd3deb0d_0 - jedi=0.18.0=py38h578d9bd_2 - jinja2=2.11.3=pyh44b312d_0 - json5=0.9.5=pyh9f0ad1d_0 - - jsonschema=3.2.0=py_2 + - jsonschema=3.2.0=py38h32f6830_1 - jupyter-offlinenotebook=0.2.1=pyhd8ed1ab_0 - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 - jupyter_client=6.1.11=pyhd8ed1ab_1 - jupyter_core=4.7.1=py38h578d9bd_0 - - jupyter_server=1.3.0=py38h578d9bd_0 + - jupyter_server=1.4.0=py38h578d9bd_0 - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 - - jupyterhub-base=1.1.0=py38h32f6830_5 - - jupyterhub-singleuser=1.1.0=py38h32f6830_5 + - jupyterhub-base=1.3.0=py38h578d9bd_0 + - jupyterhub-singleuser=1.3.0=py38h578d9bd_0 - jupyterlab=3.0.7=pyhd8ed1ab_0 - - jupyterlab_server=2.2.0=pyhd8ed1ab_0 + - jupyterlab_pygments=0.1.2=pyh9f0ad1d_0 + - jupyterlab_server=2.3.0=pyhd8ed1ab_0 - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 - krb5=1.17.2=h926e7f8_0 - ld_impl_linux-64=2.35.1=hea4e1c9_2 @@ -61,27 +62,29 @@ dependencies: - libgcc-ng=9.3.0=h2828fa1_18 - libgomp=9.3.0=h2828fa1_18 - libnghttp2=1.43.0=h812cca2_0 - - libsodium=1.0.18=h36c2ea0_1 + - libsodium=1.0.18=h516909a_1 - libssh2=1.9.0=hab1572f_5 - libstdcxx-ng=9.3.0=h6de172a_18 - mako=1.1.4=pyh44b312d_0 - markupsafe=1.1.1=py38h497a2fe_3 - mistune=0.8.4=py38h497a2fe_1003 - nbclassic=0.2.6=pyhd8ed1ab_0 - - nbconvert=5.6.1=py38h32f6830_1 + - nbclient=0.5.2=pyhd8ed1ab_0 + - nbconvert=6.0.7=py38h578d9bd_3 - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - - notebook=6.1.6=py38h578d9bd_0 + - nest-asyncio=1.5.1=pyhd3eb1b0_0 + - notebook=6.2.0=py38h578d9bd_0 - nteract_on_jupyter=2.1.3=py_0 - - oauthlib=3.0.1=py_0 - - openssl=1.1.1i=h7f98852_0 + - oauthlib=3.1.0=py_0 + - openssl=1.1.1j=h7f98852_0 - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - pandoc=2.11.4=h7f98852_0 - - pandocfilters=1.4.2=py_1 + - pandocfilters=1.4.3=py38h06a4308_1 - parso=0.8.1=pyhd8ed1ab_0 - - pexpect=4.8.0=pyh9f0ad1d_2 - - pickleshare=0.7.5=py_1003 + - pexpect=4.8.0=py38h32f6830_1 + - pickleshare=0.7.5=py38h32f6830_1002 - pip=21.0.1=pyhd8ed1ab_0 - prometheus_client=0.9.0=pyhd3deb0d_0 - prompt-toolkit=3.0.16=pyha770c72_0 @@ -95,26 +98,26 @@ dependencies: - pyparsing=2.4.7=pyh9f0ad1d_0 - pyrsistent=0.17.3=py38h497a2fe_2 - pysocks=1.7.1=py38h578d9bd_3 - - python=3.8.6=hffdb5ce_5_cpython + - python=3.8.8=hffdb5ce_0_cpython - python-dateutil=2.8.1=py_0 - python-editor=1.0.4=py_0 - python-json-logger=2.0.1=pyh9f0ad1d_0 - python_abi=3.8=1_cp38 - pytz=2021.1=pyhd8ed1ab_0 - pyzmq=22.0.3=py38h3d7ac18_0 - - readline=8.0=he28a2e2_2 + - readline=8.1=h27cfd23_0 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py38h497a2fe_2 - ruamel.yaml.clib=0.2.2=py38h497a2fe_2 - send2trash=1.5.0=py_0 - - setuptools=49.6.0=py38h578d9bd_3 + - setuptools=52.0.0=py38h06a4308_0 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py38h578d9bd_1 - sqlalchemy=1.3.23=py38h497a2fe_0 - sqlite=3.34.0=h74cdb3f_0 - terminado=0.9.2=py38h578d9bd_0 - testpath=0.4.4=py_0 - - tk=8.6.10=h21135ba_1 + - tk=8.6.10=hed695b0_1 - tornado=6.1=py38h497a2fe_1 - traitlets=5.0.5=py_0 - urllib3=1.26.3=pyhd8ed1ab_0 @@ -127,4 +130,3 @@ dependencies: - zipp=3.4.0=py_0 - zlib=1.2.11=h516909a_1010 prefix: /opt/conda/envs/r2d - diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.yml b/repo2docker/buildpacks/conda/environment.py-3.8.yml index b9e2cbb6a..e24f81eba 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.yml @@ -1,12 +1,12 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-02-16 14:48:09 UTC +# Generated on 2021-02-22 10:05:25 UTC dependencies: - python=3.8.* - ipywidgets==7.6.3 - jupyter-offlinenotebook==0.2.1 - jupyter-resource-usage==0.5.1 - jupyterlab==3.0.7 -- jupyterhub-singleuser==1.1.0 -- nbconvert==5.6.1 -- notebook==6.1.6 +- jupyterhub-singleuser==1.3.0 +- nbconvert==6.0.7 +- notebook==6.2.0 - nteract_on_jupyter==2.1.3 diff --git a/repo2docker/buildpacks/conda/environment.py-3.9.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.9.frozen.yml new file mode 100644 index 000000000..ec10043e5 --- /dev/null +++ b/repo2docker/buildpacks/conda/environment.py-3.9.frozen.yml @@ -0,0 +1,133 @@ +# AUTO GENERATED FROM environment.py-3.9.yml, DO NOT MANUALLY MODIFY +# Frozen on 2021-02-22 10:07:07 UTC +name: r2d +channels: + - conda-forge + - defaults + - conda-forge/label/broken +dependencies: + - _libgcc_mutex=0.1=conda_forge + - _openmp_mutex=4.5=1_gnu + - alembic=1.5.5=pyhd8ed1ab_0 + - anyio=2.1.0=py39hf3d152e_0 + - argon2-cffi=20.1.0=py39hbd71b63_2 + - async_generator=1.10=py_0 + - attrs=20.3.0=pyhd3deb0d_0 + - babel=2.9.0=pyhd3deb0d_0 + - backcall=0.2.0=pyh9f0ad1d_0 + - backports=1.0=py_2 + - backports.functools_lru_cache=1.6.1=py_0 + - bleach=3.3.0=pyh44b312d_0 + - blinker=1.4=py_1 + - brotlipy=0.7.0=py39h3811e60_1001 + - c-ares=1.17.1=h36c2ea0_0 + - ca-certificates=2021.1.19=h06a4308_0 + - certifi=2020.12.5=py39hf3d152e_1 + - certipy=0.1.3=py_0 + - cffi=1.14.5=py39he32792d_0 + - chardet=4.0.0=py39hf3d152e_1 + - cryptography=3.4.4=py39h95dcef6_0 + - decorator=4.4.2=py_0 + - defusedxml=0.6.0=py_0 + - entrypoints=0.3=py39hde42818_1002 + - idna=2.10=pyh9f0ad1d_0 + - importlib-metadata=3.4.0=py39hf3d152e_0 + - importlib_metadata=3.4.0=hd8ed1ab_0 + - ipykernel=5.5.0=py39hef51801_1 + - ipython=7.20.0=py39hef51801_2 + - ipython_genutils=0.2.0=py_1 + - ipywidgets=7.6.3=pyhd3deb0d_0 + - jedi=0.18.0=py39hf3d152e_2 + - jinja2=2.11.3=pyh44b312d_0 + - json5=0.9.5=pyh9f0ad1d_0 + - jsonschema=3.2.0=py_2 + - jupyter-offlinenotebook=0.2.1=pyhd8ed1ab_0 + - jupyter-resource-usage=0.5.1=pyhd8ed1ab_0 + - jupyter_client=6.1.11=pyhd8ed1ab_1 + - jupyter_core=4.7.1=py39hf3d152e_0 + - jupyter_server=1.4.0=py39hf3d152e_0 + - jupyter_telemetry=0.1.0=pyhd8ed1ab_1 + - jupyterhub-base=1.3.0=py39hf3d152e_0 + - jupyterhub-singleuser=1.3.0=py39hf3d152e_0 + - jupyterlab=3.0.7=pyhd8ed1ab_0 + - jupyterlab_pygments=0.1.2=pyh9f0ad1d_0 + - jupyterlab_server=2.3.0=pyhd8ed1ab_0 + - jupyterlab_widgets=1.0.0=pyhd8ed1ab_1 + - krb5=1.17.2=h926e7f8_0 + - ld_impl_linux-64=2.35.1=hea4e1c9_2 + - libcurl=7.71.1=hcdd3856_8 + - libedit=3.1.20191231=he28a2e2_2 + - libev=4.33=h516909a_1 + - libffi=3.3=h58526e2_2 + - libgcc-ng=9.3.0=h2828fa1_18 + - libgomp=9.3.0=h2828fa1_18 + - libnghttp2=1.43.0=h812cca2_0 + - libsodium=1.0.18=h516909a_1 + - libssh2=1.9.0=hab1572f_5 + - libstdcxx-ng=9.3.0=h6de172a_18 + - mako=1.1.4=pyh44b312d_0 + - markupsafe=1.1.1=py39h3811e60_3 + - mistune=0.8.4=py39h3811e60_1003 + - nbclassic=0.2.6=pyhd8ed1ab_0 + - nbclient=0.5.2=pyhd8ed1ab_0 + - nbconvert=6.0.7=py39hf3d152e_3 + - nbformat=5.1.2=pyhd8ed1ab_1 + - ncurses=6.2=h58526e2_4 + - nest-asyncio=1.5.1=pyhd3eb1b0_0 + - notebook=6.2.0=py39hf3d152e_0 + - nteract_on_jupyter=2.1.3=py_0 + - oauthlib=3.1.0=py_0 + - openssl=1.1.1j=h7f98852_0 + - packaging=20.9=pyh44b312d_0 + - pamela=1.0.0=py_0 + - pandoc=2.11.4=h7f98852_0 + - pandocfilters=1.4.3=py39h06a4308_1 + - parso=0.8.1=pyhd8ed1ab_0 + - pexpect=4.8.0=pyh9f0ad1d_2 + - pickleshare=0.7.5=py39hde42818_1002 + - pip=21.0.1=pyhd8ed1ab_0 + - prometheus_client=0.9.0=pyhd3deb0d_0 + - prompt-toolkit=3.0.16=pyha770c72_0 + - psutil=5.8.0=py39h3811e60_1 + - ptyprocess=0.7.0=pyhd3deb0d_0 + - pycparser=2.20=pyh9f0ad1d_2 + - pycurl=7.43.0.6=py39h72e3413_1 + - pygments=2.8.0=pyhd8ed1ab_0 + - pyjwt=2.0.1=pyhd8ed1ab_0 + - pyopenssl=20.0.1=pyhd8ed1ab_0 + - pyparsing=2.4.7=pyh9f0ad1d_0 + - pyrsistent=0.17.3=py39h3811e60_2 + - pysocks=1.7.1=py39hf3d152e_3 + - python=3.9.2=hffdb5ce_0_cpython + - python-dateutil=2.8.1=py_0 + - python-editor=1.0.4=py_0 + - python-json-logger=2.0.1=pyh9f0ad1d_0 + - python_abi=3.9=1_cp39 + - pytz=2021.1=pyhd8ed1ab_0 + - pyzmq=22.0.3=py39hea8fd45_0 + - readline=8.1=h27cfd23_0 + - requests=2.25.1=pyhd3deb0d_0 + - ruamel.yaml=0.16.12=py39h3811e60_2 + - ruamel.yaml.clib=0.2.2=py39h3811e60_2 + - send2trash=1.5.0=py_0 + - setuptools=52.0.0=py39h06a4308_0 + - six=1.15.0=pyh9f0ad1d_0 + - sniffio=1.2.0=py39hf3d152e_1 + - sqlalchemy=1.3.23=py39h3811e60_0 + - sqlite=3.34.0=h74cdb3f_0 + - terminado=0.9.2=py39hf3d152e_0 + - testpath=0.4.4=py_0 + - tk=8.6.10=hed695b0_1 + - tornado=6.1=py39h3811e60_1 + - traitlets=5.0.5=py_0 + - tzdata=2021a=he74cb21_0 + - urllib3=1.26.3=pyhd8ed1ab_0 + - wcwidth=0.2.5=pyh9f0ad1d_2 + - webencodings=0.5.1=py_1 + - wheel=0.36.2=pyhd3deb0d_0 + - widgetsnbextension=3.5.1=py39hf3d152e_4 + - xz=5.2.5=h516909a_1 + - zeromq=4.3.4=h9c3ff4c_0 + - zipp=3.4.0=py_0 + - zlib=1.2.11=h516909a_1010 +prefix: /opt/conda/envs/r2d diff --git a/repo2docker/buildpacks/conda/environment.py-3.9.yml b/repo2docker/buildpacks/conda/environment.py-3.9.yml new file mode 100644 index 000000000..f9ac01ed3 --- /dev/null +++ b/repo2docker/buildpacks/conda/environment.py-3.9.yml @@ -0,0 +1,12 @@ +# AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY +# Generated on 2021-02-22 10:07:07 UTC +dependencies: +- python=3.9.* +- ipywidgets==7.6.3 +- jupyter-offlinenotebook==0.2.1 +- jupyter-resource-usage==0.5.1 +- jupyterlab==3.0.7 +- jupyterhub-singleuser==1.3.0 +- nbconvert==6.0.7 +- notebook==6.2.0 +- nteract_on_jupyter==2.1.3 From 907c6f3ce53cb91d42a0c01b6a14c64b0d6e2c4b Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 22 Feb 2021 13:22:56 +0100 Subject: [PATCH 069/103] freeze with strict channel priority avoids pulling in newer releases from defaults this is conda's default behavior, but not mamba's --- repo2docker/buildpacks/conda/freeze.py | 1 + 1 file changed, 1 insertion(+) diff --git a/repo2docker/buildpacks/conda/freeze.py b/repo2docker/buildpacks/conda/freeze.py index eaa02e3e6..fd98ed5bb 100755 --- a/repo2docker/buildpacks/conda/freeze.py +++ b/repo2docker/buildpacks/conda/freeze.py @@ -77,6 +77,7 @@ def freeze(env_file, frozen_file): "; ".join( [ "set -ex", + "conda config --set channel_priority strict", "conda config --add channels conda-forge", f"mamba install -yq -S mamba={MAMBA_VERSION}" if MAMBA_VERSION From b73c96eecf27424bf3a28d6198113f6d7bb93a11 Mon Sep 17 00:00:00 2001 From: Min RK Date: Mon, 22 Feb 2021 13:31:29 +0100 Subject: [PATCH 070/103] refreeze with strict channel priority make sure we don't get any packages from 'defaults' --- .../buildpacks/conda/environment.frozen.yml | 14 +++++++------- .../conda/environment.py-2.7.frozen.yml | 16 +++++++++------- .../conda/environment.py-3.6.frozen.yml | 14 +++++++------- .../buildpacks/conda/environment.py-3.6.yml | 2 +- .../conda/environment.py-3.7.frozen.yml | 14 +++++++------- .../buildpacks/conda/environment.py-3.7.yml | 2 +- .../conda/environment.py-3.8.frozen.yml | 14 +++++++------- .../buildpacks/conda/environment.py-3.8.yml | 2 +- .../conda/environment.py-3.9.frozen.yml | 14 +++++++------- .../buildpacks/conda/environment.py-3.9.yml | 2 +- 10 files changed, 48 insertions(+), 46 deletions(-) diff --git a/repo2docker/buildpacks/conda/environment.frozen.yml b/repo2docker/buildpacks/conda/environment.frozen.yml index 82aa231f3..daca3f216 100644 --- a/repo2docker/buildpacks/conda/environment.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-22 10:03:32 UTC +# Frozen on 2021-02-22 12:26:46 UTC name: r2d channels: - conda-forge @@ -21,7 +21,7 @@ dependencies: - blinker=1.4=py_1 - brotlipy=0.7.0=py37hb5d75c8_1001 - c-ares=1.17.1=h36c2ea0_0 - - ca-certificates=2021.1.19=h06a4308_0 + - ca-certificates=2020.12.5=ha878542_0 - certifi=2020.12.5=py37h89c1867_1 - certipy=0.1.3=py_0 - cffi=1.14.5=py37hc58025e_0 @@ -73,15 +73,15 @@ dependencies: - nbconvert=6.0.7=py37h89c1867_3 - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - - nest-asyncio=1.5.1=pyhd3eb1b0_0 + - nest-asyncio=1.4.3=pyhd8ed1ab_0 - notebook=6.2.0=py37h89c1867_0 - nteract_on_jupyter=2.1.3=py_0 - - oauthlib=3.1.0=py_0 + - oauthlib=3.0.1=py_0 - openssl=1.1.1j=h7f98852_0 - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - pandoc=2.11.4=h7f98852_0 - - pandocfilters=1.4.3=py37h06a4308_1 + - pandocfilters=1.4.2=py_1 - parso=0.8.1=pyhd8ed1ab_0 - pexpect=4.8.0=py37hc8dfbb8_1 - pickleshare=0.7.5=py37hc8dfbb8_1002 @@ -105,12 +105,12 @@ dependencies: - python_abi=3.7=1_cp37m - pytz=2021.1=pyhd8ed1ab_0 - pyzmq=22.0.3=py37h499b945_0 - - readline=8.1=h27cfd23_0 + - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py37h5e8e339_2 - ruamel.yaml.clib=0.2.2=py37h5e8e339_2 - send2trash=1.5.0=py_0 - - setuptools=52.0.0=py37h06a4308_0 + - setuptools=49.6.0=py37h89c1867_3 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py37h89c1867_1 - sqlalchemy=1.3.23=py37h5e8e339_0 diff --git a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml index 791560641..816f8c870 100644 --- a/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-2.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-22 10:00:47 UTC +# Frozen on 2021-02-22 12:21:22 UTC name: r2d channels: - conda-forge @@ -11,9 +11,9 @@ dependencies: - backports=1.0=py27_1 - backports.shutil_get_terminal_size=1.0.0=py27_1 - backports_abc=0.5=py27_0 - - ca-certificates=2021.1.19=h06a4308_0 - - certifi=2020.6.20=pyhd3eb1b0_3 - - configparser=4.0.2=py27_0 + - ca-certificates=2020.12.5=ha878542_0 + - certifi=2019.11.28=py27h8c360ce_1 + - configparser=3.7.3=py27h8c360ce_2 - decorator=4.4.2=py_0 - entrypoints=0.3=py27h8c360ce_1001 - enum34=1.1.10=py27h8c360ce_1 @@ -23,12 +23,14 @@ dependencies: - ipython_genutils=0.2.0=py27_0 - jupyter_client=5.3.4=py27_1 - jupyter_core=4.6.3=py27h8c360ce_1 - - libffi=3.3=h58526e2_2 + - ld_impl_linux-64=2.35.1=hea4e1c9_2 + - libffi=3.2.1=he1b5a44_1007 - libgcc-ng=9.3.0=h2828fa1_18 - libgomp=9.3.0=h2828fa1_18 - libsodium=1.0.17=h516909a_0 - libstdcxx-ng=9.3.0=h6de172a_18 - ncurses=6.2=h58526e2_4 + - openssl=1.1.1j=h7f98852_0 - pathlib2=2.3.5=py27h8c360ce_1 - pexpect=4.8.0=py27h8c360ce_1 - pickleshare=0.7.5=py27h8c360ce_1001 @@ -36,11 +38,11 @@ dependencies: - prompt_toolkit=1.0.15=py27_0 - ptyprocess=0.7.0=pyhd3deb0d_0 - pygments=2.5.2=py_0 - - python=2.7.18=h15b4118_1 + - python=2.7.15=h5a48372_1011_cpython - python-dateutil=2.8.1=py_0 - python_abi=2.7=1_cp27mu - pyzmq=19.0.0=py27h76efe43_1 - - readline=8.1=h27cfd23_0 + - readline=8.0=he28a2e2_2 - scandir=1.10.0=py27hdf8410d_1 - setuptools=44.0.0=py27_0 - simplegeneric=0.8.1=py27_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml index 7b4aebd34..58a285d92 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.6.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-22 10:01:26 UTC +# Frozen on 2021-02-22 12:25:15 UTC name: r2d channels: - conda-forge @@ -21,7 +21,7 @@ dependencies: - blinker=1.4=py_1 - brotlipy=0.7.0=py36he6145b8_1001 - c-ares=1.17.1=h36c2ea0_0 - - ca-certificates=2021.1.19=h06a4308_0 + - ca-certificates=2020.12.5=ha878542_0 - certifi=2020.12.5=py36h5fab9bb_1 - certipy=0.1.3=py_0 - cffi=1.14.5=py36hc120d54_0 @@ -76,15 +76,15 @@ dependencies: - nbconvert=6.0.7=py36h5fab9bb_3 - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - - nest-asyncio=1.5.1=pyhd3eb1b0_0 + - nest-asyncio=1.4.3=pyhd8ed1ab_0 - notebook=6.2.0=py36h5fab9bb_0 - nteract_on_jupyter=2.1.3=py_0 - - oauthlib=3.1.0=py_0 + - oauthlib=3.0.1=py_0 - openssl=1.1.1j=h7f98852_0 - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - pandoc=2.11.4=h7f98852_0 - - pandocfilters=1.4.3=py36h06a4308_1 + - pandocfilters=1.4.2=py36_0 - parso=0.7.1=pyh9f0ad1d_0 - pexpect=4.8.0=py36h9f0ad1d_1 - pickleshare=0.7.5=py36h9f0ad1d_1002 @@ -108,12 +108,12 @@ dependencies: - python_abi=3.6=1_cp36m - pytz=2021.1=pyhd8ed1ab_0 - pyzmq=22.0.3=py36h81c33ee_0 - - readline=8.1=h27cfd23_0 + - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py36h8f6f2f9_2 - ruamel.yaml.clib=0.2.2=py36h8f6f2f9_2 - send2trash=1.5.0=py_0 - - setuptools=52.0.0=py36h06a4308_0 + - setuptools=49.6.0=py36h5fab9bb_3 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py36h5fab9bb_1 - sqlalchemy=1.3.23=py36h8f6f2f9_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.6.yml b/repo2docker/buildpacks/conda/environment.py-3.6.yml index 87c3a2aaa..acae085bf 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.6.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.6.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-02-22 10:01:26 UTC +# Generated on 2021-02-22 12:25:15 UTC dependencies: - python=3.6.* - ipywidgets==7.6.3 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml index 82aa231f3..daca3f216 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.7.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-22 10:03:32 UTC +# Frozen on 2021-02-22 12:26:46 UTC name: r2d channels: - conda-forge @@ -21,7 +21,7 @@ dependencies: - blinker=1.4=py_1 - brotlipy=0.7.0=py37hb5d75c8_1001 - c-ares=1.17.1=h36c2ea0_0 - - ca-certificates=2021.1.19=h06a4308_0 + - ca-certificates=2020.12.5=ha878542_0 - certifi=2020.12.5=py37h89c1867_1 - certipy=0.1.3=py_0 - cffi=1.14.5=py37hc58025e_0 @@ -73,15 +73,15 @@ dependencies: - nbconvert=6.0.7=py37h89c1867_3 - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - - nest-asyncio=1.5.1=pyhd3eb1b0_0 + - nest-asyncio=1.4.3=pyhd8ed1ab_0 - notebook=6.2.0=py37h89c1867_0 - nteract_on_jupyter=2.1.3=py_0 - - oauthlib=3.1.0=py_0 + - oauthlib=3.0.1=py_0 - openssl=1.1.1j=h7f98852_0 - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - pandoc=2.11.4=h7f98852_0 - - pandocfilters=1.4.3=py37h06a4308_1 + - pandocfilters=1.4.2=py_1 - parso=0.8.1=pyhd8ed1ab_0 - pexpect=4.8.0=py37hc8dfbb8_1 - pickleshare=0.7.5=py37hc8dfbb8_1002 @@ -105,12 +105,12 @@ dependencies: - python_abi=3.7=1_cp37m - pytz=2021.1=pyhd8ed1ab_0 - pyzmq=22.0.3=py37h499b945_0 - - readline=8.1=h27cfd23_0 + - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py37h5e8e339_2 - ruamel.yaml.clib=0.2.2=py37h5e8e339_2 - send2trash=1.5.0=py_0 - - setuptools=52.0.0=py37h06a4308_0 + - setuptools=49.6.0=py37h89c1867_3 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py37h89c1867_1 - sqlalchemy=1.3.23=py37h5e8e339_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.7.yml b/repo2docker/buildpacks/conda/environment.py-3.7.yml index 094549717..de07e850b 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.7.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.7.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-02-22 10:03:32 UTC +# Generated on 2021-02-22 12:26:46 UTC dependencies: - python=3.7.* - ipywidgets==7.6.3 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml index 54c832c00..9457d0ed5 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.8.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-22 10:05:25 UTC +# Frozen on 2021-02-22 12:22:38 UTC name: r2d channels: - conda-forge @@ -21,7 +21,7 @@ dependencies: - blinker=1.4=py_1 - brotlipy=0.7.0=py38h497a2fe_1001 - c-ares=1.17.1=h36c2ea0_0 - - ca-certificates=2021.1.19=h06a4308_0 + - ca-certificates=2020.12.5=ha878542_0 - certifi=2020.12.5=py38h578d9bd_1 - certipy=0.1.3=py_0 - cffi=1.14.5=py38ha65f79e_0 @@ -73,15 +73,15 @@ dependencies: - nbconvert=6.0.7=py38h578d9bd_3 - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - - nest-asyncio=1.5.1=pyhd3eb1b0_0 + - nest-asyncio=1.4.3=pyhd8ed1ab_0 - notebook=6.2.0=py38h578d9bd_0 - nteract_on_jupyter=2.1.3=py_0 - - oauthlib=3.1.0=py_0 + - oauthlib=3.0.1=py_0 - openssl=1.1.1j=h7f98852_0 - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - pandoc=2.11.4=h7f98852_0 - - pandocfilters=1.4.3=py38h06a4308_1 + - pandocfilters=1.4.2=py_1 - parso=0.8.1=pyhd8ed1ab_0 - pexpect=4.8.0=py38h32f6830_1 - pickleshare=0.7.5=py38h32f6830_1002 @@ -105,12 +105,12 @@ dependencies: - python_abi=3.8=1_cp38 - pytz=2021.1=pyhd8ed1ab_0 - pyzmq=22.0.3=py38h3d7ac18_0 - - readline=8.1=h27cfd23_0 + - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py38h497a2fe_2 - ruamel.yaml.clib=0.2.2=py38h497a2fe_2 - send2trash=1.5.0=py_0 - - setuptools=52.0.0=py38h06a4308_0 + - setuptools=49.6.0=py38h578d9bd_3 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py38h578d9bd_1 - sqlalchemy=1.3.23=py38h497a2fe_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.8.yml b/repo2docker/buildpacks/conda/environment.py-3.8.yml index e24f81eba..c7e8d0845 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.8.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.8.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-02-22 10:05:25 UTC +# Generated on 2021-02-22 12:22:38 UTC dependencies: - python=3.8.* - ipywidgets==7.6.3 diff --git a/repo2docker/buildpacks/conda/environment.py-3.9.frozen.yml b/repo2docker/buildpacks/conda/environment.py-3.9.frozen.yml index ec10043e5..0d7513c7a 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.9.frozen.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.9.frozen.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.py-3.9.yml, DO NOT MANUALLY MODIFY -# Frozen on 2021-02-22 10:07:07 UTC +# Frozen on 2021-02-22 12:28:32 UTC name: r2d channels: - conda-forge @@ -21,7 +21,7 @@ dependencies: - blinker=1.4=py_1 - brotlipy=0.7.0=py39h3811e60_1001 - c-ares=1.17.1=h36c2ea0_0 - - ca-certificates=2021.1.19=h06a4308_0 + - ca-certificates=2020.12.5=ha878542_0 - certifi=2020.12.5=py39hf3d152e_1 - certipy=0.1.3=py_0 - cffi=1.14.5=py39he32792d_0 @@ -73,15 +73,15 @@ dependencies: - nbconvert=6.0.7=py39hf3d152e_3 - nbformat=5.1.2=pyhd8ed1ab_1 - ncurses=6.2=h58526e2_4 - - nest-asyncio=1.5.1=pyhd3eb1b0_0 + - nest-asyncio=1.4.3=pyhd8ed1ab_0 - notebook=6.2.0=py39hf3d152e_0 - nteract_on_jupyter=2.1.3=py_0 - - oauthlib=3.1.0=py_0 + - oauthlib=3.0.1=py_0 - openssl=1.1.1j=h7f98852_0 - packaging=20.9=pyh44b312d_0 - pamela=1.0.0=py_0 - pandoc=2.11.4=h7f98852_0 - - pandocfilters=1.4.3=py39h06a4308_1 + - pandocfilters=1.4.2=py_1 - parso=0.8.1=pyhd8ed1ab_0 - pexpect=4.8.0=pyh9f0ad1d_2 - pickleshare=0.7.5=py39hde42818_1002 @@ -105,12 +105,12 @@ dependencies: - python_abi=3.9=1_cp39 - pytz=2021.1=pyhd8ed1ab_0 - pyzmq=22.0.3=py39hea8fd45_0 - - readline=8.1=h27cfd23_0 + - readline=8.0=he28a2e2_2 - requests=2.25.1=pyhd3deb0d_0 - ruamel.yaml=0.16.12=py39h3811e60_2 - ruamel.yaml.clib=0.2.2=py39h3811e60_2 - send2trash=1.5.0=py_0 - - setuptools=52.0.0=py39h06a4308_0 + - setuptools=49.6.0=py39hf3d152e_3 - six=1.15.0=pyh9f0ad1d_0 - sniffio=1.2.0=py39hf3d152e_1 - sqlalchemy=1.3.23=py39h3811e60_0 diff --git a/repo2docker/buildpacks/conda/environment.py-3.9.yml b/repo2docker/buildpacks/conda/environment.py-3.9.yml index f9ac01ed3..94144d4d1 100644 --- a/repo2docker/buildpacks/conda/environment.py-3.9.yml +++ b/repo2docker/buildpacks/conda/environment.py-3.9.yml @@ -1,5 +1,5 @@ # AUTO GENERATED FROM environment.yml, DO NOT MANUALLY MODIFY -# Generated on 2021-02-22 10:07:07 UTC +# Generated on 2021-02-22 12:28:32 UTC dependencies: - python=3.9.* - ipywidgets==7.6.3 From b36a6a75f54bfb47903b68671e9e157305f92fa6 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Feb 2021 15:52:52 +0100 Subject: [PATCH 071/103] simplify python entrypoint - remove redundant monitor sibling process - use python3-login executable instead of login shell subprocess (same effect, but in more natural order) - use non-blocking binary IO in tee instead of readline (switch to binary mode, as text wrappers don't support non-blocking mode see https://bugs.python.org/issue13322) --- repo2docker/buildpacks/base.py | 11 +- repo2docker/buildpacks/python3-login | 11 ++ repo2docker/buildpacks/repo2docker-entrypoint | 137 +++++------------- 3 files changed, 52 insertions(+), 107 deletions(-) create mode 100755 repo2docker/buildpacks/python3-login diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 6dfa2220f..145d29648 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -9,9 +9,6 @@ import sys import hashlib import escapism -import xml.etree.ElementTree as ET - -from traitlets import Dict # Only use syntax features supported by Docker 17.09 TEMPLATE = r""" @@ -182,6 +179,7 @@ # Add entrypoint ENV PYTHONUNBUFFERED=1 +COPY /python3-login /usr/local/bin/python3-login COPY /repo2docker-entrypoint /usr/local/bin/repo2docker-entrypoint ENTRYPOINT ["/usr/local/bin/repo2docker-entrypoint"] @@ -194,9 +192,7 @@ {% endif %} """ -ENTRYPOINT_FILE = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "repo2docker-entrypoint" -) +HERE = os.path.dirname(os.path.abspath(__file__)) # Also used for the group DEFAULT_NB_UID = 1000 @@ -583,7 +579,8 @@ def _filter_tar(tar): dest_path, src_path = self.generate_build_context_filename(src) tar.add(src_path, dest_path, filter=_filter_tar) - tar.add(ENTRYPOINT_FILE, "repo2docker-entrypoint", filter=_filter_tar) + for fname in ("repo2docker-entrypoint", "python3-login"): + tar.add(os.path.join(HERE, fname), fname, filter=_filter_tar) tar.add(".", "src/", filter=_filter_tar) diff --git a/repo2docker/buildpacks/python3-login b/repo2docker/buildpacks/python3-login new file mode 100755 index 000000000..a7b9dd076 --- /dev/null +++ b/repo2docker/buildpacks/python3-login @@ -0,0 +1,11 @@ +#!/bin/bash -l +# This is an executable that launches Python in a login shell +# to ensure that full profile setup occurs. +# shebang on linux only allows 1 argument, +# so we couldn't pick a login shell in one shebang line +# for a Python script + +# -u means unbuffered, which one ~always wants in a container +# otherwise output can be mysteriously missing + +exec python3 -u "$@" diff --git a/repo2docker/buildpacks/repo2docker-entrypoint b/repo2docker/buildpacks/repo2docker-entrypoint index 1ff4dd615..9e8fba52c 100755 --- a/repo2docker/buildpacks/repo2docker-entrypoint +++ b/repo2docker/buildpacks/repo2docker-entrypoint @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/local/bin/python3-login # note: must run on Python >= 3.5, which mainly means no f-strings # goals: @@ -6,153 +6,90 @@ # - preserve signal handling of subprocess (kill -TERM and friends) # - tee output to a log file -import json +import fcntl import os +import select import signal import subprocess import sys -import time - - -def get_login_env(): - """Instantiate a login shell to retrieve environment variables - - Serialize with Python to ensure proper escapes - """ - p = subprocess.run( - [ - "bash", - "-l", - "-c", - "python3 -c 'import os, json; print(json.dumps(dict(os.environ)))'", - ], - stdout=subprocess.PIPE, - ) - if p.returncode: - print("Error getting login env") - return {} - - last_line = p.stdout.splitlines()[-1] - try: - return json.loads(last_line) - except Exception as e: - print("Error getting login env: {e}".format(e=e), file=sys.stderr) - return {} - - -def monitor_parent(parent_pid, child_pgid): - """Monitor parent_pid and shutdown child_pgid if parent goes away first""" - while True: - try: - os.kill(parent_pid, 0) - except ProcessLookupError: - # parent is gone, likely by SIGKILL - # send SIGKILL to child process group - try: - os.killpg(child_pgid, signal.SIGKILL) - except (ProcessLookupError, PermissionError): - # ignore if the child is already gone - pass - return - else: - time.sleep(1) +# output chunk size to read +CHUNK_SIZE = 1024 # signals to be forwarded to the child -SIGNALS = [ - signal.SIGHUP, - signal.SIGINT, - # signal.SIGKILL, - signal.SIGQUIT, - signal.SIGTERM, - signal.SIGUSR1, - signal.SIGUSR2, - signal.SIGWINCH, -] +# everything catchable, excluding SIGCHLD +SIGNALS = set(signal.Signals) - {signal.SIGKILL, signal.SIGSTOP, signal.SIGCHLD} def main(): - # load login shell environment - login_env = get_login_env() - env = os.environ.copy() - env.update(login_env) - # open log file to send output log_file = open( os.path.join(os.environ.get("REPO_DIR", "."), ".jupyter-server-log.txt"), - "a", + "ab", ) + # build the command + # like `exec "$@"` command = sys.argv[1:] + # load entrypoint override from env r2d_entrypoint = os.environ.get("R2D_ENTRYPOINT") if r2d_entrypoint: command.insert(0, r2d_entrypoint) + # launch the subprocess child = subprocess.Popen( command, bufsize=1, - env=env, - start_new_session=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - universal_newlines=True, ) - child_pgid = os.getpgid(child.pid) - - # if parent is forcefully shutdown, - # make sure child shuts down immediately as well - parent_pid = os.getpid() - - monitor_pid = os.fork() - if monitor_pid == 0: - # child process, sibling of 'real' command - # avoid receiving signals sent to parent - os.setpgrp() - # terminate child if parent goes away, - # e.g. in ungraceful KILL not relayed to children - monitor_parent(parent_pid, child_pgid) - return # hook up ~all signals so that every signal the parent gets, # the children also get def relay_signal(sig, frame): """Relay a signal to children""" - print( - "Forwarding signal {sig} to {child_pgid}".format( - sig=sig, child_pgid=child_pgid - ) - ) - os.killpg(child_pgid, sig) - - # question: maybe use all valid_signals() except a few, e.g. SIGCHLD? - # rather than opt-in list + # DEBUG: show signal + child.send_signal(sig) + for signum in SIGNALS: signal.signal(signum, relay_signal) # tee output from child to both our stdout and the log file def tee(chunk): - for f in [sys.stdout, log_file]: + """Tee output from child to both our stdout and the log file""" + for f in [sys.stdout.buffer, log_file]: f.write(chunk) f.flush() + # make stdout pipe non-blocking + # this means child.stdout.read(nbytes) + # will always return immediately, even if there's nothing to read + flags = fcntl.fcntl(child.stdout, fcntl.F_GETFL) + fcntl.fcntl(child.stdout, fcntl.F_SETFL, flags | os.O_NONBLOCK) + poller = select.poll() + poller.register(child.stdout) + + # while child is running, constantly relay output while child.poll() is None: - tee(child.stdout.readline()) + chunk = child.stdout.read(CHUNK_SIZE) + if chunk: + tee(chunk) + else: + # empty chunk means nothing to read + # wait for output on the pipe + # timeout is in milliseconds + poller.poll(1000) - # flush the rest + # child has exited, continue relaying any remaining output + # At this point, read() will return an empty string when it's done chunk = child.stdout.read() while chunk: tee(chunk) chunk = child.stdout.read() - # child exited, cleanup monitor - try: - os.kill(monitor_pid, signal.SIGKILL) - except ProcessLookupError: - pass - - # preserve returncode + # make our returncode match the child's returncode sys.exit(child.returncode) From 4590e2c49875b9f567aede480416dbfa0281893f Mon Sep 17 00:00:00 2001 From: Tim Head Date: Tue, 23 Mar 2021 07:28:39 +0100 Subject: [PATCH 072/103] Fix logo URL in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba18b7b18..129d59793 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# repo2docker +# repo2docker [![Deploy Status](https://travis-ci.org/jupyterhub/repo2docker.svg?branch=master)](https://travis-ci.org/jupyterhub/repo2docker) [![Build Status](https://github.com/jupyterhub/repo2docker/workflows/Continuous%20Integration/badge.svg)](https://github.com/jupyterhub/repo2docker/actions) From ebbb83b6655e93f7d0f5ed3b1dfd6f2e04037e5d Mon Sep 17 00:00:00 2001 From: Kyungdahm Yun Date: Thu, 25 Mar 2021 09:22:59 -0700 Subject: [PATCH 073/103] Set default Julia version to 1.6 --- repo2docker/buildpacks/julia/julia_project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo2docker/buildpacks/julia/julia_project.py b/repo2docker/buildpacks/julia/julia_project.py index 0e9f9daa4..b340acbd2 100644 --- a/repo2docker/buildpacks/julia/julia_project.py +++ b/repo2docker/buildpacks/julia/julia_project.py @@ -47,7 +47,7 @@ def julia_version(self): compat = project_toml["compat"]["julia"] except: # Default version which needs to be manually updated with new major.minor releases - compat = "1.5" + compat = "1.6" match = find_semver_match(compat, self.all_julias) if match is None: From 033c02d07b874138264711325b53e5e27a062fab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Mar 2021 22:17:24 +0000 Subject: [PATCH 074/103] Bump pyyaml from 5.1.1 to 5.4 Bumps [pyyaml](https://github.com/yaml/pyyaml) from 5.1.1 to 5.4. - [Release notes](https://github.com/yaml/pyyaml/releases) - [Changelog](https://github.com/yaml/pyyaml/blob/master/CHANGES) - [Commits](https://github.com/yaml/pyyaml/compare/5.1.1...5.4) Signed-off-by: dependabot[bot] --- Pipfile.lock | 513 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 299 insertions(+), 214 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index 8c85e04c8..b364819e2 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e5e25d07eef39c6a63fc222436b9fe266edf7f024fd5c0ff72afd454e4176602" + "sha256": "f90b36e56c939e8b03aca161471b4b898923c4428e76f090146f91ffe8b27f06" }, "pipfile-spec": 6, "requires": {}, @@ -16,45 +16,38 @@ "default": { "certifi": { "hashes": [ - "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", - "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" + "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", + "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" ], - "version": "==2019.6.16" + "version": "==2020.12.5" }, "chardet": { "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", + "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" ], - "version": "==3.0.4" - }, - "decorator": { - "hashes": [ - "sha256:86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", - "sha256:f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6" - ], - "version": "==4.4.0" + "version": "==4.0.0" }, "docker": { "hashes": [ - "sha256:acf51b5e3e0d056925c3b780067a6f753c915fffaa46c5f2d79eb0fc1cbe6a01", - "sha256:cc5b2e94af6a2b1e1ed9d7dcbdc77eff56c36081757baf9ada6e878ea0213164" + "sha256:d3393c878f575d3a9ca3b94471a3c89a6d960b35feb92f033c0de36cc9d934db", + "sha256:f3607d5695be025fa405a12aca2e5df702a57db63790c73b927eb6a94aac60af" ], - "version": "==4.0.2" + "version": "==4.4.4" }, "escapism": { "hashes": [ - "sha256:5f1cc1fa04a95f5b85b3da194750f8a71846d493ea332f62e8798949f10c9b86", - "sha256:bde950505d0a2c0fce069620f8ffb99464635851146c25e8d1be882ca9a1bb75" + "sha256:73256bdfb4f22230f0428fc6efecee61cdc4fad531b6f98b849cb9c80711e4ec", + "sha256:d28f19edc3cb1ffc36fa238956ecc068695477e748f57157c6dde00a6b77f229" ], - "version": "==1.0.0" + "version": "==1.0.1" }, "idna": { "hashes": [ - "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", - "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" ], - "version": "==2.8" + "version": "==2.10" }, "ipython-genutils": { "hashes": [ @@ -65,10 +58,10 @@ }, "jinja2": { "hashes": [ - "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", - "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b" + "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", + "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" ], - "version": "==2.10.1" + "version": "==2.11.3" }, "markupsafe": { "hashes": [ @@ -76,38 +69,62 @@ "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", + "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", + "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", + "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", + "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", + "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", + "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", + "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", + "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" ], "version": "==1.1.1" }, "python-json-logger": { "hashes": [ - "sha256:b7a31162f2a01965a5efb94453ce69230ed208468b0bbc7fdfc56e6d8df2e281" + "sha256:f26eea7898db40609563bed0a7ca11af12e2a79858632706d835a0f961b7d398" ], - "version": "==0.1.11" + "version": "==2.0.1" }, "repo2docker": { "editable": true, @@ -115,75 +132,95 @@ }, "requests": { "hashes": [ - "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", - "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" - ], - "version": "==2.22.0" - }, - "ruamel.yaml": { - "hashes": [ - "sha256:17dbf6b7362e7aee8494f7a0f5cffd44902a6331fe89ef0853b855a7930ab845", - "sha256:23731c9efb79f3f5609dedffeb6c5c47a68125fd3d4b157d9fc71b1cd49076a9", - "sha256:2bbdd598ae57bac20968cf9028cc67d37d83bdb7942a94b9478110bc72193148", - "sha256:34586084cdd60845a3e1bece2b58f0a889be25450db8cc0ea143ddf0f40557a2", - "sha256:35957fedbb287b01313bb5c556ffdc70c0277c3500213b5e73dfd8716f748d77", - "sha256:414cb87a40974a575830b406ffab4ab8c6cbd82eeb73abd2a9d1397c1f0223e1", - "sha256:428775be75db68d908b17e4e8dda424c410222f170dc173246aa63e972d094b3", - "sha256:514f670f7d36519bda504d507edfe63e3c20489f86c86d42bc4d9a6dbdf82c7b", - "sha256:5cb962c1ac6887c5da29138fbbe3b4b7705372eb54e599907fa63d4cd743246d", - "sha256:5f6e30282cf70fb7754e1a5f101e27b5240009766376e131b31ab49f14fe81be", - "sha256:86f8e010af6af0b4f42de2d0d9b19cb441e61d3416082186f9dd03c8552d13ad", - "sha256:8d47ed1e557d546bd2dfe54f504d7274274602ff7a0652cde84c258ad6c2d96d", - "sha256:98668876720bce1ac08562d8b93a564a80e3397e442c7ea19cebdcdf73da7f74", - "sha256:9e1f0ddc18d8355dcf5586a5d90417df56074f237812b8682a93b62cca9d2043", - "sha256:a7bc812a72a79d6b7dbb96fa5bee3950464b65ec055d3abc4db6572f2373a95c", - "sha256:b72e13f9f206ee103247b07afd5a39c8b1aa98e8eba80ddba184d030337220ba", - "sha256:bcff8ea9d916789e85e24beed8830c157fb8bc7c313e554733a8151540e66c01", - "sha256:c76e78b3bab652069b8d6f7889b0e72f3455c2b854b2e0a8818393d149ad0a0d" - ], - "version": "==0.15.97" + "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", + "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" + ], + "version": "==2.25.1" + }, + "ruamel-yaml": { + "hashes": [ + "sha256:64b06e7873eb8e1125525ecef7345447d786368cadca92a7cd9b59eae62e95a3" + ], + "version": "==0.16.13" + }, + "ruamel.yaml.clib": { + "hashes": [ + "sha256:058a1cc3df2a8aecc12f983a48bda99315cebf55a3b3a5463e37bb599b05727b", + "sha256:1236df55e0f73cd138c0eca074ee086136c3f16a97c2ac719032c050f7e0622f", + "sha256:1f8c0a4577c0e6c99d208de5c4d3fd8aceed9574bb154d7a2b21c16bb924154c", + "sha256:2602e91bd5c1b874d6f93d3086f9830f3e907c543c7672cf293a97c3fabdcd91", + "sha256:28116f204103cb3a108dfd37668f20abe6e3cafd0d3fd40dba126c732457b3cc", + "sha256:2d24bd98af676f4990c4d715bcdc2a60b19c56a3fb3a763164d2d8ca0e806ba7", + "sha256:2fd336a5c6415c82e2deb40d08c222087febe0aebe520f4d21910629018ab0f3", + "sha256:30dca9bbcbb1cc858717438218d11eafb78666759e5094dd767468c0d577a7e7", + "sha256:44c7b0498c39f27795224438f1a6be6c5352f82cb887bc33d962c3a3acc00df6", + "sha256:464e66a04e740d754170be5e740657a3b3b6d2bcc567f0c3437879a6e6087ff6", + "sha256:46d6d20815064e8bb023ea8628cfb7402c0f0e83de2c2227a88097e239a7dffd", + "sha256:4df5019e7783d14b79217ad9c56edf1ba7485d614ad5a385d1b3c768635c81c0", + "sha256:4e52c96ca66de04be42ea2278012a2342d89f5e82b4512fb6fb7134e377e2e62", + "sha256:5254af7d8bdf4d5484c089f929cb7f5bafa59b4f01d4f48adda4be41e6d29f99", + "sha256:52ae5739e4b5d6317b52f5b040b1b6639e8af68a5b8fd606a8b08658fbd0cab5", + "sha256:53b9dd1abd70e257a6e32f934ebc482dac5edb8c93e23deb663eac724c30b026", + "sha256:6c0a5dc52fc74eb87c67374a4e554d4761fd42a4d01390b7e868b30d21f4b8bb", + "sha256:73b3d43e04cc4b228fa6fa5d796409ece6fcb53a6c270eb2048109cbcbc3b9c2", + "sha256:74161d827407f4db9072011adcfb825b5258a5ccb3d2cd518dd6c9edea9e30f1", + "sha256:75f0ee6839532e52a3a53f80ce64925ed4aed697dd3fa890c4c918f3304bd4f4", + "sha256:839dd72545ef7ba78fd2aa1a5dd07b33696adf3e68fae7f31327161c1093001b", + "sha256:8be05be57dc5c7b4a0b24edcaa2f7275866d9c907725226cdde46da09367d923", + "sha256:8e8fd0a22c9d92af3a34f91e8a2594eeb35cba90ab643c5e0e643567dc8be43e", + "sha256:a873e4d4954f865dcb60bdc4914af7eaae48fb56b60ed6daa1d6251c72f5337c", + "sha256:ab845f1f51f7eb750a78937be9f79baea4a42c7960f5a94dde34e69f3cce1988", + "sha256:b1e981fe1aff1fd11627f531524826a4dcc1f26c726235a52fcb62ded27d150f", + "sha256:b4b0d31f2052b3f9f9b5327024dc629a253a83d8649d4734ca7f35b60ec3e9e5", + "sha256:c6ac7e45367b1317e56f1461719c853fd6825226f45b835df7436bb04031fd8a", + "sha256:daf21aa33ee9b351f66deed30a3d450ab55c14242cfdfcd377798e2c0d25c9f1", + "sha256:e9f7d1d8c26a6a12c23421061f9022bb62704e38211fe375c645485f38df34a2", + "sha256:f6061a31880c1ed6b6ce341215336e2f3d0c1deccd84957b6fa8ca474b41e89f" + ], + "markers": "platform_python_implementation == 'CPython' and python_version < '3.10'", + "version": "==0.2.2" }, "semver": { "hashes": [ - "sha256:41c9aa26c67dc16c54be13074c352ab666bce1fa219c7110e8f03374cd4206b0", - "sha256:5b09010a66d9a3837211bb7ae5a20d10ba88f8cb49e92cb139a69ef90d5060d8" + "sha256:ced8b23dceb22134307c1b8abfa523da14198793d9787ac838e70e29e77458d4", + "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f" ], - "version": "==2.8.1" + "version": "==2.13.0" }, "six": { "hashes": [ - "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", - "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], - "version": "==1.12.0" + "version": "==1.15.0" }, "toml": { "hashes": [ - "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c", - "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e" + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" ], - "version": "==0.10.0" + "version": "==0.10.2" }, "traitlets": { "hashes": [ - "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", - "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9" + "sha256:178f4ce988f69189f7e523337a3e11d91c786ded9360174a3d9ca83e79bc5396", + "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426" ], - "version": "==4.3.2" + "version": "==5.0.5" }, "urllib3": { "hashes": [ - "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", - "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" + "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df", + "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937" ], - "version": "==1.25.3" + "version": "==1.26.4" }, "websocket-client": { "hashes": [ - "sha256:1151d5fb3a62dc129164292e1227655e4bbc5dd5340a5165dfae61128ec50aa9", - "sha256:1fd5520878b68b84b5748bb30e592b10d0a91529d5383f74f4964e72b297fd3a" + "sha256:44b5df8f08c74c3d82d28100fdc81f4536809ce98a17f0757557813275fbb663", + "sha256:63509b41d158ae5b7f67eb4ad20fecbb4eee99434e73e140354dc3ff8e09716f" ], - "version": "==0.56.0" + "version": "==0.58.0" } }, "develop": { @@ -205,123 +242,137 @@ }, "atomicwrites": { "hashes": [ - "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4", - "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6" + "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197", + "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a" ], - "version": "==1.3.0" + "version": "==1.4.0" }, "attrs": { "hashes": [ - "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", - "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399" + "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", + "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" ], - "version": "==19.1.0" + "version": "==20.3.0" }, "babel": { "hashes": [ - "sha256:af92e6106cb7c55286b25b38ad7695f8b4efb36a90ba483d7f7a6628c46158ab", - "sha256:e86135ae101e31e2c8ec20a4e0c5220f4eed12487d5cf3f78be7e98d3a57fc28" + "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5", + "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05" ], - "version": "==2.7.0" + "version": "==2.9.0" }, "certifi": { "hashes": [ - "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", - "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" + "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", + "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" ], - "version": "==2019.6.16" + "version": "==2020.12.5" }, "chardet": { "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", + "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" ], - "version": "==3.0.4" + "version": "==4.0.0" }, "commonmark": { "hashes": [ - "sha256:14c3df31e8c9c463377e287b2a1eefaa6019ab97b22dad36e2f32be59d61d68d", - "sha256:867fc5db078ede373ab811e16b6789e9d033b15ccd7296f370ca52d1ee792ce0" + "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60", + "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9" ], - "version": "==0.9.0" + "version": "==0.9.1" }, "coverage": { "hashes": [ - "sha256:3684fabf6b87a369017756b551cef29e505cb155ddb892a7a29277b978da88b9", - "sha256:39e088da9b284f1bd17c750ac672103779f7954ce6125fd4382134ac8d152d74", - "sha256:3c205bc11cc4fcc57b761c2da73b9b72a59f8d5ca89979afb0c1c6f9e53c7390", - "sha256:465ce53a8c0f3a7950dfb836438442f833cf6663d407f37d8c52fe7b6e56d7e8", - "sha256:48020e343fc40f72a442c8a1334284620f81295256a6b6ca6d8aa1350c763bbe", - "sha256:5296fc86ab612ec12394565c500b412a43b328b3907c0d14358950d06fd83baf", - "sha256:5f61bed2f7d9b6a9ab935150a6b23d7f84b8055524e7be7715b6513f3328138e", - "sha256:68a43a9f9f83693ce0414d17e019daee7ab3f7113a70c79a3dd4c2f704e4d741", - "sha256:6b8033d47fe22506856fe450470ccb1d8ba1ffb8463494a15cfc96392a288c09", - "sha256:7ad7536066b28863e5835e8cfeaa794b7fe352d99a8cded9f43d1161be8e9fbd", - "sha256:7bacb89ccf4bedb30b277e96e4cc68cd1369ca6841bde7b005191b54d3dd1034", - "sha256:839dc7c36501254e14331bcb98b27002aa415e4af7ea039d9009409b9d2d5420", - "sha256:8f9a95b66969cdea53ec992ecea5406c5bd99c9221f539bca1e8406b200ae98c", - "sha256:932c03d2d565f75961ba1d3cec41ddde00e162c5b46d03f7423edcb807734eab", - "sha256:988529edadc49039d205e0aa6ce049c5ccda4acb2d6c3c5c550c17e8c02c05ba", - "sha256:998d7e73548fe395eeb294495a04d38942edb66d1fa61eb70418871bc621227e", - "sha256:9de60893fb447d1e797f6bf08fdf0dbcda0c1e34c1b06c92bd3a363c0ea8c609", - "sha256:9e80d45d0c7fcee54e22771db7f1b0b126fb4a6c0a2e5afa72f66827207ff2f2", - "sha256:a545a3dfe5082dc8e8c3eb7f8a2cf4f2870902ff1860bd99b6198cfd1f9d1f49", - "sha256:a5d8f29e5ec661143621a8f4de51adfb300d7a476224156a39a392254f70687b", - "sha256:aca06bfba4759bbdb09bf52ebb15ae20268ee1f6747417837926fae990ebc41d", - "sha256:bb23b7a6fd666e551a3094ab896a57809e010059540ad20acbeec03a154224ce", - "sha256:bfd1d0ae7e292105f29d7deaa9d8f2916ed8553ab9d5f39ec65bcf5deadff3f9", - "sha256:c62ca0a38958f541a73cf86acdab020c2091631c137bd359c4f5bddde7b75fd4", - "sha256:c709d8bda72cf4cd348ccec2a4881f2c5848fd72903c185f363d361b2737f773", - "sha256:c968a6aa7e0b56ecbd28531ddf439c2ec103610d3e2bf3b75b813304f8cb7723", - "sha256:df785d8cb80539d0b55fd47183264b7002077859028dfe3070cf6359bf8b2d9c", - "sha256:f406628ca51e0ae90ae76ea8398677a921b36f0bd71aab2099dfed08abd0322f", - "sha256:f46087bbd95ebae244a0eda01a618aff11ec7a069b15a3ef8f6b520db523dcf1", - "sha256:f8019c5279eb32360ca03e9fac40a12667715546eed5c5eb59eb381f2f501260", - "sha256:fc5f4d209733750afd2714e9109816a29500718b32dd9a5db01c0cb3a019b96a" - ], - "version": "==4.5.3" + "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c", + "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6", + "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45", + "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a", + "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03", + "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529", + "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a", + "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a", + "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2", + "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6", + "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759", + "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53", + "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a", + "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4", + "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff", + "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502", + "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793", + "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb", + "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905", + "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821", + "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b", + "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81", + "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0", + "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b", + "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3", + "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184", + "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701", + "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a", + "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82", + "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638", + "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5", + "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083", + "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6", + "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90", + "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465", + "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a", + "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3", + "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e", + "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066", + "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf", + "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b", + "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae", + "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669", + "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873", + "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b", + "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6", + "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb", + "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160", + "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c", + "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079", + "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d", + "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6" + ], + "version": "==5.5" }, "docutils": { "hashes": [ - "sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6", - "sha256:51e64ef2ebfb29cae1faa133b3710143496eca21c530f3f71424d77687764274", - "sha256:7a4bd47eaf6596e1295ecb11361139febe29b084a87bf005bf899f9a42edc3c6" - ], - "version": "==0.14" - }, - "future": { - "hashes": [ - "sha256:67045236dcfd6816dc439556d009594abf643e5eb48992e36beac09c2ca659b8" + "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af", + "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc" ], - "version": "==0.17.1" + "version": "==0.16" }, "idna": { "hashes": [ - "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", - "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" ], - "version": "==2.8" + "version": "==2.10" }, "imagesize": { "hashes": [ - "sha256:3f349de3eb99145973fefb7dbe38554414e5c30abd0c8e4b970a7c9d09f3a1d8", - "sha256:f3832918bc3c66617f92e35f5d70729187676313caa60c187eb0f28b8fe5e3b5" + "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1", + "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1" ], - "version": "==1.1.0" + "version": "==1.2.0" }, "importlib-metadata": { "hashes": [ - "sha256:6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7", - "sha256:cb6ee23b46173539939964df59d3d72c3e0c1b5d54b84f1d8a7e912fe43612db" + "sha256:742add720a20d0467df2f444ae41704000f50e1234f46174b51f9c6031a1bd71", + "sha256:b74159469b464a99cb8cc3e21973e4d96e05d3024d337313fedb618a6e86e6f4" ], - "version": "==0.18" + "version": "==3.7.3" }, "jinja2": { "hashes": [ - "sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013", - "sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b" + "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", + "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" ], - "version": "==2.10.1" + "version": "==2.11.3" }, "markupsafe": { "hashes": [ @@ -329,75 +380,99 @@ "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", + "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", + "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", + "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", + "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", + "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", + "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", + "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", + "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", + "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", + "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", + "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", + "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", + "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", + "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", + "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", + "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", + "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", + "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" ], "version": "==1.1.1" }, "more-itertools": { "hashes": [ - "sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7", - "sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a" + "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced", + "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713" ], "markers": "python_version > '2.7'", - "version": "==7.0.0" + "version": "==8.7.0" }, "packaging": { "hashes": [ - "sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af", - "sha256:9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3" + "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5", + "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a" ], - "version": "==19.0" + "version": "==20.9" }, "pluggy": { "hashes": [ - "sha256:0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc", - "sha256:b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c" + "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0", + "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d" ], - "version": "==0.12.0" + "version": "==0.13.1" }, "py": { "hashes": [ - "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", - "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53" + "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3", + "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a" ], - "version": "==1.8.0" + "version": "==1.10.0" }, "pygments": { "hashes": [ - "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", - "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" + "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94", + "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8" ], - "version": "==2.4.2" + "version": "==2.8.1" }, "pyparsing": { "hashes": [ - "sha256:1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a", - "sha256:9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03" + "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", + "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" ], - "version": "==2.4.0" + "version": "==2.4.7" }, "pytest": { "hashes": [ @@ -417,27 +492,37 @@ }, "pytz": { "hashes": [ - "sha256:303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda", - "sha256:d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141" + "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da", + "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798" ], - "version": "==2019.1" + "version": "==2021.1" }, "pyyaml": { "hashes": [ - "sha256:57acc1d8533cbe51f6662a55434f0dbecfa2b9eaf115bede8f6fd00115a0c0d3", - "sha256:588c94b3d16b76cfed8e0be54932e5729cc185caffaa5a451e7ad2f7ed8b4043", - "sha256:68c8dd247f29f9a0d09375c9c6b8fdc64b60810ebf07ba4cdd64ceee3a58c7b7", - "sha256:70d9818f1c9cd5c48bb87804f2efc8692f1023dac7f1a1a5c61d454043c1d265", - "sha256:86a93cccd50f8c125286e637328ff4eef108400dd7089b46a7be3445eecfa391", - "sha256:a0f329125a926876f647c9fa0ef32801587a12328b4a3c741270464e3e4fa778", - "sha256:a3c252ab0fa1bb0d5a3f6449a4826732f3eb6c0270925548cac342bc9b22c225", - "sha256:b4bb4d3f5e232425e25dda21c070ce05168a786ac9eda43768ab7f3ac2770955", - "sha256:cd0618c5ba5bda5f4039b9398bb7fb6a317bb8298218c3de25c47c4740e4b95e", - "sha256:ceacb9e5f8474dcf45b940578591c7f3d960e82f926c707788a570b51ba59190", - "sha256:fe6a88094b64132c4bb3b631412e90032e8cfe9745a58370462240b8cb7553cd" + "sha256:02c78d77281d8f8d07a255e57abdbf43b02257f59f50cc6b636937d68efa5dd0", + "sha256:0dc9f2eb2e3c97640928dec63fd8dc1dd91e6b6ed236bd5ac00332b99b5c2ff9", + "sha256:124fd7c7bc1e95b1eafc60825f2daf67c73ce7b33f1194731240d24b0d1bf628", + "sha256:26fcb33776857f4072601502d93e1a619f166c9c00befb52826e7b774efaa9db", + "sha256:31ba07c54ef4a897758563e3a0fcc60077698df10180abe4b8165d9895c00ebf", + "sha256:3c49e39ac034fd64fd576d63bb4db53cda89b362768a67f07749d55f128ac18a", + "sha256:52bf0930903818e600ae6c2901f748bc4869c0c406056f679ab9614e5d21a166", + "sha256:5a3f345acff76cad4aa9cb171ee76c590f37394186325d53d1aa25318b0d4a09", + "sha256:5e7ac4e0e79a53451dc2814f6876c2fa6f71452de1498bbe29c0b54b69a986f4", + "sha256:7242790ab6c20316b8e7bb545be48d7ed36e26bbe279fd56f2c4a12510e60b4b", + "sha256:737bd70e454a284d456aa1fa71a0b429dd527bcbf52c5c33f7c8eee81ac16b89", + "sha256:8635d53223b1f561b081ff4adecb828fd484b8efffe542edcfdff471997f7c39", + "sha256:8b818b6c5a920cbe4203b5a6b14256f0e5244338244560da89b7b0f1313ea4b6", + "sha256:8bf38641b4713d77da19e91f8b5296b832e4db87338d6aeffe422d42f1ca896d", + "sha256:a36a48a51e5471513a5aea920cdad84cbd56d70a5057cca3499a637496ea379c", + "sha256:b2243dd033fd02c01212ad5c601dafb44fbb293065f430b0d3dbf03f3254d615", + "sha256:cc547d3ead3754712223abb7b403f0a184e4c3eae18c9bb7fd15adef1597cc4b", + "sha256:cc552b6434b90d9dbed6a4f13339625dc466fd82597119897e9489c953acbc22", + "sha256:f3790156c606299ff499ec44db422f66f05a7363b39eb9d5b064f17bd7d7c47b", + "sha256:f7a21e3d99aa3095ef0553e7ceba36fb693998fbb1226f1392ce33681047465f", + "sha256:fdc6b2cb4b19e431994f25a9160695cc59a4e861710cc6fc97161c5e845fc579" ], "index": "pypi", - "version": "==5.1.1" + "version": "==5.4" }, "recommonmark": { "hashes": [ @@ -449,24 +534,24 @@ }, "requests": { "hashes": [ - "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", - "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" + "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", + "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" ], - "version": "==2.22.0" + "version": "==2.25.1" }, "six": { "hashes": [ - "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", - "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" ], - "version": "==1.12.0" + "version": "==1.15.0" }, "snowballstemmer": { "hashes": [ - "sha256:919f26a68b2c17a7634da993d91339e288964f93c274f1343e3bbbe2096e1128", - "sha256:9f3bcd3c401c3e862ec0ebe6d2c069ebc012ce142cce209c098ccb5b09136e89" + "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2", + "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914" ], - "version": "==1.2.1" + "version": "==2.1.0" }, "sphinx": { "hashes": [ @@ -478,10 +563,10 @@ }, "sphinxcontrib-applehelp": { "hashes": [ - "sha256:edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897", - "sha256:fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d" + "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a", + "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58" ], - "version": "==1.0.1" + "version": "==1.0.2" }, "sphinxcontrib-autoprogram": { "hashes": [ @@ -493,17 +578,17 @@ }, "sphinxcontrib-devhelp": { "hashes": [ - "sha256:6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34", - "sha256:9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981" + "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e", + "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4" ], - "version": "==1.0.1" + "version": "==1.0.2" }, "sphinxcontrib-htmlhelp": { "hashes": [ - "sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422", - "sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7" + "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f", + "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b" ], - "version": "==1.0.2" + "version": "==1.0.3" }, "sphinxcontrib-jsmath": { "hashes": [ @@ -514,31 +599,31 @@ }, "sphinxcontrib-qthelp": { "hashes": [ - "sha256:513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20", - "sha256:79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f" + "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72", + "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6" ], - "version": "==1.0.2" + "version": "==1.0.3" }, "sphinxcontrib-serializinghtml": { "hashes": [ - "sha256:c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227", - "sha256:db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768" + "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc", + "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a" ], - "version": "==1.1.3" + "version": "==1.1.4" }, "urllib3": { "hashes": [ - "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", - "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" + "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df", + "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937" ], - "version": "==1.25.3" + "version": "==1.26.4" }, "wcwidth": { "hashes": [ - "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", - "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" + "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784", + "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83" ], - "version": "==0.1.7" + "version": "==0.2.5" }, "wheel": { "hashes": [ @@ -550,10 +635,10 @@ }, "zipp": { "hashes": [ - "sha256:8c1019c6aad13642199fbe458275ad6a84907634cc9f0989877ccc4a2840139d", - "sha256:ca943a7e809cc12257001ccfb99e3563da9af99d52f261725e96dfe0f9275bc3" + "sha256:3607921face881ba3e026887d8150cca609d517579abe052ac81fc5aeffdbd76", + "sha256:51cb66cc54621609dd593d1787f286ee42a5c0adbb4b29abea5a63edc3e03098" ], - "version": "==0.5.1" + "version": "==3.4.1" } } } From 3edc76342c3c4aa1edcc48d528ecf5b64f066234 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Thu, 1 Apr 2021 15:31:19 +0200 Subject: [PATCH 075/103] Fix regression in hydroshare introduced after moving to requests --- repo2docker/contentproviders/hydroshare.py | 8 ++++---- tests/unit/contentproviders/test_hydroshare.py | 18 ++---------------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/repo2docker/contentproviders/hydroshare.py b/repo2docker/contentproviders/hydroshare.py index eac66e21c..1cb8b6975 100755 --- a/repo2docker/contentproviders/hydroshare.py +++ b/repo2docker/contentproviders/hydroshare.py @@ -67,8 +67,8 @@ def fetch(self, spec, output_dir, yield_output=False, timeout=120): conn = self.urlopen(bag_url) total_wait_time = 0 while ( - conn.getcode() == 200 - and conn.info().get_content_type() != "application/zip" + conn.status_code == 200 + and conn.headers["content-type"] != "application/zip" ): wait_time = 10 total_wait_time += wait_time @@ -81,8 +81,8 @@ def fetch(self, spec, output_dir, yield_output=False, timeout=120): ) time.sleep(wait_time) conn = self.urlopen(bag_url) - if conn.getcode() != 200: - msg = "Failed to download bag. status code {}.\n".format(conn.getcode()) + if conn.status_code != 200: + msg = "Failed to download bag. status code {}.\n".format(conn.status_code) yield msg raise ContentProviderException(msg) # Bag creation seems to need a small time buffer after it says it's ready. diff --git a/tests/unit/contentproviders/test_hydroshare.py b/tests/unit/contentproviders/test_hydroshare.py index 4af7d1579..da80905fb 100755 --- a/tests/unit/contentproviders/test_hydroshare.py +++ b/tests/unit/contentproviders/test_hydroshare.py @@ -109,25 +109,11 @@ def hydroshare_archive(prefix="b8f6eae9d89241cf8b5904033460af61/data/contents"): yield zfile -class MockInfo: - def __init__(self, content_type): - self.content_type = content_type - - def get_content_type(self): - return self.content_type - - class MockResponse: def __init__(self, content_type, status_code): - self.content_type = content_type self.status_code = status_code - self.mock_info = MockInfo(self.content_type) - - def getcode(self): - return self.status_code - - def info(self): - return self.mock_info + self.headers = dict() + self.headers["content-type"] = content_type def test_fetch_bag(): From 3112a56f37d555693f48c9ad40ffdd0892fd6e3f Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Thu, 1 Apr 2021 17:37:34 +0200 Subject: [PATCH 076/103] install latest hg and hg evolve in CI --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5362a4977..2bf316672 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -110,7 +110,8 @@ jobs: pip freeze # hg-evolve pinned to 9.2 because hg-evolve dropped support for # hg 4.5, installed with apt in Ubuntu 18.04 - $(hg debuginstall --template "{pythonexe}") -m pip install hg-evolve==9.2 --user + pip install mercurial + $(hg debuginstall --template "{pythonexe}") -m pip install hg-evolve --user - name: "Run tests" run: | From a833c3949b089b370c6efc8f87d24a40812e7425 Mon Sep 17 00:00:00 2001 From: Mridul Seth Date: Sat, 3 Apr 2021 12:09:13 +0200 Subject: [PATCH 077/103] install setup-tools for hg --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2bf316672..a9c5e63e5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -110,8 +110,8 @@ jobs: pip freeze # hg-evolve pinned to 9.2 because hg-evolve dropped support for # hg 4.5, installed with apt in Ubuntu 18.04 - pip install mercurial - $(hg debuginstall --template "{pythonexe}") -m pip install hg-evolve --user + $(hg debuginstall --template "{pythonexe}") -m pip install setuptools --user + $(hg debuginstall --template "{pythonexe}") -m pip install hg-evolve==9.2 --user - name: "Run tests" run: | From f8d1a19feea8b111a0f27bd77abf5e87379dae97 Mon Sep 17 00:00:00 2001 From: Florian Kohrt Date: Fri, 21 May 2021 11:26:53 +0200 Subject: [PATCH 078/103] State newly used installation command `install_local` is used since 1f5469f --- docs/source/config_files.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/config_files.rst b/docs/source/config_files.rst index 4b71980cf..8bbfc9279 100644 --- a/docs/source/config_files.rst +++ b/docs/source/config_files.rst @@ -142,7 +142,7 @@ We use ``apt.txt``, for example, to install LaTeX in our To install your repository like an R package, you may include a ``DESCRIPTION`` file. repo2docker installs the package and dependencies -from the ``DESCRIPTION`` by running ``devtools::install_git(".")``. +from the ``DESCRIPTION`` by running ``devtools::install_local(getwd())``. You also need to have a ``runtime.txt`` file that is formatted as ``r---
``, where YYYY-MM-DD is a snapshot of MRAN that will be From cf6c35afd7346a30101c5a71388082048989f5b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 1 Jun 2021 22:58:54 +0000 Subject: [PATCH 079/103] Bump urllib3 from 1.26.4 to 1.26.5 Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.26.4 to 1.26.5. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/1.26.4...1.26.5) --- updated-dependencies: - dependency-name: urllib3 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- Pipfile.lock | 295 ++++++++++++++++++++++----------------------------- 1 file changed, 127 insertions(+), 168 deletions(-) diff --git a/Pipfile.lock b/Pipfile.lock index b364819e2..26b4933fd 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -16,10 +16,10 @@ "default": { "certifi": { "hashes": [ - "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", - "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" + "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee", + "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8" ], - "version": "==2020.12.5" + "version": "==2021.5.30" }, "chardet": { "hashes": [ @@ -30,10 +30,10 @@ }, "docker": { "hashes": [ - "sha256:d3393c878f575d3a9ca3b94471a3c89a6d960b35feb92f033c0de36cc9d934db", - "sha256:f3607d5695be025fa405a12aca2e5df702a57db63790c73b927eb6a94aac60af" + "sha256:3e8bc47534e0ca9331d72c32f2881bb13b93ded0bcdeab3c833fb7cf61c0a9a5", + "sha256:fc961d622160e8021c10d1bcabc388c57d55fb1f917175afbe24af442e6879bd" ], - "version": "==4.4.4" + "version": "==5.0.0" }, "escapism": { "hashes": [ @@ -58,67 +58,49 @@ }, "jinja2": { "hashes": [ - "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", - "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" + "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4", + "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4" ], - "version": "==2.11.3" + "version": "==3.0.1" }, "markupsafe": { "hashes": [ - "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", - "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", - "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", - "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", - "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", - "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", - "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", - "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", - "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", - "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", - "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", - "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", - "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", - "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", - "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", - "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", - "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", - "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", - "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", - "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", - "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", - "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", - "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", - "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", - "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", - "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", - "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", - "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", - "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", - "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", - "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", - "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", - "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", - "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", - "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", - "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", - "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", - "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", - "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", - "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", - "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", - "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", - "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", - "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", - "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", - "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", - "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", - "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", - "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" - ], - "version": "==1.1.1" + "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298", + "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64", + "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b", + "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567", + "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff", + "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74", + "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35", + "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26", + "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7", + "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75", + "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f", + "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135", + "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8", + "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a", + "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914", + "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18", + "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8", + "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2", + "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d", + "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b", + "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f", + "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb", + "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833", + "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415", + "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902", + "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9", + "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d", + "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066", + "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f", + "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5", + "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94", + "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509", + "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51", + "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872" + ], + "version": "==2.0.1" }, "python-json-logger": { "hashes": [ @@ -139,9 +121,9 @@ }, "ruamel-yaml": { "hashes": [ - "sha256:64b06e7873eb8e1125525ecef7345447d786368cadca92a7cd9b59eae62e95a3" + "sha256:25c3eaf4f0c52bd15c50c39b100a32168891240f4d2177a4690d5d9b85944bbe" ], - "version": "==0.16.13" + "version": "==0.17.7" }, "ruamel.yaml.clib": { "hashes": [ @@ -187,13 +169,6 @@ ], "version": "==2.13.0" }, - "six": { - "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" - ], - "version": "==1.15.0" - }, "toml": { "hashes": [ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", @@ -210,17 +185,18 @@ }, "urllib3": { "hashes": [ - "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df", - "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937" + "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c", + "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098" ], - "version": "==1.26.4" + "index": "pypi", + "version": "==1.26.5" }, "websocket-client": { "hashes": [ - "sha256:44b5df8f08c74c3d82d28100fdc81f4536809ce98a17f0757557813275fbb663", - "sha256:63509b41d158ae5b7f67eb4ad20fecbb4eee99434e73e140354dc3ff8e09716f" + "sha256:3e2bf58191d4619b161389a95bdce84ce9e0b24eb8107e7e590db682c2d0ca81", + "sha256:abf306dc6351dcef07f4d40453037e51cc5d9da2ef60d0fc5d0fe3bcda255372" ], - "version": "==0.58.0" + "version": "==1.0.1" } }, "develop": { @@ -249,24 +225,24 @@ }, "attrs": { "hashes": [ - "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6", - "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700" + "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1", + "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb" ], - "version": "==20.3.0" + "version": "==21.2.0" }, "babel": { "hashes": [ - "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5", - "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05" + "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9", + "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0" ], - "version": "==2.9.0" + "version": "==2.9.1" }, "certifi": { "hashes": [ - "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", - "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" + "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee", + "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8" ], - "version": "==2020.12.5" + "version": "==2021.5.30" }, "chardet": { "hashes": [ @@ -341,10 +317,10 @@ }, "docutils": { "hashes": [ - "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af", - "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc" + "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125", + "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61" ], - "version": "==0.16" + "version": "==0.17.1" }, "idna": { "hashes": [ @@ -362,82 +338,64 @@ }, "importlib-metadata": { "hashes": [ - "sha256:742add720a20d0467df2f444ae41704000f50e1234f46174b51f9c6031a1bd71", - "sha256:b74159469b464a99cb8cc3e21973e4d96e05d3024d337313fedb618a6e86e6f4" + "sha256:960d52ba7c21377c990412aca380bf3642d734c2eaab78a2c39319f67c6a5786", + "sha256:e592faad8de1bda9fe920cf41e15261e7131bcf266c30306eec00e8e225c1dd5" ], - "version": "==3.7.3" + "version": "==4.4.0" }, "jinja2": { "hashes": [ - "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", - "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" + "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4", + "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4" ], - "version": "==2.11.3" + "version": "==3.0.1" }, "markupsafe": { "hashes": [ - "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", - "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", - "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", - "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", - "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", - "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f", - "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39", - "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", - "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", - "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014", - "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f", - "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", - "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", - "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", - "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", - "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", - "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", - "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", - "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", - "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85", - "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1", - "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", - "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", - "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", - "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850", - "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0", - "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", - "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", - "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb", - "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", - "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", - "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", - "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1", - "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2", - "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", - "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", - "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", - "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7", - "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", - "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8", - "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", - "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193", - "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", - "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b", - "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", - "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5", - "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c", - "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be", - "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621" - ], - "version": "==1.1.1" + "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298", + "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64", + "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b", + "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567", + "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff", + "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74", + "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35", + "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26", + "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7", + "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75", + "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f", + "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135", + "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8", + "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a", + "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914", + "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18", + "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8", + "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2", + "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d", + "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b", + "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f", + "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb", + "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833", + "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415", + "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902", + "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9", + "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d", + "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066", + "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f", + "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5", + "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94", + "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509", + "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51", + "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872" + ], + "version": "==2.0.1" }, "more-itertools": { "hashes": [ - "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced", - "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713" + "sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d", + "sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a" ], "markers": "python_version > '2.7'", - "version": "==8.7.0" + "version": "==8.8.0" }, "packaging": { "hashes": [ @@ -462,10 +420,10 @@ }, "pygments": { "hashes": [ - "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94", - "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8" + "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f", + "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e" ], - "version": "==2.8.1" + "version": "==2.9.0" }, "pyparsing": { "hashes": [ @@ -541,10 +499,10 @@ }, "six": { "hashes": [ - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" ], - "version": "==1.15.0" + "version": "==1.16.0" }, "snowballstemmer": { "hashes": [ @@ -585,10 +543,10 @@ }, "sphinxcontrib-htmlhelp": { "hashes": [ - "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f", - "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b" + "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07", + "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2" ], - "version": "==1.0.3" + "version": "==2.0.0" }, "sphinxcontrib-jsmath": { "hashes": [ @@ -606,17 +564,18 @@ }, "sphinxcontrib-serializinghtml": { "hashes": [ - "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc", - "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a" + "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd", + "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952" ], - "version": "==1.1.4" + "version": "==1.1.5" }, "urllib3": { "hashes": [ - "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df", - "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937" + "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c", + "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098" ], - "version": "==1.26.4" + "index": "pypi", + "version": "==1.26.5" }, "wcwidth": { "hashes": [ From 767f95fa06e36cc08863294c822893ac46c60202 Mon Sep 17 00:00:00 2001 From: Sarah Gibson <44771837+sgibson91@users.noreply.github.com> Date: Fri, 11 Jun 2021 12:01:24 +0100 Subject: [PATCH 080/103] Add study participation notice to readme --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 129d59793..52623fae4 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,19 @@ For support questions please search or post to https://discourse.jupyter.org/c/b See the [contributing guide](CONTRIBUTING.md) for information on contributing to repo2docker. +--- + +Please note that this repository is participating in a study into sustainability +of open source projects. Data will be gathered about this repository for +approximately the next 12 months, starting from 2021-06-11. + +Data collected will include number of contributors, number of PRs, time taken to +close/merge these PRs, and issues closed. + +For more information, please visit +[our informational page](https://sustainable-open-science-and-software.github.io/) or download our [participant information sheet](https://sustainable-open-science-and-software.github.io/assets/PIS_sustainable_software.pdf). + +--- ## Using repo2docker ### Prerequisites From b18188d5ed1ec3853b17cabacac51f69d0883978 Mon Sep 17 00:00:00 2001 From: YuviPanda Date: Tue, 29 Jun 2021 15:29:32 +0530 Subject: [PATCH 081/103] Add build-base to build stage of docker image ruamel.yaml depends on ruamel.yaml.clib, which requires compilation on alpine. It did not seem to require the packages in build-base before, but it does now. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 818bf8653..a1151376f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ARG ALPINE_VERSION=3.12.0 FROM alpine:${ALPINE_VERSION} -RUN apk add --no-cache git python3 python3-dev py-pip +RUN apk add --no-cache git python3 python3-dev py-pip build-base # build wheels in first image ADD . /tmp/src From 76f55c5a253a733caa46ed4aa18c608f8a449c78 Mon Sep 17 00:00:00 2001 From: Yuvi Panda Date: Tue, 29 Jun 2021 15:11:26 +0530 Subject: [PATCH 082/103] Stop using deprecated add_stylesheet in sphinx add_stylesheet has been deprecated for a while. This should hopefully fix the CI failures in circleci --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 8bb8d4fa9..91bebcc78 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -47,7 +47,7 @@ def setup(app): - app.add_stylesheet("custom.css") # may also be a URL + app.add_css_file("custom.css") # may also be a URL app.add_config_value( "recommonmark_config", {"auto_toc_tree_section": "Contents"}, True ) From cf35f51a386f91bd40946339961b1133369c0dd7 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 11 Feb 2020 18:05:02 +0000 Subject: [PATCH 083/103] Add abstract interface for container engines --- repo2docker/engine.py | 245 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 repo2docker/engine.py diff --git a/repo2docker/engine.py b/repo2docker/engine.py new file mode 100644 index 000000000..151960695 --- /dev/null +++ b/repo2docker/engine.py @@ -0,0 +1,245 @@ +""" +Interface for a repo2docker container engine +""" + +from abc import ABC, abstractmethod + + +# Based on https://docker-py.readthedocs.io/en/4.2.0/containers.html + + +class Container(ABC): + """ + Abstract container returned by repo2docker engines + """ + + @abstractmethod + def reload(self): + """ + Refresh container attributes + """ + + @abstractmethod + def logs(self, stream=False): + """ + Get the container logs. + + Parameters + ---------- + stream : bool + If `True` return an iterator over the log lines, otherwise return all + logs + + Returns + ------- + str or iterator + """ + + @abstractmethod + def kill(self, *, signal="KILL"): + """ + Send a signal to the container + + Parameters + ---------- + signal : str + The signal, default `KILL` + """ + + @abstractmethod + def remove(self): + """ + Remove the container + """ + + @abstractmethod + def stop(self, *, timeout=10): + """ + Stop the container + + Parameters + ---------- + timeout : If the container doesn't gracefully stop after this timeout kill it + """ + + @property + @abstractmethod + def status(self): + """ + The status of the container + + Returns + ------- + str : The status of the container. + Values include `created` `running` `exited`. + + TODO: Does Docker have a fixed list of these? + """ + + +class ContainerEngine(ABC): + """ + Abstract container engine + """ + + # containers = Container + + # Based on https://docker-py.readthedocs.io/en/4.2.0/api.html#module-docker.api.build + + @abstractmethod + def build( + self, + *, + buildargs={}, + cache_from=[], + container_limits={}, + forcerm=False, + rm=False, + tag="", + custom_context=False, + decode=False, + dockerfile="", + fileobj=None, + path="", + ): + """ + Build a container + + Parameters + ---------- + buildargs : dict + Dictionary of build arguments + cache_from : list[str] + List of images to chech for caching + container_limits : dict + Dictionary of resources limits. These keys are supported: + - `cpusetcpus` + - `cpushares` + - `memory` + - `memswap` + forcerm : bool + Always remove containers including unsuccessful builds + rm : bool + Remove intermediate containers + tag : str + Tag to add to the image + + custom_context : bool + If `True` fileobj is a Tar file object containing the build context + TODO: Specific to Docker + decode : bool + If `True` decode responses into dicts + TODO: repo2docker sets this to True but it's not clear what other clients should return + dockerfile : str + Path to Dockerfile within the build context + fileobj : tarfile + A tar file-like object containing the build context + TODO: Specific to Docker, other clients can untar this to a tempdir + path : str + path to the Dockerfile + """ + + @abstractmethod + def images(self): + """ + List images + + Returns + ------- + list[str] : List of images + """ + + @abstractmethod + def inspect_image(self, image): + """ + Get information about an image + + TODO: This is specific to the engine, can we convert it to a standard format? + + Parameters + ---------- + image : str + The image + + Returns + ------- + dict + """ + + @abstractmethod + def push(self, image_spec, *, stream=True): + """ + Push image to a registry + + Parameters + ---------- + image_spec : str + The repository spec to push to + stream : bool + If `True` return output logs as a generator + """ + + # Note this is different from the Docker client which has Client.containers.run + def run( + image_spec, + *, + command=[], + environment=[], + detach=False, + ports={}, + publish_all_ports=False, + remove=False, + volumes={}, + ): + """ + Run a container + + Parameters + ---------- + image_spec : str + The image to run + command : list[str] + The command to run + environment : list[str] + List of environment variables in the form `ENVVAR=value` + detach : bool + If `True` run container in background + ports : dict + Container port bindings in the format expected by the engine + TODO: Should we use a fixed format and convert to whatever's required by the engine? + publish_all_ports : bool + If `True` publish all ports to host + remove : bool + If `True` delete container when it completes + volumes : dict + Volume bindings in the format expected by the engine + TODO: Should we use a fixed format and convert to whatever's required by the engine? + + Returns + ------- + Container : the running container + + Raises + ------ + NotImplementedError + This engine does not support running containers + """ + raise NotImplementedError("Running containers not supported") + + +class ContainerEngineException(Exception): + """ + Base class for exceptions in the container engine + """ + + +class BuildError(ContainerEngineException): + """ + Container build error + """ + + +class ImageLoadError(ContainerEngineException): + """ + Container load/push error + """ From 0610edfc20977cfa60f400433700ce874f6ce927 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 11 Feb 2020 18:07:16 +0000 Subject: [PATCH 084/103] Add docker implementation of abstract engine --- repo2docker/docker.py | 107 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 repo2docker/docker.py diff --git a/repo2docker/docker.py b/repo2docker/docker.py new file mode 100644 index 000000000..b6ec041d8 --- /dev/null +++ b/repo2docker/docker.py @@ -0,0 +1,107 @@ +""" +Docker container engine for repo2docker +""" + +import docker +from .engine import Container, ContainerEngine, ContainerEngineException + + +class DockerContainer(Container): + def __init__(self, container): + self._c = container + + def reload(self): + return self._c.reload() + + def logs(self, stream=False): + return self._c.logs(stream=stream) + + def kill(self, *, signal="KILL"): + return self._c.kill(signal=signal) + + def remove(self): + return self._c.remove() + + def stop(self, *, timeout=10): + return self._c.stop(timeout=timeout) + + @property + def status(self): + return self._c.status + + +class DockerEngine(ContainerEngine): + """ + https://docker-py.readthedocs.io/en/4.2.0/api.html#module-docker.api.build + """ + + def __init__(self): + try: + self._apiclient = docker.APIClient( + version="auto", **docker.utils.kwargs_from_env() + ) + self._client = docker.from_env(version="auto") + except docker.errors.DockerException as e: + raise ContainerEngineException(e) + + def build( + self, + *, + buildargs=None, + cache_from=None, + container_limits=None, + forcerm=False, + rm=False, + tag="", + custom_context=False, + decode=False, + dockerfile="", + fileobj=None, + path="", + ): + return self._apiclient.build( + buildargs=buildargs, + cache_from=cache_from, + container_limits=container_limits, + forcerm=forcerm, + rm=rm, + tag=tag, + custom_context=custom_context, + decode=decode, + dockerfile=dockerfile, + fileobj=fileobj, + path=path, + ) + + def images(self): + return self._apiclient.images() + + def inspect_image(self, image): + return self._apiclient.inspect_image(image) + + def push(self, image_spec, *, stream=True): + return self._apiclient.push(image_spec, stream=stream) + + def run( + self, + image_spec, + *, + command=None, + environment=None, + detach=False, + ports=None, + publish_all_ports=False, + remove=False, + volumes=None, + ): + container = self._client.containers.run( + image_spec, + command=command, + environment=(environment or []), + detach=detach, + ports=(ports or {}), + publish_all_ports=publish_all_ports, + remove=remove, + volumes=(volumes or {}), + ) + return DockerContainer(container) From 2864b692ef260996e3c42e5dda29bcf9bc22bf57 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 11 Feb 2020 18:30:18 +0000 Subject: [PATCH 085/103] app: use DockerEngine --- repo2docker/__main__.py | 6 +++--- repo2docker/app.py | 31 ++++++++++++------------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/repo2docker/__main__.py b/repo2docker/__main__.py index a52a734a9..28883c7a7 100644 --- a/repo2docker/__main__.py +++ b/repo2docker/__main__.py @@ -2,8 +2,8 @@ import sys import os import logging -import docker from .app import Repo2Docker +from .engine import BuildError, ImageLoadError from . import __version__ from .utils import validate_and_generate_port_mapping, is_valid_docker_image_name @@ -371,12 +371,12 @@ def main(): r2d.initialize() try: r2d.start() - except docker.errors.BuildError as e: + except BuildError as e: # This is only raised by us if r2d.log_level == logging.DEBUG: r2d.log.exception(e) sys.exit(1) - except docker.errors.ImageLoadError as e: + except ImageLoadError as e: # This is only raised by us if r2d.log_level == logging.DEBUG: r2d.log.exception(e) diff --git a/repo2docker/app.py b/repo2docker/app.py index 8a804905c..296a25194 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -16,10 +16,9 @@ import tempfile import time -import docker +from .engine import BuildError, ContainerEngineException, ImageLoadError +from .docker import DockerEngine from urllib.parse import urlparse -from docker.utils import kwargs_from_env -from docker.errors import DockerException import escapism from pythonjsonlogger import jsonlogger @@ -474,7 +473,7 @@ def initialize(self): def push_image(self): """Push docker image to registry""" - client = docker.APIClient(version="auto", **kwargs_from_env()) + client = DockerEngine() # Build a progress setup for each layer, and only emit per-layer # info every 1.5s progress_layers = {} @@ -492,7 +491,7 @@ def push_image(self): continue if "error" in progress: self.log.error(progress["error"], extra=dict(phase="failed")) - raise docker.errors.ImageLoadError(progress["error"]) + raise ImageLoadError(progress["error"]) if "id" not in progress: continue # deprecated truncated-progress data @@ -528,7 +527,7 @@ def start_container(self): Returns running container """ - client = docker.from_env(version="auto") + client = DockerEngine() docker_host = os.environ.get("DOCKER_HOST") if docker_host: @@ -565,10 +564,7 @@ def start_container(self): container_volumes = {} if self.volumes: - api_client = docker.APIClient( - version="auto", **docker.utils.kwargs_from_env() - ) - image = api_client.inspect_image(self.output_image_spec) + image = client.inspect_image(self.output_image_spec) image_workdir = image["ContainerConfig"]["WorkingDir"] for k, v in self.volumes.items(): @@ -588,7 +584,7 @@ def start_container(self): run_kwargs.update(self.extra_run_kwargs) - container = client.containers.run(self.output_image_spec, **run_kwargs) + container = client.run(self.output_image_spec, **run_kwargs) while container.status == "created": time.sleep(0.5) @@ -645,7 +641,7 @@ def find_image(self): if self.dry_run: return False # check if we already have an image for this content - client = docker.APIClient(version="auto", **kwargs_from_env()) + client = DockerEngine() for image in client.images(): if image["RepoTags"] is not None: for tag in image["RepoTags"]: @@ -660,12 +656,9 @@ def build(self): # Check if r2d can connect to docker daemon if not self.dry_run: try: - docker_client = docker.APIClient(version="auto", **kwargs_from_env()) - except DockerException as e: - self.log.error( - "\nDocker client initialization error: %s.\nCheck if docker is running on the host.\n", - e, - ) + docker_client = DockerEngine() + except ContainerEngineException as e: + self.log.error("\nContainer engine initialization error: %s\n", e) self.exit(1) # If the source to be executed is a directory, continue using the @@ -755,7 +748,7 @@ def build(self): self.log.info(l["stream"], extra=dict(phase="building")) elif "error" in l: self.log.info(l["error"], extra=dict(phase="failure")) - raise docker.errors.BuildError(l["error"], build_log="") + raise BuildError(l["error"], build_log="") elif "status" in l: self.log.info( "Fetching base image...\r", extra=dict(phase="building") From 9ac8a8314cf4e23c5f48bfe220abd4361bc22e7b Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 11 Feb 2020 19:01:18 +0000 Subject: [PATCH 086/103] Handle attrs["State"]["ExitCode"] --- repo2docker/app.py | 2 +- repo2docker/docker.py | 4 ++++ repo2docker/engine.py | 7 +++++++ tests/unit/test_ports.py | 2 +- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/repo2docker/app.py b/repo2docker/app.py index 296a25194..b938b8173 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -607,7 +607,7 @@ def wait_for_container(self, container): if container.status == "running": self.log.info("Stopping container...\n", extra=dict(phase="running")) container.kill() - exit_code = container.attrs["State"]["ExitCode"] + exit_code = container.exitcode container.wait() diff --git a/repo2docker/docker.py b/repo2docker/docker.py index b6ec041d8..04edb6f42 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -25,6 +25,10 @@ def remove(self): def stop(self, *, timeout=10): return self._c.stop(timeout=timeout) + @property + def exitcode(self): + return self._c.attrs["State"]["ExitCode"] + @property def status(self): return self._c.status diff --git a/repo2docker/engine.py b/repo2docker/engine.py index 151960695..0d667af0f 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -62,6 +62,13 @@ def stop(self, *, timeout=10): timeout : If the container doesn't gracefully stop after this timeout kill it """ + @property + @abstractmethod + def exitcode(self): + """ + The container exit code if exited + """ + @property @abstractmethod def status(self): diff --git a/tests/unit/test_ports.py b/tests/unit/test_ports.py index 407a3e02c..edb71424a 100644 --- a/tests/unit/test_ports.py +++ b/tests/unit/test_ports.py @@ -77,7 +77,7 @@ def _cleanup(): container.reload() assert container.status == "running" - port_mapping = container.attrs["NetworkSettings"]["Ports"] + port_mapping = container._c.attrs["NetworkSettings"]["Ports"] if all_ports: port = port_mapping["8888/tcp"][0]["HostPort"] From c521c3c95022e06d9ad52c194edc52260c302e34 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 11 Feb 2020 21:05:10 +0000 Subject: [PATCH 087/103] Remove constant docker args --- repo2docker/app.py | 1 - repo2docker/buildpacks/base.py | 3 --- repo2docker/buildpacks/docker.py | 3 --- repo2docker/docker.py | 16 ++++++++-------- repo2docker/engine.py | 30 +++++++++++++++++------------- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/repo2docker/app.py b/repo2docker/app.py index b938b8173..ddad7c8ea 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -576,7 +576,6 @@ def start_container(self): run_kwargs = dict( publish_all_ports=self.all_ports, ports=ports, - detach=True, command=run_cmd, volumes=container_volumes, environment=self.environment, diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 145d29648..46a407ad3 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -607,9 +607,6 @@ def _filter_tar(tar): tag=image_spec, custom_context=True, buildargs=build_args, - decode=True, - forcerm=True, - rm=True, container_limits=limits, cache_from=cache_from, ) diff --git a/repo2docker/buildpacks/docker.py b/repo2docker/buildpacks/docker.py index 5a0981655..8326aecfe 100644 --- a/repo2docker/buildpacks/docker.py +++ b/repo2docker/buildpacks/docker.py @@ -50,9 +50,6 @@ def build( dockerfile=self.binder_path(self.dockerfile), tag=image_spec, buildargs=build_args, - decode=True, - forcerm=True, - rm=True, container_limits=limits, cache_from=cache_from, ) diff --git a/repo2docker/docker.py b/repo2docker/docker.py index 04edb6f42..8e3fbbc86 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -54,11 +54,11 @@ def build( buildargs=None, cache_from=None, container_limits=None, - forcerm=False, - rm=False, + # forcerm=False, + # rm=False, tag="", custom_context=False, - decode=False, + # decode=False, dockerfile="", fileobj=None, path="", @@ -67,11 +67,11 @@ def build( buildargs=buildargs, cache_from=cache_from, container_limits=container_limits, - forcerm=forcerm, - rm=rm, + forcerm=True, + rm=True, tag=tag, custom_context=custom_context, - decode=decode, + decode=True, dockerfile=dockerfile, fileobj=fileobj, path=path, @@ -92,7 +92,7 @@ def run( *, command=None, environment=None, - detach=False, + # detach=False, ports=None, publish_all_ports=False, remove=False, @@ -102,7 +102,7 @@ def run( image_spec, command=command, environment=(environment or []), - detach=detach, + detach=True, ports=(ports or {}), publish_all_ports=publish_all_ports, remove=remove, diff --git a/repo2docker/engine.py b/repo2docker/engine.py index 0d667af0f..0d818c7ff 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -100,11 +100,11 @@ def build( buildargs={}, cache_from=[], container_limits={}, - forcerm=False, - rm=False, + # forcerm=False, + # rm=False, tag="", custom_context=False, - decode=False, + # decode=False, dockerfile="", fileobj=None, path="", @@ -124,19 +124,22 @@ def build( - `cpushares` - `memory` - `memswap` - forcerm : bool - Always remove containers including unsuccessful builds - rm : bool - Remove intermediate containers + # forcerm : bool + # Always remove containers including unsuccessful builds + # always True + # rm : bool + # Remove intermediate containers + # always True tag : str Tag to add to the image custom_context : bool If `True` fileobj is a Tar file object containing the build context TODO: Specific to Docker - decode : bool - If `True` decode responses into dicts - TODO: repo2docker sets this to True but it's not clear what other clients should return + # decode : bool + # If `True` decode responses into dicts + # TODO: repo2docker sets this to True but it's not clear what other clients should return + # always True dockerfile : str Path to Dockerfile within the build context fileobj : tarfile @@ -192,7 +195,7 @@ def run( *, command=[], environment=[], - detach=False, + # detach=False, ports={}, publish_all_ports=False, remove=False, @@ -209,8 +212,9 @@ def run( The command to run environment : list[str] List of environment variables in the form `ENVVAR=value` - detach : bool - If `True` run container in background + # detach : bool + # If `True` run container in background + # always True ports : dict Container port bindings in the format expected by the engine TODO: Should we use a fixed format and convert to whatever's required by the engine? From b6bead74cdfbbcfaf9605851b2ed7a802d2632b9 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 11 Feb 2020 21:25:05 +0000 Subject: [PATCH 088/103] Python 3.5: can't have trailing , in function arg list --- repo2docker/docker.py | 4 ++-- repo2docker/engine.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/repo2docker/docker.py b/repo2docker/docker.py index 8e3fbbc86..8f6dd3b94 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -61,7 +61,7 @@ def build( # decode=False, dockerfile="", fileobj=None, - path="", + path="" ): return self._apiclient.build( buildargs=buildargs, @@ -96,7 +96,7 @@ def run( ports=None, publish_all_ports=False, remove=False, - volumes=None, + volumes=None ): container = self._client.containers.run( image_spec, diff --git a/repo2docker/engine.py b/repo2docker/engine.py index 0d818c7ff..3d8366876 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -107,7 +107,7 @@ def build( # decode=False, dockerfile="", fileobj=None, - path="", + path="" ): """ Build a container @@ -199,7 +199,7 @@ def run( ports={}, publish_all_ports=False, remove=False, - volumes={}, + volumes={} ): """ Run a container From 787d1d3aebf9d788703c941516030182a4f55e5b Mon Sep 17 00:00:00 2001 From: Simon Li Date: Thu, 13 Feb 2020 11:46:29 +0000 Subject: [PATCH 089/103] Container engine is an entrypoint: repo2docker.engines --- repo2docker/__main__.py | 5 +++++ repo2docker/app.py | 36 ++++++++++++++++++++++++++++++++---- setup.py | 4 +++- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/repo2docker/__main__.py b/repo2docker/__main__.py index 28883c7a7..1dd49ebe9 100644 --- a/repo2docker/__main__.py +++ b/repo2docker/__main__.py @@ -217,6 +217,8 @@ def get_argparser(): "--cache-from", action="append", default=[], help=Repo2Docker.cache_from.help ) + argparser.add_argument("--engine", help="Name of the container engine") + return argparser @@ -351,6 +353,9 @@ def make_r2d(argv=None): if args.cache_from: r2d.cache_from = args.cache_from + if args.engine: + r2d.engine = args.engine + r2d.environment = args.environment # if the source exists locally we don't want to delete it at the end diff --git a/repo2docker/app.py b/repo2docker/app.py index ddad7c8ea..f77a1d368 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -11,6 +11,7 @@ import sys import logging import os +import entrypoints import getpass import shutil import tempfile @@ -381,6 +382,33 @@ def _user_name_default(self): config=True, ) + engine = Unicode( + "docker", + config=True, + help=""" + Name of the container engine. + + Defaults to 'docker'. + """, + ) + + def get_engine(self): + """Return an instance of the container engine. + + Currently no arguments are passed to the engine constructor. + """ + engines = entrypoints.get_group_named("repo2docker.engines") + try: + entry = engines[self.engine] + except KeyError: + raise ContainerEngineException( + "Container engine '{}' not found. Available engines: {}".format( + self.engine, ",".join(engines.keys()) + ) + ) + engine_class = entry.load() + return engine_class() + def fetch(self, url, ref, checkout_path): """Fetch the contents of `url` and place it in `checkout_path`. @@ -473,7 +501,7 @@ def initialize(self): def push_image(self): """Push docker image to registry""" - client = DockerEngine() + client = self.get_engine() # Build a progress setup for each layer, and only emit per-layer # info every 1.5s progress_layers = {} @@ -527,7 +555,7 @@ def start_container(self): Returns running container """ - client = DockerEngine() + client = self.get_engine() docker_host = os.environ.get("DOCKER_HOST") if docker_host: @@ -640,7 +668,7 @@ def find_image(self): if self.dry_run: return False # check if we already have an image for this content - client = DockerEngine() + client = self.get_engine() for image in client.images(): if image["RepoTags"] is not None: for tag in image["RepoTags"]: @@ -655,7 +683,7 @@ def build(self): # Check if r2d can connect to docker daemon if not self.dry_run: try: - docker_client = DockerEngine() + docker_client = self.get_engine() except ContainerEngineException as e: self.log.error("\nContainer engine initialization error: %s\n", e) self.exit(1) diff --git a/setup.py b/setup.py index af0611497..27c19c93c 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ def get_identifier(json): version=versioneer.get_version(), install_requires=[ "docker", + "entrypoints", "escapism", "jinja2", "python-json-logger", @@ -89,6 +90,7 @@ def get_identifier(json): "console_scripts": [ "jupyter-repo2docker = repo2docker.__main__:main", "repo2docker = repo2docker.__main__:main", - ] + ], + "repo2docker.engines": ["docker = repo2docker.docker:DockerEngine"], }, ) From a73595ce4c70940701ee085cd251f34ed3883451 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 13:15:04 +0000 Subject: [PATCH 090/103] Require image info in a fixed format --- repo2docker/app.py | 7 +++---- repo2docker/docker.py | 5 +++-- repo2docker/engine.py | 26 ++++++++++++++++++++++++-- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/repo2docker/app.py b/repo2docker/app.py index f77a1d368..0e83c50c5 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -670,10 +670,9 @@ def find_image(self): # check if we already have an image for this content client = self.get_engine() for image in client.images(): - if image["RepoTags"] is not None: - for tag in image["RepoTags"]: - if tag == self.output_image_spec + ":latest": - return True + for tag in image.tags: + if tag == self.output_image_spec + ":latest": + return True return False def build(self): diff --git a/repo2docker/docker.py b/repo2docker/docker.py index 8f6dd3b94..d158037b0 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -3,7 +3,7 @@ """ import docker -from .engine import Container, ContainerEngine, ContainerEngineException +from .engine import Container, ContainerEngine, ContainerEngineException, Image class DockerContainer(Container): @@ -78,7 +78,8 @@ def build( ) def images(self): - return self._apiclient.images() + images = self._apiclient.images() + return [Image(tags=image["RepoTags"]) for image in images] def inspect_image(self, image): return self._apiclient.inspect_image(image) diff --git a/repo2docker/engine.py b/repo2docker/engine.py index 3d8366876..118c8c8e3 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -84,9 +84,31 @@ def status(self): """ +class Image: + """ + Information about a container image + """ + + def __init__(self, *, tags): + self._tags = tags or [] + + @property + def tags(self): + """ + A list of tags associated with an image. + + If locally built images have a localhost prefix this prefix should be removed or the image may not be recognised. + If there are no tags [] will be returned. + """ + return self._tags + + def __repr__(self): + return "Image(tags={})".format(self.tags) + + class ContainerEngine(ABC): """ - Abstract container engine + Abstract container engine. """ # containers = Container @@ -156,7 +178,7 @@ def images(self): Returns ------- - list[str] : List of images + list[Image] : List of Image objects. """ @abstractmethod From 4a06ef19d95952d59be9dae34612826df84c7138 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 13:37:31 +0000 Subject: [PATCH 091/103] ContainerEngine is now LoggingConfigurable --- repo2docker/app.py | 2 +- repo2docker/docker.py | 3 ++- repo2docker/engine.py | 25 +++++++++++++++++++------ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/repo2docker/app.py b/repo2docker/app.py index 0e83c50c5..4c00e190f 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -407,7 +407,7 @@ def get_engine(self): ) ) engine_class = entry.load() - return engine_class() + return engine_class(parent=self) def fetch(self, url, ref, checkout_path): """Fetch the contents of `url` and place it in `checkout_path`. diff --git a/repo2docker/docker.py b/repo2docker/docker.py index d158037b0..53fc33b54 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -39,7 +39,8 @@ class DockerEngine(ContainerEngine): https://docker-py.readthedocs.io/en/4.2.0/api.html#module-docker.api.build """ - def __init__(self): + def __init__(self, *, parent): + super().__init__(parent=parent) try: self._apiclient = docker.APIClient( version="auto", **docker.utils.kwargs_from_env() diff --git a/repo2docker/engine.py b/repo2docker/engine.py index 118c8c8e3..cb6a58d42 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -3,6 +3,7 @@ """ from abc import ABC, abstractmethod +from traitlets.config import LoggingConfigurable # Based on https://docker-py.readthedocs.io/en/4.2.0/containers.html @@ -106,16 +107,27 @@ def __repr__(self): return "Image(tags={})".format(self.tags) -class ContainerEngine(ABC): +class ContainerEngine(LoggingConfigurable): """ Abstract container engine. + + Inherits from LoggingConfigurable, which means it has a log property. + Initialised with a reference to the parent so can also be configured using traitlets. """ - # containers = Container + def __init__(self, *, parent): + """ + Initialise the container engine + + Parameters + ---------- + parent: Application + Reference to the parent application so that its configuration file can be used in this plugin. + """ + super().__init__(parent=parent) # Based on https://docker-py.readthedocs.io/en/4.2.0/api.html#module-docker.api.build - @abstractmethod def build( self, *, @@ -170,8 +182,8 @@ def build( path : str path to the Dockerfile """ + raise NotImplementedError("build not implemented") - @abstractmethod def images(self): """ List images @@ -180,8 +192,8 @@ def images(self): ------- list[Image] : List of Image objects. """ + raise NotImplementedError("images not implemented") - @abstractmethod def inspect_image(self, image): """ Get information about an image @@ -197,8 +209,8 @@ def inspect_image(self, image): ------- dict """ + raise NotImplementedError("inspect_image not implemented") - @abstractmethod def push(self, image_spec, *, stream=True): """ Push image to a registry @@ -210,6 +222,7 @@ def push(self, image_spec, *, stream=True): stream : bool If `True` return output logs as a generator """ + raise NotImplementedError("push not implemented") # Note this is different from the Docker client which has Client.containers.run def run( From 17af0c7293ebc2ea9031d55926488d144d34ea86 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 13:37:59 +0000 Subject: [PATCH 092/103] BuildError only takes positional params --- repo2docker/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo2docker/app.py b/repo2docker/app.py index 4c00e190f..0bab5d97a 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -774,7 +774,7 @@ def build(self): self.log.info(l["stream"], extra=dict(phase="building")) elif "error" in l: self.log.info(l["error"], extra=dict(phase="failure")) - raise BuildError(l["error"], build_log="") + raise BuildError(l["error"]) elif "status" in l: self.log.info( "Fetching base image...\r", extra=dict(phase="building") From 8215f0b065df1dade9ed8d33f970812004aec127 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 13:40:02 +0000 Subject: [PATCH 093/103] Remove commented engine.py code --- repo2docker/docker.py | 4 ---- repo2docker/engine.py | 18 +----------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/repo2docker/docker.py b/repo2docker/docker.py index 53fc33b54..d08224fde 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -55,11 +55,8 @@ def build( buildargs=None, cache_from=None, container_limits=None, - # forcerm=False, - # rm=False, tag="", custom_context=False, - # decode=False, dockerfile="", fileobj=None, path="" @@ -94,7 +91,6 @@ def run( *, command=None, environment=None, - # detach=False, ports=None, publish_all_ports=False, remove=False, diff --git a/repo2docker/engine.py b/repo2docker/engine.py index cb6a58d42..5e6b72398 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -134,11 +134,8 @@ def build( buildargs={}, cache_from=[], container_limits={}, - # forcerm=False, - # rm=False, tag="", custom_context=False, - # decode=False, dockerfile="", fileobj=None, path="" @@ -158,22 +155,12 @@ def build( - `cpushares` - `memory` - `memswap` - # forcerm : bool - # Always remove containers including unsuccessful builds - # always True - # rm : bool - # Remove intermediate containers - # always True tag : str Tag to add to the image custom_context : bool If `True` fileobj is a Tar file object containing the build context TODO: Specific to Docker - # decode : bool - # If `True` decode responses into dicts - # TODO: repo2docker sets this to True but it's not clear what other clients should return - # always True dockerfile : str Path to Dockerfile within the build context fileobj : tarfile @@ -226,11 +213,11 @@ def push(self, image_spec, *, stream=True): # Note this is different from the Docker client which has Client.containers.run def run( + self, image_spec, *, command=[], environment=[], - # detach=False, ports={}, publish_all_ports=False, remove=False, @@ -247,9 +234,6 @@ def run( The command to run environment : list[str] List of environment variables in the form `ENVVAR=value` - # detach : bool - # If `True` run container in background - # always True ports : dict Container port bindings in the format expected by the engine TODO: Should we use a fixed format and convert to whatever's required by the engine? From 00df21069ee2556d75677698ed44f67aee9945b7 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 16:20:55 +0000 Subject: [PATCH 094/103] ContainerEngine .build .push default to returning strings (stream) --- repo2docker/app.py | 12 ++++++++++-- repo2docker/docker.py | 8 +++++--- repo2docker/engine.py | 35 ++++++++++++++++++++++++++++------- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/repo2docker/app.py b/repo2docker/app.py index 0bab5d97a..52fd45efe 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -507,7 +507,12 @@ def push_image(self): progress_layers = {} layers = {} last_emit_time = time.time() - for chunk in client.push(self.output_image_spec, stream=True): + for chunk in client.push(self.output_image_spec): + if client.string_output: + self.log.info(chunk, extra=dict(phase="pushing")) + continue + # else this is Docker output + # each chunk can be one or more lines of json events # split lines here in case multiple are delivered at once for line in chunk.splitlines(): @@ -770,7 +775,10 @@ def build(self): self.cache_from, self.extra_build_kwargs, ): - if "stream" in l: + if docker_client.string_output: + self.log.info(l, extra=dict(phase="building")) + # else this is Docker output + elif "stream" in l: self.log.info(l["stream"], extra=dict(phase="building")) elif "error" in l: self.log.info(l["error"], extra=dict(phase="failure")) diff --git a/repo2docker/docker.py b/repo2docker/docker.py index d08224fde..6e0e04f4c 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -13,7 +13,7 @@ def __init__(self, container): def reload(self): return self._c.reload() - def logs(self, stream=False): + def logs(self, *, stream=False): return self._c.logs(stream=stream) def kill(self, *, signal="KILL"): @@ -39,6 +39,8 @@ class DockerEngine(ContainerEngine): https://docker-py.readthedocs.io/en/4.2.0/api.html#module-docker.api.build """ + string_output = False + def __init__(self, *, parent): super().__init__(parent=parent) try: @@ -82,8 +84,8 @@ def images(self): def inspect_image(self, image): return self._apiclient.inspect_image(image) - def push(self, image_spec, *, stream=True): - return self._apiclient.push(image_spec, stream=stream) + def push(self, image_spec): + return self._apiclient.push(image_spec, stream=True) def run( self, diff --git a/repo2docker/engine.py b/repo2docker/engine.py index 5e6b72398..9e0aa6dd1 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -21,19 +21,18 @@ def reload(self): """ @abstractmethod - def logs(self, stream=False): + def logs(self, *, stream=False): """ Get the container logs. Parameters ---------- stream : bool - If `True` return an iterator over the log lines, otherwise return all - logs + If `True` return an iterator over the log lines, otherwise return all logs Returns ------- - str or iterator + str or generator of log strings """ @abstractmethod @@ -115,6 +114,16 @@ class ContainerEngine(LoggingConfigurable): Initialised with a reference to the parent so can also be configured using traitlets. """ + string_output = True + """ + Whether progress events should be strings or an object. + + Originally Docker was the only container engine supported by repo2docker. + Some operations including build() and push() would return generators of events in a Docker specific format. + This format of events is not easily constructable with other engines so the default is to return strings and raise an exception if an error occurs. + If an engine returns docker style events set this variable to False. + """ + def __init__(self, *, parent): """ Initialise the container engine @@ -168,6 +177,13 @@ def build( TODO: Specific to Docker, other clients can untar this to a tempdir path : str path to the Dockerfile + + Returns + ------- + A generator of strings. If an error occurs an exception must be thrown. + + If `string_output=True` this should instead be whatever Docker returns: + https://github.com/jupyter/repo2docker/blob/0.11.0/repo2docker/app.py#L725-L735 """ raise NotImplementedError("build not implemented") @@ -198,7 +214,7 @@ def inspect_image(self, image): """ raise NotImplementedError("inspect_image not implemented") - def push(self, image_spec, *, stream=True): + def push(self, image_spec): """ Push image to a registry @@ -206,8 +222,13 @@ def push(self, image_spec, *, stream=True): ---------- image_spec : str The repository spec to push to - stream : bool - If `True` return output logs as a generator + + Returns + ------- + A generator of strings. If an error occurs an exception must be thrown. + + If `string_output=True` this should instead be whatever Docker returns: + https://github.com/jupyter/repo2docker/blob/0.11.0/repo2docker/app.py#L469-L495 """ raise NotImplementedError("push not implemented") From ad5701dd3acb5696042cc5e5e1bc51994546aa49 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 16:27:25 +0000 Subject: [PATCH 095/103] Remove unnecessary import --- repo2docker/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/repo2docker/app.py b/repo2docker/app.py index 52fd45efe..75f1d54b1 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -18,7 +18,6 @@ import time from .engine import BuildError, ContainerEngineException, ImageLoadError -from .docker import DockerEngine from urllib.parse import urlparse import escapism from pythonjsonlogger import jsonlogger From 4647dd07028199f48c000d0344065f7265eaa612 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 16:38:42 +0000 Subject: [PATCH 096/103] Container .build .run need to support **kwargs --- repo2docker/docker.py | 8 ++++++-- repo2docker/engine.py | 6 ++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/repo2docker/docker.py b/repo2docker/docker.py index 6e0e04f4c..0c501f8c1 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -61,7 +61,8 @@ def build( custom_context=False, dockerfile="", fileobj=None, - path="" + path="", + **kwargs, ): return self._apiclient.build( buildargs=buildargs, @@ -75,6 +76,7 @@ def build( dockerfile=dockerfile, fileobj=fileobj, path=path, + **kwargs, ) def images(self): @@ -96,7 +98,8 @@ def run( ports=None, publish_all_ports=False, remove=False, - volumes=None + volumes=None, + **kwargs, ): container = self._client.containers.run( image_spec, @@ -107,5 +110,6 @@ def run( publish_all_ports=publish_all_ports, remove=remove, volumes=(volumes or {}), + **kwargs, ) return DockerContainer(container) diff --git a/repo2docker/engine.py b/repo2docker/engine.py index 9e0aa6dd1..7dede1661 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -147,7 +147,8 @@ def build( custom_context=False, dockerfile="", fileobj=None, - path="" + path="", + **kwargs ): """ Build a container @@ -242,7 +243,8 @@ def run( ports={}, publish_all_ports=False, remove=False, - volumes={} + volumes={}, + **kwargs ): """ Run a container From 52b672a17a891df59a22bb8dca001eb0eddf83d6 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 16:49:08 +0000 Subject: [PATCH 097/103] Docker: print helpful message if daemon not running --- repo2docker/docker.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/repo2docker/docker.py b/repo2docker/docker.py index 0c501f8c1..ef516b0a7 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -47,9 +47,8 @@ def __init__(self, *, parent): self._apiclient = docker.APIClient( version="auto", **docker.utils.kwargs_from_env() ) - self._client = docker.from_env(version="auto") except docker.errors.DockerException as e: - raise ContainerEngineException(e) + raise ContainerEngineException("Check if docker is running on the host.", e) def build( self, @@ -101,7 +100,8 @@ def run( volumes=None, **kwargs, ): - container = self._client.containers.run( + client = docker.from_env(version="auto") + container = client.containers.run( image_spec, command=command, environment=(environment or []), From 5c2c8f73858f984e96d518b5b10ef983c509b1da Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 17:27:01 +0000 Subject: [PATCH 098/103] Python 3.5: can't have trailing , in function arg list (again) --- repo2docker/docker.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/repo2docker/docker.py b/repo2docker/docker.py index ef516b0a7..cabe79f84 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -61,7 +61,10 @@ def build( dockerfile="", fileobj=None, path="", - **kwargs, + # fmt: off + # black adds a trailing , but this is invalid in Python 3.5 + **kwargs + # fmt: on ): return self._apiclient.build( buildargs=buildargs, @@ -98,7 +101,10 @@ def run( publish_all_ports=False, remove=False, volumes=None, - **kwargs, + # fmt: off + # black adds a trailing , but this is invalid in Python 3.5 + **kwargs + # fmt: on ): client = docker.from_env(version="auto") container = client.containers.run( From 4ed6685dc995021d370be8bbb3387c1212e8bfe5 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 15:13:09 +0000 Subject: [PATCH 099/103] Fix unit tests --- tests/unit/test_app.py | 4 ++-- tests/unit/test_editable.py | 6 ++++-- tests/unit/test_external_scripts.py | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/unit/test_app.py b/tests/unit/test_app.py index d45c28a4c..25b39cdb1 100644 --- a/tests/unit/test_app.py +++ b/tests/unit/test_app.py @@ -14,7 +14,7 @@ def test_find_image(): images = [{"RepoTags": ["some-org/some-repo:latest"]}] - with patch("repo2docker.app.docker.APIClient") as FakeDockerClient: + with patch("repo2docker.docker.docker.APIClient") as FakeDockerClient: instance = FakeDockerClient.return_value instance.images.return_value = images @@ -28,7 +28,7 @@ def test_find_image(): def test_dont_find_image(): images = [{"RepoTags": ["some-org/some-image-name:latest"]}] - with patch("repo2docker.app.docker.APIClient") as FakeDockerClient: + with patch("repo2docker.docker.docker.APIClient") as FakeDockerClient: instance = FakeDockerClient.return_value instance.images.return_value = images diff --git a/tests/unit/test_editable.py b/tests/unit/test_editable.py index a84e4ba2e..6d711e25f 100644 --- a/tests/unit/test_editable.py +++ b/tests/unit/test_editable.py @@ -44,12 +44,14 @@ def test_editable_by_host(): try: with tempfile.NamedTemporaryFile(dir=DIR, prefix="testfile", suffix=".txt"): - status, output = container.exec_run(["sh", "-c", "ls testfile????????.txt"]) + status, output = container._c.exec_run( + ["sh", "-c", "ls testfile????????.txt"] + ) assert status == 0 assert re.match(br"^testfile\w{8}\.txt\n$", output) is not None # After exiting the with block the file should stop existing # in the container as well as locally - status, output = container.exec_run(["sh", "-c", "ls testfile????????.txt"]) + status, output = container._c.exec_run(["sh", "-c", "ls testfile????????.txt"]) assert status == 2 assert re.match(br"^testfile\w{8}\.txt\n$", output) is None diff --git a/tests/unit/test_external_scripts.py b/tests/unit/test_external_scripts.py index 1265d3ac5..d03debe55 100644 --- a/tests/unit/test_external_scripts.py +++ b/tests/unit/test_external_scripts.py @@ -32,7 +32,7 @@ def get_build_script_files(self): assert container.status == "running" try: - status, output = container.exec_run(["sh", "-c", "cat /tmp/my_extra_script"]) + status, output = container._c.exec_run(["sh", "-c", "cat /tmp/my_extra_script"]) assert status == 0 assert output.decode("utf-8") == "Hello World of Absolute Paths!" finally: From 0e7e0f003ef13f9b4b681790afcb60a1798ee740 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 22:00:34 +0000 Subject: [PATCH 100/103] Use standard-ish image_inspect format --- repo2docker/app.py | 2 +- repo2docker/docker.py | 3 ++- repo2docker/engine.py | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/repo2docker/app.py b/repo2docker/app.py index 75f1d54b1..470e9c0a9 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -597,7 +597,7 @@ def start_container(self): container_volumes = {} if self.volumes: image = client.inspect_image(self.output_image_spec) - image_workdir = image["ContainerConfig"]["WorkingDir"] + image_workdir = image.config["WorkingDir"] for k, v in self.volumes.items(): container_volumes[os.path.abspath(k)] = { diff --git a/repo2docker/docker.py b/repo2docker/docker.py index cabe79f84..7a3735463 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -86,7 +86,8 @@ def images(self): return [Image(tags=image["RepoTags"]) for image in images] def inspect_image(self, image): - return self._apiclient.inspect_image(image) + image = self._apiclient.inspect_image(image) + return Image(tags=image["RepoTags"], config=image["ContainerConfig"]) def push(self, image_spec): return self._apiclient.push(image_spec, stream=True) diff --git a/repo2docker/engine.py b/repo2docker/engine.py index 7dede1661..c0d7f6f53 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -89,8 +89,9 @@ class Image: Information about a container image """ - def __init__(self, *, tags): + def __init__(self, *, tags, config=None): self._tags = tags or [] + self._config = config @property def tags(self): @@ -102,8 +103,19 @@ def tags(self): """ return self._tags + @property + def config(self): + """ + A dictionary of image configuration information + + If this is `None` the information has not been loaded. + If not `None` this must include the following fields: + - WorkingDir: The default working directory + """ + return self._config + def __repr__(self): - return "Image(tags={})".format(self.tags) + return "Image(tags={},config={})".format(self.tags, self.config) class ContainerEngine(LoggingConfigurable): @@ -211,7 +223,7 @@ def inspect_image(self, image): Returns ------- - dict + Image object with .config dict. """ raise NotImplementedError("inspect_image not implemented") From e32c7b02df9f5e3726dec1a83df2db79d37e838e Mon Sep 17 00:00:00 2001 From: Simon Li Date: Fri, 14 Feb 2020 22:01:08 +0000 Subject: [PATCH 101/103] describe ContainerEngine volume and port formats --- repo2docker/engine.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/repo2docker/engine.py b/repo2docker/engine.py index c0d7f6f53..6b9fa1170 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -80,7 +80,8 @@ def status(self): str : The status of the container. Values include `created` `running` `exited`. - TODO: Does Docker have a fixed list of these? + Full list of statuses: + https://github.com/moby/moby/blob/v19.03.5/api/swagger.yaml#L4832 """ @@ -182,12 +183,10 @@ def build( custom_context : bool If `True` fileobj is a Tar file object containing the build context - TODO: Specific to Docker dockerfile : str Path to Dockerfile within the build context fileobj : tarfile A tar file-like object containing the build context - TODO: Specific to Docker, other clients can untar this to a tempdir path : str path to the Dockerfile @@ -270,15 +269,14 @@ def run( environment : list[str] List of environment variables in the form `ENVVAR=value` ports : dict - Container port bindings in the format expected by the engine - TODO: Should we use a fixed format and convert to whatever's required by the engine? + Container port bindings in the form generated by `repo2docker.utils.validate_and_generate_port_mapping` + https://github.com/jupyter/repo2docker/blob/0.11.0/repo2docker/utils.py#L95 publish_all_ports : bool If `True` publish all ports to host remove : bool If `True` delete container when it completes volumes : dict - Volume bindings in the format expected by the engine - TODO: Should we use a fixed format and convert to whatever's required by the engine? + Volume bindings in the form `{src : dest}` Returns ------- From 5778ed0381f909dd714340cfcd8dabad5c10c2b4 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Tue, 31 Mar 2020 16:02:46 +0100 Subject: [PATCH 102/103] docker.py: remove workaround for old black --- repo2docker/docker.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/repo2docker/docker.py b/repo2docker/docker.py index 7a3735463..cb3a5dbaa 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -61,10 +61,7 @@ def build( dockerfile="", fileobj=None, path="", - # fmt: off - # black adds a trailing , but this is invalid in Python 3.5 **kwargs - # fmt: on ): return self._apiclient.build( buildargs=buildargs, @@ -102,10 +99,7 @@ def run( publish_all_ports=False, remove=False, volumes=None, - # fmt: off - # black adds a trailing , but this is invalid in Python 3.5 **kwargs - # fmt: on ): client = docker.from_env(version="auto") container = client.containers.run( From d47b74492f966241e217735aad508e6f3d005b29 Mon Sep 17 00:00:00 2001 From: Simon Li Date: Sun, 21 Feb 2021 00:19:54 +0000 Subject: [PATCH 103/103] Add Container.wait() --- repo2docker/docker.py | 7 +++++-- repo2docker/engine.py | 10 ++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/repo2docker/docker.py b/repo2docker/docker.py index cb3a5dbaa..d637674ad 100644 --- a/repo2docker/docker.py +++ b/repo2docker/docker.py @@ -25,6 +25,9 @@ def remove(self): def stop(self, *, timeout=10): return self._c.stop(timeout=timeout) + def wait(self): + return self._c.wait() + @property def exitcode(self): return self._c.attrs["State"]["ExitCode"] @@ -61,7 +64,7 @@ def build( dockerfile="", fileobj=None, path="", - **kwargs + **kwargs, ): return self._apiclient.build( buildargs=buildargs, @@ -99,7 +102,7 @@ def run( publish_all_ports=False, remove=False, volumes=None, - **kwargs + **kwargs, ): client = docker.from_env(version="auto") container = client.containers.run( diff --git a/repo2docker/engine.py b/repo2docker/engine.py index 6b9fa1170..2db8bfca2 100644 --- a/repo2docker/engine.py +++ b/repo2docker/engine.py @@ -62,6 +62,12 @@ def stop(self, *, timeout=10): timeout : If the container doesn't gracefully stop after this timeout kill it """ + @abstractmethod + def wait(self): + """ + Wait for the container to stop + """ + @property @abstractmethod def exitcode(self): @@ -161,7 +167,7 @@ def build( dockerfile="", fileobj=None, path="", - **kwargs + **kwargs, ): """ Build a container @@ -255,7 +261,7 @@ def run( publish_all_ports=False, remove=False, volumes={}, - **kwargs + **kwargs, ): """ Run a container