mantis-cli 22.0.2__tar.gz → 22.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/PKG-INFO +29 -1
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/README.md +28 -0
- mantis_cli-22.1.0/mantis/__init__.py +1 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/managers.py +3 -1
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/PKG-INFO +29 -1
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/SOURCES.txt +1 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/requires.txt +1 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/setup.py +3 -1
- mantis_cli-22.1.0/tests/test_build_config.py +143 -0
- mantis_cli-22.0.2/mantis/__init__.py +0 -1
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/LICENSE +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/MANIFEST.in +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/__main__.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/app.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/command_line.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/__init__.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/compose.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/configuration.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/connection.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/containers.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/core.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/django.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/images.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/nginx.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/postgres.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/secrets.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/services.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/volumes.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/config.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/cryptography.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/environment.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/extensions/__init__.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/extensions/django.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/extensions/nginx.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/extensions/postgres.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/helpers.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/mantis.tpl +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/schema.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/dependency_links.txt +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/entry_points.txt +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/top_level.txt +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/setup.cfg +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/tests/__init__.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/tests/test_command_line.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/tests/test_config.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/tests/test_helpers.py +0 -0
- {mantis_cli-22.0.2 → mantis_cli-22.1.0}/tests/test_managers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mantis_cli
|
|
3
|
-
Version: 22.0
|
|
3
|
+
Version: 22.1.0
|
|
4
4
|
Summary: Management command to build and deploy webapps, especially based on Django
|
|
5
5
|
Home-page: https://github.com/PragmaticMates/mantis-cli
|
|
6
6
|
Author: Erik Telepovský
|
|
@@ -75,6 +75,34 @@ TODO:
|
|
|
75
75
|
|
|
76
76
|
See [template file](https://github.com/PragmaticMates/mantis-cli/blob/master/mantis/mantis.tpl) for exact JSON structure.
|
|
77
77
|
|
|
78
|
+
### Build settings
|
|
79
|
+
|
|
80
|
+
With `build.tool` set to `"docker"`, mantis builds each service that declares a `build:` section
|
|
81
|
+
in your compose files, reading `context`, `dockerfile`, `args`, `platform`, `cache_from` and
|
|
82
|
+
`cache_to` from it. Nothing else needs configuring — the compose file stays the single source of
|
|
83
|
+
truth:
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
services:
|
|
87
|
+
backend:
|
|
88
|
+
image: acme/app:production
|
|
89
|
+
build:
|
|
90
|
+
context: ../../../
|
|
91
|
+
dockerfile: ./configs/docker/production/backend/Dockerfile
|
|
92
|
+
cache_from:
|
|
93
|
+
- type=registry,ref=acme/app:production
|
|
94
|
+
cache_to:
|
|
95
|
+
- type=inline
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`cache_from` and `cache_to` become `--cache-from` / `--cache-to` on `docker build`, which is how
|
|
99
|
+
you keep a layer cache across machines that share no local docker state — CI runners, for
|
|
100
|
+
instance. Inline cache stores the metadata in the image being pushed and reads it back from the
|
|
101
|
+
previous one, so no separate cache tag is needed; note that it records only the final image's
|
|
102
|
+
layers, which is enough whenever earlier build stages copy their output forward.
|
|
103
|
+
|
|
104
|
+
With `build.tool` set to `"compose"`, `docker compose build` handles all of this itself.
|
|
105
|
+
|
|
78
106
|
### Connections
|
|
79
107
|
|
|
80
108
|
Mantis supports two connection modes: **multi-environment** and **single connection**.
|
|
@@ -53,6 +53,34 @@ TODO:
|
|
|
53
53
|
|
|
54
54
|
See [template file](https://github.com/PragmaticMates/mantis-cli/blob/master/mantis/mantis.tpl) for exact JSON structure.
|
|
55
55
|
|
|
56
|
+
### Build settings
|
|
57
|
+
|
|
58
|
+
With `build.tool` set to `"docker"`, mantis builds each service that declares a `build:` section
|
|
59
|
+
in your compose files, reading `context`, `dockerfile`, `args`, `platform`, `cache_from` and
|
|
60
|
+
`cache_to` from it. Nothing else needs configuring — the compose file stays the single source of
|
|
61
|
+
truth:
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
services:
|
|
65
|
+
backend:
|
|
66
|
+
image: acme/app:production
|
|
67
|
+
build:
|
|
68
|
+
context: ../../../
|
|
69
|
+
dockerfile: ./configs/docker/production/backend/Dockerfile
|
|
70
|
+
cache_from:
|
|
71
|
+
- type=registry,ref=acme/app:production
|
|
72
|
+
cache_to:
|
|
73
|
+
- type=inline
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`cache_from` and `cache_to` become `--cache-from` / `--cache-to` on `docker build`, which is how
|
|
77
|
+
you keep a layer cache across machines that share no local docker state — CI runners, for
|
|
78
|
+
instance. Inline cache stores the metadata in the image being pushed and reads it back from the
|
|
79
|
+
previous one, so no separate cache tag is needed; note that it records only the final image's
|
|
80
|
+
layers, which is enough whenever earlier build stages copy their output forward.
|
|
81
|
+
|
|
82
|
+
With `build.tool` set to `"compose"`, `docker compose build` handles all of this itself.
|
|
83
|
+
|
|
56
84
|
### Connections
|
|
57
85
|
|
|
58
86
|
Mantis supports two connection modes: **multi-environment** and **single connection**.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = '22.1.0'
|
|
@@ -823,6 +823,7 @@ class BaseManager(AbstractManager):
|
|
|
823
823
|
for service, info in self.services_to_build().items():
|
|
824
824
|
platform = f"--platform={info['platform']}" if info['platform'] != '' else ''
|
|
825
825
|
cache_from = ' '.join([f"--cache-from {cache}" for cache in info['cache_from']]) if info['cache_from'] != [] else ''
|
|
826
|
+
cache_to = ' '.join([f"--cache-to {cache}" for cache in info['cache_to']]) if info['cache_to'] != [] else ''
|
|
826
827
|
args = ' '.join([f"--build-arg {key}={value}" for key, value in info['args'].items()]) if info['args'] != {} else ''
|
|
827
828
|
image = info['image'] if info['image'] != '' else f"{info['project_name']}-{service}".lstrip('-')
|
|
828
829
|
|
|
@@ -831,7 +832,7 @@ class BaseManager(AbstractManager):
|
|
|
831
832
|
dockerfile = str(Path(context) / info['dockerfile'])
|
|
832
833
|
|
|
833
834
|
self.docker(
|
|
834
|
-
f"build {context} {build_args} {args} {platform} {cache_from} -t {image} -f {dockerfile} {params}",
|
|
835
|
+
f"build {context} {build_args} {args} {platform} {cache_from} {cache_to} -t {image} -f {dockerfile} {params}",
|
|
835
836
|
use_connection=False)
|
|
836
837
|
else:
|
|
837
838
|
CLI.error(f'Unknown build tool: {build_tool}. Available tools: {", ".join(available_tools)}')
|
|
@@ -899,6 +900,7 @@ class BaseManager(AbstractManager):
|
|
|
899
900
|
'dockerfile': build.get('dockerfile', 'Dockerfile'),
|
|
900
901
|
'context': build.get('context', '.'),
|
|
901
902
|
'cache_from': build.get('cache_from', []),
|
|
903
|
+
'cache_to': build.get('cache_to', []),
|
|
902
904
|
'args': build.get('args', {}),
|
|
903
905
|
'image': service_config.get('image', ''),
|
|
904
906
|
'platform': service_config.get('platform', '')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mantis-cli
|
|
3
|
-
Version: 22.0
|
|
3
|
+
Version: 22.1.0
|
|
4
4
|
Summary: Management command to build and deploy webapps, especially based on Django
|
|
5
5
|
Home-page: https://github.com/PragmaticMates/mantis-cli
|
|
6
6
|
Author: Erik Telepovský
|
|
@@ -75,6 +75,34 @@ TODO:
|
|
|
75
75
|
|
|
76
76
|
See [template file](https://github.com/PragmaticMates/mantis-cli/blob/master/mantis/mantis.tpl) for exact JSON structure.
|
|
77
77
|
|
|
78
|
+
### Build settings
|
|
79
|
+
|
|
80
|
+
With `build.tool` set to `"docker"`, mantis builds each service that declares a `build:` section
|
|
81
|
+
in your compose files, reading `context`, `dockerfile`, `args`, `platform`, `cache_from` and
|
|
82
|
+
`cache_to` from it. Nothing else needs configuring — the compose file stays the single source of
|
|
83
|
+
truth:
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
services:
|
|
87
|
+
backend:
|
|
88
|
+
image: acme/app:production
|
|
89
|
+
build:
|
|
90
|
+
context: ../../../
|
|
91
|
+
dockerfile: ./configs/docker/production/backend/Dockerfile
|
|
92
|
+
cache_from:
|
|
93
|
+
- type=registry,ref=acme/app:production
|
|
94
|
+
cache_to:
|
|
95
|
+
- type=inline
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`cache_from` and `cache_to` become `--cache-from` / `--cache-to` on `docker build`, which is how
|
|
99
|
+
you keep a layer cache across machines that share no local docker state — CI runners, for
|
|
100
|
+
instance. Inline cache stores the metadata in the image being pushed and reads it back from the
|
|
101
|
+
previous one, so no separate cache tag is needed; note that it records only the final image's
|
|
102
|
+
layers, which is enough whenever earlier build stages copy their output forward.
|
|
103
|
+
|
|
104
|
+
With `build.tool` set to `"compose"`, `docker compose build` handles all of this itself.
|
|
105
|
+
|
|
78
106
|
### Connections
|
|
79
107
|
|
|
80
108
|
Mantis supports two connection modes: **multi-environment** and **single connection**.
|
|
@@ -16,7 +16,9 @@ setup(
|
|
|
16
16
|
url='https://github.com/PragmaticMates/mantis-cli',
|
|
17
17
|
packages=find_packages(),
|
|
18
18
|
include_package_data=True,
|
|
19
|
-
|
|
19
|
+
# click is imported directly by command_line.py. It used to arrive transitively via typer,
|
|
20
|
+
# but typer 0.27.0 dropped that dependency, so a fresh install lost it.
|
|
21
|
+
install_requires=['cffi', 'click', 'cryptography', 'pycryptodome', 'pydantic', 'PyYAML', 'rich', 'typer'],
|
|
20
22
|
entry_points={
|
|
21
23
|
'console_scripts': ['mantis=mantis.command_line:run'],
|
|
22
24
|
},
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"""Tests for build configuration read out of compose files."""
|
|
2
|
+
import textwrap
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from mantis.managers import BaseManager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _manager_with_compose(tmp_path, compose_yaml):
|
|
10
|
+
"""A manager wired to a single compose file, without running __init__."""
|
|
11
|
+
compose_file = tmp_path / "project.yml"
|
|
12
|
+
compose_file.write_text(textwrap.dedent(compose_yaml))
|
|
13
|
+
|
|
14
|
+
manager = BaseManager.__new__(BaseManager)
|
|
15
|
+
manager.compose_files = [str(compose_file)]
|
|
16
|
+
return manager
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class TestServicesToBuild:
|
|
20
|
+
"""Tests for services_to_build parsing of the compose build section."""
|
|
21
|
+
|
|
22
|
+
def test_cache_from_and_cache_to_are_read(self, tmp_path):
|
|
23
|
+
"""Both cache keys come through from the compose build section."""
|
|
24
|
+
manager = _manager_with_compose(tmp_path, """
|
|
25
|
+
services:
|
|
26
|
+
backend:
|
|
27
|
+
image: acme/app:production
|
|
28
|
+
build:
|
|
29
|
+
context: ../..
|
|
30
|
+
dockerfile: ./Dockerfile
|
|
31
|
+
cache_from:
|
|
32
|
+
- type=registry,ref=acme/app:production
|
|
33
|
+
cache_to:
|
|
34
|
+
- type=inline
|
|
35
|
+
""")
|
|
36
|
+
|
|
37
|
+
info = manager.services_to_build()["backend"]
|
|
38
|
+
|
|
39
|
+
assert info["cache_from"] == ["type=registry,ref=acme/app:production"]
|
|
40
|
+
assert info["cache_to"] == ["type=inline"]
|
|
41
|
+
|
|
42
|
+
def test_cache_keys_default_to_empty(self, tmp_path):
|
|
43
|
+
"""A build section naming neither key yields empty lists, not None."""
|
|
44
|
+
manager = _manager_with_compose(tmp_path, """
|
|
45
|
+
services:
|
|
46
|
+
backend:
|
|
47
|
+
image: acme/app:production
|
|
48
|
+
build:
|
|
49
|
+
context: .
|
|
50
|
+
""")
|
|
51
|
+
|
|
52
|
+
info = manager.services_to_build()["backend"]
|
|
53
|
+
|
|
54
|
+
assert info["cache_from"] == []
|
|
55
|
+
assert info["cache_to"] == []
|
|
56
|
+
|
|
57
|
+
def test_multiple_cache_entries(self, tmp_path):
|
|
58
|
+
"""Several sources or destinations are all preserved, in order."""
|
|
59
|
+
manager = _manager_with_compose(tmp_path, """
|
|
60
|
+
services:
|
|
61
|
+
backend:
|
|
62
|
+
image: acme/app:production
|
|
63
|
+
build:
|
|
64
|
+
context: .
|
|
65
|
+
cache_from:
|
|
66
|
+
- type=registry,ref=acme/app:buildcache
|
|
67
|
+
- type=registry,ref=acme/app:production
|
|
68
|
+
cache_to:
|
|
69
|
+
- type=registry,ref=acme/app:buildcache,mode=max
|
|
70
|
+
""")
|
|
71
|
+
|
|
72
|
+
info = manager.services_to_build()["backend"]
|
|
73
|
+
|
|
74
|
+
assert info["cache_from"] == [
|
|
75
|
+
"type=registry,ref=acme/app:buildcache",
|
|
76
|
+
"type=registry,ref=acme/app:production",
|
|
77
|
+
]
|
|
78
|
+
assert info["cache_to"] == ["type=registry,ref=acme/app:buildcache,mode=max"]
|
|
79
|
+
|
|
80
|
+
def test_services_without_build_are_skipped(self, tmp_path):
|
|
81
|
+
"""Only services that declare a build section are returned."""
|
|
82
|
+
manager = _manager_with_compose(tmp_path, """
|
|
83
|
+
services:
|
|
84
|
+
backend:
|
|
85
|
+
image: acme/app:production
|
|
86
|
+
build:
|
|
87
|
+
context: .
|
|
88
|
+
db:
|
|
89
|
+
image: postgres:18
|
|
90
|
+
""")
|
|
91
|
+
|
|
92
|
+
assert list(manager.services_to_build().keys()) == ["backend"]
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class TestBuildCommand:
|
|
96
|
+
"""Tests that the cache flags reach the docker build command."""
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
def _capture(manager):
|
|
100
|
+
"""Record the commands build() would run instead of running them."""
|
|
101
|
+
commands = []
|
|
102
|
+
manager.config = {"build": {"tool": "docker", "args": {}}}
|
|
103
|
+
manager.compose_path = "."
|
|
104
|
+
manager.docker = lambda command, **kwargs: commands.append(command)
|
|
105
|
+
return commands
|
|
106
|
+
|
|
107
|
+
def test_both_cache_flags_are_emitted(self, tmp_path):
|
|
108
|
+
"""cache_from and cache_to each become a flag on docker build."""
|
|
109
|
+
manager = _manager_with_compose(tmp_path, """
|
|
110
|
+
services:
|
|
111
|
+
backend:
|
|
112
|
+
image: acme/app:production
|
|
113
|
+
build:
|
|
114
|
+
context: .
|
|
115
|
+
dockerfile: ./Dockerfile
|
|
116
|
+
cache_from:
|
|
117
|
+
- type=registry,ref=acme/app:production
|
|
118
|
+
cache_to:
|
|
119
|
+
- type=inline
|
|
120
|
+
""")
|
|
121
|
+
commands = self._capture(manager)
|
|
122
|
+
|
|
123
|
+
manager.build()
|
|
124
|
+
|
|
125
|
+
assert len(commands) == 1
|
|
126
|
+
assert "--cache-from type=registry,ref=acme/app:production" in commands[0]
|
|
127
|
+
assert "--cache-to type=inline" in commands[0]
|
|
128
|
+
|
|
129
|
+
def test_absent_cache_keys_emit_no_flags(self, tmp_path):
|
|
130
|
+
"""No cache config means neither flag appears at all."""
|
|
131
|
+
manager = _manager_with_compose(tmp_path, """
|
|
132
|
+
services:
|
|
133
|
+
backend:
|
|
134
|
+
image: acme/app:production
|
|
135
|
+
build:
|
|
136
|
+
context: .
|
|
137
|
+
""")
|
|
138
|
+
commands = self._capture(manager)
|
|
139
|
+
|
|
140
|
+
manager.build()
|
|
141
|
+
|
|
142
|
+
assert "--cache-from" not in commands[0]
|
|
143
|
+
assert "--cache-to" not in commands[0]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
VERSION = '22.0.2'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|