Browse Source

Interpret 'build:' as relative to the yml file

* This fix introduces one side-effect: the build parameter is now
validated early, when the service dicionary is first constructed.
That leads to less scary stack traces when the path is not valid.

* The tests for the changes introduced here alter the fixtures
of those (otherwise unrelated) tests that make use of the 'build:'
parameter)

Signed-off-by: Moysés Borges Furtado <[email protected]>
Moysés Borges 10 years ago
parent
commit
8584525e8d

+ 14 - 0
compose/config.py

@@ -171,6 +171,9 @@ def process_container_options(service_dict, working_dir=None):
     if 'volumes' in service_dict:
         service_dict['volumes'] = resolve_host_paths(service_dict['volumes'], working_dir=working_dir)
 
+    if 'build' in service_dict:
+        service_dict['build'] = resolve_build_path(service_dict['build'], working_dir=working_dir)
+
     return service_dict
 
 
@@ -330,6 +333,17 @@ def resolve_host_path(volume, working_dir):
         return container_path
 
 
+def resolve_build_path(build_path, working_dir=None):
+    if working_dir is None:
+        raise Exception("No working_dir passed to resolve_build_path")
+
+    _path = expand_path(working_dir, build_path)
+    if not os.path.exists(_path) or not os.access(_path, os.R_OK):
+        raise ConfigurationError("build path %s either does not exist or is not accessible." % _path)
+    else:
+        return _path
+
+
 def merge_volumes(base, override):
     d = dict_from_volumes(base)
     d.update(dict_from_volumes(override))

+ 3 - 2
docs/yml.md

@@ -29,8 +29,9 @@ image: a4bc65fd
 
 ### build
 
-Path to a directory containing a Dockerfile. This directory is also the
-build context that is sent to the Docker daemon.
+Path to a directory containing a Dockerfile. When the value supplied is a 
+relative path, it is interpreted as relative to the location of the yml file 
+itself. This directory is also the build context that is sent to the Docker daemon.
 
 Compose will build and tag it with a generated name, and use that image thereafter.
 

+ 2 - 0
tests/fixtures/build-ctx/Dockerfile

@@ -0,0 +1,2 @@
+FROM busybox:latest
+CMD echo "success"

+ 2 - 0
tests/fixtures/build-path/docker-compose.yml

@@ -0,0 +1,2 @@
+foo:
+  build: ../build-ctx/

+ 1 - 1
tests/fixtures/dockerfile_with_entrypoint/docker-compose.yml

@@ -1,2 +1,2 @@
 service:
-  build: tests/fixtures/dockerfile_with_entrypoint
+  build: .

+ 1 - 1
tests/fixtures/simple-dockerfile/docker-compose.yml

@@ -1,2 +1,2 @@
 simple:
-  build: tests/fixtures/simple-dockerfile
+  build: .

+ 33 - 0
tests/unit/config_test.py

@@ -383,3 +383,36 @@ class ExtendsTest(unittest.TestCase):
         ]
 
         self.assertEqual(set(dicts[0]['volumes']), set(paths))
+
+
+class BuildPathTest(unittest.TestCase):
+    def setUp(self):
+        self.abs_context_path = os.path.join(os.getcwd(), 'tests/fixtures/build-ctx')
+
+    def test_nonexistent_path(self):
+        options = {'build': 'nonexistent.path'}
+        self.assertRaises(
+            config.ConfigurationError,
+            lambda: config.make_service_dict('foo', options, 'tests/fixtures/build-path'),
+        )
+
+    def test_relative_path(self):
+        relative_build_path = '../build-ctx/'
+        service_dict = config.make_service_dict(
+            'relpath',
+            {'build': relative_build_path},
+            working_dir='tests/fixtures/build-path'
+        )
+        self.assertEquals(service_dict['build'], self.abs_context_path)
+
+    def test_absolute_path(self):
+        service_dict = config.make_service_dict(
+            'abspath',
+            {'build': self.abs_context_path},
+            working_dir='tests/fixtures/build-path'
+        )
+        self.assertEquals(service_dict['build'], self.abs_context_path)
+
+    def test_from_file(self):
+        service_dict = config.load('tests/fixtures/build-path/docker-compose.yml')
+        self.assertEquals(service_dict, [{'name': 'foo', 'build': self.abs_context_path}])