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.
Files changed (47) hide show
  1. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/PKG-INFO +29 -1
  2. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/README.md +28 -0
  3. mantis_cli-22.1.0/mantis/__init__.py +1 -0
  4. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/managers.py +3 -1
  5. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/PKG-INFO +29 -1
  6. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/SOURCES.txt +1 -0
  7. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/requires.txt +1 -0
  8. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/setup.py +3 -1
  9. mantis_cli-22.1.0/tests/test_build_config.py +143 -0
  10. mantis_cli-22.0.2/mantis/__init__.py +0 -1
  11. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/LICENSE +0 -0
  12. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/MANIFEST.in +0 -0
  13. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/__main__.py +0 -0
  14. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/app.py +0 -0
  15. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/command_line.py +0 -0
  16. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/__init__.py +0 -0
  17. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/compose.py +0 -0
  18. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/configuration.py +0 -0
  19. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/connection.py +0 -0
  20. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/containers.py +0 -0
  21. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/core.py +0 -0
  22. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/django.py +0 -0
  23. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/images.py +0 -0
  24. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/nginx.py +0 -0
  25. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/postgres.py +0 -0
  26. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/secrets.py +0 -0
  27. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/services.py +0 -0
  28. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/commands/volumes.py +0 -0
  29. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/config.py +0 -0
  30. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/cryptography.py +0 -0
  31. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/environment.py +0 -0
  32. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/extensions/__init__.py +0 -0
  33. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/extensions/django.py +0 -0
  34. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/extensions/nginx.py +0 -0
  35. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/extensions/postgres.py +0 -0
  36. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/helpers.py +0 -0
  37. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/mantis.tpl +0 -0
  38. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis/schema.py +0 -0
  39. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/dependency_links.txt +0 -0
  40. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/entry_points.txt +0 -0
  41. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/mantis_cli.egg-info/top_level.txt +0 -0
  42. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/setup.cfg +0 -0
  43. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/tests/__init__.py +0 -0
  44. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/tests/test_command_line.py +0 -0
  45. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/tests/test_config.py +0 -0
  46. {mantis_cli-22.0.2 → mantis_cli-22.1.0}/tests/test_helpers.py +0 -0
  47. {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.2
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.2
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**.
@@ -38,6 +38,7 @@ mantis_cli.egg-info/entry_points.txt
38
38
  mantis_cli.egg-info/requires.txt
39
39
  mantis_cli.egg-info/top_level.txt
40
40
  tests/__init__.py
41
+ tests/test_build_config.py
41
42
  tests/test_command_line.py
42
43
  tests/test_config.py
43
44
  tests/test_helpers.py
@@ -1,4 +1,5 @@
1
1
  cffi
2
+ click
2
3
  cryptography
3
4
  pycryptodome
4
5
  pydantic
@@ -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
- install_requires=['cffi', 'cryptography', 'pycryptodome', 'pydantic', 'PyYAML', 'rich', 'typer'],
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