From 8755b467c1199980e1f2648e01f9f0f02ad84b1d Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 9 Feb 2018 17:56:48 +0100 Subject: [PATCH 01/12] add assemble_subset - get_assemble_files() returns list of files needed for assembly - if buildpack.assemble_with_subset is set, only load assemble_files prior to running assembly scripts. Load the rest of the repo afterward conda opts in to this, but currently I think this works for everything *except* requirements.txt --- repo2docker/buildpacks/conda/__init__.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/repo2docker/buildpacks/conda/__init__.py b/repo2docker/buildpacks/conda/__init__.py index 8e818bc4d..27446c4d8 100644 --- a/repo2docker/buildpacks/conda/__init__.py +++ b/repo2docker/buildpacks/conda/__init__.py @@ -178,6 +178,18 @@ def py2(self): """Am I building a Python 2 kernel environment?""" return self.python_version and self.python_version.split(".")[0] == "2" + def get_assemble_files(self): + """Specify that assembly only requires environment.yml + + enables caching assembly result even when + repo contents change + """ + assemble_files = super().get_assemble_files() + environment_yml = self.binder_path('environment.yml') + if os.path.exists(environment_yml): + assemble_files.append(environment_yml) + return assemble_files + def get_assemble_scripts(self): """Return series of build-steps specific to this source repository. """ From 6ecdbf4ef6f952bba38d4a65c5ca12a21f867a45 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 18 Sep 2018 14:42:58 +0200 Subject: [PATCH 02/12] check requirements.txt for local references allows assemble_from_subset when requirements.txt doesn't contain any local references --- repo2docker/buildpacks/python/__init__.py | 56 +++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/repo2docker/buildpacks/python/__init__.py b/repo2docker/buildpacks/python/__init__.py index 4db5c72d8..0d9a5b39a 100644 --- a/repo2docker/buildpacks/python/__init__.py +++ b/repo2docker/buildpacks/python/__init__.py @@ -34,6 +34,62 @@ def python_version(self): self._python_version = py_version return self._python_version + def get_assemble_files(self): + assemble_files = super().get_assemble_files() + for name in ('requirements.txt', 'requirements3.txt'): + requirements_txt = self.binder_path(name) + if os.path.exists(requirements_txt): + assemble_files.append(requirements_txt) + return assemble_files + + def _is_local_requirement(self, line): + """Return whether a line in a requirements.txt file references a local file""" + # trim comments and skip empty lines + line = line.split('#', 1)[0].strip() + if not line: + return False + if line.startswith(('-r', '-c')): + # local -r or -c references break isolation + return True + # strip off `-e, etc.` + if line.startswith('-'): + line = line.split(None, 1)[1] + if 'file://' in line: + # file references break isolation + return True + if '://' in line: + # handle git://../local/file + path = line.split('://', 1)[1] + else: + path = line + if path.startswith('.'): + # references a local file + return True + return False + + @property + def assemble_from_subset(self): + """Peek in requirements.txt to determine if we can assemble from only env files + + If there are any local references, e.g. `-e .`, + stage the whole repo prior to installation. + """ + if not os.path.exists('binder') and os.path.exists('setup.py'): + # can't install from subset if we're using setup.py + return False + for name in ('requirements.txt', 'requirements3.txt'): + requirements_txt = self.binder_path(name) + if not os.path.exists(requirements_txt): + continue + with open(requirements_txt) as f: + for line in f: + if self._is_local_requirement(line): + return False + + # didn't find any local references, + # allow assembly from subset + return True + def get_assemble_scripts(self): """Return series of build-steps specific to this repository. """ From eb32f84f28ff9fb9a8e494cca702150a23c8f5c0 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 16 Jul 2019 08:35:35 +0200 Subject: [PATCH 03/12] move stencila to preassemble --- repo2docker/buildpacks/base.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 0bb9703bb..6d77ad273 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -711,12 +711,8 @@ def get_preassemble_scripts(self): except FileNotFoundError: pass - return scripts - - def get_assemble_scripts(self): - assemble_scripts = [] if "py" in self.stencila_contexts: - assemble_scripts.extend( + scripts.extend( [ ( "${NB_USER}", @@ -728,7 +724,7 @@ def get_assemble_scripts(self): ] ) if self.stencila_manifest_dir: - assemble_scripts.extend( + scripts.extend( [ ( "${NB_USER}", @@ -741,7 +737,11 @@ def get_assemble_scripts(self): ) ] ) - return assemble_scripts + return scripts + + def get_assemble_scripts(self): + """Return directives to run after the entire repository has been added to the image""" + return [] def get_post_build_scripts(self): post_build = self.binder_path("postBuild") From 9fba04c8710c467dffe2303d0598d8ae0abbdbf1 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 16 Jul 2019 08:36:06 +0200 Subject: [PATCH 04/12] conda install occurs in preassemble --- repo2docker/buildpacks/conda/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/repo2docker/buildpacks/conda/__init__.py b/repo2docker/buildpacks/conda/__init__.py index 27446c4d8..2aa45c37f 100644 --- a/repo2docker/buildpacks/conda/__init__.py +++ b/repo2docker/buildpacks/conda/__init__.py @@ -178,19 +178,19 @@ def py2(self): """Am I building a Python 2 kernel environment?""" return self.python_version and self.python_version.split(".")[0] == "2" - def get_assemble_files(self): - """Specify that assembly only requires environment.yml + def get_preassemble_script_files(self): + """preassembly only requires environment.yml enables caching assembly result even when repo contents change """ - assemble_files = super().get_assemble_files() - environment_yml = self.binder_path('environment.yml') + assemble_files = super().get_preassemble_script_files() + environment_yml = self.binder_path("environment.yml") if os.path.exists(environment_yml): - assemble_files.append(environment_yml) + assemble_files[environment_yml] = environment_yml return assemble_files - def get_assemble_scripts(self): + def get_preassemble_scripts(self): """Return series of build-steps specific to this source repository. """ assembly_scripts = [] @@ -209,7 +209,7 @@ def get_assemble_scripts(self): ), ) ) - return super().get_assemble_scripts() + assembly_scripts + return super().get_preassemble_scripts() + assembly_scripts def detect(self): """Check if current repo should be built with the Conda BuildPack. From bf45edad335fc8a22397456e3786b7e9f5db9ef1 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 16 Jul 2019 09:02:59 +0200 Subject: [PATCH 05/12] preassemble python --- repo2docker/buildpacks/python/__init__.py | 110 +++++++++++++--------- 1 file changed, 65 insertions(+), 45 deletions(-) diff --git a/repo2docker/buildpacks/python/__init__.py b/repo2docker/buildpacks/python/__init__.py index 0d9a5b39a..7b6fc5afc 100644 --- a/repo2docker/buildpacks/python/__init__.py +++ b/repo2docker/buildpacks/python/__init__.py @@ -34,50 +34,79 @@ def python_version(self): self._python_version = py_version return self._python_version - def get_assemble_files(self): - assemble_files = super().get_assemble_files() - for name in ('requirements.txt', 'requirements3.txt'): - requirements_txt = self.binder_path(name) - if os.path.exists(requirements_txt): - assemble_files.append(requirements_txt) - return assemble_files - def _is_local_requirement(self, line): """Return whether a line in a requirements.txt file references a local file""" # trim comments and skip empty lines - line = line.split('#', 1)[0].strip() + line = line.split("#", 1)[0].strip() if not line: return False - if line.startswith(('-r', '-c')): + if line.startswith(("-r", "-c")): # local -r or -c references break isolation return True # strip off `-e, etc.` - if line.startswith('-'): + if line.startswith("-"): line = line.split(None, 1)[1] - if 'file://' in line: + if "file://" in line: # file references break isolation return True - if '://' in line: + if "://" in line: # handle git://../local/file - path = line.split('://', 1)[1] + path = line.split("://", 1)[1] else: path = line - if path.startswith('.'): + if path.startswith("."): # references a local file return True return False + def _get_pip_scripts(self): + """Get pip install scripts + + added to preassemble unless local references are found, + in which case this happens in assemble. + """ + # KERNEL_PYTHON_PREFIX is the env with the kernel, + # whether it's distinct from the notebook or the same. + pip = "${KERNEL_PYTHON_PREFIX}/bin/pip" + scripts = [] + if self.py2: + # using python 2 kernel, + # requirements3.txt allows installation in the notebook server env + nb_requirements_file = self.binder_path("requirements3.txt") + if os.path.exists(nb_requirements_file): + scripts.append( + ( + "${NB_USER}", + # want the $NB_PYHTON_PREFIX environment variable, not for + # Python's string formatting to try and replace this + '${{NB_PYTHON_PREFIX}}/bin/pip install --no-cache-dir -r "{}"'.format( + nb_requirements_file + ), + ) + ) + + # install requirements.txt in the kernel env + requirements_file = self.binder_path("requirements.txt") + if os.path.exists(requirements_file): + scripts.append( + ( + "${NB_USER}", + '{} install --no-cache-dir -r "{}"'.format(pip, requirements_file), + ) + ) + return scripts + @property - def assemble_from_subset(self): + def _should_preassemble_pip(self): """Peek in requirements.txt to determine if we can assemble from only env files If there are any local references, e.g. `-e .`, stage the whole repo prior to installation. """ - if not os.path.exists('binder') and os.path.exists('setup.py'): + if not os.path.exists("binder") and os.path.exists("setup.py"): # can't install from subset if we're using setup.py return False - for name in ('requirements.txt', 'requirements3.txt'): + for name in ("requirements.txt", "requirements3.txt"): requirements_txt = self.binder_path(name) if not os.path.exists(requirements_txt): continue @@ -90,9 +119,23 @@ def assemble_from_subset(self): # allow assembly from subset return True + def get_preassemble_script_files(self): + assemble_files = super().get_preassemble_script_files() + for name in ("requirements.txt", "requirements3.txt"): + requirements_txt = self.binder_path(name) + if os.path.exists(requirements_txt): + assemble_files[requirements_txt] = requirements_txt + return assemble_files + + def get_preassemble_scripts(self): + """Return scripts to run before adding the full repository""" + scripts = super().get_preassemble_scripts() + if self._should_preassemble_pip: + scripts.extend(self._get_pip_scripts()) + return scripts + def get_assemble_scripts(self): - """Return series of build-steps specific to this repository. - """ + """Return series of build steps that require the full repository""" # If we have a runtime.txt & that's set to python-2.7, # requirements.txt will be installed in the *kernel* env # and requirements3.txt (if it exists) @@ -102,31 +145,8 @@ def get_assemble_scripts(self): # KERNEL_PYTHON_PREFIX is the env with the kernel, # whether it's distinct from the notebook or the same. pip = "${KERNEL_PYTHON_PREFIX}/bin/pip" - if self.py2: - # using python 2 kernel, - # requirements3.txt allows installation in the notebook server env - nb_requirements_file = self.binder_path("requirements3.txt") - if os.path.exists(nb_requirements_file): - assemble_scripts.append( - ( - "${NB_USER}", - # want the $NB_PYHTON_PREFIX environment variable, not for - # Python's string formatting to try and replace this - '${{NB_PYTHON_PREFIX}}/bin/pip install --no-cache-dir -r "{}"'.format( - nb_requirements_file - ), - ) - ) - - # install requirements.txt in the kernel env - requirements_file = self.binder_path("requirements.txt") - if os.path.exists(requirements_file): - assemble_scripts.append( - ( - "${NB_USER}", - '{} install --no-cache-dir -r "{}"'.format(pip, requirements_file), - ) - ) + if not self._should_preassemble_pip: + assemble_scripts.extend(self._get_pip_scripts()) # setup.py exists *and* binder dir is not used if not self.binder_dir and os.path.exists(setup_py): From 8227156d91ee60fa387d07c5dfd65e118a0d83d9 Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 16 Jul 2019 09:11:38 +0200 Subject: [PATCH 06/12] preassembly for pipfiles --- repo2docker/buildpacks/pipfile/__init__.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/repo2docker/buildpacks/pipfile/__init__.py b/repo2docker/buildpacks/pipfile/__init__.py index e91e63aa5..c60a1f881 100644 --- a/repo2docker/buildpacks/pipfile/__init__.py +++ b/repo2docker/buildpacks/pipfile/__init__.py @@ -65,7 +65,16 @@ def python_version(self): self._python_version = self.major_pythons["3"] return self._python_version - def get_assemble_scripts(self): + def get_preassemble_script_files(self): + """Return files needed for preassembly""" + files = super().get_preassemble_script_files() + for name in ("requirements3.txt", "Pipfile", "Pipfile.lock"): + path = self.binder_path(name) + if os.path.exists(path): + files[path] = path + return files + + def get_preassemble_scripts(self): """Return series of build-steps specific to this repository. """ # If we have either Pipfile.lock, Pipfile, or runtime.txt declare the From a8703a441bca2417aef47a21e8a4b6b3a9b4cb0c Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Jul 2019 14:39:35 +0200 Subject: [PATCH 07/12] check for local pip requirements in conda environments --- repo2docker/buildpacks/conda/__init__.py | 104 +++++++++++++++------- repo2docker/buildpacks/python/__init__.py | 28 +----- repo2docker/utils.py | 26 ++++++ 3 files changed, 102 insertions(+), 56 deletions(-) diff --git a/repo2docker/buildpacks/conda/__init__.py b/repo2docker/buildpacks/conda/__init__.py index 2aa45c37f..87865250e 100644 --- a/repo2docker/buildpacks/conda/__init__.py +++ b/repo2docker/buildpacks/conda/__init__.py @@ -6,6 +6,7 @@ from ruamel.yaml import YAML from ..base import BaseImage +from ...utils import is_local_pip_requirement # pattern for parsing conda dependency line PYTHON_REGEX = re.compile(r"python\s*=+\s*([\d\.]*)") @@ -127,6 +128,50 @@ def get_build_script_files(self): files.update(super().get_build_script_files()) return files + _environment_yaml = None + + @property + def environment_yaml(self): + if self._environment_yaml is not None: + return self._environment_yaml + + environment_yml = self.binder_path("environment.yml") + if not os.path.exists(environment_yml): + self._environment_yaml = {} + return self._environment_yaml + + with open(environment_yml) as f: + env = YAML().load(f) + # check if the env file is empty, if so instantiate an empty dictionary. + if env is None: + env = {} + # check if the env file provided a dick-like thing not a list or other data structure. + if not isinstance(env, Mapping): + raise TypeError( + "environment.yml should contain a dictionary. Got %r" % type(env) + ) + self._environment_yaml = env + + return self._environment_yaml + + @property + def _should_preassemble_env(self): + """Check for local pip requirements in environment.yaml + + If there are any local references, e.g. `-e .`, + stage the whole repo prior to installation. + """ + dependencies = self.environment_yaml.get("dependencies", []) + pip_requirements = None + for dep in dependencies: + if isinstance(dep, dict) and dep.get("pip"): + pip_requirements = dep["pip"] + if isinstance(pip_requirements, list): + for line in pip_requirements: + if is_local_pip_requirement(line): + return False + return True + @property def python_version(self): """Detect the Python version for a given `environment.yml` @@ -135,31 +180,17 @@ def python_version(self): or a Falsy empty string '' if not found. """ - environment_yml = self.binder_path("environment.yml") - if not os.path.exists(environment_yml): - return "" - if not hasattr(self, "_python_version"): py_version = None - with open(environment_yml) as f: - env = YAML().load(f) - # check if the env file is empty, if so instantiate an empty dictionary. - if env is None: - env = {} - # check if the env file provided a dick-like thing not a list or other data structure. - if not isinstance(env, Mapping): - raise TypeError( - "environment.yml should contain a dictionary. Got %r" - % type(env) - ) - for dep in env.get("dependencies", []): - if not isinstance(dep, str): - continue - match = PYTHON_REGEX.match(dep) - if not match: - continue - py_version = match.group(1) - break + env = self.environment_yaml + for dep in env.get("dependencies", []): + if not isinstance(dep, str): + continue + match = PYTHON_REGEX.match(dep) + if not match: + continue + py_version = match.group(1) + break # extract major.minor if py_version: @@ -185,19 +216,20 @@ def get_preassemble_script_files(self): repo contents change """ assemble_files = super().get_preassemble_script_files() - environment_yml = self.binder_path("environment.yml") - if os.path.exists(environment_yml): - assemble_files[environment_yml] = environment_yml + if self._should_preassemble_env: + environment_yml = self.binder_path("environment.yml") + if os.path.exists(environment_yml): + assemble_files[environment_yml] = environment_yml return assemble_files - def get_preassemble_scripts(self): + def get_env_scripts(self): """Return series of build-steps specific to this source repository. """ - assembly_scripts = [] + scripts = [] environment_yml = self.binder_path("environment.yml") env_prefix = "${KERNEL_PYTHON_PREFIX}" if self.py2 else "${NB_PYTHON_PREFIX}" if os.path.exists(environment_yml): - assembly_scripts.append( + scripts.append( ( "${NB_USER}", r""" @@ -209,7 +241,19 @@ def get_preassemble_scripts(self): ), ) ) - return super().get_preassemble_scripts() + assembly_scripts + return scripts + + def get_preassemble_scripts(self): + scripts = super().get_preassemble_scripts() + if self._should_preassemble_env: + scripts.extend(self.get_env_scripts()) + return scripts + + def get_assemble_scripts(self): + scripts = super().get_assemble_scripts() + if not self._should_preassemble_env: + scripts.extend(self.get_env_scripts()) + return scripts def detect(self): """Check if current repo should be built with the Conda BuildPack. diff --git a/repo2docker/buildpacks/python/__init__.py b/repo2docker/buildpacks/python/__init__.py index 7b6fc5afc..72029d78b 100644 --- a/repo2docker/buildpacks/python/__init__.py +++ b/repo2docker/buildpacks/python/__init__.py @@ -2,6 +2,7 @@ import os from ..conda import CondaBuildPack +from ...utils import is_local_pip_requirement class PythonBuildPack(CondaBuildPack): @@ -34,31 +35,6 @@ def python_version(self): self._python_version = py_version return self._python_version - def _is_local_requirement(self, line): - """Return whether a line in a requirements.txt file references a local file""" - # trim comments and skip empty lines - line = line.split("#", 1)[0].strip() - if not line: - return False - if line.startswith(("-r", "-c")): - # local -r or -c references break isolation - return True - # strip off `-e, etc.` - if line.startswith("-"): - line = line.split(None, 1)[1] - if "file://" in line: - # file references break isolation - return True - if "://" in line: - # handle git://../local/file - path = line.split("://", 1)[1] - else: - path = line - if path.startswith("."): - # references a local file - return True - return False - def _get_pip_scripts(self): """Get pip install scripts @@ -112,7 +88,7 @@ def _should_preassemble_pip(self): continue with open(requirements_txt) as f: for line in f: - if self._is_local_requirement(line): + if is_local_pip_requirement(line): return False # didn't find any local references, diff --git a/repo2docker/utils.py b/repo2docker/utils.py index a53ba5ef1..1c0a43341 100644 --- a/repo2docker/utils.py +++ b/repo2docker/utils.py @@ -431,3 +431,29 @@ def normalize_doi(val): (e.g. https://doi.org/10.1234/jshd123)""" m = doi_regexp.match(val) return m.group(2) + + +def is_local_pip_requirement(line): + """Return whether a pip requirement (e.g. in requirements.txt file) references a local file""" + # trim comments and skip empty lines + line = line.split("#", 1)[0].strip() + if not line: + return False + if line.startswith(("-r", "-c")): + # local -r or -c references break isolation + return True + # strip off `-e, etc.` + if line.startswith("-"): + line = line.split(None, 1)[1] + if "file://" in line: + # file references break isolation + return True + if "://" in line: + # handle git://../local/file + path = line.split("://", 1)[1] + else: + path = line + if path.startswith("."): + # references a local file + return True + return False From edf14b646f9ebd88d643bec1ae2ee3a29e124cf2 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Jul 2019 14:40:08 +0200 Subject: [PATCH 08/12] set permissions on repo_dir prior to preassemble --- repo2docker/buildpacks/base.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 6d77ad273..b2772e9ef 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -132,6 +132,10 @@ {% endfor -%} {% endif -%} +{% if preassemble_script_directives -%} +RUN chown -R ${NB_USER}:${NB_USER} ${REPO_DIR} +{% endif -%} + {% for sd in preassemble_script_directives -%} {{ sd }} {% endfor %} From 65e3fec4c48a5a7eeea977de3ea78705f88d495f Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Jul 2019 14:40:17 +0200 Subject: [PATCH 09/12] test local pip requirements --- tests/unit/test_utils.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 6ad1ff281..951f2085d 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -110,3 +110,20 @@ def test_normalize_doi(): assert utils.normalize_doi("http://doi.org/10.1234/jshd123") == "10.1234/jshd123" assert utils.normalize_doi("https://doi.org/10.1234/jshd123") == "10.1234/jshd123" assert utils.normalize_doi("http://dx.doi.org/10.1234/jshd123") == "10.1234/jshd123" + + +@pytest.mark.parametrize( + "req, is_local", + [ + ("-r requirements.txt", True), + ("-e .", True), + ("file://subdir", True), + ("file://./subdir", True), + ("git://github.com/jupyter/repo2docker", False), + ("git+https://github.com/jupyter/repo2docker", False), + ("numpy", False), + ("# -e .", False), + ], +) +def test_local_pip_requirement(req, is_local): + assert utils.is_local_pip_requirement(req) == is_local From 909643972b6d8999a2e57724e9ed42e03a26ba7c Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Jul 2019 16:25:04 +0200 Subject: [PATCH 10/12] only install pipenv in preassemble save preassembly of pipenv for a later patch --- repo2docker/buildpacks/pipfile/__init__.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/repo2docker/buildpacks/pipfile/__init__.py b/repo2docker/buildpacks/pipfile/__init__.py index c60a1f881..b38db08a8 100644 --- a/repo2docker/buildpacks/pipfile/__init__.py +++ b/repo2docker/buildpacks/pipfile/__init__.py @@ -75,6 +75,15 @@ def get_preassemble_script_files(self): return files def get_preassemble_scripts(self): + """scripts to run prior to staging the repo contents""" + scripts = super().get_preassemble_scripts() + # install pipenv to install dependencies within Pipfile.lock or Pipfile + scripts.append( + ("${NB_USER}", "${KERNEL_PYTHON_PREFIX}/bin/pip install pipenv==2018.11.26") + ) + return scripts + + def get_assemble_scripts(self): """Return series of build-steps specific to this repository. """ # If we have either Pipfile.lock, Pipfile, or runtime.txt declare the @@ -113,11 +122,6 @@ def get_preassemble_scripts(self): # my_package_example = {path=".", editable=true} working_directory = self.binder_dir or "." - # install pipenv to install dependencies within Pipfile.lock or Pipfile - assemble_scripts.append( - ("${NB_USER}", "${KERNEL_PYTHON_PREFIX}/bin/pip install pipenv==2018.11.26") - ) - # NOTES: # - Without prioritizing the PATH to KERNEL_PYTHON_PREFIX over # NB_SERVER_PYTHON_PREFIX, 'pipenv' draws the wrong conclusion about From 7d439efa7aa1e3c26650cc5dab681d08531fce98 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 18 Jul 2019 17:55:38 +0200 Subject: [PATCH 11/12] run chown as root --- repo2docker/buildpacks/base.py | 1 + 1 file changed, 1 insertion(+) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index b2772e9ef..140a1d331 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -133,6 +133,7 @@ {% endif -%} {% if preassemble_script_directives -%} +USER root RUN chown -R ${NB_USER}:${NB_USER} ${REPO_DIR} {% endif -%} From 3aa7e39c1be98f8c44748621fbdcebc1fb877143 Mon Sep 17 00:00:00 2001 From: Min RK Date: Fri, 19 Jul 2019 08:56:06 +0200 Subject: [PATCH 12/12] Update repo2docker/buildpacks/conda/__init__.py Co-Authored-By: Tim Head --- repo2docker/buildpacks/conda/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo2docker/buildpacks/conda/__init__.py b/repo2docker/buildpacks/conda/__init__.py index 87865250e..c781f00a0 100644 --- a/repo2docker/buildpacks/conda/__init__.py +++ b/repo2docker/buildpacks/conda/__init__.py @@ -145,7 +145,7 @@ def environment_yaml(self): # check if the env file is empty, if so instantiate an empty dictionary. if env is None: env = {} - # check if the env file provided a dick-like thing not a list or other data structure. + # check if the env file provided a dict-like thing not a list or other data structure. if not isinstance(env, Mapping): raise TypeError( "environment.yml should contain a dictionary. Got %r" % type(env)