devservices 1.0.2__tar.gz → 1.0.4__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 (57) hide show
  1. {devservices-1.0.2 → devservices-1.0.4}/PKG-INFO +1 -1
  2. {devservices-1.0.2 → devservices-1.0.4}/README.md +4 -4
  3. {devservices-1.0.2 → devservices-1.0.4}/devservices/commands/check_for_update.py +1 -1
  4. devservices-1.0.4/devservices/commands/down.py +145 -0
  5. {devservices-1.0.2 → devservices-1.0.4}/devservices/commands/list_dependencies.py +6 -1
  6. devservices-1.0.4/devservices/commands/logs.py +120 -0
  7. {devservices-1.0.2 → devservices-1.0.4}/devservices/commands/purge.py +32 -1
  8. {devservices-1.0.2 → devservices-1.0.4}/devservices/commands/status.py +62 -10
  9. devservices-1.0.4/devservices/commands/up.py +204 -0
  10. {devservices-1.0.2 → devservices-1.0.4}/devservices/commands/update.py +2 -2
  11. {devservices-1.0.2 → devservices-1.0.4}/devservices/constants.py +2 -0
  12. {devservices-1.0.2 → devservices-1.0.4}/devservices/exceptions.py +11 -0
  13. {devservices-1.0.2 → devservices-1.0.4}/devservices/main.py +24 -14
  14. {devservices-1.0.2 → devservices-1.0.4}/devservices/utils/dependencies.py +99 -7
  15. {devservices-1.0.2 → devservices-1.0.4}/devservices/utils/docker_compose.py +15 -55
  16. {devservices-1.0.2 → devservices-1.0.4}/devservices.egg-info/PKG-INFO +1 -1
  17. {devservices-1.0.2 → devservices-1.0.4}/devservices.egg-info/SOURCES.txt +4 -4
  18. {devservices-1.0.2 → devservices-1.0.4}/pyproject.toml +1 -1
  19. devservices-1.0.2/tests/commands/test_stop.py → devservices-1.0.4/tests/commands/test_down.py +98 -7
  20. devservices-1.0.4/tests/commands/test_logs.py +150 -0
  21. {devservices-1.0.2 → devservices-1.0.4}/tests/commands/test_purge.py +39 -4
  22. devservices-1.0.4/tests/commands/test_up.py +601 -0
  23. {devservices-1.0.2 → devservices-1.0.4}/tests/utils/test_dependencies.py +538 -0
  24. {devservices-1.0.2 → devservices-1.0.4}/tests/utils/test_docker_compose.py +132 -27
  25. devservices-1.0.2/devservices/commands/logs.py +0 -68
  26. devservices-1.0.2/devservices/commands/start.py +0 -70
  27. devservices-1.0.2/devservices/commands/stop.py +0 -78
  28. devservices-1.0.2/tests/commands/test_logs.py +0 -107
  29. devservices-1.0.2/tests/commands/test_start.py +0 -183
  30. {devservices-1.0.2 → devservices-1.0.4}/LICENSE.md +0 -0
  31. {devservices-1.0.2 → devservices-1.0.4}/devservices/__init__.py +0 -0
  32. {devservices-1.0.2 → devservices-1.0.4}/devservices/commands/__init__.py +0 -0
  33. {devservices-1.0.2 → devservices-1.0.4}/devservices/commands/list_services.py +0 -0
  34. {devservices-1.0.2 → devservices-1.0.4}/devservices/configs/service_config.py +0 -0
  35. {devservices-1.0.2 → devservices-1.0.4}/devservices/utils/__init__.py +0 -0
  36. {devservices-1.0.2 → devservices-1.0.4}/devservices/utils/console.py +0 -0
  37. {devservices-1.0.2 → devservices-1.0.4}/devservices/utils/devenv.py +0 -0
  38. {devservices-1.0.2 → devservices-1.0.4}/devservices/utils/docker.py +0 -0
  39. {devservices-1.0.2 → devservices-1.0.4}/devservices/utils/file_lock.py +0 -0
  40. {devservices-1.0.2 → devservices-1.0.4}/devservices/utils/install_binary.py +0 -0
  41. {devservices-1.0.2 → devservices-1.0.4}/devservices/utils/services.py +0 -0
  42. {devservices-1.0.2 → devservices-1.0.4}/devservices/utils/state.py +0 -0
  43. {devservices-1.0.2 → devservices-1.0.4}/devservices.egg-info/dependency_links.txt +0 -0
  44. {devservices-1.0.2 → devservices-1.0.4}/devservices.egg-info/entry_points.txt +0 -0
  45. {devservices-1.0.2 → devservices-1.0.4}/devservices.egg-info/requires.txt +0 -0
  46. {devservices-1.0.2 → devservices-1.0.4}/devservices.egg-info/top_level.txt +0 -0
  47. {devservices-1.0.2 → devservices-1.0.4}/setup.cfg +0 -0
  48. {devservices-1.0.2 → devservices-1.0.4}/testing/__init__.py +0 -0
  49. {devservices-1.0.2 → devservices-1.0.4}/testing/utils.py +0 -0
  50. {devservices-1.0.2 → devservices-1.0.4}/tests/__init__.py +0 -0
  51. {devservices-1.0.2 → devservices-1.0.4}/tests/commands/test_list_services.py +0 -0
  52. {devservices-1.0.2 → devservices-1.0.4}/tests/commands/test_update.py +0 -0
  53. {devservices-1.0.2 → devservices-1.0.4}/tests/configs/test_service_config.py +0 -0
  54. {devservices-1.0.2 → devservices-1.0.4}/tests/conftest.py +0 -0
  55. {devservices-1.0.2 → devservices-1.0.4}/tests/utils/test_docker.py +0 -0
  56. {devservices-1.0.2 → devservices-1.0.4}/tests/utils/test_install_binary.py +0 -0
  57. {devservices-1.0.2 → devservices-1.0.4}/tests/utils/test_state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: devservices
3
- Version: 1.0.2
3
+ Version: 1.0.4
4
4
  Requires-Python: >=3.10
5
5
  License-File: LICENSE.md
6
6
  Requires-Dist: pyyaml
@@ -1,6 +1,6 @@
1
1
  # devservices
2
2
 
3
- A standalone cli tool used to manage dependencies for services. It simplifies the process of starting, stopping, and managing services for development purposes.
3
+ A standalone cli tool used to manage dependencies for services. It simplifies the process of managing services for development purposes and bringing services up/down.
4
4
 
5
5
  ## Overview
6
6
 
@@ -11,7 +11,7 @@ A standalone cli tool used to manage dependencies for services. It simplifies th
11
11
  The recommended way to install devservices is through a virtualenv in the requirements.txt.
12
12
 
13
13
  ```
14
- devservices==1.0.2
14
+ devservices==1.0.4
15
15
  ```
16
16
 
17
17
 
@@ -23,8 +23,8 @@ devservices provides several commands to manage your services:
23
23
 
24
24
  NOTE: service-name is an optional parameter. If not provided, devservices will attempt to automatically find a devservices configuration in the current directory in order to proceed.
25
25
 
26
- - `devservices start <service-name>`: Start a service and its dependencies.
27
- - `devservices stop <service-name>`: Stop a service including its dependencies.
26
+ - `devservices up <service-name>`: Bring up a service and its dependencies.
27
+ - `devservices down <service-name>`: Bring down service including its dependencies.
28
28
  - `devservices status <service-name>`: Display the current status of all services, including their dependencies and ports.
29
29
  - `devservices logs <service-name>`: View logs for a specific service.
30
30
  - `devservices list-services`: List all available Sentry services.
@@ -4,7 +4,7 @@ import json
4
4
  from urllib.request import urlopen
5
5
 
6
6
 
7
- def check_for_update(current_version: str) -> str | None:
7
+ def check_for_update() -> str | None:
8
8
  url = "https://api.github.com/repos/getsentry/devservices/releases/latest"
9
9
  with urlopen(url) as response:
10
10
  if response.status == 200:
@@ -0,0 +1,145 @@
1
+ from __future__ import annotations
2
+
3
+ import concurrent.futures
4
+ import os
5
+ import subprocess
6
+ from argparse import _SubParsersAction
7
+ from argparse import ArgumentParser
8
+ from argparse import Namespace
9
+
10
+ from sentry_sdk import capture_exception
11
+
12
+ from devservices.constants import CONFIG_FILE_NAME
13
+ from devservices.constants import DEPENDENCY_CONFIG_VERSION
14
+ from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
15
+ from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
16
+ from devservices.constants import DEVSERVICES_DIR_NAME
17
+ from devservices.constants import DOCKER_COMPOSE_COMMAND_LENGTH
18
+ from devservices.exceptions import ConfigError
19
+ from devservices.exceptions import DependencyError
20
+ from devservices.exceptions import DockerComposeError
21
+ from devservices.exceptions import ServiceNotFoundError
22
+ from devservices.utils.console import Console
23
+ from devservices.utils.console import Status
24
+ from devservices.utils.dependencies import get_non_shared_remote_dependencies
25
+ from devservices.utils.dependencies import install_and_verify_dependencies
26
+ from devservices.utils.dependencies import InstalledRemoteDependency
27
+ from devservices.utils.docker_compose import get_docker_compose_commands_to_run
28
+ from devservices.utils.docker_compose import run_cmd
29
+ from devservices.utils.services import find_matching_service
30
+ from devservices.utils.services import Service
31
+ from devservices.utils.state import State
32
+
33
+
34
+ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
35
+ parser = subparsers.add_parser(
36
+ "down", help="Bring down a service and its dependencies"
37
+ )
38
+ parser.add_argument(
39
+ "service_name",
40
+ help="Name of the service to bring down",
41
+ nargs="?",
42
+ default=None,
43
+ )
44
+ parser.add_argument(
45
+ "--debug",
46
+ help="Enable debug mode",
47
+ action="store_true",
48
+ default=False,
49
+ )
50
+ parser.set_defaults(func=down)
51
+
52
+
53
+ def down(args: Namespace) -> None:
54
+ """Bring down a service and its dependencies."""
55
+ console = Console()
56
+ service_name = args.service_name
57
+ try:
58
+ service = find_matching_service(service_name)
59
+ except (ConfigError, ServiceNotFoundError) as e:
60
+ capture_exception(e)
61
+ console.failure(str(e))
62
+ exit(1)
63
+
64
+ modes = service.config.modes
65
+
66
+ state = State()
67
+ started_services = state.get_started_services()
68
+ if service.name not in started_services:
69
+ console.warning(f"{service.name} is not running")
70
+ exit(0)
71
+
72
+ mode = state.get_mode_for_service(service.name) or "default"
73
+ mode_dependencies = modes[mode]
74
+
75
+ with Status(
76
+ lambda: console.warning(f"Stopping {service.name}"),
77
+ lambda: console.success(f"{service.name} stopped"),
78
+ ) as status:
79
+ try:
80
+ remote_dependencies = install_and_verify_dependencies(service, mode=mode)
81
+ except DependencyError as de:
82
+ capture_exception(de)
83
+ status.failure(str(de))
84
+ exit(1)
85
+ remote_dependencies = get_non_shared_remote_dependencies(
86
+ service, remote_dependencies
87
+ )
88
+ try:
89
+ _down(service, remote_dependencies, mode_dependencies, status)
90
+ except DockerComposeError as dce:
91
+ capture_exception(dce)
92
+ status.failure(f"Failed to stop {service.name}: {dce.stderr}")
93
+ exit(1)
94
+
95
+ # TODO: We should factor in healthchecks here before marking service as not running
96
+ state = State()
97
+ state.remove_started_service(service.name)
98
+
99
+
100
+ def _bring_down_dependency(
101
+ cmd: list[str], current_env: dict[str, str], status: Status
102
+ ) -> subprocess.CompletedProcess[str]:
103
+ # TODO: Get rid of these constants, we need a smarter way to determine the containers being brought down
104
+ for dependency in cmd[DOCKER_COMPOSE_COMMAND_LENGTH:]:
105
+ status.info(f"Stopping {dependency}")
106
+ return run_cmd(cmd, current_env)
107
+
108
+
109
+ def _down(
110
+ service: Service,
111
+ remote_dependencies: set[InstalledRemoteDependency],
112
+ mode_dependencies: list[str],
113
+ status: Status,
114
+ ) -> None:
115
+ relative_local_dependency_directory = os.path.relpath(
116
+ os.path.join(DEVSERVICES_DEPENDENCIES_CACHE_DIR, DEPENDENCY_CONFIG_VERSION),
117
+ service.repo_path,
118
+ )
119
+ service_config_file_path = os.path.join(
120
+ service.repo_path, DEVSERVICES_DIR_NAME, CONFIG_FILE_NAME
121
+ )
122
+ # Set the environment variable for the local dependencies directory to be used by docker compose
123
+ current_env = os.environ.copy()
124
+ current_env[
125
+ DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
126
+ ] = relative_local_dependency_directory
127
+ docker_compose_commands = get_docker_compose_commands_to_run(
128
+ service=service,
129
+ remote_dependencies=list(remote_dependencies),
130
+ current_env=current_env,
131
+ command="down",
132
+ options=[],
133
+ service_config_file_path=service_config_file_path,
134
+ mode_dependencies=mode_dependencies,
135
+ )
136
+
137
+ cmd_outputs = []
138
+
139
+ with concurrent.futures.ThreadPoolExecutor() as executor:
140
+ futures = [
141
+ executor.submit(_bring_down_dependency, cmd, current_env, status)
142
+ for cmd in docker_compose_commands
143
+ ]
144
+ for future in concurrent.futures.as_completed(futures):
145
+ cmd_outputs.append(future.result())
@@ -4,6 +4,10 @@ from argparse import _SubParsersAction
4
4
  from argparse import ArgumentParser
5
5
  from argparse import Namespace
6
6
 
7
+ from sentry_sdk import capture_exception
8
+
9
+ from devservices.exceptions import ConfigError
10
+ from devservices.exceptions import ServiceNotFoundError
7
11
  from devservices.utils.console import Console
8
12
  from devservices.utils.services import find_matching_service
9
13
 
@@ -28,7 +32,8 @@ def list_dependencies(args: Namespace) -> None:
28
32
 
29
33
  try:
30
34
  service = find_matching_service(service_name)
31
- except Exception as e:
35
+ except (ConfigError, ServiceNotFoundError) as e:
36
+ capture_exception(e)
32
37
  console.failure(str(e))
33
38
  exit(1)
34
39
 
@@ -0,0 +1,120 @@
1
+ from __future__ import annotations
2
+
3
+ import concurrent.futures
4
+ import os
5
+ import subprocess
6
+ from argparse import _SubParsersAction
7
+ from argparse import ArgumentParser
8
+ from argparse import Namespace
9
+
10
+ from sentry_sdk import capture_exception
11
+
12
+ from devservices.constants import CONFIG_FILE_NAME
13
+ from devservices.constants import DEPENDENCY_CONFIG_VERSION
14
+ from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
15
+ from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
16
+ from devservices.constants import DEVSERVICES_DIR_NAME
17
+ from devservices.constants import MAX_LOG_LINES
18
+ from devservices.exceptions import ConfigError
19
+ from devservices.exceptions import DependencyError
20
+ from devservices.exceptions import DockerComposeError
21
+ from devservices.exceptions import ServiceNotFoundError
22
+ from devservices.utils.console import Console
23
+ from devservices.utils.dependencies import install_and_verify_dependencies
24
+ from devservices.utils.dependencies import InstalledRemoteDependency
25
+ from devservices.utils.docker_compose import get_docker_compose_commands_to_run
26
+ from devservices.utils.docker_compose import run_cmd
27
+ from devservices.utils.services import find_matching_service
28
+ from devservices.utils.services import Service
29
+ from devservices.utils.state import State
30
+
31
+
32
+ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
33
+ parser = subparsers.add_parser("logs", help="View logs for a service")
34
+ parser.add_argument(
35
+ "service_name",
36
+ help="Name of the service to view logs for",
37
+ nargs="?",
38
+ default=None,
39
+ )
40
+ parser.set_defaults(func=logs)
41
+
42
+
43
+ def logs(args: Namespace) -> None:
44
+ """View the logs for a specified service."""
45
+ console = Console()
46
+ service_name = args.service_name
47
+ try:
48
+ service = find_matching_service(service_name)
49
+ except (ConfigError, ServiceNotFoundError) as e:
50
+ capture_exception(e)
51
+ console.failure(str(e))
52
+ exit(1)
53
+
54
+ modes = service.config.modes
55
+ # TODO: allow custom modes to be used
56
+ mode_to_use = "default"
57
+ mode_dependencies = modes[mode_to_use]
58
+
59
+ state = State()
60
+ running_services = state.get_started_services()
61
+ if service.name not in running_services:
62
+ console.warning(f"Service {service.name} is not running")
63
+ return
64
+
65
+ try:
66
+ remote_dependencies = install_and_verify_dependencies(service)
67
+ except DependencyError as de:
68
+ capture_exception(de)
69
+ console.failure(str(de))
70
+ exit(1)
71
+ try:
72
+ logs_output = _logs(service, remote_dependencies, mode_dependencies)
73
+ except DockerComposeError as dce:
74
+ capture_exception(dce)
75
+ console.failure(f"Failed to get logs for {service.name}: {dce.stderr}")
76
+ exit(1)
77
+ for log in logs_output:
78
+ log_stdout: str | None = log.stdout
79
+ if log_stdout is not None:
80
+ console.info(log_stdout)
81
+
82
+
83
+ def _logs(
84
+ service: Service,
85
+ remote_dependencies: set[InstalledRemoteDependency],
86
+ mode_dependencies: list[str],
87
+ ) -> list[subprocess.CompletedProcess[str]]:
88
+ relative_local_dependency_directory = os.path.relpath(
89
+ os.path.join(DEVSERVICES_DEPENDENCIES_CACHE_DIR, DEPENDENCY_CONFIG_VERSION),
90
+ service.repo_path,
91
+ )
92
+ service_config_file_path = os.path.join(
93
+ service.repo_path, DEVSERVICES_DIR_NAME, CONFIG_FILE_NAME
94
+ )
95
+ # Set the environment variable for the local dependencies directory to be used by docker compose
96
+ current_env = os.environ.copy()
97
+ current_env[
98
+ DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
99
+ ] = relative_local_dependency_directory
100
+ docker_compose_commands = get_docker_compose_commands_to_run(
101
+ service=service,
102
+ remote_dependencies=list(remote_dependencies),
103
+ current_env=current_env,
104
+ command="logs",
105
+ options=["-n", MAX_LOG_LINES],
106
+ service_config_file_path=service_config_file_path,
107
+ mode_dependencies=mode_dependencies,
108
+ )
109
+
110
+ cmd_outputs = []
111
+
112
+ with concurrent.futures.ThreadPoolExecutor() as executor:
113
+ futures = [
114
+ executor.submit(run_cmd, cmd, current_env)
115
+ for cmd in docker_compose_commands
116
+ ]
117
+ for future in concurrent.futures.as_completed(futures):
118
+ cmd_outputs.append(future.result())
119
+
120
+ return cmd_outputs
@@ -2,11 +2,13 @@ from __future__ import annotations
2
2
 
3
3
  import os
4
4
  import shutil
5
+ import subprocess
5
6
  from argparse import _SubParsersAction
6
7
  from argparse import ArgumentParser
7
8
  from argparse import Namespace
8
9
 
9
10
  from devservices.constants import DEVSERVICES_CACHE_DIR
11
+ from devservices.constants import DOCKER_NETWORK_NAME
10
12
  from devservices.exceptions import DockerDaemonNotRunningError
11
13
  from devservices.utils.console import Console
12
14
  from devservices.utils.console import Status
@@ -19,9 +21,10 @@ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
19
21
  parser.set_defaults(func=purge)
20
22
 
21
23
 
22
- def purge(args: Namespace) -> None:
24
+ def purge(_args: Namespace) -> None:
23
25
  """Purge the local devservices cache."""
24
26
  console = Console()
27
+
25
28
  # Prompt the user to stop all running containers
26
29
  should_stop_containers = console.confirm(
27
30
  "Warning: Purging stops all running containers and clears devservices state. Would you like to continue?"
@@ -47,4 +50,32 @@ def purge(args: Namespace) -> None:
47
50
  except DockerDaemonNotRunningError:
48
51
  console.warning("The docker daemon not running, no containers to stop")
49
52
 
53
+ console.warning("Removing any devservices networks")
54
+ devservices_networks = (
55
+ subprocess.check_output(
56
+ [
57
+ "docker",
58
+ "network",
59
+ "ls",
60
+ "--filter",
61
+ f"name={DOCKER_NETWORK_NAME}",
62
+ "--format",
63
+ "{{.ID}}",
64
+ ]
65
+ )
66
+ .decode()
67
+ .strip()
68
+ .splitlines()
69
+ )
70
+ if len(devservices_networks) == 0:
71
+ console.success("No devservices networks found to remove")
72
+ for network in devservices_networks:
73
+ subprocess.run(
74
+ ["docker", "network", "rm", network],
75
+ check=True,
76
+ stdout=subprocess.DEVNULL,
77
+ stderr=subprocess.DEVNULL,
78
+ )
79
+ console.success(f"Network {network} removed")
80
+
50
81
  console.success("The local devservices cache and state has been purged")
@@ -1,16 +1,31 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import concurrent.futures
3
4
  import json
5
+ import os
6
+ import subprocess
4
7
  from argparse import _SubParsersAction
5
8
  from argparse import ArgumentParser
6
9
  from argparse import Namespace
7
10
 
11
+ from sentry_sdk import capture_exception
12
+
13
+ from devservices.constants import CONFIG_FILE_NAME
14
+ from devservices.constants import DEPENDENCY_CONFIG_VERSION
15
+ from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
16
+ from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
17
+ from devservices.constants import DEVSERVICES_DIR_NAME
18
+ from devservices.exceptions import ConfigError
8
19
  from devservices.exceptions import DependencyError
9
20
  from devservices.exceptions import DockerComposeError
21
+ from devservices.exceptions import ServiceNotFoundError
10
22
  from devservices.utils.console import Console
11
23
  from devservices.utils.dependencies import install_and_verify_dependencies
12
- from devservices.utils.docker_compose import run_docker_compose_command
24
+ from devservices.utils.dependencies import InstalledRemoteDependency
25
+ from devservices.utils.docker_compose import get_docker_compose_commands_to_run
26
+ from devservices.utils.docker_compose import run_cmd
13
27
  from devservices.utils.services import find_matching_service
28
+ from devservices.utils.services import Service
14
29
 
15
30
  LINE_LENGTH = 40
16
31
 
@@ -61,12 +76,13 @@ def format_status_output(status_json: str) -> str:
61
76
 
62
77
 
63
78
  def status(args: Namespace) -> None:
64
- """Start a service and its dependencies."""
79
+ """Get the status of a specified service."""
65
80
  console = Console()
66
81
  service_name = args.service_name
67
82
  try:
68
83
  service = find_matching_service(service_name)
69
- except Exception as e:
84
+ except (ConfigError, ServiceNotFoundError) as e:
85
+ capture_exception(e)
70
86
  console.failure(str(e))
71
87
  exit(1)
72
88
 
@@ -78,17 +94,13 @@ def status(args: Namespace) -> None:
78
94
  try:
79
95
  remote_dependencies = install_and_verify_dependencies(service)
80
96
  except DependencyError as de:
97
+ capture_exception(de)
81
98
  console.failure(str(de))
82
99
  exit(1)
83
100
  try:
84
- status_json_results = run_docker_compose_command(
85
- service,
86
- "ps",
87
- mode_dependencies,
88
- remote_dependencies,
89
- options=["--format", "json"],
90
- )
101
+ status_json_results = _status(service, remote_dependencies, mode_dependencies)
91
102
  except DockerComposeError as dce:
103
+ capture_exception(dce)
92
104
  console.failure(f"Failed to get status for {service.name}: {dce.stderr}")
93
105
  exit(1)
94
106
 
@@ -104,3 +116,43 @@ def status(args: Namespace) -> None:
104
116
  output += format_status_output(status_json.stdout)
105
117
  output += "=" * LINE_LENGTH
106
118
  console.info(output + "\n")
119
+
120
+
121
+ def _status(
122
+ service: Service,
123
+ remote_dependencies: set[InstalledRemoteDependency],
124
+ mode_dependencies: list[str],
125
+ ) -> list[subprocess.CompletedProcess[str]]:
126
+ relative_local_dependency_directory = os.path.relpath(
127
+ os.path.join(DEVSERVICES_DEPENDENCIES_CACHE_DIR, DEPENDENCY_CONFIG_VERSION),
128
+ service.repo_path,
129
+ )
130
+ service_config_file_path = os.path.join(
131
+ service.repo_path, DEVSERVICES_DIR_NAME, CONFIG_FILE_NAME
132
+ )
133
+ # Set the environment variable for the local dependencies directory to be used by docker compose
134
+ current_env = os.environ.copy()
135
+ current_env[
136
+ DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
137
+ ] = relative_local_dependency_directory
138
+ docker_compose_commands = get_docker_compose_commands_to_run(
139
+ service=service,
140
+ remote_dependencies=list(remote_dependencies),
141
+ current_env=current_env,
142
+ command="ps",
143
+ options=["--format", "json"],
144
+ service_config_file_path=service_config_file_path,
145
+ mode_dependencies=mode_dependencies,
146
+ )
147
+
148
+ cmd_outputs = []
149
+
150
+ with concurrent.futures.ThreadPoolExecutor() as executor:
151
+ futures = [
152
+ executor.submit(run_cmd, cmd, current_env)
153
+ for cmd in docker_compose_commands
154
+ ]
155
+ for future in concurrent.futures.as_completed(futures):
156
+ cmd_outputs.append(future.result())
157
+
158
+ return cmd_outputs