devservices 1.2.4__tar.gz → 1.3.1__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 (70) hide show
  1. {devservices-1.2.4 → devservices-1.3.1}/PKG-INFO +1 -7
  2. {devservices-1.2.4 → devservices-1.3.1}/README.md +10 -1
  3. {devservices-1.2.4 → devservices-1.3.1}/devservices/__init__.py +1 -0
  4. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/down.py +13 -12
  5. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/foreground.py +5 -6
  6. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/list_dependencies.py +4 -2
  7. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/logs.py +63 -20
  8. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/reset.py +5 -6
  9. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/serve.py +2 -2
  10. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/status.py +17 -17
  11. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/toggle.py +8 -9
  12. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/up.py +14 -10
  13. {devservices-1.2.4 → devservices-1.3.1}/devservices/configs/service_config.py +19 -3
  14. {devservices-1.2.4 → devservices-1.3.1}/devservices/constants.py +2 -2
  15. {devservices-1.2.4 → devservices-1.3.1}/devservices/main.py +6 -0
  16. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/console.py +6 -4
  17. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/dependencies.py +10 -12
  18. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/docker.py +60 -5
  19. devservices-1.3.1/devservices/utils/services.py +116 -0
  20. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/state.py +16 -0
  21. {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/PKG-INFO +1 -7
  22. {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/SOURCES.txt +0 -1
  23. {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/requires.txt +0 -7
  24. {devservices-1.2.4 → devservices-1.3.1}/pyproject.toml +26 -5
  25. devservices-1.3.1/setup.cfg +4 -0
  26. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_down.py +55 -3
  27. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_list_dependencies.py +6 -4
  28. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_logs.py +116 -9
  29. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_status.py +8 -8
  30. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_toggle.py +13 -5
  31. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_up.py +45 -16
  32. {devservices-1.2.4 → devservices-1.3.1}/tests/configs/test_service_config.py +42 -1
  33. {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_dependencies.py +57 -5
  34. {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_docker.py +119 -3
  35. {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_services.py +27 -1
  36. devservices-1.2.4/devservices/utils/services.py +0 -75
  37. devservices-1.2.4/setup.cfg +0 -9
  38. {devservices-1.2.4 → devservices-1.3.1}/LICENSE.md +0 -0
  39. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/__init__.py +0 -0
  40. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/list_services.py +1 -1
  41. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/purge.py +1 -1
  42. {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/update.py +1 -1
  43. {devservices-1.2.4 → devservices-1.3.1}/devservices/exceptions.py +0 -0
  44. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/__init__.py +0 -0
  45. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/check_for_update.py +0 -0
  46. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/devenv.py +0 -0
  47. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/docker_compose.py +2 -2
  48. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/file_lock.py +0 -0
  49. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/git.py +0 -0
  50. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/install_binary.py +0 -0
  51. {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/supervisor.py +0 -0
  52. {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/dependency_links.txt +0 -0
  53. {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/entry_points.txt +0 -0
  54. {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/top_level.txt +0 -0
  55. {devservices-1.2.4 → devservices-1.3.1}/testing/__init__.py +0 -0
  56. {devservices-1.2.4 → devservices-1.3.1}/testing/utils.py +0 -0
  57. {devservices-1.2.4 → devservices-1.3.1}/tests/__init__.py +0 -0
  58. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_foreground.py +1 -1
  59. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_list_services.py +0 -0
  60. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_purge.py +0 -0
  61. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_reset.py +0 -0
  62. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_serve.py +1 -1
  63. {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_update.py +0 -0
  64. {devservices-1.2.4 → devservices-1.3.1}/tests/conftest.py +0 -0
  65. {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_check_for_update.py +0 -0
  66. {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_docker_compose.py +1 -1
  67. {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_git.py +0 -0
  68. {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_install_binary.py +0 -0
  69. {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_state.py +0 -0
  70. {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_supervisor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devservices
3
- Version: 1.2.4
3
+ Version: 1.3.1
4
4
  Requires-Python: >=3.11
5
5
  License-File: LICENSE.md
6
6
  Requires-Dist: pyyaml
@@ -8,10 +8,4 @@ Requires-Dist: sentry-devenv
8
8
  Requires-Dist: sentry-sdk
9
9
  Requires-Dist: packaging
10
10
  Requires-Dist: supervisor
11
- Provides-Extra: dev
12
- Requires-Dist: black; extra == "dev"
13
- Requires-Dist: mypy; extra == "dev"
14
- Requires-Dist: pre-commit; extra == "dev"
15
- Requires-Dist: pytest; extra == "dev"
16
- Requires-Dist: types-PyYAML; extra == "dev"
17
11
  Dynamic: license-file
@@ -33,7 +33,7 @@ NOTE: service-name is an optional parameter. If not provided, devservices will a
33
33
  The recommended way to install devservices is through a virtualenv in the requirements.txt. Once that is installed and a devservices config file is added, you should be able to run `devservices up` to begin local development.
34
34
 
35
35
  ```
36
- devservices==1.2.4
36
+ devservices==1.3.1
37
37
  ```
38
38
 
39
39
  ### 2. Add devservices config files
@@ -50,6 +50,7 @@ The configuration file is a yaml file that looks like this:
50
50
  # - local: A dependency that is defined in the config file. These dependencies do not have a remote field and must correspond to either a service defined in the 'services' section or a program defined in the 'x-programs' section.
51
51
  # - remote: A dependency that is defined in the devservices directory in a remote repository. These configs are automatically fetched from the remote repository and installed. Any dependency with a remote field will be treated as a remote dependency. Example: https://github.com/getsentry/snuba/blob/59a5258ccbb502827ebc1d3b1bf80c607a3301bf/devservices/config.yml#L8
52
52
  # - modes: A list of modes for the service. Each mode includes a list of dependencies that are used in that mode.
53
+ # - healthcheck_timeout: Optional. The number of seconds to wait for all containers to become healthy before failing. Defaults to 180.
53
54
  x-sentry-service-config:
54
55
  version: 0.1
55
56
  service_name: example-service
@@ -167,3 +168,11 @@ networks:
167
168
  name: devservices
168
169
  external: true
169
170
  ```
171
+
172
+ ## Dev
173
+
174
+ ```sh
175
+ uv sync
176
+ direnv allow
177
+ pytest -n4
178
+ ```
@@ -1,4 +1,5 @@
1
1
  """
2
2
  DevServices CLI tool for managing Docker Compose services.
3
3
  """
4
+
4
5
  from __future__ import annotations
@@ -3,19 +3,19 @@ from __future__ import annotations
3
3
  import concurrent.futures
4
4
  import os
5
5
  import subprocess
6
- from argparse import _SubParsersAction
7
6
  from argparse import ArgumentParser
8
7
  from argparse import Namespace
8
+ from argparse import _SubParsersAction
9
9
 
10
10
  from sentry_sdk import capture_exception
11
11
  from sentry_sdk import logger as sentry_logger
12
12
 
13
13
  from devservices.constants import CONFIG_FILE_NAME
14
14
  from devservices.constants import DEPENDENCY_CONFIG_VERSION
15
- from devservices.constants import DependencyType
16
15
  from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
17
16
  from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
18
17
  from devservices.constants import DEVSERVICES_DIR_NAME
18
+ from devservices.constants import DependencyType
19
19
  from devservices.exceptions import ConfigError
20
20
  from devservices.exceptions import ConfigNotFoundError
21
21
  from devservices.exceptions import DependencyError
@@ -25,16 +25,17 @@ from devservices.exceptions import SupervisorConfigError
25
25
  from devservices.exceptions import SupervisorError
26
26
  from devservices.utils.console import Console
27
27
  from devservices.utils.console import Status
28
- from devservices.utils.dependencies import construct_dependency_graph
29
28
  from devservices.utils.dependencies import DependencyNode
29
+ from devservices.utils.dependencies import InstalledRemoteDependency
30
+ from devservices.utils.dependencies import construct_dependency_graph
30
31
  from devservices.utils.dependencies import get_non_shared_remote_dependencies
31
32
  from devservices.utils.dependencies import install_and_verify_dependencies
32
- from devservices.utils.dependencies import InstalledRemoteDependency
33
33
  from devservices.utils.docker_compose import DockerComposeCommand
34
34
  from devservices.utils.docker_compose import get_docker_compose_commands_to_run
35
35
  from devservices.utils.docker_compose import run_cmd
36
- from devservices.utils.services import find_matching_service
37
36
  from devservices.utils.services import Service
37
+ from devservices.utils.services import find_matching_service
38
+ from devservices.utils.services import get_active_service_names
38
39
  from devservices.utils.state import ServiceRuntime
39
40
  from devservices.utils.state import State
40
41
  from devservices.utils.state import StateTables
@@ -71,7 +72,9 @@ def down(args: Namespace) -> None:
71
72
  console = Console()
72
73
  service_name = args.service_name
73
74
  try:
74
- service = find_matching_service(service_name)
75
+ service = find_matching_service(
76
+ service_name, config_path=getattr(args, "config", None)
77
+ )
75
78
  except ConfigNotFoundError as e:
76
79
  capture_exception(e, level="info")
77
80
  console.failure(
@@ -90,9 +93,7 @@ def down(args: Namespace) -> None:
90
93
  exclude_local = getattr(args, "exclude_local", False)
91
94
 
92
95
  state = State()
93
- starting_services = set(state.get_service_entries(StateTables.STARTING_SERVICES))
94
- started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
95
- active_services = starting_services.union(started_services)
96
+ active_services = get_active_service_names(clean_stale_entries=True)
96
97
  if service.name not in active_services:
97
98
  console.warning(f"{service.name} is not running")
98
99
  return # Since exit(0) is captured as an internal_error by sentry
@@ -232,9 +233,9 @@ def bring_down_service(
232
233
 
233
234
  # Set the environment variable for the local dependencies directory to be used by docker compose
234
235
  current_env = os.environ.copy()
235
- current_env[
236
- DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
237
- ] = relative_local_dependency_directory
236
+ current_env[DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY] = (
237
+ relative_local_dependency_directory
238
+ )
238
239
  state = State()
239
240
 
240
241
  # We want to ignore any dependencies that are set to run locally if we are excluding local dependencies
@@ -3,15 +3,15 @@ from __future__ import annotations
3
3
  import os
4
4
  import pty
5
5
  import shlex
6
- from argparse import _SubParsersAction
7
6
  from argparse import ArgumentParser
8
7
  from argparse import Namespace
8
+ from argparse import _SubParsersAction
9
9
 
10
10
  from sentry_sdk import capture_exception
11
11
 
12
12
  from devservices.constants import CONFIG_FILE_NAME
13
- from devservices.constants import DependencyType
14
13
  from devservices.constants import DEVSERVICES_DIR_NAME
14
+ from devservices.constants import DependencyType
15
15
  from devservices.exceptions import ConfigError
16
16
  from devservices.exceptions import ConfigNotFoundError
17
17
  from devservices.exceptions import ServiceNotFoundError
@@ -19,6 +19,7 @@ from devservices.exceptions import SupervisorConfigError
19
19
  from devservices.exceptions import SupervisorProcessError
20
20
  from devservices.utils.console import Console
21
21
  from devservices.utils.services import find_matching_service
22
+ from devservices.utils.services import get_active_service_names
22
23
  from devservices.utils.state import State
23
24
  from devservices.utils.state import StateTables
24
25
  from devservices.utils.supervisor import SupervisorManager
@@ -39,7 +40,7 @@ def foreground(args: Namespace) -> None:
39
40
  console = Console()
40
41
  program_name = args.program_name
41
42
  try:
42
- service = find_matching_service()
43
+ service = find_matching_service(config_path=getattr(args, "config", None))
43
44
  except ConfigNotFoundError as e:
44
45
  capture_exception(e, level="info")
45
46
  console.failure(
@@ -60,9 +61,7 @@ def foreground(args: Namespace) -> None:
60
61
  )
61
62
  return
62
63
  state = State()
63
- starting_services = set(state.get_service_entries(StateTables.STARTING_SERVICES))
64
- started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
65
- active_services = starting_services.union(started_services)
64
+ active_services = get_active_service_names()
66
65
  if service.name not in active_services:
67
66
  console.warning(f"{service.name} is not running")
68
67
  return
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from argparse import _SubParsersAction
4
3
  from argparse import ArgumentParser
5
4
  from argparse import Namespace
5
+ from argparse import _SubParsersAction
6
6
 
7
7
  from sentry_sdk import capture_exception
8
8
 
@@ -32,7 +32,9 @@ def list_dependencies(args: Namespace) -> None:
32
32
  service_name = args.service_name
33
33
 
34
34
  try:
35
- service = find_matching_service(service_name)
35
+ service = find_matching_service(
36
+ service_name, config_path=getattr(args, "config", None)
37
+ )
36
38
  except ConfigNotFoundError as e:
37
39
  capture_exception(e, level="info")
38
40
  console.failure(
@@ -3,19 +3,19 @@ from __future__ import annotations
3
3
  import concurrent.futures
4
4
  import os
5
5
  import subprocess
6
- from argparse import _SubParsersAction
7
6
  from argparse import ArgumentParser
8
7
  from argparse import Namespace
8
+ from argparse import _SubParsersAction
9
9
 
10
10
  from sentry_sdk import capture_exception
11
11
 
12
12
  from devservices.constants import CONFIG_FILE_NAME
13
13
  from devservices.constants import DEPENDENCY_CONFIG_VERSION
14
- from devservices.constants import DependencyType
15
14
  from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
16
15
  from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
17
16
  from devservices.constants import DEVSERVICES_DIR_NAME
18
17
  from devservices.constants import MAX_LOG_LINES
18
+ from devservices.constants import DependencyType
19
19
  from devservices.exceptions import ConfigError
20
20
  from devservices.exceptions import ConfigNotFoundError
21
21
  from devservices.exceptions import DependencyError
@@ -24,12 +24,16 @@ from devservices.exceptions import ServiceNotFoundError
24
24
  from devservices.exceptions import SupervisorConfigError
25
25
  from devservices.exceptions import SupervisorError
26
26
  from devservices.utils.console import Console
27
- from devservices.utils.dependencies import install_and_verify_dependencies
28
27
  from devservices.utils.dependencies import InstalledRemoteDependency
28
+ from devservices.utils.dependencies import install_and_verify_dependencies
29
+ from devservices.utils.docker import get_container_health
30
+ from devservices.utils.docker_compose import DockerComposeCommand
31
+ from devservices.utils.docker_compose import get_container_names_for_project
29
32
  from devservices.utils.docker_compose import get_docker_compose_commands_to_run
30
33
  from devservices.utils.docker_compose import run_cmd
31
- from devservices.utils.services import find_matching_service
32
34
  from devservices.utils.services import Service
35
+ from devservices.utils.services import find_matching_service
36
+ from devservices.utils.services import get_active_service_names
33
37
  from devservices.utils.state import State
34
38
  from devservices.utils.state import StateTables
35
39
  from devservices.utils.supervisor import SupervisorManager
@@ -51,7 +55,9 @@ def logs(args: Namespace) -> None:
51
55
  console = Console()
52
56
  service_name = args.service_name
53
57
  try:
54
- service = find_matching_service(service_name)
58
+ service = find_matching_service(
59
+ service_name, config_path=getattr(args, "config", None)
60
+ )
55
61
  except ConfigNotFoundError as e:
56
62
  capture_exception(e, level="info")
57
63
  console.failure(
@@ -84,9 +90,7 @@ def logs(args: Namespace) -> None:
84
90
  if not mode_dependencies and "default" in modes:
85
91
  mode_dependencies.update(modes["default"])
86
92
 
87
- starting_services = set(state.get_service_entries(StateTables.STARTING_SERVICES))
88
- started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
89
- running_services = starting_services.union(started_services)
93
+ running_services = get_active_service_names()
90
94
  if service.name not in running_services:
91
95
  console.warning(f"Service {service.name} is not running")
92
96
  return
@@ -102,7 +106,9 @@ def logs(args: Namespace) -> None:
102
106
  )
103
107
  exit(1)
104
108
  try:
105
- logs_output = _logs(service, remote_dependencies, list(mode_dependencies))
109
+ logs_output, docker_compose_commands = _logs(
110
+ service, list(remote_dependencies), list(mode_dependencies)
111
+ )
106
112
  except DockerComposeError as dce:
107
113
  capture_exception(dce, level="info")
108
114
  console.failure(f"Failed to get logs for {service.name}: {dce.stderr}")
@@ -128,12 +134,14 @@ def logs(args: Namespace) -> None:
128
134
  console.info(f"=== Logs for supervisor program: {program_name} ===")
129
135
  console.info(log_content)
130
136
 
137
+ _print_health_logs(console, docker_compose_commands)
138
+
131
139
 
132
140
  def _logs(
133
141
  service: Service,
134
- remote_dependencies: set[InstalledRemoteDependency],
142
+ remote_dependencies: list[InstalledRemoteDependency],
135
143
  mode_dependencies: list[str],
136
- ) -> list[subprocess.CompletedProcess[str]]:
144
+ ) -> tuple[list[subprocess.CompletedProcess[str]], list[DockerComposeCommand]]:
137
145
  relative_local_dependency_directory = os.path.relpath(
138
146
  os.path.join(DEVSERVICES_DEPENDENCIES_CACHE_DIR, DEPENDENCY_CONFIG_VERSION),
139
147
  service.repo_path,
@@ -141,23 +149,20 @@ def _logs(
141
149
  service_config_file_path = os.path.join(
142
150
  service.repo_path, DEVSERVICES_DIR_NAME, CONFIG_FILE_NAME
143
151
  )
144
- # Set the environment variable for the local dependencies directory to be used by docker compose
145
152
  current_env = os.environ.copy()
146
- current_env[
147
- DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
148
- ] = relative_local_dependency_directory
153
+ current_env[DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY] = (
154
+ relative_local_dependency_directory
155
+ )
149
156
  docker_compose_commands = get_docker_compose_commands_to_run(
150
157
  service=service,
151
- remote_dependencies=list(remote_dependencies),
158
+ remote_dependencies=remote_dependencies,
152
159
  current_env=current_env,
153
160
  command="logs",
154
- options=["-n", MAX_LOG_LINES],
161
+ options=["--timestamps", "-n", MAX_LOG_LINES],
155
162
  service_config_file_path=service_config_file_path,
156
163
  mode_dependencies=mode_dependencies,
157
164
  )
158
-
159
165
  cmd_outputs = []
160
-
161
166
  with concurrent.futures.ThreadPoolExecutor() as executor:
162
167
  futures = [
163
168
  executor.submit(run_cmd, cmd.full_command, current_env)
@@ -165,8 +170,46 @@ def _logs(
165
170
  ]
166
171
  for future in concurrent.futures.as_completed(futures):
167
172
  cmd_outputs.append(future.result())
173
+ return cmd_outputs, docker_compose_commands
174
+
175
+
176
+ def _print_health_logs(
177
+ console: Console, docker_compose_commands: list[DockerComposeCommand]
178
+ ) -> None:
179
+ container_names: list[str] = []
180
+ for cmd in docker_compose_commands:
181
+ try:
182
+ container_names += [
183
+ c.name
184
+ for c in get_container_names_for_project(
185
+ cmd.project_name, cmd.config_path, cmd.services
186
+ )
187
+ ]
188
+ except Exception:
189
+ continue
190
+
191
+ if not container_names:
192
+ return
193
+
194
+ health_results = get_container_health(container_names)
195
+ if not health_results:
196
+ return
168
197
 
169
- return cmd_outputs
198
+ console.info("=== Container health ===")
199
+ for h in health_results:
200
+ if h.status == "healthy":
201
+ console.success(f" {h.name}: {h.status}")
202
+ elif h.status in ("unhealthy", "starting"):
203
+ console.warning(f" {h.name}: {h.status}")
204
+ else:
205
+ console.info(f" {h.name}: no healthcheck")
206
+
207
+ for h in health_results:
208
+ if not h.log or h.status == "healthy":
209
+ continue
210
+ console.info(f"\n--- {h.name} health check log ---")
211
+ for entry in h.log:
212
+ console.info(f" exit={entry.exit_code} {entry.output}")
170
213
 
171
214
 
172
215
  def _supervisor_logs(
@@ -1,25 +1,26 @@
1
1
  from __future__ import annotations
2
2
 
3
- from argparse import _SubParsersAction
4
3
  from argparse import ArgumentParser
5
4
  from argparse import Namespace
5
+ from argparse import _SubParsersAction
6
6
 
7
7
  from sentry_sdk import capture_exception
8
8
 
9
9
  from devservices.commands.down import down
10
- from devservices.constants import DependencyType
11
10
  from devservices.constants import DEVSERVICES_ORCHESTRATOR_LABEL
11
+ from devservices.constants import DependencyType
12
12
  from devservices.exceptions import DockerDaemonNotRunningError
13
13
  from devservices.exceptions import DockerError
14
14
  from devservices.utils.console import Console
15
15
  from devservices.utils.console import Status
16
- from devservices.utils.dependencies import construct_dependency_graph
17
16
  from devservices.utils.dependencies import DependencyNode
17
+ from devservices.utils.dependencies import construct_dependency_graph
18
18
  from devservices.utils.docker import get_matching_containers
19
19
  from devservices.utils.docker import get_volumes_for_containers
20
20
  from devservices.utils.docker import remove_docker_resources
21
21
  from devservices.utils.docker import stop_containers
22
22
  from devservices.utils.services import find_matching_service
23
+ from devservices.utils.services import get_active_service_names
23
24
  from devservices.utils.state import State
24
25
  from devservices.utils.state import StateTables
25
26
 
@@ -69,9 +70,7 @@ def reset(args: Namespace) -> None:
69
70
  exit(1)
70
71
 
71
72
  state = State()
72
- started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
73
- starting_services = set(state.get_service_entries(StateTables.STARTING_SERVICES))
74
- active_service_names = starting_services.union(started_services)
73
+ active_service_names = get_active_service_names(clean_stale_entries=True)
75
74
 
76
75
  # TODO: We should add threading here to speed up the process
77
76
  for active_service_name in active_service_names:
@@ -3,9 +3,9 @@ from __future__ import annotations
3
3
  import os
4
4
  import pty
5
5
  import shlex
6
- from argparse import _SubParsersAction
7
6
  from argparse import ArgumentParser
8
7
  from argparse import Namespace
8
+ from argparse import _SubParsersAction
9
9
 
10
10
  from sentry_sdk import capture_exception
11
11
 
@@ -35,7 +35,7 @@ def serve(args: Namespace) -> None:
35
35
  console = Console()
36
36
 
37
37
  try:
38
- service = find_matching_service()
38
+ service = find_matching_service(config_path=getattr(args, "config", None))
39
39
  except ConfigNotFoundError as e:
40
40
  console.failure(
41
41
  f"{str(e)}. Please run the command from a directory with a valid devservices configuration."
@@ -4,22 +4,22 @@ import concurrent.futures
4
4
  import json
5
5
  import os
6
6
  import subprocess
7
- from argparse import _SubParsersAction
8
7
  from argparse import ArgumentParser
9
8
  from argparse import Namespace
9
+ from argparse import _SubParsersAction
10
10
  from collections import namedtuple
11
11
  from datetime import timedelta
12
12
  from typing import TypedDict
13
13
 
14
14
  from sentry_sdk import capture_exception
15
15
 
16
- from devservices.constants import Color
17
16
  from devservices.constants import CONFIG_FILE_NAME
18
17
  from devservices.constants import DEPENDENCY_CONFIG_VERSION
19
- from devservices.constants import DependencyType
20
18
  from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
21
19
  from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
22
20
  from devservices.constants import DEVSERVICES_DIR_NAME
21
+ from devservices.constants import Color
22
+ from devservices.constants import DependencyType
23
23
  from devservices.exceptions import ConfigError
24
24
  from devservices.exceptions import ConfigNotFoundError
25
25
  from devservices.exceptions import DependencyError
@@ -27,15 +27,16 @@ from devservices.exceptions import DockerComposeError
27
27
  from devservices.exceptions import ServiceNotFoundError
28
28
  from devservices.exceptions import SupervisorConfigError
29
29
  from devservices.utils.console import Console
30
- from devservices.utils.dependencies import construct_dependency_graph
31
30
  from devservices.utils.dependencies import DependencyGraph
32
31
  from devservices.utils.dependencies import DependencyNode
33
- from devservices.utils.dependencies import install_and_verify_dependencies
34
32
  from devservices.utils.dependencies import InstalledRemoteDependency
33
+ from devservices.utils.dependencies import construct_dependency_graph
34
+ from devservices.utils.dependencies import install_and_verify_dependencies
35
35
  from devservices.utils.docker_compose import get_docker_compose_commands_to_run
36
36
  from devservices.utils.docker_compose import run_cmd
37
- from devservices.utils.services import find_matching_service
38
37
  from devservices.utils.services import Service
38
+ from devservices.utils.services import find_matching_service
39
+ from devservices.utils.services import get_active_service_names
39
40
  from devservices.utils.state import ServiceRuntime
40
41
  from devservices.utils.state import State
41
42
  from devservices.utils.state import StateTables
@@ -80,7 +81,9 @@ def status(args: Namespace) -> None:
80
81
  console = Console()
81
82
  service_name = args.service_name
82
83
  try:
83
- service = find_matching_service(service_name)
84
+ service = find_matching_service(
85
+ service_name, config_path=getattr(args, "config", None)
86
+ )
84
87
  except ConfigNotFoundError as e:
85
88
  capture_exception(e)
86
89
  console.failure(
@@ -95,10 +98,7 @@ def status(args: Namespace) -> None:
95
98
  console.failure(str(e))
96
99
  exit(1)
97
100
 
98
- state = State()
99
- starting_services = set(state.get_service_entries(StateTables.STARTING_SERVICES))
100
- started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
101
- active_services = starting_services.union(started_services)
101
+ active_services = get_active_service_names()
102
102
  if service.name not in active_services:
103
103
  console.warning(f"Status unavailable. {service.name} is not running standalone")
104
104
  return # Since exit(0) is captured as an internal_error by sentry
@@ -182,9 +182,9 @@ def get_status_json_results(
182
182
  )
183
183
  # Set the environment variable for the local dependencies directory to be used by docker compose
184
184
  current_env = os.environ.copy()
185
- current_env[
186
- DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
187
- ] = relative_local_dependency_directory
185
+ current_env[DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY] = (
186
+ relative_local_dependency_directory
187
+ )
188
188
  docker_compose_commands = get_docker_compose_commands_to_run(
189
189
  service=service,
190
190
  remote_dependencies=list(remote_dependencies),
@@ -318,9 +318,9 @@ def parse_docker_compose_status(
318
318
  docker_compose_service_status
319
319
  )
320
320
  compose_service = docker_compose_service_status_json["Service"]
321
- docker_compose_service_to_status[
322
- compose_service
323
- ] = docker_compose_service_status_json
321
+ docker_compose_service_to_status[compose_service] = (
322
+ docker_compose_service_status_json
323
+ )
324
324
 
325
325
  return docker_compose_service_to_status
326
326
 
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from argparse import _SubParsersAction
4
3
  from argparse import ArgumentParser
5
4
  from argparse import Namespace
5
+ from argparse import _SubParsersAction
6
6
 
7
7
  from sentry_sdk import capture_exception
8
8
 
@@ -19,8 +19,9 @@ from devservices.utils.console import Status
19
19
  from devservices.utils.dependencies import construct_dependency_graph
20
20
  from devservices.utils.dependencies import get_non_shared_remote_dependencies
21
21
  from devservices.utils.dependencies import install_and_verify_dependencies
22
- from devservices.utils.services import find_matching_service
23
22
  from devservices.utils.services import Service
23
+ from devservices.utils.services import find_matching_service
24
+ from devservices.utils.services import get_active_service_names
24
25
  from devservices.utils.state import ServiceRuntime
25
26
  from devservices.utils.state import State
26
27
  from devservices.utils.state import StateTables
@@ -52,7 +53,9 @@ def toggle(args: Namespace) -> None:
52
53
  console = Console()
53
54
  service_name = args.service_name
54
55
  try:
55
- service = find_matching_service(service_name)
56
+ service = find_matching_service(
57
+ service_name, config_path=getattr(args, "config", None)
58
+ )
56
59
  except ConfigNotFoundError as e:
57
60
  capture_exception(e, level="info")
58
61
  console.failure(
@@ -97,9 +100,7 @@ def handle_transition_to_local_runtime(service_to_transition: Service) -> None:
97
100
  console = Console()
98
101
  state = State()
99
102
 
100
- starting_services = set(state.get_service_entries(StateTables.STARTING_SERVICES))
101
- started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
102
- active_services = starting_services.union(started_services)
103
+ active_services = get_active_service_names(clean_stale_entries=True)
103
104
 
104
105
  # If the service is already running standalone, we can just update the runtime
105
106
  if service_to_transition.name in active_services:
@@ -158,9 +159,7 @@ def handle_transition_to_containerized_runtime(service: Service) -> None:
158
159
  """Handle the transition to a containerized runtime for a service."""
159
160
  console = Console()
160
161
  state = State()
161
- starting_services = set(state.get_service_entries(StateTables.STARTING_SERVICES))
162
- started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
163
- active_services = starting_services.union(started_services)
162
+ active_services = get_active_service_names(clean_stale_entries=True)
164
163
  if service.name in active_services:
165
164
  console.warning(f"{service.name} is running, please stop it first")
166
165
  return
@@ -3,9 +3,9 @@ from __future__ import annotations
3
3
  import concurrent.futures
4
4
  import os
5
5
  import subprocess
6
- from argparse import _SubParsersAction
7
6
  from argparse import ArgumentParser
8
7
  from argparse import Namespace
8
+ from argparse import _SubParsersAction
9
9
 
10
10
  from sentry_sdk import capture_exception
11
11
  from sentry_sdk import logger as sentry_logger
@@ -14,10 +14,10 @@ from sentry_sdk import start_span
14
14
 
15
15
  from devservices.constants import CONFIG_FILE_NAME
16
16
  from devservices.constants import DEPENDENCY_CONFIG_VERSION
17
- from devservices.constants import DependencyType
18
17
  from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
19
18
  from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
20
19
  from devservices.constants import DEVSERVICES_DIR_NAME
20
+ from devservices.constants import DependencyType
21
21
  from devservices.exceptions import ConfigError
22
22
  from devservices.exceptions import ConfigNotFoundError
23
23
  from devservices.exceptions import ContainerHealthcheckFailedError
@@ -29,17 +29,17 @@ from devservices.exceptions import SupervisorConfigError
29
29
  from devservices.exceptions import SupervisorError
30
30
  from devservices.utils.console import Console
31
31
  from devservices.utils.console import Status
32
- from devservices.utils.dependencies import construct_dependency_graph
33
32
  from devservices.utils.dependencies import DependencyNode
34
- from devservices.utils.dependencies import install_and_verify_dependencies
35
33
  from devservices.utils.dependencies import InstalledRemoteDependency
34
+ from devservices.utils.dependencies import construct_dependency_graph
35
+ from devservices.utils.dependencies import install_and_verify_dependencies
36
36
  from devservices.utils.docker import check_all_containers_healthy
37
37
  from devservices.utils.docker_compose import DockerComposeCommand
38
38
  from devservices.utils.docker_compose import get_container_names_for_project
39
39
  from devservices.utils.docker_compose import get_docker_compose_commands_to_run
40
40
  from devservices.utils.docker_compose import run_cmd
41
- from devservices.utils.services import find_matching_service
42
41
  from devservices.utils.services import Service
42
+ from devservices.utils.services import find_matching_service
43
43
  from devservices.utils.state import ServiceRuntime
44
44
  from devservices.utils.state import State
45
45
  from devservices.utils.state import StateTables
@@ -76,7 +76,9 @@ def up(args: Namespace, existing_status: Status | None = None) -> None:
76
76
  console = Console()
77
77
  service_name = args.service_name
78
78
  try:
79
- service = find_matching_service(service_name)
79
+ service = find_matching_service(
80
+ service_name, config_path=getattr(args, "config", None)
81
+ )
80
82
  except ConfigNotFoundError as e:
81
83
  capture_exception(e, level="info")
82
84
  console.failure(
@@ -318,9 +320,9 @@ def bring_up_docker_compose_services(
318
320
  )
319
321
  # Set the environment variable for the local dependencies directory to be used by docker compose
320
322
  current_env = os.environ.copy()
321
- current_env[
322
- DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
323
- ] = relative_local_dependency_directory
323
+ current_env[DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY] = (
324
+ relative_local_dependency_directory
325
+ )
324
326
  dependency_graph = construct_dependency_graph(service, modes=modes)
325
327
  starting_order = dependency_graph.get_starting_order()
326
328
  sorted_remote_dependencies = sorted(
@@ -388,7 +390,9 @@ def bring_up_docker_compose_services(
388
390
  )
389
391
  exit(1)
390
392
  try:
391
- check_all_containers_healthy(status, containers_to_check)
393
+ check_all_containers_healthy(
394
+ status, containers_to_check, timeout=service.config.healthcheck_timeout
395
+ )
392
396
  except ContainerHealthcheckFailedError as e:
393
397
  status.failure(str(e))
394
398
  exit(1)