devservices 0.0.4__tar.gz → 1.0.0__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 (55) hide show
  1. {devservices-0.0.4 → devservices-1.0.0}/PKG-INFO +1 -1
  2. {devservices-0.0.4 → devservices-1.0.0}/README.md +4 -9
  3. {devservices-0.0.4 → devservices-1.0.0}/devservices/commands/list_dependencies.py +6 -4
  4. {devservices-0.0.4 → devservices-1.0.0}/devservices/commands/list_services.py +14 -12
  5. devservices-1.0.0/devservices/commands/logs.py +68 -0
  6. devservices-1.0.0/devservices/commands/purge.py +50 -0
  7. {devservices-0.0.4 → devservices-1.0.0}/devservices/commands/start.py +32 -4
  8. {devservices-0.0.4 → devservices-1.0.0}/devservices/commands/status.py +28 -11
  9. devservices-1.0.0/devservices/commands/stop.py +78 -0
  10. {devservices-0.0.4 → devservices-1.0.0}/devservices/commands/update.py +13 -9
  11. {devservices-0.0.4 → devservices-1.0.0}/devservices/constants.py +3 -0
  12. {devservices-0.0.4 → devservices-1.0.0}/devservices/exceptions.py +22 -2
  13. {devservices-0.0.4 → devservices-1.0.0}/devservices/main.py +25 -4
  14. devservices-1.0.0/devservices/utils/console.py +125 -0
  15. {devservices-0.0.4 → devservices-1.0.0}/devservices/utils/dependencies.py +45 -13
  16. devservices-1.0.0/devservices/utils/docker.py +36 -0
  17. {devservices-0.0.4 → devservices-1.0.0}/devservices/utils/docker_compose.py +22 -41
  18. {devservices-0.0.4 → devservices-1.0.0}/devservices/utils/install_binary.py +9 -5
  19. {devservices-0.0.4 → devservices-1.0.0}/devservices/utils/services.py +4 -1
  20. devservices-1.0.0/devservices/utils/state.py +90 -0
  21. {devservices-0.0.4 → devservices-1.0.0}/devservices.egg-info/PKG-INFO +1 -1
  22. {devservices-0.0.4 → devservices-1.0.0}/devservices.egg-info/SOURCES.txt +7 -1
  23. {devservices-0.0.4 → devservices-1.0.0}/pyproject.toml +1 -1
  24. {devservices-0.0.4 → devservices-1.0.0}/tests/commands/test_list_services.py +9 -14
  25. devservices-1.0.0/tests/commands/test_purge.py +132 -0
  26. {devservices-0.0.4 → devservices-1.0.0}/tests/commands/test_start.py +64 -4
  27. {devservices-0.0.4 → devservices-1.0.0}/tests/commands/test_stop.py +27 -7
  28. {devservices-0.0.4 → devservices-1.0.0}/tests/commands/test_update.py +1 -3
  29. devservices-1.0.0/tests/conftest.py +10 -0
  30. {devservices-0.0.4 → devservices-1.0.0}/tests/utils/test_dependencies.py +227 -0
  31. devservices-1.0.0/tests/utils/test_docker.py +47 -0
  32. {devservices-0.0.4 → devservices-1.0.0}/tests/utils/test_docker_compose.py +87 -24
  33. devservices-1.0.0/tests/utils/test_state.py +54 -0
  34. devservices-0.0.4/devservices/commands/logs.py +0 -45
  35. devservices-0.0.4/devservices/commands/stop.py +0 -40
  36. devservices-0.0.4/devservices/utils/console.py +0 -67
  37. devservices-0.0.4/tests/conftest.py +0 -0
  38. {devservices-0.0.4 → devservices-1.0.0}/LICENSE.md +0 -0
  39. {devservices-0.0.4 → devservices-1.0.0}/devservices/__init__.py +0 -0
  40. {devservices-0.0.4 → devservices-1.0.0}/devservices/commands/__init__.py +0 -0
  41. {devservices-0.0.4 → devservices-1.0.0}/devservices/commands/check_for_update.py +0 -0
  42. {devservices-0.0.4 → devservices-1.0.0}/devservices/configs/service_config.py +0 -0
  43. {devservices-0.0.4 → devservices-1.0.0}/devservices/utils/__init__.py +0 -0
  44. {devservices-0.0.4 → devservices-1.0.0}/devservices/utils/devenv.py +0 -0
  45. {devservices-0.0.4 → devservices-1.0.0}/devservices/utils/file_lock.py +0 -0
  46. {devservices-0.0.4 → devservices-1.0.0}/devservices.egg-info/dependency_links.txt +0 -0
  47. {devservices-0.0.4 → devservices-1.0.0}/devservices.egg-info/entry_points.txt +0 -0
  48. {devservices-0.0.4 → devservices-1.0.0}/devservices.egg-info/requires.txt +0 -0
  49. {devservices-0.0.4 → devservices-1.0.0}/devservices.egg-info/top_level.txt +0 -0
  50. {devservices-0.0.4 → devservices-1.0.0}/setup.cfg +0 -0
  51. {devservices-0.0.4 → devservices-1.0.0}/testing/__init__.py +0 -0
  52. {devservices-0.0.4 → devservices-1.0.0}/testing/utils.py +0 -0
  53. {devservices-0.0.4 → devservices-1.0.0}/tests/__init__.py +0 -0
  54. {devservices-0.0.4 → devservices-1.0.0}/tests/configs/test_service_config.py +0 -0
  55. {devservices-0.0.4 → devservices-1.0.0}/tests/utils/test_install_binary.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: devservices
3
- Version: 0.0.4
3
+ Version: 1.0.0
4
4
  Requires-Python: >=3.10
5
5
  License-File: LICENSE.md
6
6
  Requires-Dist: pyyaml
@@ -8,20 +8,13 @@ A standalone cli tool used to manage dependencies for services. It simplifies th
8
8
 
9
9
  ## Installation
10
10
 
11
- A system-wide installation can be done by downloading the binary of the latest release
11
+ The recommended way to install devservices is through a virtualenv in the requirements.txt.
12
12
 
13
13
  ```
14
- PLATFORM=darwin # Options: darwin/linux
15
- INSTALL_DIR="$HOME/.local/bin"
16
- curl -L "https://github.com/getsentry/devservices/releases/download/0.0.4/devservices-$PLATFORM" -o "$INSTALL_DIR/devservices"
17
- chmod +x "$INSTALL_DIR/devservices"
14
+ devservices==1.0.0
18
15
  ```
19
16
 
20
- Alternatively, if the repository you're working in has a python virtualenv, you can simply add this to the requirements-dev.txt:
21
17
 
22
- ```
23
- devservices==0.0.4
24
- ```
25
18
  ## Usage
26
19
 
27
20
  devservices provides several commands to manage your services:
@@ -36,3 +29,5 @@ NOTE: service-name is an optional parameter. If not provided, devservices will a
36
29
  - `devservices logs <service-name>`: View logs for a specific service.
37
30
  - `devservices list-services`: List all available Sentry services.
38
31
  - `devservices list-dependencies <service-name>`: List all dependencies for a service and whether they are enabled/disabled.
32
+ - `devservices update` Update devservices to the latest version.
33
+ - `devservices purge`: Purge the local devservices cache.
@@ -4,6 +4,7 @@ from argparse import _SubParsersAction
4
4
  from argparse import ArgumentParser
5
5
  from argparse import Namespace
6
6
 
7
+ from devservices.utils.console import Console
7
8
  from devservices.utils.services import find_matching_service
8
9
 
9
10
 
@@ -22,20 +23,21 @@ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
22
23
 
23
24
  def list_dependencies(args: Namespace) -> None:
24
25
  """List the dependencies of a service."""
26
+ console = Console()
25
27
  service_name = args.service_name
26
28
 
27
29
  try:
28
30
  service = find_matching_service(service_name)
29
31
  except Exception as e:
30
- print(e)
32
+ console.failure(str(e))
31
33
  exit(1)
32
34
 
33
35
  dependencies = service.config.dependencies
34
36
 
35
37
  if not dependencies:
36
- print(f"No dependencies found for {service.name}")
38
+ console.info(f"No dependencies found for {service.name}")
37
39
  return
38
40
 
39
- print(f"Dependencies of {service.name}:")
41
+ console.info(f"Dependencies of {service.name}:")
40
42
  for dependency_key, dependency_info in dependencies.items():
41
- print("-", dependency_key, ":", dependency_info.description)
43
+ console.info("-" + dependency_key + ":" + dependency_info.description)
@@ -4,9 +4,10 @@ from argparse import _SubParsersAction
4
4
  from argparse import ArgumentParser
5
5
  from argparse import Namespace
6
6
 
7
+ from devservices.utils.console import Console
7
8
  from devservices.utils.devenv import get_coderoot
8
- from devservices.utils.docker_compose import get_active_docker_compose_projects
9
9
  from devservices.utils.services import get_local_services
10
+ from devservices.utils.state import State
10
11
 
11
12
 
12
13
  def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
@@ -24,34 +25,35 @@ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
24
25
 
25
26
  def list_services(args: Namespace) -> None:
26
27
  """List the services installed locally."""
27
-
28
+ console = Console()
28
29
  # Get all of the services installed locally
29
30
  coderoot = get_coderoot()
30
31
  services = get_local_services(coderoot)
31
- running_projects = get_active_docker_compose_projects()
32
+ state = State()
33
+ running_services = state.get_started_services()
32
34
 
33
35
  if not services:
34
- print("No services found")
36
+ console.warning("No services found")
35
37
  return
36
38
 
37
39
  services_to_show = (
38
- services if args.all else [s for s in services if s.name in running_projects]
40
+ services if args.all else [s for s in services if s.name in running_services]
39
41
  )
40
42
 
41
43
  if args.all:
42
- print("Services installed locally:")
44
+ console.info("Services installed locally:")
43
45
  else:
44
- print("Running services:")
46
+ console.info("Running services:")
45
47
 
46
48
  for service in services_to_show:
47
- status = "running" if service.name in running_projects else "stopped"
48
- print(f"- {service.name}")
49
- print(f" status: {status}")
50
- print(f" location: {service.repo_path}")
49
+ status = "running" if service.name in running_services else "stopped"
50
+ console.info(f"- {service.name}")
51
+ console.info(f" status: {status}")
52
+ console.info(f" location: {service.repo_path}")
51
53
 
52
54
  if not args.all:
53
55
  stopped_count = len(services) - len(services_to_show)
54
56
  if stopped_count > 0:
55
- print(
57
+ console.info(
56
58
  f"\n{stopped_count} stopped service(s) not shown. Use --all/-a to see them."
57
59
  )
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from argparse import _SubParsersAction
4
+ from argparse import ArgumentParser
5
+ from argparse import Namespace
6
+
7
+ from devservices.constants import MAX_LOG_LINES
8
+ from devservices.exceptions import DependencyError
9
+ from devservices.exceptions import DockerComposeError
10
+ from devservices.utils.console import Console
11
+ from devservices.utils.dependencies import install_and_verify_dependencies
12
+ from devservices.utils.docker_compose import run_docker_compose_command
13
+ from devservices.utils.services import find_matching_service
14
+ from devservices.utils.state import State
15
+
16
+
17
+ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
18
+ parser = subparsers.add_parser("logs", help="View logs for a service")
19
+ parser.add_argument(
20
+ "service_name",
21
+ help="Name of the service to view logs for",
22
+ nargs="?",
23
+ default=None,
24
+ )
25
+ parser.set_defaults(func=logs)
26
+
27
+
28
+ def logs(args: Namespace) -> None:
29
+ """View the logs for a specified service."""
30
+ console = Console()
31
+ service_name = args.service_name
32
+ try:
33
+ service = find_matching_service(service_name)
34
+ except Exception as e:
35
+ console.failure(str(e))
36
+ exit(1)
37
+
38
+ modes = service.config.modes
39
+ # TODO: allow custom modes to be used
40
+ mode_to_use = "default"
41
+ mode_dependencies = modes[mode_to_use]
42
+
43
+ state = State()
44
+ running_services = state.get_started_services()
45
+ if service_name not in running_services:
46
+ console.warning(f"Service {service_name} is not running")
47
+ return
48
+
49
+ try:
50
+ remote_dependencies = install_and_verify_dependencies(service)
51
+ except DependencyError as de:
52
+ console.failure(str(de))
53
+ exit(1)
54
+ try:
55
+ logs_output = run_docker_compose_command(
56
+ service,
57
+ "logs",
58
+ mode_dependencies,
59
+ remote_dependencies,
60
+ options=["-n", MAX_LOG_LINES],
61
+ )
62
+ except DockerComposeError as dce:
63
+ console.failure(f"Failed to get logs for {service.name}: {dce.stderr}")
64
+ exit(1)
65
+ for log in logs_output:
66
+ log_stdout: str | None = log.stdout
67
+ if log_stdout is not None:
68
+ console.info(log_stdout)
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import shutil
5
+ from argparse import _SubParsersAction
6
+ from argparse import ArgumentParser
7
+ from argparse import Namespace
8
+
9
+ from devservices.constants import DEVSERVICES_CACHE_DIR
10
+ from devservices.exceptions import DockerDaemonNotRunningError
11
+ from devservices.utils.console import Console
12
+ from devservices.utils.console import Status
13
+ from devservices.utils.docker import stop_all_running_containers
14
+ from devservices.utils.state import State
15
+
16
+
17
+ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
18
+ parser = subparsers.add_parser("purge", help="Purge the local devservices cache")
19
+ parser.set_defaults(func=purge)
20
+
21
+
22
+ def purge(args: Namespace) -> None:
23
+ """Purge the local devservices cache."""
24
+ console = Console()
25
+ # Prompt the user to stop all running containers
26
+ should_stop_containers = console.confirm(
27
+ "Warning: Purging stops all running containers and clears devservices state. Would you like to continue?"
28
+ )
29
+ if not should_stop_containers:
30
+ console.warning("Purge canceled")
31
+ return
32
+
33
+ if os.path.exists(DEVSERVICES_CACHE_DIR):
34
+ try:
35
+ shutil.rmtree(DEVSERVICES_CACHE_DIR)
36
+ except PermissionError as e:
37
+ console.failure(f"Failed to purge cache: {e}")
38
+ exit(1)
39
+ state = State()
40
+ state.clear_state()
41
+ with Status(
42
+ lambda: console.warning("Stopping all running containers"),
43
+ lambda: console.success("All running containers have been stopped"),
44
+ ):
45
+ try:
46
+ stop_all_running_containers()
47
+ except DockerDaemonNotRunningError:
48
+ console.warning("The docker daemon not running, no containers to stop")
49
+
50
+ console.success("The local devservices cache and state has been purged")
@@ -4,10 +4,14 @@ from argparse import _SubParsersAction
4
4
  from argparse import ArgumentParser
5
5
  from argparse import Namespace
6
6
 
7
+ from devservices.exceptions import DependencyError
7
8
  from devservices.exceptions import DockerComposeError
9
+ from devservices.utils.console import Console
8
10
  from devservices.utils.console import Status
11
+ from devservices.utils.dependencies import install_and_verify_dependencies
9
12
  from devservices.utils.docker_compose import run_docker_compose_command
10
13
  from devservices.utils.services import find_matching_service
14
+ from devservices.utils.state import State
11
15
 
12
16
 
13
17
  def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
@@ -15,16 +19,23 @@ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
15
19
  parser.add_argument(
16
20
  "service_name", help="Name of the service to start", nargs="?", default=None
17
21
  )
22
+ parser.add_argument(
23
+ "--debug",
24
+ help="Enable debug mode",
25
+ action="store_true",
26
+ default=False,
27
+ )
18
28
  parser.set_defaults(func=start)
19
29
 
20
30
 
21
31
  def start(args: Namespace) -> None:
22
32
  """Start a service and its dependencies."""
33
+ console = Console()
23
34
  service_name = args.service_name
24
35
  try:
25
36
  service = find_matching_service(service_name)
26
37
  except Exception as e:
27
- print(e)
38
+ console.failure(str(e))
28
39
  exit(1)
29
40
 
30
41
  modes = service.config.modes
@@ -32,11 +43,28 @@ def start(args: Namespace) -> None:
32
43
  mode_to_start = "default"
33
44
  mode_dependencies = modes[mode_to_start]
34
45
 
35
- with Status(f"Starting {service.name}", f"{service.name} started") as status:
46
+ with Status(
47
+ lambda: console.warning(f"Starting {service.name}"),
48
+ lambda: console.success(f"{service.name} started"),
49
+ ) as status:
50
+ try:
51
+ remote_dependencies = install_and_verify_dependencies(
52
+ service, force_update_dependencies=True
53
+ )
54
+ except DependencyError as de:
55
+ status.failure(str(de))
56
+ exit(1)
36
57
  try:
37
58
  run_docker_compose_command(
38
- service, "up", mode_dependencies, ["-d"], force_update_dependencies=True
59
+ service,
60
+ "up",
61
+ mode_dependencies,
62
+ remote_dependencies,
63
+ options=["-d"],
39
64
  )
40
65
  except DockerComposeError as dce:
41
- status.print(f"Failed to start {service.name}: {dce.stderr}")
66
+ status.failure(f"Failed to start {service.name}: {dce.stderr}")
42
67
  exit(1)
68
+ # TODO: We should factor in healthchecks here before marking service as running
69
+ state = State()
70
+ state.add_started_service(service.name, mode_to_start)
@@ -1,12 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
- import sys
5
4
  from argparse import _SubParsersAction
6
5
  from argparse import ArgumentParser
7
6
  from argparse import Namespace
8
7
 
8
+ from devservices.exceptions import DependencyError
9
9
  from devservices.exceptions import DockerComposeError
10
+ from devservices.utils.console import Console
11
+ from devservices.utils.dependencies import install_and_verify_dependencies
10
12
  from devservices.utils.docker_compose import run_docker_compose_command
11
13
  from devservices.utils.services import find_matching_service
12
14
 
@@ -33,11 +35,13 @@ def format_status_output(status_json: str) -> str:
33
35
  service = json.loads(service_status)
34
36
  name = service["Service"]
35
37
  state = service["State"]
38
+ container_name = service["Name"]
36
39
  health = service.get("Health", "N/A")
37
40
  ports = service.get("Publishers", [])
38
41
  running_for = service.get("RunningFor", "N/A")
39
42
 
40
43
  output.append(f"{name}")
44
+ output.append(f"Container: {container_name}")
41
45
  output.append(f"Status: {state}")
42
46
  output.append(f"Health: {health}")
43
47
  output.append(f"Uptime: {running_for}")
@@ -58,11 +62,12 @@ def format_status_output(status_json: str) -> str:
58
62
 
59
63
  def status(args: Namespace) -> None:
60
64
  """Start a service and its dependencies."""
65
+ console = Console()
61
66
  service_name = args.service_name
62
67
  try:
63
68
  service = find_matching_service(service_name)
64
69
  except Exception as e:
65
- print(e)
70
+ console.failure(str(e))
66
71
  exit(1)
67
72
 
68
73
  modes = service.config.modes
@@ -71,19 +76,31 @@ def status(args: Namespace) -> None:
71
76
  mode_dependencies = modes[mode_to_view]
72
77
 
73
78
  try:
74
- status_jsons = run_docker_compose_command(
75
- service, "ps", mode_dependencies, options=["--format", "json"]
79
+ remote_dependencies = install_and_verify_dependencies(service)
80
+ except DependencyError as de:
81
+ console.failure(str(de))
82
+ exit(1)
83
+ try:
84
+ status_json_results = run_docker_compose_command(
85
+ service,
86
+ "ps",
87
+ mode_dependencies,
88
+ remote_dependencies,
89
+ options=["--format", "json"],
76
90
  )
77
91
  except DockerComposeError as dce:
78
- print(f"Failed to get status for {service.name}: {dce.stderr}")
92
+ console.failure(f"Failed to get status for {service.name}: {dce.stderr}")
79
93
  exit(1)
80
- # If the service is not running, the status_json will be empty
81
- if len(status_jsons) == 0:
82
- print(f"{service.name} is not running")
94
+
95
+ # Filter out empty stdout to help us determine if the service is running
96
+ status_json_results = [
97
+ status_json for status_json in status_json_results if status_json.stdout
98
+ ]
99
+ if len(status_json_results) == 0:
100
+ console.warning(f"{service.name} is not running")
83
101
  return
84
102
  output = f"Service: {service.name}\n\n"
85
- for status_json in status_jsons:
103
+ for status_json in status_json_results:
86
104
  output += format_status_output(status_json.stdout)
87
105
  output += "=" * LINE_LENGTH
88
- sys.stdout.write(output + "\n")
89
- sys.stdout.flush()
106
+ console.info(output + "\n")
@@ -0,0 +1,78 @@
1
+ from __future__ import annotations
2
+
3
+ from argparse import _SubParsersAction
4
+ from argparse import ArgumentParser
5
+ from argparse import Namespace
6
+
7
+ from devservices.exceptions import DependencyError
8
+ from devservices.exceptions import DockerComposeError
9
+ from devservices.utils.console import Console
10
+ from devservices.utils.console import Status
11
+ from devservices.utils.dependencies import get_non_shared_remote_dependencies
12
+ from devservices.utils.dependencies import install_and_verify_dependencies
13
+ from devservices.utils.docker_compose import run_docker_compose_command
14
+ from devservices.utils.services import find_matching_service
15
+ from devservices.utils.state import State
16
+
17
+
18
+ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
19
+ parser = subparsers.add_parser("stop", help="Stop a service and its dependencies")
20
+ parser.add_argument(
21
+ "service_name", help="Name of the service to stop", nargs="?", default=None
22
+ )
23
+ parser.add_argument(
24
+ "--debug",
25
+ help="Enable debug mode",
26
+ action="store_true",
27
+ default=False,
28
+ )
29
+ parser.set_defaults(func=stop)
30
+
31
+
32
+ def stop(args: Namespace) -> None:
33
+ """Stop a service and its dependencies."""
34
+ console = Console()
35
+ service_name = args.service_name
36
+ try:
37
+ service = find_matching_service(service_name)
38
+ except Exception as e:
39
+ console.failure(str(e))
40
+ exit(1)
41
+
42
+ modes = service.config.modes
43
+ # TODO: allow custom modes to be used
44
+ mode_to_stop = "default"
45
+ mode_dependencies = modes[mode_to_stop]
46
+
47
+ state = State()
48
+ started_services = state.get_started_services()
49
+ if service.name not in started_services:
50
+ console.warning(f"{service.name} is not running")
51
+ exit(0)
52
+
53
+ with Status(
54
+ lambda: console.warning(f"Stopping {service.name}"),
55
+ lambda: console.success(f"{service.name} stopped"),
56
+ ) as status:
57
+ try:
58
+ remote_dependencies = install_and_verify_dependencies(service)
59
+ except DependencyError as de:
60
+ status.failure(str(de))
61
+ exit(1)
62
+ remote_dependencies = get_non_shared_remote_dependencies(
63
+ service, remote_dependencies
64
+ )
65
+ try:
66
+ run_docker_compose_command(
67
+ service,
68
+ "down",
69
+ mode_dependencies,
70
+ remote_dependencies,
71
+ )
72
+ except DockerComposeError as dce:
73
+ status.failure(f"Failed to stop {service.name}: {dce.stderr}")
74
+ exit(1)
75
+
76
+ # TODO: We should factor in healthchecks here before marking service as stopped
77
+ state = State()
78
+ state.remove_started_service(service.name)
@@ -11,6 +11,7 @@ from devservices.commands.check_for_update import check_for_update
11
11
  from devservices.constants import DEVSERVICES_DOWNLOAD_URL
12
12
  from devservices.exceptions import BinaryInstallError
13
13
  from devservices.exceptions import DevservicesUpdateError
14
+ from devservices.utils.console import Console
14
15
  from devservices.utils.install_binary import install_binary
15
16
 
16
17
 
@@ -21,14 +22,16 @@ def is_in_virtualenv() -> bool:
21
22
 
22
23
 
23
24
  def update_version(exec_path: str, latest_version: str) -> None:
25
+ console = Console()
24
26
  system = platform.system().lower()
25
27
  url = f"{DEVSERVICES_DOWNLOAD_URL}/{latest_version}/devservices-{system}"
26
28
  try:
27
29
  install_binary("devservices", exec_path, latest_version, url)
28
30
  except BinaryInstallError as e:
29
- raise DevservicesUpdateError(f"Failed to update devservices: {e}")
31
+ console.failure(f"Failed to update devservices: {e}")
32
+ exit(1)
30
33
 
31
- print(f"Devservices {latest_version} updated successfully")
34
+ console.success(f"Devservices {latest_version} updated successfully")
32
35
 
33
36
 
34
37
  def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
@@ -39,6 +42,7 @@ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
39
42
 
40
43
 
41
44
  def update(args: Namespace) -> None:
45
+ console = Console()
42
46
  current_version = metadata.version("devservices")
43
47
  latest_version = check_for_update(current_version)
44
48
 
@@ -46,21 +50,21 @@ def update(args: Namespace) -> None:
46
50
  raise DevservicesUpdateError("Failed to check for updates.")
47
51
 
48
52
  if latest_version == current_version:
49
- print("You're already on the latest version.")
53
+ console.warning("You're already on the latest version.")
50
54
  return
51
55
 
52
- print(f"A new version of devservices is available: {latest_version}")
56
+ console.warning(f"A new version of devservices is available: {latest_version}")
53
57
 
54
58
  if is_in_virtualenv():
55
- print("You are running in a virtual environment.")
56
- print(
59
+ console.warning("You are running in a virtual environment.")
60
+ console.warning(
57
61
  "To update, please update your requirements.txt or requirements-dev.txt file with the new version."
58
62
  )
59
- print(
63
+ console.warning(
60
64
  f"For example, update the line in requirements.txt to: devservices=={latest_version}"
61
65
  )
62
- print("Then, run: pip install --update -r requirements.txt")
66
+ console.warning("Then, run: pip install --update -r requirements.txt")
63
67
  return
64
68
 
65
- print("Upgrading to the latest version...")
69
+ console.info("Upgrading to the latest version...")
66
70
  update_version(sys.executable, latest_version)
@@ -11,6 +11,7 @@ DEVSERVICES_CACHE_DIR = os.path.expanduser("~/.cache/sentry-devservices")
11
11
  DEVSERVICES_LOCAL_DIR = os.path.expanduser("~/.local/share/sentry-devservices")
12
12
  DEVSERVICES_DEPENDENCIES_CACHE_DIR = os.path.join(DEVSERVICES_CACHE_DIR, "dependencies")
13
13
  DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY = "DEVSERVICES_DEPENDENCIES_CACHE_DIR"
14
+ STATE_DB_FILE = os.path.join(DEVSERVICES_LOCAL_DIR, "state")
14
15
 
15
16
  DEPENDENCY_CONFIG_VERSION = "v1"
16
17
  DEPENDENCY_GIT_PARTIAL_CLONE_CONFIG_OPTIONS = {
@@ -22,3 +23,5 @@ DEPENDENCY_GIT_PARTIAL_CLONE_CONFIG_OPTIONS = {
22
23
  DOCKER_COMPOSE_DOWNLOAD_URL = "https://github.com/docker/compose/releases/download"
23
24
  DEVSERVICES_DOWNLOAD_URL = "https://github.com/getsentry/devservices/releases/download"
24
25
  BINARY_PERMISSIONS = 0o755
26
+ MAX_LOG_LINES = "100"
27
+ LOGGER_NAME = "devservices"
@@ -43,6 +43,14 @@ class DevservicesUpdateError(BinaryInstallError):
43
43
  pass
44
44
 
45
45
 
46
+ class DockerDaemonNotRunningError(Exception):
47
+ """Raised when the Docker daemon is not running."""
48
+
49
+ def __str__(self) -> str:
50
+ # TODO: Provide explicit instructions on what to do
51
+ return "Unable to connect to the docker daemon. Is the docker daemon running?"
52
+
53
+
46
54
  class DockerComposeInstallationError(BinaryInstallError):
47
55
  """Raised when the Docker Compose installation fails."""
48
56
 
@@ -67,17 +75,29 @@ class DependencyError(Exception):
67
75
  self.repo_link = repo_link
68
76
  self.branch = branch
69
77
 
78
+ def __str__(self) -> str:
79
+ return f"DependencyError: {self.repo_name} ({self.repo_link}) on {self.branch}"
80
+
81
+
82
+ class UnableToCloneDependencyError(DependencyError):
83
+ """Raised when a dependency is unable to be cloned."""
84
+
85
+ def __str__(self) -> str:
86
+ return f"Unable to clone dependency: {self.repo_name} ({self.repo_link}) on {self.branch}"
87
+
70
88
 
71
89
  class InvalidDependencyConfigError(DependencyError):
72
90
  """Raised when a dependency's config is invalid."""
73
91
 
74
- pass
92
+ def __str__(self) -> str:
93
+ return f"Invalid config for dependency: {self.repo_name} ({self.repo_link}) on {self.branch}"
75
94
 
76
95
 
77
96
  class DependencyNotInstalledError(DependencyError):
78
97
  """Raised when a dependency is not installed correctly."""
79
98
 
80
- pass
99
+ def __str__(self) -> str:
100
+ return f"Dependency not installed correctly: {self.repo_name} ({self.repo_link}) on {self.branch}"
81
101
 
82
102
 
83
103
  class GitConfigError(Exception):