devservices 1.0.7__tar.gz → 1.0.8__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.
- {devservices-1.0.7 → devservices-1.0.8}/PKG-INFO +3 -1
- {devservices-1.0.7 → devservices-1.0.8}/README.md +1 -1
- {devservices-1.0.7 → devservices-1.0.8}/devservices/commands/status.py +18 -8
- {devservices-1.0.7 → devservices-1.0.8}/devservices/commands/up.py +2 -3
- {devservices-1.0.7 → devservices-1.0.8}/devservices/configs/service_config.py +1 -1
- {devservices-1.0.7 → devservices-1.0.8}/devservices/constants.py +1 -1
- {devservices-1.0.7 → devservices-1.0.8}/devservices/main.py +0 -9
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/docker.py +1 -1
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/file_lock.py +1 -1
- {devservices-1.0.7 → devservices-1.0.8}/devservices.egg-info/PKG-INFO +3 -1
- {devservices-1.0.7 → devservices-1.0.8}/devservices.egg-info/requires.txt +2 -0
- {devservices-1.0.7 → devservices-1.0.8}/pyproject.toml +3 -1
- {devservices-1.0.7 → devservices-1.0.8}/tests/commands/test_status.py +75 -1
- {devservices-1.0.7 → devservices-1.0.8}/tests/commands/test_up.py +1 -1
- {devservices-1.0.7 → devservices-1.0.8}/LICENSE.md +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/__init__.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/commands/__init__.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/commands/down.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/commands/list_dependencies.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/commands/list_services.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/commands/logs.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/commands/purge.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/commands/update.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/exceptions.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/__init__.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/check_for_update.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/console.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/dependencies.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/devenv.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/docker_compose.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/install_binary.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/services.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices/utils/state.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices.egg-info/SOURCES.txt +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices.egg-info/dependency_links.txt +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices.egg-info/entry_points.txt +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/devservices.egg-info/top_level.txt +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/setup.cfg +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/testing/__init__.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/testing/utils.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/__init__.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/commands/test_down.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/commands/test_list_dependencies.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/commands/test_list_services.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/commands/test_logs.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/commands/test_purge.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/commands/test_update.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/configs/test_service_config.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/conftest.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/utils/test_check_for_update.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/utils/test_dependencies.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/utils/test_docker.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/utils/test_docker_compose.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/utils/test_install_binary.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/utils/test_services.py +0 -0
- {devservices-1.0.7 → devservices-1.0.8}/tests/utils/test_state.py +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: devservices
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Requires-Python: >=3.10
|
|
5
5
|
License-File: LICENSE.md
|
|
6
6
|
Requires-Dist: pyyaml
|
|
7
7
|
Requires-Dist: sentry-devenv
|
|
8
|
+
Requires-Dist: sentry-sdk
|
|
9
|
+
Requires-Dist: packaging
|
|
8
10
|
Provides-Extra: dev
|
|
9
11
|
Requires-Dist: black; extra == "dev"
|
|
10
12
|
Requires-Dist: mypy; extra == "dev"
|
|
@@ -7,6 +7,7 @@ import subprocess
|
|
|
7
7
|
from argparse import _SubParsersAction
|
|
8
8
|
from argparse import ArgumentParser
|
|
9
9
|
from argparse import Namespace
|
|
10
|
+
from collections import namedtuple
|
|
10
11
|
|
|
11
12
|
from sentry_sdk import capture_exception
|
|
12
13
|
|
|
@@ -30,6 +31,9 @@ from devservices.utils.services import Service
|
|
|
30
31
|
LINE_LENGTH = 40
|
|
31
32
|
|
|
32
33
|
|
|
34
|
+
ServiceStatus = namedtuple("ServiceStatus", ["name", "formatted_output"])
|
|
35
|
+
|
|
36
|
+
|
|
33
37
|
def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
34
38
|
parser = subparsers.add_parser("status", help="View status of a service")
|
|
35
39
|
parser.add_argument(
|
|
@@ -41,12 +45,12 @@ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
|
|
|
41
45
|
parser.set_defaults(func=status)
|
|
42
46
|
|
|
43
47
|
|
|
44
|
-
def format_status_output(
|
|
48
|
+
def format_status_output(service_status_json: str) -> list[ServiceStatus]:
|
|
45
49
|
# Docker compose ps is line delimited json, so this constructs this into an array we can use
|
|
46
|
-
service_statuses =
|
|
47
|
-
|
|
48
|
-
output.append("-" * LINE_LENGTH)
|
|
50
|
+
service_statuses = service_status_json.split("\n")[:-1]
|
|
51
|
+
outputs = []
|
|
49
52
|
for service_status in service_statuses:
|
|
53
|
+
output = []
|
|
50
54
|
service = json.loads(service_status)
|
|
51
55
|
name = service["Service"]
|
|
52
56
|
state = service["State"]
|
|
@@ -71,8 +75,9 @@ def format_status_output(status_json: str) -> str:
|
|
|
71
75
|
output.append("No ports exposed")
|
|
72
76
|
|
|
73
77
|
output.append("") # Empty line for readability
|
|
78
|
+
outputs.append(ServiceStatus(name=name, formatted_output="\n".join(output)))
|
|
74
79
|
|
|
75
|
-
return
|
|
80
|
+
return outputs
|
|
76
81
|
|
|
77
82
|
|
|
78
83
|
def status(args: Namespace) -> None:
|
|
@@ -115,10 +120,15 @@ def status(args: Namespace) -> None:
|
|
|
115
120
|
console.warning(f"{service.name} is not running")
|
|
116
121
|
return
|
|
117
122
|
output = f"Service: {service.name}\n\n"
|
|
123
|
+
output += "=" * LINE_LENGTH + "\n"
|
|
124
|
+
formatted_status_outputs = []
|
|
118
125
|
for status_json in status_json_results:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
126
|
+
formatted_status_outputs.extend(format_status_output(status_json.stdout))
|
|
127
|
+
formatted_status_outputs.sort(key=lambda x: x.name)
|
|
128
|
+
for formatted_status_output in formatted_status_outputs:
|
|
129
|
+
output += formatted_status_output[1]
|
|
130
|
+
output += "-" * LINE_LENGTH + "\n"
|
|
131
|
+
console.info(output)
|
|
122
132
|
|
|
123
133
|
|
|
124
134
|
def _status(
|
|
@@ -107,7 +107,6 @@ def up(args: Namespace) -> None:
|
|
|
107
107
|
def _bring_up_dependency(
|
|
108
108
|
cmd: DockerComposeCommand, current_env: dict[str, str], status: Status
|
|
109
109
|
) -> subprocess.CompletedProcess[str]:
|
|
110
|
-
# TODO: Get rid of these constants, we need a smarter way to determine the containers being brought up
|
|
111
110
|
for dependency in cmd.services:
|
|
112
111
|
status.info(f"Starting {dependency}")
|
|
113
112
|
return run_cmd(cmd.full_command, current_env)
|
|
@@ -132,7 +131,6 @@ def _up(
|
|
|
132
131
|
current_env[
|
|
133
132
|
DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
|
|
134
133
|
] = relative_local_dependency_directory
|
|
135
|
-
options = ["-d", "--pull", "always"]
|
|
136
134
|
dependency_graph = construct_dependency_graph(service, modes=modes)
|
|
137
135
|
starting_order = dependency_graph.get_starting_order()
|
|
138
136
|
sorted_remote_dependencies = sorted(
|
|
@@ -143,7 +141,7 @@ def _up(
|
|
|
143
141
|
remote_dependencies=sorted_remote_dependencies,
|
|
144
142
|
current_env=current_env,
|
|
145
143
|
command="up",
|
|
146
|
-
options=
|
|
144
|
+
options=["-d", "--pull", "always"],
|
|
147
145
|
service_config_file_path=service_config_file_path,
|
|
148
146
|
mode_dependencies=mode_dependencies,
|
|
149
147
|
)
|
|
@@ -180,4 +178,5 @@ def _create_devservices_network() -> None:
|
|
|
180
178
|
["docker", "network", "create", "devservices"],
|
|
181
179
|
stdout=subprocess.DEVNULL,
|
|
182
180
|
stderr=subprocess.DEVNULL,
|
|
181
|
+
check=True,
|
|
183
182
|
)
|
|
@@ -68,7 +68,7 @@ def load_service_config_from_file(repo_path: str) -> ServiceConfig:
|
|
|
68
68
|
config_path = os.path.join(repo_path, DEVSERVICES_DIR_NAME, CONFIG_FILE_NAME)
|
|
69
69
|
if not os.path.exists(config_path):
|
|
70
70
|
raise ConfigNotFoundError(f"Config file not found in directory: {config_path}")
|
|
71
|
-
with open(config_path, "r") as stream:
|
|
71
|
+
with open(config_path, "r", encoding="utf-8") as stream:
|
|
72
72
|
try:
|
|
73
73
|
config = yaml.safe_load(stream)
|
|
74
74
|
except yaml.YAMLError as yml_error:
|
|
@@ -26,7 +26,6 @@ from devservices.commands import update
|
|
|
26
26
|
from devservices.constants import LOGGER_NAME
|
|
27
27
|
from devservices.exceptions import DockerComposeInstallationError
|
|
28
28
|
from devservices.exceptions import DockerDaemonNotRunningError
|
|
29
|
-
from devservices.utils.check_for_update import check_for_update
|
|
30
29
|
from devservices.utils.console import Console
|
|
31
30
|
from devservices.utils.docker_compose import check_docker_compose_version
|
|
32
31
|
|
|
@@ -102,14 +101,6 @@ def main() -> None:
|
|
|
102
101
|
else:
|
|
103
102
|
parser.print_help()
|
|
104
103
|
|
|
105
|
-
if args.command != "update" and os.environ.get("CI") != "true":
|
|
106
|
-
newest_version = check_for_update()
|
|
107
|
-
if newest_version != current_version:
|
|
108
|
-
console.warning(
|
|
109
|
-
f"WARNING: A new version of devservices is available: {newest_version}"
|
|
110
|
-
)
|
|
111
|
-
console.warning('To update, run: "devservices update"')
|
|
112
|
-
|
|
113
104
|
|
|
114
105
|
if __name__ == "__main__":
|
|
115
106
|
main()
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: devservices
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
4
4
|
Requires-Python: >=3.10
|
|
5
5
|
License-File: LICENSE.md
|
|
6
6
|
Requires-Dist: pyyaml
|
|
7
7
|
Requires-Dist: sentry-devenv
|
|
8
|
+
Requires-Dist: sentry-sdk
|
|
9
|
+
Requires-Dist: packaging
|
|
8
10
|
Provides-Extra: dev
|
|
9
11
|
Requires-Dist: black; extra == "dev"
|
|
10
12
|
Requires-Dist: mypy; extra == "dev"
|
|
@@ -4,12 +4,14 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devservices"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.8"
|
|
8
8
|
# 3.10 is just for internal pypi compat
|
|
9
9
|
requires-python = ">=3.10"
|
|
10
10
|
dependencies = [
|
|
11
11
|
"pyyaml",
|
|
12
12
|
"sentry-devenv",
|
|
13
|
+
"sentry-sdk",
|
|
14
|
+
"packaging",
|
|
13
15
|
]
|
|
14
16
|
|
|
15
17
|
[project.optional-dependencies]
|
|
@@ -8,6 +8,7 @@ from unittest import mock
|
|
|
8
8
|
import pytest
|
|
9
9
|
|
|
10
10
|
from devservices.commands.status import status
|
|
11
|
+
from devservices.configs.service_config import Dependency
|
|
11
12
|
from devservices.configs.service_config import ServiceConfig
|
|
12
13
|
from devservices.exceptions import DependencyError
|
|
13
14
|
from devservices.exceptions import ServiceNotFoundError
|
|
@@ -156,7 +157,7 @@ def test_status_service_running(
|
|
|
156
157
|
assert (
|
|
157
158
|
"""Service: test-service
|
|
158
159
|
|
|
159
|
-
|
|
160
|
+
========================================
|
|
160
161
|
test-service
|
|
161
162
|
Container: test-container
|
|
162
163
|
Status: running
|
|
@@ -164,7 +165,80 @@ Health: healthy
|
|
|
164
165
|
Uptime: 2 days ago
|
|
165
166
|
Ports:
|
|
166
167
|
http://localhost:8080:8080 -> 8080/tcp
|
|
168
|
+
----------------------------------------
|
|
169
|
+
|
|
170
|
+
"""
|
|
171
|
+
== captured.out
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@mock.patch("devservices.commands.status._status")
|
|
176
|
+
@mock.patch("devservices.commands.status.find_matching_service")
|
|
177
|
+
@mock.patch("devservices.commands.status.install_and_verify_dependencies")
|
|
178
|
+
def test_status_services_running_sorted_order(
|
|
179
|
+
mock_install_and_verify_dependencies: mock.Mock,
|
|
180
|
+
mock_find_matching_service: mock.Mock,
|
|
181
|
+
mock_status: mock.Mock,
|
|
182
|
+
capsys: pytest.CaptureFixture[str],
|
|
183
|
+
tmp_path: Path,
|
|
184
|
+
) -> None:
|
|
185
|
+
args = Namespace(service_name="test-service")
|
|
186
|
+
service = Service(
|
|
187
|
+
name="test-service",
|
|
188
|
+
repo_path=str(tmp_path),
|
|
189
|
+
config=ServiceConfig(
|
|
190
|
+
version=0.1,
|
|
191
|
+
service_name="test-service",
|
|
192
|
+
dependencies={
|
|
193
|
+
"test-dependency": Dependency(
|
|
194
|
+
description="Test dependency",
|
|
195
|
+
)
|
|
196
|
+
},
|
|
197
|
+
modes={"default": []},
|
|
198
|
+
),
|
|
199
|
+
)
|
|
200
|
+
mock_find_matching_service.return_value = service
|
|
201
|
+
mock_install_and_verify_dependencies.return_value = set()
|
|
202
|
+
mock_status.return_value = [
|
|
203
|
+
subprocess.CompletedProcess(
|
|
204
|
+
args=[],
|
|
205
|
+
returncode=0,
|
|
206
|
+
stdout='{"Service": "test-service", "State": "running", "Name": "test-container", "Health": "healthy", "RunningFor": "2 days ago", "Publishers": [{"URL": "http://localhost:8080", "PublishedPort": 8080, "TargetPort": 8080, "Protocol": "tcp"}]}\n',
|
|
207
|
+
),
|
|
208
|
+
subprocess.CompletedProcess(
|
|
209
|
+
args=[],
|
|
210
|
+
returncode=0,
|
|
211
|
+
stdout='{"Service": "test-dependency", "State": "running", "Name": "test-dependency-container", "Health": "healthy", "RunningFor": "2 days ago", "Publishers": [{"URL": "http://localhost:8081", "PublishedPort": 8081, "TargetPort": 8081, "Protocol": "tcp"}]}\n',
|
|
212
|
+
),
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
status(args)
|
|
216
|
+
|
|
217
|
+
mock_find_matching_service.assert_called_once_with("test-service")
|
|
218
|
+
mock_install_and_verify_dependencies.assert_called_once_with(service)
|
|
219
|
+
mock_status.assert_called_once_with(service, set(), [])
|
|
220
|
+
|
|
221
|
+
captured = capsys.readouterr()
|
|
222
|
+
assert (
|
|
223
|
+
"""Service: test-service
|
|
224
|
+
|
|
167
225
|
========================================
|
|
226
|
+
test-dependency
|
|
227
|
+
Container: test-dependency-container
|
|
228
|
+
Status: running
|
|
229
|
+
Health: healthy
|
|
230
|
+
Uptime: 2 days ago
|
|
231
|
+
Ports:
|
|
232
|
+
http://localhost:8081:8081 -> 8081/tcp
|
|
233
|
+
----------------------------------------
|
|
234
|
+
test-service
|
|
235
|
+
Container: test-container
|
|
236
|
+
Status: running
|
|
237
|
+
Health: healthy
|
|
238
|
+
Uptime: 2 days ago
|
|
239
|
+
Ports:
|
|
240
|
+
http://localhost:8080:8080 -> 8080/tcp
|
|
241
|
+
----------------------------------------
|
|
168
242
|
|
|
169
243
|
"""
|
|
170
244
|
== captured.out
|
|
@@ -457,7 +457,7 @@ def test_up_docker_compose_container_healthcheck_failed(
|
|
|
457
457
|
assert "Starting clickhouse" in captured.out.strip()
|
|
458
458
|
assert "Starting redis" in captured.out.strip()
|
|
459
459
|
assert (
|
|
460
|
-
"Container container1 did not become healthy within
|
|
460
|
+
"Container container1 did not become healthy within 45 seconds."
|
|
461
461
|
in captured.out.strip()
|
|
462
462
|
)
|
|
463
463
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|