diff --git a/compose/cli/command.py b/compose/cli/command.py index c74e585de..4e2722648 100644 --- a/compose/cli/command.py +++ b/compose/cli/command.py @@ -33,7 +33,8 @@ def project_from_options(project_dir, options): verbose=options.get('--verbose'), host=host, tls_config=tls_config_from_options(options), - environment=environment + environment=environment, + override_dir=options.get('--project-directory'), ) @@ -94,10 +95,10 @@ def get_client(environment, verbose=False, version=None, tls_config=None, host=N def get_project(project_dir, config_path=None, project_name=None, verbose=False, - host=None, tls_config=None, environment=None): + host=None, tls_config=None, environment=None, override_dir=None): if not environment: environment = Environment.from_env_file(project_dir) - config_details = config.find(project_dir, config_path, environment) + config_details = config.find(project_dir, config_path, environment, override_dir) project_name = get_project_name( config_details.working_dir, project_name, environment ) diff --git a/compose/cli/main.py b/compose/cli/main.py index 4d31e25e8..84786abc7 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -168,6 +168,8 @@ class TopLevelCommand(object): --skip-hostname-check Don't check the daemon's hostname against the name specified in the client certificate (for example if your docker host is an IP address) + --project-directory PATH Specify an alternate working directory + (default: the path of the compose file) Commands: build Build or rebuild services diff --git a/compose/config/config.py b/compose/config/config.py index c85ffdabb..5d74fc76f 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -235,10 +235,10 @@ class ServiceConfig(namedtuple('_ServiceConfig', 'working_dir filename name conf config) -def find(base_dir, filenames, environment): +def find(base_dir, filenames, environment, override_dir='.'): if filenames == ['-']: return ConfigDetails( - os.getcwd(), + os.path.abspath(override_dir), [ConfigFile(None, yaml.safe_load(sys.stdin))], environment ) @@ -250,7 +250,7 @@ def find(base_dir, filenames, environment): log.debug("Using configuration files: {}".format(",".join(filenames))) return ConfigDetails( - os.path.dirname(filenames[0]), + override_dir or os.path.dirname(filenames[0]), [ConfigFile.from_filename(f) for f in filenames], environment ) diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py index 31853a11b..6a498e250 100644 --- a/tests/acceptance/cli_test.py +++ b/tests/acceptance/cli_test.py @@ -107,6 +107,7 @@ class CLITestCase(DockerClientTestCase): def setUp(self): super(CLITestCase, self).setUp() self.base_dir = 'tests/fixtures/simple-composefile' + self.override_dir = None def tearDown(self): if self.base_dir: @@ -129,7 +130,7 @@ class CLITestCase(DockerClientTestCase): def project(self): # Hack: allow project to be overridden if not hasattr(self, '_project'): - self._project = get_project(self.base_dir) + self._project = get_project(self.base_dir, override_dir=self.override_dir) return self._project def dispatch(self, options, project_options=None, returncode=0): @@ -518,6 +519,23 @@ class CLITestCase(DockerClientTestCase): }, } + def test_build_override_dir(self): + self.base_dir = 'tests/fixtures/build-path-override-dir' + self.override_dir = os.path.abspath('tests/fixtures') + result = self.dispatch([ + '--project-directory', self.override_dir, + 'build']) + + assert 'Successfully built' in result.stdout + + def test_build_override_dir_invalid_path(self): + config_path = os.path.abspath('tests/fixtures/build-path-override-dir/docker-compose.yml') + result = self.dispatch([ + '-f', config_path, + 'build'], returncode=1) + + assert 'does not exist, is not accessible, or is not a valid URL' in result.stderr + def test_create(self): self.dispatch(['create']) service = self.project.get_service('simple') diff --git a/tests/fixtures/build-path-override-dir/docker-compose.yml b/tests/fixtures/build-path-override-dir/docker-compose.yml new file mode 100644 index 000000000..15dbb3e68 --- /dev/null +++ b/tests/fixtures/build-path-override-dir/docker-compose.yml @@ -0,0 +1,2 @@ +foo: + build: ./build-ctx/ diff --git a/tests/unit/config/config_test.py b/tests/unit/config/config_test.py index d7d342afa..1b98a5ece 100644 --- a/tests/unit/config/config_test.py +++ b/tests/unit/config/config_test.py @@ -2875,9 +2875,9 @@ class EnvTest(unittest.TestCase): set([VolumeSpec.parse('/opt/tmp:/opt/host/tmp')])) -def load_from_filename(filename): +def load_from_filename(filename, override_dir=None): return config.load( - config.find('.', [filename], Environment.from_env_file('.')) + config.find('.', [filename], Environment.from_env_file('.'), override_dir=override_dir) ).services @@ -3443,6 +3443,12 @@ class BuildPathTest(unittest.TestCase): service_dict = load_from_filename('tests/fixtures/build-path/docker-compose.yml') self.assertEqual(service_dict, [{'name': 'foo', 'build': {'context': self.abs_context_path}}]) + def test_from_file_override_dir(self): + override_dir = os.path.join(os.getcwd(), 'tests/fixtures/') + service_dict = load_from_filename( + 'tests/fixtures/build-path-override-dir/docker-compose.yml', override_dir=override_dir) + self.assertEquals(service_dict, [{'name': 'foo', 'build': {'context': self.abs_context_path}}]) + def test_valid_url_in_build_path(self): valid_urls = [ 'git://github.com/docker/docker',