Look in .binder directory for files

If .binder exists, top-level files are ignored. This allows for top-level Dockerfiles for other uses, but binder-specific environment.yml, etc.
pull/83/head
Min RK 2017-09-08 11:41:56 +02:00
rodzic be3ca5802d
commit a7f7038682
1 zmienionych plików z 35 dodań i 25 usunięć

Wyświetl plik

@ -330,6 +330,13 @@ class BuildPack(LoggingConfigurable):
result.components = (self, ) + self.components + (other, ) + other.components result.components = (self, ) + self.components + (other, ) + other.components
return result return result
def binder_path(self, path):
"""Locate a file"""
if os.path.exists('.binder'):
return os.path.join('.binder', path)
else:
return path
def detect(self): def detect(self):
return all([p.detect() for p in self.components]) return all([p.detect() for p in self.components])
@ -457,9 +464,10 @@ class BaseImage(BuildPack):
@default('post_build_scripts') @default('post_build_scripts')
def setup_post_build_scripts(self): def setup_post_build_scripts(self):
if os.path.exists('postBuild'): post_build = self.binder_path('postBuild')
if stat.S_IXUSR & os.stat('postBuild')[stat.ST_MODE]: if os.path.exists(post_build):
return ['postBuild'] if stat.S_IXUSR & os.stat(post_build)[stat.ST_MODE]:
return [post_build]
return [] return []
class PythonBuildPack(BuildPack): class PythonBuildPack(BuildPack):
@ -520,18 +528,18 @@ class PythonBuildPack(BuildPack):
# will be installed in python3 venv. This is less of a # will be installed in python3 venv. This is less of a
# surprise than requiring python2 to be requirements2.txt tho. # surprise than requiring python2 to be requirements2.txt tho.
try: try:
with open('runtime.txt') as f: with open(self.binder_path('runtime.txt')) as f:
runtime = f.read().strip() runtime = f.read().strip()
except FileNotFoundError: except FileNotFoundError:
runtime = 'python-3.5' runtime = 'python-3.5'
if runtime == 'python-2.7': if runtime == 'python-2.7':
requirements_file = 'requirements3.txt' requirements_file = self.binder_path('requirements3.txt')
else: else:
requirements_file = 'requirements.txt' requirements_file = self.binder_path('requirements.txt')
if os.path.exists(requirements_file): if os.path.exists(requirements_file):
return [( return [(
'${NB_USER}', '${NB_USER}',
'pip3 install --no-cache-dir -r {}'.format(requirements_file) 'pip3 install --no-cache-dir -r "{}"'.format(requirements_file)
)] )]
return [] return []
@ -566,18 +574,19 @@ class CondaBuildPack(BuildPack):
@default('assemble_scripts') @default('assemble_scripts')
def setup_assembly(self): def setup_assembly(self):
assembly_scripts = [] assembly_scripts = []
if os.path.exists('environment.yml'): environment_yml = self.binder_path('environment.yml')
if os.path.exists(environment_yml):
assembly_scripts.append(( assembly_scripts.append((
'${NB_USER}', '${NB_USER}',
r""" r"""
conda env update -n root -f environment.yml && \ conda env update -n root -f "{}" && \
conda clean -tipsy conda clean -tipsy
""" """.format(environment_yml)
)) ))
return assembly_scripts return assembly_scripts
def detect(self): def detect(self):
return os.path.exists('environment.yml') and super().detect() return os.path.exists(self.binder_path('environment.yml')) and super().detect()
class Python2BuildPack(BuildPack): class Python2BuildPack(BuildPack):
@ -632,14 +641,13 @@ class Python2BuildPack(BuildPack):
] ]
def detect(self): def detect(self):
if os.path.exists('requirements.txt'): requirements_txt = self.binder_path('requirements.txt')
try: runtime_txt = self.binder_path('runtime.txt')
with open('runtime.txt') as f: if os.path.exists(requirements_txt) and os.path.exists(runtime_txt):
runtime = f.read().strip() with open(runtime_txt) as f:
if runtime == 'python-2.7': runtime = f.read().strip()
return True if runtime == 'python-2.7':
except FileNotFoundError: return True
return False
return False return False
class JuliaBuildPack(BuildPack): class JuliaBuildPack(BuildPack):
@ -685,23 +693,24 @@ class JuliaBuildPack(BuildPack):
@default('assemble_scripts') @default('assemble_scripts')
def setup_assembly(self): def setup_assembly(self):
require = self.binder_path('REQUIRE')
return [( return [(
"${NB_USER}", "${NB_USER}",
# Pre-compile all libraries if they've opted into it. `using {libraryname}` does the # Pre-compile all libraries if they've opted into it. `using {libraryname}` does the
# right thing # right thing
r""" r"""
cat REQUIRE >> ${JULIA_PKGDIR}/v0.6/REQUIRE && \ cat "%s" >> ${JULIA_PKGDIR}/v0.6/REQUIRE && \
julia -e ' \ julia -e ' \
Pkg.resolve(); \ Pkg.resolve(); \
for pkg in keys(Pkg.Reqs.parse("REQUIRE")) \ for pkg in keys(Pkg.Reqs.parse("REQUIRE")) \
eval(:(using $(Symbol(pkg)))) \ eval(:(using $(Symbol(pkg)))) \
end \ end \
' '
""" """ % require
)] )]
def detect(self): def detect(self):
return os.path.exists('REQUIRE') and super() return os.path.exists(self.binder_path('REQUIRE')) and super()
class DockerBuildPack(BuildPack): class DockerBuildPack(BuildPack):
@ -709,17 +718,18 @@ class DockerBuildPack(BuildPack):
dockerfile = "Dockerfile" dockerfile = "Dockerfile"
def detect(self): def detect(self):
return os.path.exists('Dockerfile') return os.path.exists(self.binder_path('Dockerfile'))
def render(self): def render(self):
with open('Dockerfile') as f: Dockerfile = self.binder_path('Dockerfile')
with open(Dockerfile) as f:
return f.read() return f.read()
def build(self, image_spec): def build(self, image_spec):
client = docker.APIClient(version='auto', **docker.utils.kwargs_from_env()) client = docker.APIClient(version='auto', **docker.utils.kwargs_from_env())
for line in client.build( for line in client.build(
path=os.getcwd(), path=os.getcwd(),
dockerfile=self.dockerfile, dockerfile=self.binder_path(self.dockerfile),
tag=image_spec, tag=image_spec,
buildargs={ buildargs={
'JUPYTERHUB_VERSION': self.jupyterhub_version, 'JUPYTERHUB_VERSION': self.jupyterhub_version,