devservices 1.0.13__tar.gz → 1.0.15__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 (58) hide show
  1. {devservices-1.0.13 → devservices-1.0.15}/PKG-INFO +1 -1
  2. {devservices-1.0.13 → devservices-1.0.15}/README.md +1 -1
  3. {devservices-1.0.13 → devservices-1.0.15}/devservices/commands/update.py +1 -1
  4. {devservices-1.0.13 → devservices-1.0.15}/devservices/exceptions.py +9 -0
  5. {devservices-1.0.13 → devservices-1.0.15}/devservices/main.py +15 -2
  6. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/dependencies.py +30 -2
  7. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/docker_compose.py +4 -1
  8. devservices-1.0.15/devservices/utils/git.py +21 -0
  9. {devservices-1.0.13 → devservices-1.0.15}/devservices.egg-info/PKG-INFO +1 -1
  10. {devservices-1.0.13 → devservices-1.0.15}/devservices.egg-info/SOURCES.txt +2 -0
  11. {devservices-1.0.13 → devservices-1.0.15}/pyproject.toml +1 -1
  12. {devservices-1.0.13 → devservices-1.0.15}/tests/utils/test_dependencies.py +42 -2
  13. {devservices-1.0.13 → devservices-1.0.15}/tests/utils/test_docker_compose.py +8 -2
  14. devservices-1.0.15/tests/utils/test_git.py +33 -0
  15. {devservices-1.0.13 → devservices-1.0.15}/LICENSE.md +0 -0
  16. {devservices-1.0.13 → devservices-1.0.15}/devservices/__init__.py +0 -0
  17. {devservices-1.0.13 → devservices-1.0.15}/devservices/commands/__init__.py +0 -0
  18. {devservices-1.0.13 → devservices-1.0.15}/devservices/commands/down.py +0 -0
  19. {devservices-1.0.13 → devservices-1.0.15}/devservices/commands/list_dependencies.py +0 -0
  20. {devservices-1.0.13 → devservices-1.0.15}/devservices/commands/list_services.py +0 -0
  21. {devservices-1.0.13 → devservices-1.0.15}/devservices/commands/logs.py +0 -0
  22. {devservices-1.0.13 → devservices-1.0.15}/devservices/commands/purge.py +0 -0
  23. {devservices-1.0.13 → devservices-1.0.15}/devservices/commands/status.py +0 -0
  24. {devservices-1.0.13 → devservices-1.0.15}/devservices/commands/up.py +0 -0
  25. {devservices-1.0.13 → devservices-1.0.15}/devservices/configs/service_config.py +0 -0
  26. {devservices-1.0.13 → devservices-1.0.15}/devservices/constants.py +0 -0
  27. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/__init__.py +0 -0
  28. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/check_for_update.py +0 -0
  29. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/console.py +0 -0
  30. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/devenv.py +0 -0
  31. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/docker.py +0 -0
  32. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/file_lock.py +0 -0
  33. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/install_binary.py +0 -0
  34. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/services.py +0 -0
  35. {devservices-1.0.13 → devservices-1.0.15}/devservices/utils/state.py +0 -0
  36. {devservices-1.0.13 → devservices-1.0.15}/devservices.egg-info/dependency_links.txt +0 -0
  37. {devservices-1.0.13 → devservices-1.0.15}/devservices.egg-info/entry_points.txt +0 -0
  38. {devservices-1.0.13 → devservices-1.0.15}/devservices.egg-info/requires.txt +0 -0
  39. {devservices-1.0.13 → devservices-1.0.15}/devservices.egg-info/top_level.txt +0 -0
  40. {devservices-1.0.13 → devservices-1.0.15}/setup.cfg +0 -0
  41. {devservices-1.0.13 → devservices-1.0.15}/testing/__init__.py +0 -0
  42. {devservices-1.0.13 → devservices-1.0.15}/testing/utils.py +0 -0
  43. {devservices-1.0.13 → devservices-1.0.15}/tests/__init__.py +0 -0
  44. {devservices-1.0.13 → devservices-1.0.15}/tests/commands/test_down.py +0 -0
  45. {devservices-1.0.13 → devservices-1.0.15}/tests/commands/test_list_dependencies.py +0 -0
  46. {devservices-1.0.13 → devservices-1.0.15}/tests/commands/test_list_services.py +0 -0
  47. {devservices-1.0.13 → devservices-1.0.15}/tests/commands/test_logs.py +0 -0
  48. {devservices-1.0.13 → devservices-1.0.15}/tests/commands/test_purge.py +0 -0
  49. {devservices-1.0.13 → devservices-1.0.15}/tests/commands/test_status.py +0 -0
  50. {devservices-1.0.13 → devservices-1.0.15}/tests/commands/test_up.py +0 -0
  51. {devservices-1.0.13 → devservices-1.0.15}/tests/commands/test_update.py +0 -0
  52. {devservices-1.0.13 → devservices-1.0.15}/tests/configs/test_service_config.py +0 -0
  53. {devservices-1.0.13 → devservices-1.0.15}/tests/conftest.py +0 -0
  54. {devservices-1.0.13 → devservices-1.0.15}/tests/utils/test_check_for_update.py +0 -0
  55. {devservices-1.0.13 → devservices-1.0.15}/tests/utils/test_docker.py +0 -0
  56. {devservices-1.0.13 → devservices-1.0.15}/tests/utils/test_install_binary.py +0 -0
  57. {devservices-1.0.13 → devservices-1.0.15}/tests/utils/test_services.py +0 -0
  58. {devservices-1.0.13 → devservices-1.0.15}/tests/utils/test_state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: devservices
3
- Version: 1.0.13
3
+ Version: 1.0.15
4
4
  Requires-Python: >=3.10
5
5
  License-File: LICENSE.md
6
6
  Requires-Dist: pyyaml
@@ -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.13
14
+ devservices==1.0.15
15
15
  ```
16
16
 
17
17
 
@@ -36,7 +36,7 @@ def update_version(exec_path: str, latest_version: str) -> None:
36
36
 
37
37
  def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
38
38
  parser = subparsers.add_parser(
39
- "update", help="update devservices to the latest version"
39
+ "update", help="Update devservices to the latest version"
40
40
  )
41
41
  parser.set_defaults(func=update)
42
42
 
@@ -123,6 +123,15 @@ class DependencyNotInstalledError(DependencyError):
123
123
  return f"Dependency not installed correctly: {self.repo_name} ({self.repo_link}) on {self.branch}"
124
124
 
125
125
 
126
+ class GitError(Exception):
127
+ """Base class for git related errors."""
128
+
129
+ def __init__(self, command: str, returncode: int, stderr: str):
130
+ self.command = command
131
+ self.returncode = returncode
132
+ self.stderr = stderr
133
+
134
+
126
135
  class GitConfigError(Exception):
127
136
  """Base class for git config related errors."""
128
137
 
@@ -5,6 +5,7 @@ import atexit
5
5
  import getpass
6
6
  import logging
7
7
  import os
8
+ import platform
8
9
  from importlib import metadata
9
10
 
10
11
  from sentry_sdk import capture_exception
@@ -26,14 +27,18 @@ from devservices.commands import update
26
27
  from devservices.constants import LOGGER_NAME
27
28
  from devservices.exceptions import DockerComposeInstallationError
28
29
  from devservices.exceptions import DockerDaemonNotRunningError
30
+ from devservices.exceptions import GitError
29
31
  from devservices.utils.console import Console
30
32
  from devservices.utils.docker_compose import check_docker_compose_version
33
+ from devservices.utils.git import get_git_version
31
34
 
32
35
  sentry_environment = (
33
- "development" if os.environ.get("IS_DEV", default=False) else "production"
36
+ "development" if os.environ.get("IS_DEV", default="0") == "1" else "production"
34
37
  )
38
+ if os.environ.get("CI", default="false") == "true":
39
+ sentry_environment = "CI"
35
40
 
36
- disable_sentry = os.environ.get("DEVSERVICES_DISABLE_SENTRY", default=False)
41
+ disable_sentry = os.environ.get("DEVSERVICES_DISABLE_SENTRY", default="0") == "1"
37
42
  logging.basicConfig(level=logging.INFO)
38
43
  current_version = metadata.version("devservices")
39
44
 
@@ -49,6 +54,14 @@ if not disable_sentry:
49
54
  )
50
55
  username = getpass.getuser()
51
56
  set_user({"username": username})
57
+ set_tag("platform", platform.platform())
58
+ try:
59
+ git_version = get_git_version()
60
+ set_tag("git_version", git_version)
61
+ except GitError as e:
62
+ capture_exception(e)
63
+ logging.debug("Failed to get git version: %s", e)
64
+ set_tag("git_version", "unknown")
52
65
 
53
66
 
54
67
  @atexit.register
@@ -272,6 +272,17 @@ def get_non_shared_remote_dependencies(
272
272
  active_services = starting_services.union(started_services)
273
273
  # We don't care about the remote dependencies of the service we are stopping
274
274
  active_services.remove(service_to_stop.name)
275
+
276
+ active_modes: dict[str, list[str]] = dict()
277
+ for active_service in active_services:
278
+ starting_modes = state.get_active_modes_for_service(
279
+ active_service, StateTables.STARTING_SERVICES
280
+ )
281
+ started_modes = state.get_active_modes_for_service(
282
+ active_service, StateTables.STARTED_SERVICES
283
+ )
284
+ active_modes[active_service] = starting_modes or started_modes
285
+
275
286
  other_running_remote_dependencies: set[InstalledRemoteDependency] = set()
276
287
  base_running_service_names: set[str] = set()
277
288
  for started_service_name in active_services:
@@ -279,8 +290,18 @@ def get_non_shared_remote_dependencies(
279
290
  for dependency_name in service_to_stop.config.dependencies.keys():
280
291
  if dependency_name == started_service.config.service_name:
281
292
  base_running_service_names.add(started_service_name)
293
+
294
+ started_service_modes = active_modes[started_service_name]
295
+ # Only consider the dependencies of the modes that are running
296
+ started_service_dependencies: dict[str, Dependency] = dict()
297
+ for started_service_mode in started_service_modes:
298
+ for dependency_name in started_service.config.modes[started_service_mode]:
299
+ started_service_dependencies[
300
+ dependency_name
301
+ ] = started_service.config.dependencies[dependency_name]
302
+
282
303
  installed_remote_dependencies = get_installed_remote_dependencies(
283
- list(started_service.config.dependencies.values())
304
+ list(started_service_dependencies.values())
284
305
  )
285
306
  # TODO: There is an edge case here where there is a shared remote dependency with different modes
286
307
  other_running_remote_dependencies = other_running_remote_dependencies.union(
@@ -472,7 +493,14 @@ def _update_dependency(
472
493
 
473
494
  try:
474
495
  _run_command_with_retries(
475
- ["git", "fetch", "origin", dependency.branch, "--filter=blob:none"],
496
+ [
497
+ "git",
498
+ "fetch",
499
+ "origin",
500
+ dependency.branch,
501
+ "--filter=blob:none",
502
+ "--no-recurse-submodules", # Avoid fetching submodules
503
+ ],
476
504
  cwd=dependency_repo_dir,
477
505
  )
478
506
  except subprocess.CalledProcessError as e:
@@ -84,7 +84,10 @@ def install_docker_compose() -> None:
84
84
  # Verify the installation
85
85
  try:
86
86
  version = subprocess.run(
87
- ["docker", "compose", "version", "--short"], capture_output=True, text=True
87
+ ["docker", "compose", "version", "--short"],
88
+ capture_output=True,
89
+ check=True,
90
+ text=True,
88
91
  ).stdout
89
92
  except Exception as e:
90
93
  raise DockerComposeInstallationError(
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+
5
+ from devservices.exceptions import GitError
6
+
7
+
8
+ def get_git_version() -> str:
9
+ """Get the git version"""
10
+ try:
11
+ return subprocess.check_output(
12
+ ["git", "version"],
13
+ text=True,
14
+ stderr=subprocess.PIPE,
15
+ ).strip()
16
+ except subprocess.CalledProcessError as e:
17
+ raise GitError(
18
+ command="git --version",
19
+ returncode=e.returncode,
20
+ stderr=e.stderr,
21
+ ) from e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: devservices
3
- Version: 1.0.13
3
+ Version: 1.0.15
4
4
  Requires-Python: >=3.10
5
5
  License-File: LICENSE.md
6
6
  Requires-Dist: pyyaml
@@ -30,6 +30,7 @@ devservices/utils/devenv.py
30
30
  devservices/utils/docker.py
31
31
  devservices/utils/docker_compose.py
32
32
  devservices/utils/file_lock.py
33
+ devservices/utils/git.py
33
34
  devservices/utils/install_binary.py
34
35
  devservices/utils/services.py
35
36
  devservices/utils/state.py
@@ -50,6 +51,7 @@ tests/utils/test_check_for_update.py
50
51
  tests/utils/test_dependencies.py
51
52
  tests/utils/test_docker.py
52
53
  tests/utils/test_docker_compose.py
54
+ tests/utils/test_git.py
53
55
  tests/utils/test_install_binary.py
54
56
  tests/utils/test_services.py
55
57
  tests/utils/test_state.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "devservices"
7
- version = "1.0.13"
7
+ version = "1.0.15"
8
8
  # 3.10 is just for internal pypi compat
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
@@ -681,6 +681,7 @@ def test_install_dependency_git_fetch_transient_failure(tmp_path: Path) -> None:
681
681
  "origin",
682
682
  "main",
683
683
  "--filter=blob:none",
684
+ "--no-recurse-submodules",
684
685
  ],
685
686
  cwd=str(
686
687
  tmp_path
@@ -697,6 +698,7 @@ def test_install_dependency_git_fetch_transient_failure(tmp_path: Path) -> None:
697
698
  "origin",
698
699
  "main",
699
700
  "--filter=blob:none",
701
+ "--no-recurse-submodules",
700
702
  ],
701
703
  cwd=str(
702
704
  tmp_path
@@ -713,6 +715,7 @@ def test_install_dependency_git_fetch_transient_failure(tmp_path: Path) -> None:
713
715
  "origin",
714
716
  "main",
715
717
  "--filter=blob:none",
718
+ "--no-recurse-submodules",
716
719
  ],
717
720
  cwd=str(
718
721
  tmp_path
@@ -800,6 +803,7 @@ def test_install_dependency_git_fetch_failure_with_retries(tmp_path: Path) -> No
800
803
  "origin",
801
804
  "main",
802
805
  "--filter=blob:none",
806
+ "--no-recurse-submodules",
803
807
  ],
804
808
  cwd=str(
805
809
  tmp_path
@@ -816,6 +820,7 @@ def test_install_dependency_git_fetch_failure_with_retries(tmp_path: Path) -> No
816
820
  "origin",
817
821
  "main",
818
822
  "--filter=blob:none",
823
+ "--no-recurse-submodules",
819
824
  ],
820
825
  cwd=str(
821
826
  tmp_path
@@ -832,6 +837,7 @@ def test_install_dependency_git_fetch_failure_with_retries(tmp_path: Path) -> No
832
837
  "origin",
833
838
  "main",
834
839
  "--filter=blob:none",
840
+ "--no-recurse-submodules",
835
841
  ],
836
842
  cwd=str(
837
843
  tmp_path
@@ -919,6 +925,7 @@ def test_install_dependency_update_git_checkout_failure(tmp_path: Path) -> None:
919
925
  "origin",
920
926
  "main",
921
927
  "--filter=blob:none",
928
+ "--no-recurse-submodules",
922
929
  ],
923
930
  cwd=str(
924
931
  tmp_path
@@ -2157,6 +2164,8 @@ def test_get_non_shared_remote_dependencies_shared_dependencies(
2157
2164
  ),
2158
2165
  )
2159
2166
  assert len(shared_remote_dependencies) == 0
2167
+ mock_find_matching_service.assert_called_once_with("service-2")
2168
+ mock_get_installed_remote_dependencies.assert_called_once_with([])
2160
2169
 
2161
2170
 
2162
2171
  @mock.patch(
@@ -2179,8 +2188,25 @@ def test_get_non_shared_remote_dependencies_shared_dependencies(
2179
2188
  config=ServiceConfig(
2180
2189
  version=0.1,
2181
2190
  service_name="service-2",
2182
- dependencies={},
2183
- modes={"default": []},
2191
+ dependencies={
2192
+ "dependency-3": Dependency(
2193
+ description="dependency-3",
2194
+ remote=RemoteConfig(
2195
+ repo_name="dependency-3",
2196
+ repo_link="file://path/to/dependency-3",
2197
+ branch="main",
2198
+ ),
2199
+ ),
2200
+ "dependency-4": Dependency(
2201
+ description="dependency-4",
2202
+ remote=RemoteConfig(
2203
+ repo_name="dependency-4",
2204
+ repo_link="file://path/to/dependency-4",
2205
+ branch="main",
2206
+ ),
2207
+ ),
2208
+ },
2209
+ modes={"default": ["dependency-3"], "other": ["dependency-4"]},
2184
2210
  ),
2185
2211
  ),
2186
2212
  )
@@ -2245,6 +2271,20 @@ def test_get_non_shared_remote_dependencies_complex(
2245
2271
  mode="default",
2246
2272
  )
2247
2273
  }
2274
+ mock_find_matching_service.assert_called_once_with("service-2")
2275
+ # dependency-4 is not installed, so it should not be passed to get_installed_remote_dependencies
2276
+ mock_get_installed_remote_dependencies.assert_called_once_with(
2277
+ [
2278
+ Dependency(
2279
+ description="dependency-3",
2280
+ remote=RemoteConfig(
2281
+ repo_name="dependency-3",
2282
+ repo_link="file://path/to/dependency-3",
2283
+ branch="main",
2284
+ ),
2285
+ )
2286
+ ]
2287
+ )
2248
2288
 
2249
2289
 
2250
2290
  @mock.patch("devservices.utils.dependencies.install_dependencies", return_value=[])
@@ -192,7 +192,10 @@ def test_install_docker_compose_macos_arm64(
192
192
  os.path.expanduser("~/.docker/cli-plugins/docker-compose"),
193
193
  )
194
194
  mock_subprocess_run.assert_called_once_with(
195
- ["docker", "compose", "version", "--short"], capture_output=True, text=True
195
+ ["docker", "compose", "version", "--short"],
196
+ capture_output=True,
197
+ check=True,
198
+ text=True,
196
199
  )
197
200
 
198
201
 
@@ -231,7 +234,10 @@ def test_install_docker_compose_linux_x86(
231
234
  os.path.expanduser("~/.docker/cli-plugins/docker-compose"),
232
235
  )
233
236
  mock_subprocess_run.assert_called_once_with(
234
- ["docker", "compose", "version", "--short"], capture_output=True, text=True
237
+ ["docker", "compose", "version", "--short"],
238
+ capture_output=True,
239
+ check=True,
240
+ text=True,
235
241
  )
236
242
 
237
243
 
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+ from unittest import mock
5
+
6
+ import pytest
7
+
8
+ from devservices.exceptions import GitError
9
+ from devservices.utils.git import get_git_version
10
+
11
+
12
+ @mock.patch(
13
+ "devservices.utils.git.subprocess.check_output", return_value="git version 2.42.0"
14
+ )
15
+ def test_get_git_version_success(mock_get_git_version: mock.Mock) -> None:
16
+ assert get_git_version() == "git version 2.42.0"
17
+ mock_get_git_version.assert_called_once_with(
18
+ ["git", "version"], text=True, stderr=subprocess.PIPE
19
+ )
20
+
21
+
22
+ @mock.patch(
23
+ "devservices.utils.git.subprocess.check_output",
24
+ side_effect=subprocess.CalledProcessError(
25
+ returncode=1, cmd="git version", stderr="error"
26
+ ),
27
+ )
28
+ def test_get_git_version_error(mock_get_git_version: mock.Mock) -> None:
29
+ with pytest.raises(GitError):
30
+ get_git_version()
31
+ mock_get_git_version.assert_called_once_with(
32
+ ["git", "version"], text=True, stderr=subprocess.PIPE
33
+ )
File without changes
File without changes