Skip to content

Commit

Permalink
Merge pull request #421 from evertrol/add-editable-mode
Browse files Browse the repository at this point in the history
Add an edit-mode option
  • Loading branch information
betatim committed Oct 8, 2018
2 parents d95f7dc + 754edd0 commit a0e4dff
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 0 deletions.
28 changes: 28 additions & 0 deletions docs/source/faq.md
Expand Up @@ -70,3 +70,31 @@ or similar traditional docker command.

Check out the [binder-examples](http://github.com/binder-examples/) GitHub
organization for example repositories you can copy & modify for your own use!

## Can I use repo2docker to edit a local repository within a Docker environment?

Yes: use the `--editable` or `-E` flag (don't confuse it with the `-e`
flag for environment variables), and run repo2docker on a local
repository: `repo2docker -E my-repository/.`.

This builds a Docker container from the files in that repository
(using, for example, a `requirements.txt` file or `Dockerfile`), then
runs that container, while connecting the home directory inside the
container to the local repository outside the container. For example,
in case there is a notebook file (`.ipynb`), this will open in a local
webbrowser, and one can edit it and save it. The resulting notebook is
updated in both the Docker container and the local repository. Once
the container is exited, the changed file will still be in the local
repository.

This allows for easy testing of the container while debugging some
items, as well as using a fully customizable container to edit, for
example, notebooks.

**note**

Editable mode is a convenience option that will mount the repository
to container working directory (usually `/home/$USER`). If you need to
mount to a different location in the container, use the `--volumes`
option instead. Similarly, for a fully customized user Dockerfile,
this option is not guaranteed to work.
9 changes: 9 additions & 0 deletions repo2docker/app.py
Expand Up @@ -342,6 +342,13 @@ def get_argparser(self):
default=[]
)

argparser.add_argument(
'--editable', '-E',
dest='editable',
action='store_true',
help='Use the local repository in edit mode',
)

argparser.add_argument(
'--appendix',
type=str,
Expand Down Expand Up @@ -392,6 +399,8 @@ def initialize(self, argv=None):
self.repo = args.repo
self.ref = None
self.cleanup_checkout = False
if args.editable:
self.volumes[os.path.abspath(args.repo)] = '.'
else:
self.repo_type = 'remote'
self.repo = args.repo
Expand Down
8 changes: 8 additions & 0 deletions tests/conftest.py
Expand Up @@ -68,6 +68,14 @@ def test():
return test


@pytest.fixture()
def run_repo2docker():
def run_test(args):
return make_test_func(args)()
return run_test



class Repo2DockerTest(pytest.Function):
"""A pytest.Item for running repo2docker"""
def __init__(self, name, parent, args):
Expand Down
13 changes: 13 additions & 0 deletions tests/dockerfile/editable/Dockerfile
@@ -0,0 +1,13 @@
FROM python:3.5

RUN pip install --no-cache notebook

CMD "/bin/sh"

ADD change.sh /usr/local/bin/change.sh

ARG NB_UID
ENV HOME /tmp
WORKDIR ${HOME}

USER $NB_UID
19 changes: 19 additions & 0 deletions tests/dockerfile/editable/README.rst
@@ -0,0 +1,19 @@
Docker - Edit mode
------------------

Using the --editable option with a local repository, one can modify a
file or create a new file in the container, and this change is
reflected in the respective host directory. It is essentially a
shortcut for `--mount
type=bind,source=<local-host-repository>,target=.` (where the target
resolves into the container working directory).

This is tested by running the change.sh script inside the container
(using the 'cmd' argument to the Repo2Docker app), which creates a new
file, and then verifying on the host side the new file is created with
the proper contents.

In practice, this can be used to run a notebook from inside a
container (which provides the proper environment), making changes as
necessary, which are then immediately reflected in the host
repository.
6 changes: 6 additions & 0 deletions tests/dockerfile/editable/change.sh
@@ -0,0 +1,6 @@
#!/bin/bash

cat <<EOF > newfile
new contents
EOF
exit 0
56 changes: 56 additions & 0 deletions tests/test_editable.py
@@ -0,0 +1,56 @@
import os
import time
import re
import tempfile
from conftest import make_test_func
from repo2docker.app import Repo2Docker


DIR = os.path.join(os.path.dirname(__file__), 'dockerfile', 'editable')


def test_editable(run_repo2docker):
"""Run a local repository in edit mode. Verify a new file has been
created afterwards"""
newfile = os.path.join(DIR, 'newfile')
try:
# If the file didn't get properly cleaned up last time, we
# need to do that now
os.remove(newfile)
except FileNotFoundError:
pass
argv = ['--editable', DIR, '/usr/local/bin/change.sh']
run_repo2docker(argv)
try:
with open(newfile) as fp:
contents = fp.read()
assert contents == "new contents\n"
finally:
os.remove(newfile)


def test_editable_by_host():
"""Test whether a new file created by the host environment, is
detected in the container"""

app = Repo2Docker()
app.initialize(['--editable', DIR])
app.run = False
app.start() # This just build the image and does not run it.
container = app.start_container()
# give the container a chance to start
time.sleep(1)
try:
with tempfile.NamedTemporaryFile(dir=DIR, prefix='testfile', suffix='.txt'):
status, output = container.exec_run(['sh', '-c', 'ls testfile????????.txt'])
assert status == 0
assert re.match(br'^testfile\w{8}\.txt\n$', output) is not None
# File should be removed in the container as well
status, output = container.exec_run(['sh', '-c', 'ls testfile????????.txt'])
assert status != 1
assert re.match(br'^testfile\w{8}\.txt\n$', output) is None

finally:
# stop the container
container.stop()
app.wait_for_container(container)

0 comments on commit a0e4dff

Please sign in to comment.