devservices 1.0.15__tar.gz → 1.0.17__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.15 → devservices-1.0.17}/PKG-INFO +1 -1
  2. {devservices-1.0.15 → devservices-1.0.17}/README.md +1 -1
  3. {devservices-1.0.15 → devservices-1.0.17}/devservices/commands/down.py +3 -3
  4. {devservices-1.0.15 → devservices-1.0.17}/devservices/commands/list_dependencies.py +1 -1
  5. {devservices-1.0.15 → devservices-1.0.17}/devservices/commands/logs.py +2 -2
  6. {devservices-1.0.15 → devservices-1.0.17}/devservices/commands/up.py +2 -2
  7. {devservices-1.0.15 → devservices-1.0.17}/devservices/constants.py +2 -1
  8. {devservices-1.0.15 → devservices-1.0.17}/devservices/main.py +33 -4
  9. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/docker_compose.py +27 -18
  10. {devservices-1.0.15 → devservices-1.0.17}/devservices.egg-info/PKG-INFO +1 -1
  11. {devservices-1.0.15 → devservices-1.0.17}/pyproject.toml +1 -1
  12. {devservices-1.0.15 → devservices-1.0.17}/tests/utils/test_docker_compose.py +88 -34
  13. {devservices-1.0.15 → devservices-1.0.17}/LICENSE.md +0 -0
  14. {devservices-1.0.15 → devservices-1.0.17}/devservices/__init__.py +0 -0
  15. {devservices-1.0.15 → devservices-1.0.17}/devservices/commands/__init__.py +0 -0
  16. {devservices-1.0.15 → devservices-1.0.17}/devservices/commands/list_services.py +0 -0
  17. {devservices-1.0.15 → devservices-1.0.17}/devservices/commands/purge.py +0 -0
  18. {devservices-1.0.15 → devservices-1.0.17}/devservices/commands/status.py +0 -0
  19. {devservices-1.0.15 → devservices-1.0.17}/devservices/commands/update.py +0 -0
  20. {devservices-1.0.15 → devservices-1.0.17}/devservices/configs/service_config.py +0 -0
  21. {devservices-1.0.15 → devservices-1.0.17}/devservices/exceptions.py +0 -0
  22. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/__init__.py +0 -0
  23. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/check_for_update.py +0 -0
  24. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/console.py +0 -0
  25. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/dependencies.py +0 -0
  26. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/devenv.py +0 -0
  27. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/docker.py +0 -0
  28. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/file_lock.py +0 -0
  29. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/git.py +0 -0
  30. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/install_binary.py +0 -0
  31. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/services.py +0 -0
  32. {devservices-1.0.15 → devservices-1.0.17}/devservices/utils/state.py +0 -0
  33. {devservices-1.0.15 → devservices-1.0.17}/devservices.egg-info/SOURCES.txt +0 -0
  34. {devservices-1.0.15 → devservices-1.0.17}/devservices.egg-info/dependency_links.txt +0 -0
  35. {devservices-1.0.15 → devservices-1.0.17}/devservices.egg-info/entry_points.txt +0 -0
  36. {devservices-1.0.15 → devservices-1.0.17}/devservices.egg-info/requires.txt +0 -0
  37. {devservices-1.0.15 → devservices-1.0.17}/devservices.egg-info/top_level.txt +0 -0
  38. {devservices-1.0.15 → devservices-1.0.17}/setup.cfg +0 -0
  39. {devservices-1.0.15 → devservices-1.0.17}/testing/__init__.py +0 -0
  40. {devservices-1.0.15 → devservices-1.0.17}/testing/utils.py +0 -0
  41. {devservices-1.0.15 → devservices-1.0.17}/tests/__init__.py +0 -0
  42. {devservices-1.0.15 → devservices-1.0.17}/tests/commands/test_down.py +0 -0
  43. {devservices-1.0.15 → devservices-1.0.17}/tests/commands/test_list_dependencies.py +0 -0
  44. {devservices-1.0.15 → devservices-1.0.17}/tests/commands/test_list_services.py +0 -0
  45. {devservices-1.0.15 → devservices-1.0.17}/tests/commands/test_logs.py +0 -0
  46. {devservices-1.0.15 → devservices-1.0.17}/tests/commands/test_purge.py +0 -0
  47. {devservices-1.0.15 → devservices-1.0.17}/tests/commands/test_status.py +0 -0
  48. {devservices-1.0.15 → devservices-1.0.17}/tests/commands/test_up.py +0 -0
  49. {devservices-1.0.15 → devservices-1.0.17}/tests/commands/test_update.py +0 -0
  50. {devservices-1.0.15 → devservices-1.0.17}/tests/configs/test_service_config.py +0 -0
  51. {devservices-1.0.15 → devservices-1.0.17}/tests/conftest.py +0 -0
  52. {devservices-1.0.15 → devservices-1.0.17}/tests/utils/test_check_for_update.py +0 -0
  53. {devservices-1.0.15 → devservices-1.0.17}/tests/utils/test_dependencies.py +0 -0
  54. {devservices-1.0.15 → devservices-1.0.17}/tests/utils/test_docker.py +0 -0
  55. {devservices-1.0.15 → devservices-1.0.17}/tests/utils/test_git.py +0 -0
  56. {devservices-1.0.15 → devservices-1.0.17}/tests/utils/test_install_binary.py +0 -0
  57. {devservices-1.0.15 → devservices-1.0.17}/tests/utils/test_services.py +0 -0
  58. {devservices-1.0.15 → devservices-1.0.17}/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.15
3
+ Version: 1.0.17
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.15
14
+ devservices==1.0.17
15
15
  ```
16
16
 
17
17
 
@@ -60,7 +60,7 @@ def down(args: Namespace) -> None:
60
60
  try:
61
61
  service = find_matching_service(service_name)
62
62
  except ConfigNotFoundError as e:
63
- capture_exception(e)
63
+ capture_exception(e, level="info")
64
64
  console.failure(
65
65
  f"{str(e)}. Please specify a service (i.e. `devservices down sentry`) or run the command from a directory with a devservices configuration."
66
66
  )
@@ -81,7 +81,7 @@ def down(args: Namespace) -> None:
81
81
  active_services = starting_services.union(started_services)
82
82
  if service.name not in active_services:
83
83
  console.warning(f"{service.name} is not running")
84
- exit(0)
84
+ return # Since exit(0) is captured as an internal_error by sentry
85
85
 
86
86
  active_starting_modes = state.get_active_modes_for_service(
87
87
  service.name, StateTables.STARTING_SERVICES
@@ -148,7 +148,7 @@ def down(args: Namespace) -> None:
148
148
  try:
149
149
  _down(service, remote_dependencies, list(mode_dependencies), status)
150
150
  except DockerComposeError as dce:
151
- capture_exception(dce)
151
+ capture_exception(dce, level="info")
152
152
  status.failure(f"Failed to stop {service.name}: {dce.stderr}")
153
153
  exit(1)
154
154
  else:
@@ -34,7 +34,7 @@ def list_dependencies(args: Namespace) -> None:
34
34
  try:
35
35
  service = find_matching_service(service_name)
36
36
  except ConfigNotFoundError as e:
37
- capture_exception(e)
37
+ capture_exception(e, level="info")
38
38
  console.failure(
39
39
  f"{str(e)}. Please specify a service (i.e. `devservices list-dependencies sentry`) or run the command from a directory with a devservices configuration."
40
40
  )
@@ -49,7 +49,7 @@ def logs(args: Namespace) -> None:
49
49
  try:
50
50
  service = find_matching_service(service_name)
51
51
  except ConfigNotFoundError as e:
52
- capture_exception(e)
52
+ capture_exception(e, level="info")
53
53
  console.failure(
54
54
  f"{str(e)}. Please specify a service (i.e. `devservices logs sentry`) or run the command from a directory with a devservices configuration."
55
55
  )
@@ -86,7 +86,7 @@ def logs(args: Namespace) -> None:
86
86
  try:
87
87
  logs_output = _logs(service, remote_dependencies, mode_dependencies)
88
88
  except DockerComposeError as dce:
89
- capture_exception(dce)
89
+ capture_exception(dce, level="info")
90
90
  console.failure(f"Failed to get logs for {service.name}: {dce.stderr}")
91
91
  exit(1)
92
92
  for log in logs_output:
@@ -63,7 +63,7 @@ def up(args: Namespace) -> None:
63
63
  try:
64
64
  service = find_matching_service(service_name)
65
65
  except ConfigNotFoundError as e:
66
- capture_exception(e)
66
+ capture_exception(e, level="info")
67
67
  console.failure(
68
68
  f"{str(e)}. Please specify a service (i.e. `devservices up sentry`) or run the command from a directory with a devservices configuration."
69
69
  )
@@ -110,7 +110,7 @@ def up(args: Namespace) -> None:
110
110
  mode_dependencies = modes[mode]
111
111
  _up(service, [mode], remote_dependencies, mode_dependencies, status)
112
112
  except DockerComposeError as dce:
113
- capture_exception(dce)
113
+ capture_exception(dce, level="info")
114
114
  status.failure(f"Failed to start {service.name}: {dce.stderr}")
115
115
  exit(1)
116
116
  # TODO: We should factor in healthchecks here before marking service as running
@@ -6,7 +6,8 @@ from datetime import timedelta
6
6
  MINIMUM_DOCKER_COMPOSE_VERSION = "2.29.7"
7
7
  DEVSERVICES_DIR_NAME = "devservices"
8
8
  CONFIG_FILE_NAME = "config.yml"
9
- DOCKER_USER_PLUGIN_DIR = os.path.expanduser("~/.docker/cli-plugins/")
9
+ DOCKER_CONFIG_DIR = os.environ.get("DOCKER_CONFIG", os.path.expanduser("~/.docker"))
10
+ DOCKER_USER_PLUGIN_DIR = os.path.join(DOCKER_CONFIG_DIR, "cli-plugins/")
10
11
 
11
12
  DEVSERVICES_CACHE_DIR = os.path.expanduser("~/.cache/sentry-devservices")
12
13
  DEVSERVICES_LOCAL_DIR = os.path.expanduser("~/.local/share/sentry-devservices")
@@ -15,6 +15,8 @@ from sentry_sdk import set_tag
15
15
  from sentry_sdk import set_user
16
16
  from sentry_sdk import start_transaction
17
17
  from sentry_sdk.integrations.argv import ArgvIntegration
18
+ from sentry_sdk.types import Event
19
+ from sentry_sdk.types import Hint
18
20
 
19
21
  from devservices.commands import down
20
22
  from devservices.commands import list_dependencies
@@ -42,6 +44,31 @@ disable_sentry = os.environ.get("DEVSERVICES_DISABLE_SENTRY", default="0") == "1
42
44
  logging.basicConfig(level=logging.INFO)
43
45
  current_version = metadata.version("devservices")
44
46
 
47
+ error_trace_ids = set()
48
+
49
+
50
+ def before_send_error(event: Event, hint: Hint) -> Event:
51
+ """Gets the trace_id from the errors we care about.
52
+
53
+ This function is used as a before_send callback for Sentry to track error trace IDs.
54
+ It adds the trace_id to error_trace_ids set for non-info level events.
55
+ """
56
+ if event["level"] != "info":
57
+ error_trace_ids.add(event["contexts"]["trace"]["trace_id"])
58
+ return event
59
+
60
+
61
+ def before_send_transaction(event: Event, hint: Hint) -> Event:
62
+ """Manually sets the status of a transaction.
63
+
64
+ This function is used as a before_send_transaction callback for Sentry to mark transaction status
65
+ as unknown if they don't correspond to errors we care about.
66
+ """
67
+ if event["contexts"]["trace"]["trace_id"] not in error_trace_ids:
68
+ event["contexts"]["trace"]["status"] = "unknown"
69
+ return event
70
+
71
+
45
72
  if not disable_sentry:
46
73
  init(
47
74
  dsn="https://56470da7302c16e83141f62f88e46449@o1.ingest.us.sentry.io/4507946704961536",
@@ -50,16 +77,18 @@ if not disable_sentry:
50
77
  enable_tracing=True,
51
78
  integrations=[ArgvIntegration()],
52
79
  environment=sentry_environment,
80
+ before_send=before_send_error,
81
+ before_send_transaction=before_send_transaction,
53
82
  release=current_version,
54
83
  )
55
84
  username = getpass.getuser()
56
85
  set_user({"username": username})
57
- set_tag("platform", platform.platform())
86
+ set_tag("user_platform", platform.platform())
58
87
  try:
59
88
  git_version = get_git_version()
60
89
  set_tag("git_version", git_version)
61
90
  except GitError as e:
62
- capture_exception(e)
91
+ capture_exception(e, level="info")
63
92
  logging.debug("Failed to get git version: %s", e)
64
93
  set_tag("git_version", "unknown")
65
94
 
@@ -75,11 +104,11 @@ def main() -> None:
75
104
  try:
76
105
  check_docker_compose_version()
77
106
  except DockerDaemonNotRunningError as e:
78
- capture_exception(e)
107
+ capture_exception(e, level="info")
79
108
  console.failure(str(e))
80
109
  exit(1)
81
110
  except DockerComposeInstallationError as e:
82
- capture_exception(e)
111
+ capture_exception(e, level="info")
83
112
  console.failure("Failed to ensure docker compose is installed and up-to-date")
84
113
  exit(1)
85
114
  parser = argparse.ArgumentParser(
@@ -83,15 +83,10 @@ def install_docker_compose() -> None:
83
83
 
84
84
  # Verify the installation
85
85
  try:
86
- version = subprocess.run(
87
- ["docker", "compose", "version", "--short"],
88
- capture_output=True,
89
- check=True,
90
- text=True,
91
- ).stdout
92
- except Exception as e:
86
+ version = get_docker_compose_version()
87
+ except DockerComposeError as e:
93
88
  raise DockerComposeInstallationError(
94
- f"Failed to verify Docker Compose installation: {e}"
89
+ f"Failed to verify Docker Compose installation: {e.stderr}"
95
90
  )
96
91
 
97
92
  console.success(f"Verified Docker Compose installation: v{version}")
@@ -129,20 +124,17 @@ def check_docker_compose_version() -> None:
129
124
  check_docker_daemon_running()
130
125
  try:
131
126
  # Run the docker compose version command
132
- result = subprocess.run(
133
- ["docker", "compose", "version", "--short"],
134
- capture_output=True,
135
- text=True,
136
- check=True,
137
- )
138
- except subprocess.CalledProcessError:
139
- result = None
127
+ docker_compose_version = get_docker_compose_version()
128
+ except DockerComposeError:
129
+ docker_compose_version = None
140
130
  console.warning(
141
131
  f"Docker Compose is not installed, attempting to install v{MINIMUM_DOCKER_COMPOSE_VERSION}"
142
132
  )
143
133
 
144
134
  # Extract the version number from the output
145
- version_output = result.stdout.strip() if result is not None else ""
135
+ version_output = (
136
+ docker_compose_version if docker_compose_version is not None else ""
137
+ )
146
138
 
147
139
  # Use regex to find the version number
148
140
  pattern = r"^(\d+\.\d+\.\d+)"
@@ -171,7 +163,24 @@ def check_docker_compose_version() -> None:
171
163
  install_docker_compose()
172
164
 
173
165
 
174
- # TODO: Consider removing this in favor of in house logic for determining non-remote services
166
+ def get_docker_compose_version() -> str:
167
+ try:
168
+ result = subprocess.run(
169
+ ["docker", "compose", "version", "--short"],
170
+ capture_output=True,
171
+ text=True,
172
+ check=True,
173
+ )
174
+ except subprocess.CalledProcessError as e:
175
+ raise DockerComposeError(
176
+ command="docker compose versions --short",
177
+ returncode=e.returncode,
178
+ stdout=e.stdout,
179
+ stderr=e.stderr,
180
+ )
181
+ return result.stdout.strip()
182
+
183
+
175
184
  def get_non_remote_services(
176
185
  service_config_path: str, current_env: dict[str, str]
177
186
  ) -> set[str]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: devservices
3
- Version: 1.0.15
3
+ Version: 1.0.17
4
4
  Requires-Python: >=3.10
5
5
  License-File: LICENSE.md
6
6
  Requires-Dist: pyyaml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "devservices"
7
- version = "1.0.15"
7
+ version = "1.0.17"
8
8
  # 3.10 is just for internal pypi compat
9
9
  requires-python = ">=3.10"
10
10
  dependencies = [
@@ -18,6 +18,7 @@ from devservices.utils.dependencies import InstalledRemoteDependency
18
18
  from devservices.utils.docker_compose import check_docker_compose_version
19
19
  from devservices.utils.docker_compose import DockerComposeCommand
20
20
  from devservices.utils.docker_compose import get_docker_compose_commands_to_run
21
+ from devservices.utils.docker_compose import get_docker_compose_version
21
22
  from devservices.utils.docker_compose import get_non_remote_services
22
23
  from devservices.utils.docker_compose import install_docker_compose
23
24
  from devservices.utils.services import Service
@@ -30,6 +31,26 @@ def test_check_docker_compose_version_success(mock_run: mock.Mock) -> None:
30
31
  check_docker_compose_version() # Should not raise any exception
31
32
 
32
33
 
34
+ @mock.patch("subprocess.run")
35
+ def test_get_docker_compose_version(mock_run: mock.Mock) -> None:
36
+ mock_run.return_value.stdout = "2.29.7\n"
37
+ assert get_docker_compose_version() == "2.29.7"
38
+
39
+
40
+ @mock.patch(
41
+ "subprocess.run",
42
+ side_effect=subprocess.CalledProcessError(
43
+ returncode=1,
44
+ cmd="docker compose version --short",
45
+ stderr="Docker Compose failed",
46
+ ),
47
+ )
48
+ def test_get_docker_compose_version_error(mock_run: mock.Mock) -> None:
49
+ with pytest.raises(DockerComposeError) as e:
50
+ get_docker_compose_version()
51
+ assert e.value.stderr == "Docker Compose failed"
52
+
53
+
33
54
  @mock.patch("subprocess.run")
34
55
  @mock.patch(
35
56
  "devservices.utils.docker_compose.install_docker_compose", side_effect=lambda: None
@@ -76,18 +97,27 @@ def test_check_docker_compose_docker_daemon_not_running(
76
97
  "subprocess.run",
77
98
  side_effect=[
78
99
  0, # First call is to check if docker daemon is running
79
- subprocess.CalledProcessError(
80
- returncode=1, cmd="docker compose version --short"
81
- ),
82
100
  ],
83
101
  )
102
+ @mock.patch(
103
+ "devservices.utils.docker_compose.get_docker_compose_version",
104
+ side_effect=DockerComposeError(
105
+ command="docker compose version --short",
106
+ returncode=1,
107
+ stdout="",
108
+ stderr="",
109
+ ),
110
+ )
84
111
  @mock.patch(
85
112
  "devservices.utils.docker_compose.install_docker_compose", side_effect=lambda: None
86
113
  )
87
114
  def test_check_docker_compose_command_failure(
88
- mock_install_docker_compose: mock.Mock, _mock_run: mock.Mock
115
+ mock_install_docker_compose: mock.Mock,
116
+ mock_get_docker_compose_version: mock.Mock,
117
+ _mock_run: mock.Mock,
89
118
  ) -> None:
90
119
  check_docker_compose_version()
120
+ mock_get_docker_compose_version.assert_called_once()
91
121
  mock_install_docker_compose.assert_called_once()
92
122
 
93
123
 
@@ -139,11 +169,16 @@ def test_install_docker_compose_binary_install_error(
139
169
  @mock.patch("devservices.utils.install_binary.os.chmod")
140
170
  @mock.patch("devservices.utils.install_binary.shutil.move")
141
171
  @mock.patch(
142
- "devservices.utils.docker_compose.subprocess.run",
143
- side_effect=Exception("Docker Compose failed"),
172
+ "devservices.utils.docker_compose.get_docker_compose_version",
173
+ side_effect=DockerComposeError(
174
+ command="docker compose version --short",
175
+ returncode=1,
176
+ stdout="",
177
+ stderr="Docker Compose failed",
178
+ ),
144
179
  )
145
180
  def test_install_docker_compose_compose_verification_error(
146
- _mock_subprocess_run: mock.Mock,
181
+ _mock_get_docker_compose_version: mock.Mock,
147
182
  _mock_shutil_move: mock.Mock,
148
183
  _mock_chmod: mock.Mock,
149
184
  _mock_urlretrieve: mock.Mock,
@@ -157,22 +192,18 @@ def test_install_docker_compose_compose_verification_error(
157
192
  install_docker_compose()
158
193
 
159
194
 
160
- @mock.patch("tempfile.TemporaryDirectory")
195
+ @mock.patch("devservices.utils.install_binary.tempfile.TemporaryDirectory")
161
196
  @mock.patch("platform.system", return_value="Darwin")
162
197
  @mock.patch("platform.machine", return_value="arm64")
163
198
  @mock.patch("devservices.utils.install_binary.urlretrieve")
164
199
  @mock.patch("devservices.utils.install_binary.os.chmod")
165
200
  @mock.patch("devservices.utils.install_binary.shutil.move")
166
201
  @mock.patch(
167
- "devservices.utils.docker_compose.subprocess.run",
168
- return_value=subprocess.CompletedProcess(
169
- args=["docker", "compose", "version", "--short"],
170
- returncode=0,
171
- stdout="2.29.7\n",
172
- ),
202
+ "devservices.utils.docker_compose.get_docker_compose_version",
203
+ return_value="2.29.7\n",
173
204
  )
174
205
  def test_install_docker_compose_macos_arm64(
175
- mock_subprocess_run: mock.Mock,
206
+ mock_get_docker_compose_version: mock.Mock,
176
207
  mock_shutil_move: mock.Mock,
177
208
  mock_chmod: mock.Mock,
178
209
  mock_urlretrieve: mock.Mock,
@@ -191,30 +222,21 @@ def test_install_docker_compose_macos_arm64(
191
222
  "tempdir/docker-compose",
192
223
  os.path.expanduser("~/.docker/cli-plugins/docker-compose"),
193
224
  )
194
- mock_subprocess_run.assert_called_once_with(
195
- ["docker", "compose", "version", "--short"],
196
- capture_output=True,
197
- check=True,
198
- text=True,
199
- )
225
+ mock_get_docker_compose_version.assert_called_once()
200
226
 
201
227
 
202
- @mock.patch("tempfile.TemporaryDirectory")
228
+ @mock.patch("devservices.utils.install_binary.tempfile.TemporaryDirectory")
203
229
  @mock.patch("platform.system", return_value="Linux")
204
230
  @mock.patch("platform.machine", return_value="x86_64")
205
231
  @mock.patch("devservices.utils.install_binary.urlretrieve")
206
232
  @mock.patch("devservices.utils.install_binary.os.chmod")
207
233
  @mock.patch("devservices.utils.install_binary.shutil.move")
208
234
  @mock.patch(
209
- "devservices.utils.docker_compose.subprocess.run",
210
- return_value=subprocess.CompletedProcess(
211
- args=["docker", "compose", "version", "--short"],
212
- returncode=0,
213
- stdout="2.29.7\n",
214
- ),
235
+ "devservices.utils.docker_compose.get_docker_compose_version",
236
+ return_value="2.29.7\n",
215
237
  )
216
238
  def test_install_docker_compose_linux_x86(
217
- mock_subprocess_run: mock.Mock,
239
+ mock_get_docker_compose_version: mock.Mock,
218
240
  mock_shutil_move: mock.Mock,
219
241
  mock_chmod: mock.Mock,
220
242
  mock_urlretrieve: mock.Mock,
@@ -233,12 +255,44 @@ def test_install_docker_compose_linux_x86(
233
255
  "tempdir/docker-compose",
234
256
  os.path.expanduser("~/.docker/cli-plugins/docker-compose"),
235
257
  )
236
- mock_subprocess_run.assert_called_once_with(
237
- ["docker", "compose", "version", "--short"],
238
- capture_output=True,
239
- check=True,
240
- text=True,
258
+ mock_get_docker_compose_version.assert_called_once()
259
+
260
+
261
+ @mock.patch("devservices.utils.install_binary.tempfile.TemporaryDirectory")
262
+ @mock.patch("platform.system", return_value="Darwin")
263
+ @mock.patch("platform.machine", return_value="arm64")
264
+ @mock.patch("devservices.utils.install_binary.urlretrieve")
265
+ @mock.patch("devservices.utils.install_binary.os.chmod")
266
+ @mock.patch("devservices.utils.install_binary.shutil.move")
267
+ @mock.patch(
268
+ "devservices.utils.docker_compose.get_docker_compose_version",
269
+ return_value="2.29.7\n",
270
+ )
271
+ def test_install_docker_compose_custom_docker_config_dir(
272
+ mock_get_docker_compose_version: mock.Mock,
273
+ mock_shutil_move: mock.Mock,
274
+ mock_chmod: mock.Mock,
275
+ mock_urlretrieve: mock.Mock,
276
+ _mock_machine: mock.Mock,
277
+ _mock_system: mock.Mock,
278
+ mock_tempdir: mock.Mock,
279
+ ) -> None:
280
+ mock_tempdir.return_value.__enter__.return_value = "tempdir"
281
+ with mock.patch(
282
+ "devservices.utils.docker_compose.DOCKER_USER_PLUGIN_DIR",
283
+ "tempdir/docker/config/cli-plugins",
284
+ ):
285
+ install_docker_compose()
286
+ mock_urlretrieve.assert_called_once_with(
287
+ "https://github.com/docker/compose/releases/download/v2.29.7/docker-compose-darwin-aarch64",
288
+ "tempdir/docker-compose",
289
+ )
290
+ mock_chmod.assert_called_once_with("tempdir/docker-compose", 0o755)
291
+ mock_shutil_move.assert_called_once_with(
292
+ "tempdir/docker-compose",
293
+ "tempdir/docker/config/cli-plugins/docker-compose",
241
294
  )
295
+ mock_get_docker_compose_version.assert_called_once()
242
296
 
243
297
 
244
298
  @mock.patch(
File without changes
File without changes