diff --git a/CHANGELOG.md b/CHANGELOG.md index 3653a434..f14bc99e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,6 @@ Change log version requires to be used with Docker Engine 1.13 or above and is specifically designed to work with the `docker stack` commands. - - Added support for the `stop_grace_period` option in service definitions. - #### Compose file version 2.1 and up - Healthcheck configuration can now be done in the service definition using @@ -30,6 +28,10 @@ Change log - Compose now adds identifying labels to networks and volumes it creates +#### Compose file version 2.0 and up + +- Added support for the `stop_grace_period` option in service definitions. + ### Bugfixes - Colored output now works properly on Windows. @@ -40,6 +42,12 @@ Change log - Networks created by Compose are now always made attachable (Compose files v2.1 and up). +- Fixed a bug where falsy values of `COMPOSE_CONVERT_WINDOWS_PATHS` + (`0`, `false`, empty value) were being interpreted as true. + +- Fixed a bug where forward slashes in some .dockerignore patterns weren't + being parsed correctly on Windows + 1.9.0 (2016-11-16) ----------------- diff --git a/Dockerfile.run b/Dockerfile.run index 4e76d64f..de46e35e 100644 --- a/Dockerfile.run +++ b/Dockerfile.run @@ -1,5 +1,6 @@ FROM alpine:3.4 +ARG version RUN apk -U add \ python \ py-pip @@ -7,7 +8,7 @@ RUN apk -U add \ COPY requirements.txt /code/requirements.txt RUN pip install -r /code/requirements.txt -ADD dist/docker-compose-release.tar.gz /code/docker-compose -RUN pip install --no-deps /code/docker-compose/docker-compose-* +COPY dist/docker_compose-${version}-py2.py3-none-any.whl /code/ +RUN pip install --no-deps /code/docker_compose-${version}-py2.py3-none-any.whl ENTRYPOINT ["/usr/bin/docker-compose"] diff --git a/compose/__init__.py b/compose/__init__.py index f1bad0e1..476033b2 100644 --- a/compose/__init__.py +++ b/compose/__init__.py @@ -1,4 +1,4 @@ from __future__ import absolute_import from __future__ import unicode_literals -__version__ = '1.10.0-rc1' +__version__ = '1.10.0-rc2' diff --git a/compose/config/config.py b/compose/config/config.py index fd935591..c11460fa 100644 --- a/compose/config/config.py +++ b/compose/config/config.py @@ -712,7 +712,7 @@ def finalize_service(service_config, service_names, version, environment): if 'volumes' in service_dict: service_dict['volumes'] = [ VolumeSpec.parse( - v, environment.get('COMPOSE_CONVERT_WINDOWS_PATHS') + v, environment.get_boolean('COMPOSE_CONVERT_WINDOWS_PATHS') ) for v in service_dict['volumes'] ] diff --git a/compose/config/config_schema_v2.0.json b/compose/config/config_schema_v2.0.json index 76688916..77494715 100644 --- a/compose/config/config_schema_v2.0.json +++ b/compose/config/config_schema_v2.0.json @@ -192,6 +192,7 @@ "security_opt": {"type": "array", "items": {"type": "string"}, "uniqueItems": true}, "shm_size": {"type": ["number", "string"]}, "stdin_open": {"type": "boolean"}, + "stop_grace_period": {"type": "string", "format": "duration"}, "stop_signal": {"type": "string"}, "tmpfs": {"$ref": "#/definitions/string_or_list"}, "tty": {"type": "boolean"}, diff --git a/compose/config/config_schema_v2.1.json b/compose/config/config_schema_v2.1.json index d0d5233a..97ec5fa1 100644 --- a/compose/config/config_schema_v2.1.json +++ b/compose/config/config_schema_v2.1.json @@ -217,6 +217,7 @@ "shm_size": {"type": ["number", "string"]}, "sysctls": {"$ref": "#/definitions/list_or_dict"}, "stdin_open": {"type": "boolean"}, + "stop_grace_period": {"type": "string", "format": "duration"}, "stop_signal": {"type": "string"}, "tmpfs": {"$ref": "#/definitions/string_or_list"}, "tty": {"type": "boolean"}, diff --git a/compose/config/config_schema_v3.0.json b/compose/config/config_schema_v3.0.json index 8d075d47..df321b20 100644 --- a/compose/config/config_schema_v3.0.json +++ b/compose/config/config_schema_v3.0.json @@ -169,8 +169,8 @@ "shm_size": {"type": ["number", "string"]}, "sysctls": {"$ref": "#/definitions/list_or_dict"}, "stdin_open": {"type": "boolean"}, - "stop_signal": {"type": "string"}, "stop_grace_period": {"type": "string", "format": "duration"}, + "stop_signal": {"type": "string"}, "tmpfs": {"$ref": "#/definitions/string_or_list"}, "tty": {"type": "boolean"}, "ulimits": { @@ -270,7 +270,7 @@ "cpus": {"type": "string"}, "memory": {"type": "string"} }, - "additionaProperties": false + "additionalProperties": false }, "network": { diff --git a/compose/config/environment.py b/compose/config/environment.py index 5d6b5af6..7b926930 100644 --- a/compose/config/environment.py +++ b/compose/config/environment.py @@ -105,3 +105,14 @@ class Environment(dict): super(Environment, self).get(key.upper(), *args, **kwargs) ) return super(Environment, self).get(key, *args, **kwargs) + + def get_boolean(self, key): + # Convert a value to a boolean using "common sense" rules. + # Unset, empty, "0" and "false" (i-case) yield False. + # All other values yield True. + value = self.get(key) + if not value: + return False + if value.lower() in ['0', 'false']: + return False + return True diff --git a/compose/service.py b/compose/service.py index 1338f154..20a40c68 100644 --- a/compose/service.py +++ b/compose/service.py @@ -10,6 +10,7 @@ from operator import attrgetter import enum import six from docker.errors import APIError +from docker.errors import ImageNotFound from docker.errors import NotFound from docker.types import LogConfig from docker.utils.ports import build_port_bindings @@ -323,11 +324,8 @@ class Service(object): def image(self): try: return self.client.inspect_image(self.image_name) - except APIError as e: - if e.response.status_code == 404 and e.explanation and 'No such image' in str(e.explanation): - raise NoSuchImageError("Image '{}' not found".format(self.image_name)) - else: - raise + except ImageNotFound: + raise NoSuchImageError("Image '{}' not found".format(self.image_name)) @property def image_name(self): diff --git a/requirements.txt b/requirements.txt index bae5d9ea..4b7c7b76 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ PyYAML==3.11 backports.ssl-match-hostname==3.5.0.1; python_version < '3' cached-property==1.2.0 colorama==0.3.7 -docker==2.0.0 +docker==2.0.1 dockerpty==0.4.1 docopt==0.6.1 enum34==1.0.4; python_version < '3.4' diff --git a/script/build/image b/script/build/image index bdd98f03..3590ce14 100755 --- a/script/build/image +++ b/script/build/image @@ -11,6 +11,5 @@ TAG=$1 VERSION="$(python setup.py --version)" ./script/build/write-git-sha -python setup.py sdist -cp dist/docker-compose-$VERSION.tar.gz dist/docker-compose-release.tar.gz -docker build -t docker/compose:$TAG -f Dockerfile.run . +python setup.py sdist bdist_wheel +docker build --build-arg version=$VERSION -t docker/compose:$TAG -f Dockerfile.run . diff --git a/script/release/push-release b/script/release/push-release index d5ae3de9..d1a9e3f6 100755 --- a/script/release/push-release +++ b/script/release/push-release @@ -54,13 +54,13 @@ git push $GITHUB_REPO $VERSION echo "Uploading the docker image" docker push docker/compose:$VERSION -echo "Uploading sdist to PyPI" +echo "Uploading package to PyPI" pandoc -f markdown -t rst README.md -o README.rst sed -i -e 's/logo.png?raw=true/https:\/\/github.com\/docker\/compose\/raw\/master\/logo.png?raw=true/' README.rst ./script/build/write-git-sha -python setup.py sdist +python setup.py sdist bdist_wheel if [ "$(command -v twine 2> /dev/null)" ]; then - twine upload ./dist/docker-compose-${VERSION/-/}.tar.gz + twine upload ./dist/docker-compose-${VERSION/-/}.tar.gz ./dist/docker-compose-${VERSION/-/}-py2.py3-none-any.whl else python setup.py upload fi diff --git a/script/run/run.sh b/script/run/run.sh index 5354b5f0..51385a57 100755 --- a/script/run/run.sh +++ b/script/run/run.sh @@ -15,7 +15,7 @@ set -e -VERSION="1.10.0-rc1" +VERSION="1.10.0-rc2" IMAGE="docker/compose:$VERSION" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..3c6e79cf --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal=1 diff --git a/setup.py b/setup.py index 8b4cf709..2f2ba742 100644 --- a/setup.py +++ b/setup.py @@ -4,10 +4,12 @@ from __future__ import absolute_import from __future__ import unicode_literals import codecs +import logging import os import re import sys +import pkg_resources from setuptools import find_packages from setuptools import setup @@ -35,7 +37,7 @@ install_requires = [ 'requests >= 2.6.1, != 2.11.0, < 2.12', 'texttable >= 0.8.1, < 0.9', 'websocket-client >= 0.32.0, < 1.0', - 'docker >= 2.0.0, < 3.0', + 'docker >= 2.0.1, < 3.0', 'dockerpty >= 0.4.1, < 0.5', 'six >= 1.3.0, < 2', 'jsonschema >= 2.5.1, < 3', @@ -49,7 +51,27 @@ tests_require = [ if sys.version_info[:2] < (3, 4): tests_require.append('mock >= 1.0.1') - install_requires.append('enum34 >= 1.0.4, < 2') + +extras_require = { + ':python_version < "3.4"': ['enum34 >= 1.0.4, < 2'], + ':python_version < "3.5"': ['backports.ssl_match_hostname >= 3.5'], + ':python_version < "3.3"': ['ipaddress >= 1.0.16'], +} + + +try: + if 'bdist_wheel' not in sys.argv: + for key, value in extras_require.items(): + if key.startswith(':') and pkg_resources.evaluate_marker(key[1:]): + install_requires.extend(value) +except Exception: + logging.getLogger(__name__).exception( + 'Failed to compute platform dependencies. All dependencies will be ' + 'installed as a result.' + ) + for key, value in extras_require.items(): + if key.startswith(':'): + install_requires.extend(value) setup( @@ -63,6 +85,7 @@ setup( include_package_data=True, test_suite='nose.collector', install_requires=install_requires, + extras_require=extras_require, tests_require=tests_require, entry_points=""" [console_scripts] diff --git a/tests/unit/config/environment_test.py b/tests/unit/config/environment_test.py new file mode 100644 index 00000000..20446d2b --- /dev/null +++ b/tests/unit/config/environment_test.py @@ -0,0 +1,40 @@ +# encoding: utf-8 +from __future__ import absolute_import +from __future__ import print_function +from __future__ import unicode_literals + +from compose.config.environment import Environment +from tests import unittest + + +class EnvironmentTest(unittest.TestCase): + def test_get_simple(self): + env = Environment({ + 'FOO': 'bar', + 'BAR': '1', + 'BAZ': '' + }) + + assert env.get('FOO') == 'bar' + assert env.get('BAR') == '1' + assert env.get('BAZ') == '' + + def test_get_undefined(self): + env = Environment({ + 'FOO': 'bar' + }) + assert env.get('FOOBAR') is None + + def test_get_boolean(self): + env = Environment({ + 'FOO': '', + 'BAR': '0', + 'BAZ': 'FALSE', + 'FOOBAR': 'true', + }) + + assert env.get_boolean('FOO') is False + assert env.get_boolean('BAR') is False + assert env.get_boolean('BAZ') is False + assert env.get_boolean('FOOBAR') is True + assert env.get_boolean('UNDEFINED') is False