diff --git a/compose/cli/main.py b/compose/cli/main.py index 61461ae7be..9b03ea6763 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -153,9 +153,11 @@ class TopLevelCommand(Command): Options: --no-cache Do not use cache when building the image. + --pull Always attempt to pull a newer version of the image. """ no_cache = bool(options.get('--no-cache', False)) - project.build(service_names=options['SERVICE'], no_cache=no_cache) + pull = bool(options.get('--pull', False)) + project.build(service_names=options['SERVICE'], no_cache=no_cache, pull=pull) def help(self, project, options): """ diff --git a/compose/project.py b/compose/project.py index 9a6e98e01d..f34cc0c349 100644 --- a/compose/project.py +++ b/compose/project.py @@ -257,10 +257,10 @@ class Project(object): for service in self.get_services(service_names): service.restart(**options) - def build(self, service_names=None, no_cache=False): + def build(self, service_names=None, no_cache=False, pull=False): for service in self.get_services(service_names): if service.can_be_built(): - service.build(no_cache) + service.build(no_cache, pull) else: log.info('%s uses an image, skipping' % service.name) diff --git a/compose/service.py b/compose/service.py index 69c29edd5a..cf3b627091 100644 --- a/compose/service.py +++ b/compose/service.py @@ -701,7 +701,7 @@ class Service(object): ipc_mode=options.get('ipc') ) - def build(self, no_cache=False): + def build(self, no_cache=False, pull=False): log.info('Building %s' % self.name) path = self.options['build'] @@ -715,7 +715,7 @@ class Service(object): tag=self.image_name, stream=True, rm=True, - pull=False, + pull=pull, nocache=no_cache, dockerfile=self.options.get('dockerfile', None), ) diff --git a/docs/reference/build.md b/docs/reference/build.md index 77d87def49..c427199fec 100644 --- a/docs/reference/build.md +++ b/docs/reference/build.md @@ -16,6 +16,7 @@ Usage: build [options] [SERVICE...] Options: --no-cache Do not use cache when building the image. +--pull Always attempt to pull a newer version of the image. ``` Services are built once and then tagged as `project_service`, e.g., diff --git a/tests/integration/cli_test.py b/tests/integration/cli_test.py index 4a80d33695..9dadd0368d 100644 --- a/tests/integration/cli_test.py +++ b/tests/integration/cli_test.py @@ -97,6 +97,19 @@ class CLITestCase(DockerClientTestCase): 'Pulling digest (busybox@' 'sha256:38a203e1986cf79639cfb9b2e1d6e773de84002feea2d4eb006b52004ee8502d)...') + @mock.patch('sys.stdout', new_callable=StringIO) + def test_build_plain(self, mock_stdout): + self.command.base_dir = 'tests/fixtures/simple-dockerfile' + self.command.dispatch(['build', 'simple'], None) + + mock_stdout.truncate(0) + cache_indicator = 'Using cache' + pull_indicator = 'Status: Image is up to date for busybox:latest' + self.command.dispatch(['build', 'simple'], None) + output = mock_stdout.getvalue() + self.assertIn(cache_indicator, output) + self.assertNotIn(pull_indicator, output) + @mock.patch('sys.stdout', new_callable=StringIO) def test_build_no_cache(self, mock_stdout): self.command.base_dir = 'tests/fixtures/simple-dockerfile' @@ -104,14 +117,37 @@ class CLITestCase(DockerClientTestCase): mock_stdout.truncate(0) cache_indicator = 'Using cache' - self.command.dispatch(['build', 'simple'], None) - output = mock_stdout.getvalue() - self.assertIn(cache_indicator, output) - - mock_stdout.truncate(0) + pull_indicator = 'Status: Image is up to date for busybox:latest' self.command.dispatch(['build', '--no-cache', 'simple'], None) output = mock_stdout.getvalue() self.assertNotIn(cache_indicator, output) + self.assertNotIn(pull_indicator, output) + + @mock.patch('sys.stdout', new_callable=StringIO) + def test_build_pull(self, mock_stdout): + self.command.base_dir = 'tests/fixtures/simple-dockerfile' + self.command.dispatch(['build', 'simple'], None) + + mock_stdout.truncate(0) + cache_indicator = 'Using cache' + pull_indicator = 'Status: Image is up to date for busybox:latest' + self.command.dispatch(['build', '--pull', 'simple'], None) + output = mock_stdout.getvalue() + self.assertIn(cache_indicator, output) + self.assertIn(pull_indicator, output) + + @mock.patch('sys.stdout', new_callable=StringIO) + def test_build_no_cache_pull(self, mock_stdout): + self.command.base_dir = 'tests/fixtures/simple-dockerfile' + self.command.dispatch(['build', 'simple'], None) + + mock_stdout.truncate(0) + cache_indicator = 'Using cache' + pull_indicator = 'Status: Image is up to date for busybox:latest' + self.command.dispatch(['build', '--no-cache', '--pull', 'simple'], None) + output = mock_stdout.getvalue() + self.assertNotIn(cache_indicator, output) + self.assertIn(pull_indicator, output) def test_up_detached(self): self.command.dispatch(['up', '-d'], None)