mirror of
https://github.com/docker/compose.git
synced 2026-02-15 04:59:24 +08:00
Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5db8d86f12 | ||
|
|
b79b2a24ac | ||
|
|
8ce5e235e4 | ||
|
|
c1dddbe608 | ||
|
|
981b0cd641 | ||
|
|
5ec8af582c | ||
|
|
f5342b600c | ||
|
|
4a26d95de4 | ||
|
|
5b7851f55b | ||
|
|
eaa22df151 | ||
|
|
551f680751 | ||
|
|
3e071ec8d9 | ||
|
|
2a7c06a050 | ||
|
|
d0b7bc3110 | ||
|
|
fe4f16e448 | ||
|
|
1da4301650 | ||
|
|
c594cb3fc3 | ||
|
|
c4eb3a1f47 | ||
|
|
665efeed42 | ||
|
|
89ad637d50 | ||
|
|
6ca2aed7ec | ||
|
|
fc744a0cc9 | ||
|
|
245ede1d75 | ||
|
|
72f7b086d7 | ||
|
|
2f48b6f5e9 | ||
|
|
e6fcde422c | ||
|
|
75b2d7905f | ||
|
|
efa5969086 | ||
|
|
2a4aca7f54 | ||
|
|
9c8f5a5705 | ||
|
|
62bbc5cfe2 |
36
CHANGELOG.md
36
CHANGELOG.md
@@ -1,6 +1,42 @@
|
||||
Change log
|
||||
==========
|
||||
|
||||
1.28.6 (2021-03-23)
|
||||
-------------------
|
||||
|
||||
[List of PRs / issues for this release](https://github.com/docker/compose/milestone/57?closed=1)
|
||||
|
||||
### Bugs
|
||||
|
||||
- Make `--env-file` relative to the current working directory and error out for invalid paths. Environment file paths set
|
||||
with `--env-file` are relative to the current working directory while the default `.env` file is located in the project
|
||||
directory which by default is the base directory of the Compose file.
|
||||
|
||||
- Fix missing service property `storage_opt` by updating the compose schema
|
||||
|
||||
- Fix build `extra_hosts` list format
|
||||
|
||||
- Remove extra error message on `exec`
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
- Add `compose.yml` and `compose.yaml` to default filename list
|
||||
|
||||
1.28.5 (2021-02-25)
|
||||
-------------------
|
||||
|
||||
[List of PRs / issues for this release](https://github.com/docker/compose/milestone/55?closed=1)
|
||||
|
||||
### Bugs
|
||||
|
||||
- Fix OpenSSL version mismatch error when shelling out to the ssh client (via bump to docker-py 4.4.4 which contains the fix)
|
||||
|
||||
- Add missing build flags to the native builder: `platform`, `isolation` and `extra_hosts`
|
||||
|
||||
- Remove info message on native build
|
||||
|
||||
- Avoid fetching logs when service logging driver is set to 'none'
|
||||
|
||||
1.28.4 (2021-02-18)
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = '1.28.4'
|
||||
__version__ = '1.28.6'
|
||||
|
||||
@@ -23,6 +23,7 @@ from ..config import resolve_build_args
|
||||
from ..config.environment import Environment
|
||||
from ..config.serialize import serialize_config
|
||||
from ..config.types import VolumeSpec
|
||||
from ..const import IS_LINUX_PLATFORM
|
||||
from ..const import IS_WINDOWS_PLATFORM
|
||||
from ..errors import StreamParseError
|
||||
from ..metrics.decorator import metrics
|
||||
@@ -78,6 +79,8 @@ def main(): # noqa: C901
|
||||
try:
|
||||
command_func = dispatch()
|
||||
command_func()
|
||||
if not IS_LINUX_PLATFORM and command == 'help':
|
||||
print("\nDocker Compose is now in the Docker CLI, try `docker compose` help")
|
||||
except (KeyboardInterrupt, signals.ShutdownException):
|
||||
exit_with_metrics(command, "Aborting.", status=Status.FAILURE)
|
||||
except (UserError, NoSuchService, ConfigurationError,
|
||||
@@ -98,6 +101,8 @@ def main(): # noqa: C901
|
||||
e.service.name), status=Status.FAILURE)
|
||||
except NoSuchCommand as e:
|
||||
commands = "\n".join(parse_doc_section("commands:", getdoc(e.supercommand)))
|
||||
if not IS_LINUX_PLATFORM:
|
||||
commands += "\n\nDocker Compose is now in the Docker CLI, try `docker compose`"
|
||||
exit_with_metrics(e.command, "No such command: {}\n\n{}".format(e.command, commands))
|
||||
except (errors.ConnectionError, StreamParseError):
|
||||
exit_with_metrics(command, status=Status.FAILURE)
|
||||
@@ -116,6 +121,10 @@ def main(): # noqa: C901
|
||||
code = 0
|
||||
if isinstance(e.code, int):
|
||||
code = e.code
|
||||
|
||||
if not IS_LINUX_PLATFORM and not command:
|
||||
msg += "\n\nDocker Compose is now in the Docker CLI, try `docker compose`"
|
||||
|
||||
exit_with_metrics(command, log_msg=msg, status=status,
|
||||
exit_code=code)
|
||||
|
||||
@@ -128,7 +137,7 @@ def get_filtered_args(args):
|
||||
|
||||
|
||||
def exit_with_metrics(command, log_msg=None, status=Status.SUCCESS, exit_code=1):
|
||||
if log_msg:
|
||||
if log_msg and command != 'exec':
|
||||
if not exit_code:
|
||||
log.info(log_msg)
|
||||
else:
|
||||
@@ -1123,6 +1132,9 @@ class TopLevelCommand:
|
||||
attach_dependencies = options.get('--attach-dependencies')
|
||||
keep_prefix = not options.get('--no-log-prefix')
|
||||
|
||||
if not IS_LINUX_PLATFORM:
|
||||
print('Docker Compose is now in the Docker CLI, try `docker compose up`\n')
|
||||
|
||||
if detached and (cascade_stop or exit_value_from or attach_dependencies):
|
||||
raise UserError(
|
||||
"-d cannot be combined with --abort-on-container-exit or --attach-dependencies.")
|
||||
@@ -1482,7 +1494,7 @@ def log_printer_from_project(
|
||||
keep_prefix=True,
|
||||
):
|
||||
return LogPrinter(
|
||||
containers,
|
||||
[c for c in containers if c.log_driver not in (None, 'none')],
|
||||
build_log_presenters(project.service_names, monochrome, keep_prefix),
|
||||
event_stream or project.events(),
|
||||
cascade_stop=cascade_stop,
|
||||
|
||||
@@ -335,7 +335,6 @@
|
||||
"read_only": {"type": "boolean"},
|
||||
"restart": {"type": "string"},
|
||||
"runtime": {
|
||||
"deprecated": true,
|
||||
"type": "string"
|
||||
},
|
||||
"scale": {
|
||||
@@ -367,6 +366,7 @@
|
||||
"stdin_open": {"type": "boolean"},
|
||||
"stop_grace_period": {"type": "string", "format": "duration"},
|
||||
"stop_signal": {"type": "string"},
|
||||
"storage_opt": {"type": "object"},
|
||||
"tmpfs": {"$ref": "#/definitions/string_or_list"},
|
||||
"tty": {"type": "boolean"},
|
||||
"ulimits": {
|
||||
|
||||
@@ -10,7 +10,11 @@ from operator import attrgetter
|
||||
from operator import itemgetter
|
||||
|
||||
import yaml
|
||||
from cached_property import cached_property
|
||||
|
||||
try:
|
||||
from functools import cached_property
|
||||
except ImportError:
|
||||
from cached_property import cached_property
|
||||
|
||||
from . import types
|
||||
from ..const import COMPOSE_SPEC as VERSION
|
||||
@@ -149,9 +153,14 @@ DOCKER_VALID_URL_PREFIXES = (
|
||||
SUPPORTED_FILENAMES = [
|
||||
'docker-compose.yml',
|
||||
'docker-compose.yaml',
|
||||
'compose.yml',
|
||||
'compose.yaml',
|
||||
]
|
||||
|
||||
DEFAULT_OVERRIDE_FILENAMES = ('docker-compose.override.yml', 'docker-compose.override.yaml')
|
||||
DEFAULT_OVERRIDE_FILENAMES = ('docker-compose.override.yml',
|
||||
'docker-compose.override.yaml',
|
||||
'compose.override.yml',
|
||||
'compose.override.yaml')
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -304,7 +313,16 @@ def find(base_dir, filenames, environment, override_dir=None):
|
||||
if filenames:
|
||||
filenames = [os.path.join(base_dir, f) for f in filenames]
|
||||
else:
|
||||
# search for compose files in the base dir and its parents
|
||||
filenames = get_default_config_files(base_dir)
|
||||
if not filenames and not override_dir:
|
||||
# none found in base_dir and no override_dir defined
|
||||
raise ComposeFileNotFound(SUPPORTED_FILENAMES)
|
||||
if not filenames:
|
||||
# search for compose files in the project directory and its parents
|
||||
filenames = get_default_config_files(override_dir)
|
||||
if not filenames:
|
||||
raise ComposeFileNotFound(SUPPORTED_FILENAMES)
|
||||
|
||||
log.debug("Using configuration files: {}".format(",".join(filenames)))
|
||||
return ConfigDetails(
|
||||
@@ -335,7 +353,7 @@ def get_default_config_files(base_dir):
|
||||
(candidates, path) = find_candidates_in_parent_dirs(SUPPORTED_FILENAMES, base_dir)
|
||||
|
||||
if not candidates:
|
||||
raise ComposeFileNotFound(SUPPORTED_FILENAMES)
|
||||
return None
|
||||
|
||||
winner = candidates[0]
|
||||
|
||||
@@ -556,8 +574,7 @@ def process_config_section(config_file, config, section, environment, interpolat
|
||||
config_file.version,
|
||||
config,
|
||||
section,
|
||||
environment
|
||||
)
|
||||
environment)
|
||||
else:
|
||||
return config
|
||||
|
||||
|
||||
@@ -54,9 +54,10 @@ class Environment(dict):
|
||||
if base_dir is None:
|
||||
return result
|
||||
if env_file:
|
||||
env_file_path = os.path.join(base_dir, env_file)
|
||||
else:
|
||||
env_file_path = os.path.join(base_dir, '.env')
|
||||
env_file_path = os.path.join(os.getcwd(), env_file)
|
||||
return cls(env_vars_from_file(env_file_path))
|
||||
|
||||
env_file_path = os.path.join(base_dir, '.env')
|
||||
try:
|
||||
return cls(env_vars_from_file(env_file_path))
|
||||
except EnvFileNotFound:
|
||||
|
||||
@@ -5,6 +5,7 @@ from .version import ComposeVersion
|
||||
DEFAULT_TIMEOUT = 10
|
||||
HTTP_TIMEOUT = 60
|
||||
IS_WINDOWS_PLATFORM = (sys.platform == "win32")
|
||||
IS_LINUX_PLATFORM = (sys.platform == "linux")
|
||||
LABEL_CONTAINER_NUMBER = 'com.docker.compose.container-number'
|
||||
LABEL_ONE_OFF = 'com.docker.compose.oneoff'
|
||||
LABEL_PROJECT = 'com.docker.compose.project'
|
||||
|
||||
@@ -490,8 +490,6 @@ class Project:
|
||||
log.info('%s uses an image, skipping' % service.name)
|
||||
|
||||
if cli:
|
||||
log.info("Building with native build. Learn about native build in Compose here: "
|
||||
"https://docs.docker.com/go/compose-native-build/")
|
||||
if parallel_build:
|
||||
log.warning("Flag '--parallel' is ignored when building with "
|
||||
"COMPOSE_DOCKER_CLI_BUILD=1")
|
||||
@@ -651,10 +649,6 @@ class Project:
|
||||
override_options=None,
|
||||
):
|
||||
|
||||
if cli:
|
||||
log.info("Building with native build. Learn about native build in Compose here: "
|
||||
"https://docs.docker.com/go/compose-native-build/")
|
||||
|
||||
self.initialize()
|
||||
if not ignore_orphans:
|
||||
self.find_orphan_containers(remove_orphans)
|
||||
|
||||
@@ -1855,7 +1855,7 @@ class _CLIBuilder:
|
||||
Returns:
|
||||
A generator for the build output.
|
||||
"""
|
||||
if dockerfile:
|
||||
if dockerfile and os.path.isdir(path):
|
||||
dockerfile = os.path.join(path, dockerfile)
|
||||
iidfile = tempfile.mktemp()
|
||||
|
||||
@@ -1873,6 +1873,15 @@ class _CLIBuilder:
|
||||
command_builder.add_arg("--tag", tag)
|
||||
command_builder.add_arg("--target", target)
|
||||
command_builder.add_arg("--iidfile", iidfile)
|
||||
command_builder.add_arg("--platform", platform)
|
||||
command_builder.add_arg("--isolation", isolation)
|
||||
|
||||
if extra_hosts:
|
||||
if isinstance(extra_hosts, dict):
|
||||
extra_hosts = ["{}:{}".format(host, ip) for host, ip in extra_hosts.items()]
|
||||
for host in extra_hosts:
|
||||
command_builder.add_arg("--add-host", "{}".format(host))
|
||||
|
||||
args = command_builder.build([path])
|
||||
|
||||
magic_word = "Successfully built "
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
backports.shutil_get_terminal_size==1.0.0
|
||||
cached-property==1.5.1
|
||||
cached-property==1.5.1; python_version < '3.8'
|
||||
certifi==2020.6.20
|
||||
chardet==3.0.4
|
||||
colorama==0.4.3; sys_platform == 'win32'
|
||||
distro==1.5.0
|
||||
docker==4.4.3
|
||||
docker==4.4.4
|
||||
docker-pycreds==0.4.0
|
||||
dockerpty==0.4.1
|
||||
docopt==0.6.2
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
set -e
|
||||
|
||||
VERSION="1.28.4"
|
||||
VERSION="1.28.6"
|
||||
IMAGE="docker/compose:$VERSION"
|
||||
|
||||
|
||||
|
||||
4
setup.py
4
setup.py
@@ -25,14 +25,13 @@ def find_version(*file_paths):
|
||||
|
||||
|
||||
install_requires = [
|
||||
'cached-property >= 1.2.0, < 2',
|
||||
'docopt >= 0.6.1, < 1',
|
||||
'PyYAML >= 3.10, < 6',
|
||||
'requests >= 2.20.0, < 3',
|
||||
'texttable >= 0.9.0, < 2',
|
||||
'websocket-client >= 0.32.0, < 1',
|
||||
'distro >= 1.5.0, < 2',
|
||||
'docker[ssh] >= 4.4.3, < 5',
|
||||
'docker[ssh] >= 4.4.4, < 5',
|
||||
'dockerpty >= 0.4.1, < 1',
|
||||
'jsonschema >= 2.5.1, < 4',
|
||||
'python-dotenv >= 0.13.0, < 1',
|
||||
@@ -50,6 +49,7 @@ if sys.version_info[:2] < (3, 4):
|
||||
|
||||
extras_require = {
|
||||
':python_version < "3.5"': ['backports.ssl_match_hostname >= 3.5, < 4'],
|
||||
':python_version < "3.8"': ['cached-property >= 1.2.0, < 2'],
|
||||
':sys_platform == "win32"': ['colorama >= 0.4, < 1'],
|
||||
'socks': ['PySocks >= 1.5.6, != 1.5.7, < 2'],
|
||||
'tests': tests_require,
|
||||
|
||||
1
tests/fixtures/env-file-override/.env
vendored
Normal file
1
tests/fixtures/env-file-override/.env
vendored
Normal file
@@ -0,0 +1 @@
|
||||
WHEREAMI=default
|
||||
@@ -1,5 +1,6 @@
|
||||
import tempfile
|
||||
|
||||
import pytest
|
||||
from ddt import data
|
||||
from ddt import ddt
|
||||
|
||||
@@ -8,6 +9,7 @@ from ..acceptance.cli_test import dispatch
|
||||
from compose.cli.command import get_project
|
||||
from compose.cli.command import project_from_options
|
||||
from compose.config.environment import Environment
|
||||
from compose.config.errors import EnvFileNotFound
|
||||
from tests.integration.testcases import DockerClientTestCase
|
||||
|
||||
|
||||
@@ -55,13 +57,36 @@ services:
|
||||
class EnvironmentOverrideFileTest(DockerClientTestCase):
|
||||
def test_env_file_override(self):
|
||||
base_dir = 'tests/fixtures/env-file-override'
|
||||
# '--env-file' are relative to the current working dir
|
||||
env = Environment.from_env_file(base_dir, base_dir+'/.env.override')
|
||||
dispatch(base_dir, ['--env-file', '.env.override', 'up'])
|
||||
project = get_project(project_dir=base_dir,
|
||||
config_path=['docker-compose.yml'],
|
||||
environment=Environment.from_env_file(base_dir, '.env.override'),
|
||||
environment=env,
|
||||
override_dir=base_dir)
|
||||
containers = project.containers(stopped=True)
|
||||
assert len(containers) == 1
|
||||
assert "WHEREAMI=override" in containers[0].get('Config.Env')
|
||||
assert "DEFAULT_CONF_LOADED=true" in containers[0].get('Config.Env')
|
||||
dispatch(base_dir, ['--env-file', '.env.override', 'down'], None)
|
||||
|
||||
def test_env_file_not_found_error(self):
|
||||
base_dir = 'tests/fixtures/env-file-override'
|
||||
with pytest.raises(EnvFileNotFound) as excinfo:
|
||||
Environment.from_env_file(base_dir, '.env.override')
|
||||
|
||||
assert "Couldn't find env file" in excinfo.exconly()
|
||||
|
||||
def test_dot_env_file(self):
|
||||
base_dir = 'tests/fixtures/env-file-override'
|
||||
# '.env' is relative to the project_dir (base_dir)
|
||||
env = Environment.from_env_file(base_dir, None)
|
||||
dispatch(base_dir, ['up'])
|
||||
project = get_project(project_dir=base_dir,
|
||||
config_path=['docker-compose.yml'],
|
||||
environment=env,
|
||||
override_dir=base_dir)
|
||||
containers = project.containers(stopped=True)
|
||||
assert len(containers) == 1
|
||||
assert "WHEREAMI=default" in containers[0].get('Config.Env')
|
||||
dispatch(base_dir, ['down'], None)
|
||||
|
||||
@@ -3567,9 +3567,11 @@ class InterpolationTest(unittest.TestCase):
|
||||
@mock.patch.dict(os.environ)
|
||||
def test_config_file_with_options_environment_file(self):
|
||||
project_dir = 'tests/fixtures/default-env-file'
|
||||
# env-file is relative to current working dir
|
||||
env = Environment.from_env_file(project_dir, project_dir + '/.env2')
|
||||
service_dicts = config.load(
|
||||
config.find(
|
||||
project_dir, None, Environment.from_env_file(project_dir, '.env2')
|
||||
project_dir, None, env
|
||||
)
|
||||
).services
|
||||
|
||||
@@ -5233,6 +5235,8 @@ class GetDefaultConfigFilesTestCase(unittest.TestCase):
|
||||
files = [
|
||||
'docker-compose.yml',
|
||||
'docker-compose.yaml',
|
||||
'compose.yml',
|
||||
'compose.yaml',
|
||||
]
|
||||
|
||||
def test_get_config_path_default_file_in_basedir(self):
|
||||
@@ -5266,8 +5270,10 @@ def get_config_filename_for_files(filenames, subdir=None):
|
||||
base_dir = tempfile.mkdtemp(dir=project_dir)
|
||||
else:
|
||||
base_dir = project_dir
|
||||
filename, = config.get_default_config_files(base_dir)
|
||||
return os.path.basename(filename)
|
||||
filenames = config.get_default_config_files(base_dir)
|
||||
if not filenames:
|
||||
raise config.ComposeFileNotFound(config.SUPPORTED_FILENAMES)
|
||||
return os.path.basename(filenames[0])
|
||||
finally:
|
||||
shutil.rmtree(project_dir)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user