devservices 0.0.5__tar.gz → 1.0.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 (55) hide show
  1. {devservices-0.0.5 → devservices-1.0.1}/PKG-INFO +1 -1
  2. {devservices-0.0.5 → devservices-1.0.1}/README.md +4 -9
  3. {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/list_dependencies.py +6 -4
  4. {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/list_services.py +9 -8
  5. {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/logs.py +19 -10
  6. devservices-1.0.1/devservices/commands/purge.py +50 -0
  7. {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/start.py +24 -8
  8. {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/status.py +17 -10
  9. {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/stop.py +30 -6
  10. {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/update.py +13 -9
  11. {devservices-0.0.5 → devservices-1.0.1}/devservices/constants.py +1 -0
  12. {devservices-0.0.5 → devservices-1.0.1}/devservices/exceptions.py +3 -1
  13. {devservices-0.0.5 → devservices-1.0.1}/devservices/main.py +15 -5
  14. devservices-1.0.1/devservices/utils/console.py +125 -0
  15. {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/dependencies.py +21 -12
  16. devservices-1.0.1/devservices/utils/docker.py +36 -0
  17. {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/docker_compose.py +13 -26
  18. {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/install_binary.py +9 -5
  19. {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/services.py +4 -1
  20. {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/state.py +9 -0
  21. {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/PKG-INFO +1 -1
  22. {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/SOURCES.txt +1 -0
  23. {devservices-0.0.5 → devservices-1.0.1}/pyproject.toml +1 -1
  24. devservices-1.0.1/tests/commands/test_purge.py +132 -0
  25. {devservices-0.0.5 → devservices-1.0.1}/tests/commands/test_start.py +51 -2
  26. {devservices-0.0.5 → devservices-1.0.1}/tests/commands/test_stop.py +2 -2
  27. {devservices-0.0.5 → devservices-1.0.1}/tests/commands/test_update.py +1 -3
  28. devservices-1.0.1/tests/utils/test_docker.py +47 -0
  29. devservices-0.0.5/devservices/commands/purge.py +0 -25
  30. devservices-0.0.5/devservices/utils/console.py +0 -67
  31. devservices-0.0.5/devservices/utils/docker.py +0 -19
  32. devservices-0.0.5/tests/commands/test_purge.py +0 -35
  33. {devservices-0.0.5 → devservices-1.0.1}/LICENSE.md +0 -0
  34. {devservices-0.0.5 → devservices-1.0.1}/devservices/__init__.py +0 -0
  35. {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/__init__.py +0 -0
  36. {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/check_for_update.py +0 -0
  37. {devservices-0.0.5 → devservices-1.0.1}/devservices/configs/service_config.py +0 -0
  38. {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/__init__.py +0 -0
  39. {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/devenv.py +0 -0
  40. {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/file_lock.py +0 -0
  41. {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/dependency_links.txt +0 -0
  42. {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/entry_points.txt +0 -0
  43. {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/requires.txt +0 -0
  44. {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/top_level.txt +0 -0
  45. {devservices-0.0.5 → devservices-1.0.1}/setup.cfg +0 -0
  46. {devservices-0.0.5 → devservices-1.0.1}/testing/__init__.py +0 -0
  47. {devservices-0.0.5 → devservices-1.0.1}/testing/utils.py +0 -0
  48. {devservices-0.0.5 → devservices-1.0.1}/tests/__init__.py +0 -0
  49. {devservices-0.0.5 → devservices-1.0.1}/tests/commands/test_list_services.py +0 -0
  50. {devservices-0.0.5 → devservices-1.0.1}/tests/configs/test_service_config.py +0 -0
  51. {devservices-0.0.5 → devservices-1.0.1}/tests/conftest.py +0 -0
  52. {devservices-0.0.5 → devservices-1.0.1}/tests/utils/test_dependencies.py +0 -0
  53. {devservices-0.0.5 → devservices-1.0.1}/tests/utils/test_docker_compose.py +0 -0
  54. {devservices-0.0.5 → devservices-1.0.1}/tests/utils/test_install_binary.py +0 -0
  55. {devservices-0.0.5 → devservices-1.0.1}/tests/utils/test_state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: devservices
3
- Version: 0.0.5
3
+ Version: 1.0.1
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.5/devservices-$PLATFORM" -o "$INSTALL_DIR/devservices"
17
- chmod +x "$INSTALL_DIR/devservices"
14
+ devservices==1.0.1
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.5
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,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.devenv import get_coderoot
8
9
  from devservices.utils.services import get_local_services
9
10
  from devservices.utils.state import State
@@ -24,7 +25,7 @@ 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)
@@ -32,7 +33,7 @@ def list_services(args: Namespace) -> None:
32
33
  running_services = state.get_started_services()
33
34
 
34
35
  if not services:
35
- print("No services found")
36
+ console.warning("No services found")
36
37
  return
37
38
 
38
39
  services_to_show = (
@@ -40,19 +41,19 @@ def list_services(args: Namespace) -> None:
40
41
  )
41
42
 
42
43
  if args.all:
43
- print("Services installed locally:")
44
+ console.info("Services installed locally:")
44
45
  else:
45
- print("Running services:")
46
+ console.info("Running services:")
46
47
 
47
48
  for service in services_to_show:
48
49
  status = "running" if service.name in running_services else "stopped"
49
- print(f"- {service.name}")
50
- print(f" status: {status}")
51
- print(f" location: {service.repo_path}")
50
+ console.info(f"- {service.name}")
51
+ console.info(f" status: {status}")
52
+ console.info(f" location: {service.repo_path}")
52
53
 
53
54
  if not args.all:
54
55
  stopped_count = len(services) - len(services_to_show)
55
56
  if stopped_count > 0:
56
- print(
57
+ console.info(
57
58
  f"\n{stopped_count} stopped service(s) not shown. Use --all/-a to see them."
58
59
  )
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import sys
4
3
  from argparse import _SubParsersAction
5
4
  from argparse import ArgumentParser
6
5
  from argparse import Namespace
@@ -8,6 +7,8 @@ from argparse import Namespace
8
7
  from devservices.constants import MAX_LOG_LINES
9
8
  from devservices.exceptions import DependencyError
10
9
  from devservices.exceptions import DockerComposeError
10
+ from devservices.utils.console import Console
11
+ from devservices.utils.dependencies import install_and_verify_dependencies
11
12
  from devservices.utils.docker_compose import run_docker_compose_command
12
13
  from devservices.utils.services import find_matching_service
13
14
  from devservices.utils.state import State
@@ -26,11 +27,12 @@ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
26
27
 
27
28
  def logs(args: Namespace) -> None:
28
29
  """View the logs for a specified service."""
30
+ console = Console()
29
31
  service_name = args.service_name
30
32
  try:
31
33
  service = find_matching_service(service_name)
32
34
  except Exception as e:
33
- print(e)
35
+ console.failure(str(e))
34
36
  exit(1)
35
37
 
36
38
  modes = service.config.modes
@@ -41,19 +43,26 @@ def logs(args: Namespace) -> None:
41
43
  state = State()
42
44
  running_services = state.get_started_services()
43
45
  if service_name not in running_services:
44
- print(f"Service {service_name} is not running")
46
+ console.warning(f"Service {service_name} is not running")
45
47
  return
46
48
 
47
49
  try:
48
- logs_output = run_docker_compose_command(
49
- service, "logs", mode_dependencies, options=["-n", MAX_LOG_LINES]
50
- )
50
+ remote_dependencies = install_and_verify_dependencies(service)
51
51
  except DependencyError as de:
52
- print(str(de))
52
+ console.failure(str(de))
53
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
+ )
54
62
  except DockerComposeError as dce:
55
- print(f"Failed to get logs for {service.name}: {dce.stderr}")
63
+ console.failure(f"Failed to get logs for {service.name}: {dce.stderr}")
56
64
  exit(1)
57
65
  for log in logs_output:
58
- sys.stdout.write(log.stdout)
59
- sys.stdout.flush()
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")
@@ -6,7 +6,9 @@ from argparse import Namespace
6
6
 
7
7
  from devservices.exceptions import DependencyError
8
8
  from devservices.exceptions import DockerComposeError
9
+ from devservices.utils.console import Console
9
10
  from devservices.utils.console import Status
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
  from devservices.utils.state import State
@@ -17,16 +19,23 @@ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
17
19
  parser.add_argument(
18
20
  "service_name", help="Name of the service to start", nargs="?", default=None
19
21
  )
22
+ parser.add_argument(
23
+ "--debug",
24
+ help="Enable debug mode",
25
+ action="store_true",
26
+ default=False,
27
+ )
20
28
  parser.set_defaults(func=start)
21
29
 
22
30
 
23
31
  def start(args: Namespace) -> None:
24
32
  """Start a service and its dependencies."""
33
+ console = Console()
25
34
  service_name = args.service_name
26
35
  try:
27
36
  service = find_matching_service(service_name)
28
37
  except Exception as e:
29
- print(e)
38
+ console.failure(str(e))
30
39
  exit(1)
31
40
 
32
41
  modes = service.config.modes
@@ -34,20 +43,27 @@ def start(args: Namespace) -> None:
34
43
  mode_to_start = "default"
35
44
  mode_dependencies = modes[mode_to_start]
36
45
 
37
- 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)
38
57
  try:
39
58
  run_docker_compose_command(
40
59
  service,
41
60
  "up",
42
61
  mode_dependencies,
43
- ["-d"],
44
- force_update_dependencies=True,
62
+ remote_dependencies,
63
+ options=["-d"],
45
64
  )
46
- except DependencyError as de:
47
- status.print(str(de))
48
- exit(1)
49
65
  except DockerComposeError as dce:
50
- status.print(f"Failed to start {service.name}: {dce.stderr}")
66
+ status.failure(f"Failed to start {service.name}: {dce.stderr}")
51
67
  exit(1)
52
68
  # TODO: We should factor in healthchecks here before marking service as running
53
69
  state = State()
@@ -1,13 +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
 
9
8
  from devservices.exceptions import DependencyError
10
9
  from devservices.exceptions import DockerComposeError
10
+ from devservices.utils.console import Console
11
+ from devservices.utils.dependencies import install_and_verify_dependencies
11
12
  from devservices.utils.docker_compose import run_docker_compose_command
12
13
  from devservices.utils.services import find_matching_service
13
14
 
@@ -61,11 +62,12 @@ def format_status_output(status_json: str) -> str:
61
62
 
62
63
  def status(args: Namespace) -> None:
63
64
  """Start a service and its dependencies."""
65
+ console = Console()
64
66
  service_name = args.service_name
65
67
  try:
66
68
  service = find_matching_service(service_name)
67
69
  except Exception as e:
68
- print(e)
70
+ console.failure(str(e))
69
71
  exit(1)
70
72
 
71
73
  modes = service.config.modes
@@ -74,14 +76,20 @@ def status(args: Namespace) -> None:
74
76
  mode_dependencies = modes[mode_to_view]
75
77
 
76
78
  try:
77
- status_json_results = run_docker_compose_command(
78
- service, "ps", mode_dependencies, options=["--format", "json"]
79
- )
79
+ remote_dependencies = install_and_verify_dependencies(service)
80
80
  except DependencyError as de:
81
- print(str(de))
81
+ console.failure(str(de))
82
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"],
90
+ )
83
91
  except DockerComposeError as dce:
84
- print(f"Failed to get status for {service.name}: {dce.stderr}")
92
+ console.failure(f"Failed to get status for {service.name}: {dce.stderr}")
85
93
  exit(1)
86
94
 
87
95
  # Filter out empty stdout to help us determine if the service is running
@@ -89,11 +97,10 @@ def status(args: Namespace) -> None:
89
97
  status_json for status_json in status_json_results if status_json.stdout
90
98
  ]
91
99
  if len(status_json_results) == 0:
92
- print(f"{service.name} is not running")
100
+ console.warning(f"{service.name} is not running")
93
101
  return
94
102
  output = f"Service: {service.name}\n\n"
95
103
  for status_json in status_json_results:
96
104
  output += format_status_output(status_json.stdout)
97
105
  output += "=" * LINE_LENGTH
98
- sys.stdout.write(output + "\n")
99
- sys.stdout.flush()
106
+ console.info(output + "\n")
@@ -6,7 +6,10 @@ from argparse import Namespace
6
6
 
7
7
  from devservices.exceptions import DependencyError
8
8
  from devservices.exceptions import DockerComposeError
9
+ from devservices.utils.console import Console
9
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
10
13
  from devservices.utils.docker_compose import run_docker_compose_command
11
14
  from devservices.utils.services import find_matching_service
12
15
  from devservices.utils.state import State
@@ -17,36 +20,57 @@ def add_parser(subparsers: _SubParsersAction[ArgumentParser]) -> None:
17
20
  parser.add_argument(
18
21
  "service_name", help="Name of the service to stop", nargs="?", default=None
19
22
  )
23
+ parser.add_argument(
24
+ "--debug",
25
+ help="Enable debug mode",
26
+ action="store_true",
27
+ default=False,
28
+ )
20
29
  parser.set_defaults(func=stop)
21
30
 
22
31
 
23
32
  def stop(args: Namespace) -> None:
24
33
  """Stop a service and its dependencies."""
34
+ console = Console()
25
35
  service_name = args.service_name
26
36
  try:
27
37
  service = find_matching_service(service_name)
28
38
  except Exception as e:
29
- print(e)
39
+ console.failure(str(e))
30
40
  exit(1)
31
41
 
32
42
  modes = service.config.modes
33
43
  # TODO: allow custom modes to be used
34
44
  mode_to_stop = "default"
35
45
  mode_dependencies = modes[mode_to_stop]
46
+
36
47
  state = State()
37
48
  started_services = state.get_started_services()
38
49
  if service.name not in started_services:
39
- print(f"{service.name} is not running")
50
+ console.warning(f"{service.name} is not running")
40
51
  exit(0)
41
52
 
42
- with Status(f"Stopping {service.name}", f"{service.name} stopped") as status:
53
+ with Status(
54
+ lambda: console.warning(f"Stopping {service.name}"),
55
+ lambda: console.success(f"{service.name} stopped"),
56
+ ) as status:
43
57
  try:
44
- run_docker_compose_command(service, "down", mode_dependencies)
58
+ remote_dependencies = install_and_verify_dependencies(service)
45
59
  except DependencyError as de:
46
- status.print(str(de))
60
+ status.failure(str(de))
47
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
+ )
48
72
  except DockerComposeError as dce:
49
- status.print(f"Failed to stop {service.name}: {dce.stderr}")
73
+ status.failure(f"Failed to stop {service.name}: {dce.stderr}")
50
74
  exit(1)
51
75
 
52
76
  # TODO: We should factor in healthchecks here before marking service as stopped
@@ -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)
@@ -24,3 +24,4 @@ DOCKER_COMPOSE_DOWNLOAD_URL = "https://github.com/docker/compose/releases/downlo
24
24
  DEVSERVICES_DOWNLOAD_URL = "https://github.com/getsentry/devservices/releases/download"
25
25
  BINARY_PERMISSIONS = 0o755
26
26
  MAX_LOG_LINES = "100"
27
+ LOGGER_NAME = "devservices"
@@ -46,7 +46,9 @@ class DevservicesUpdateError(BinaryInstallError):
46
46
  class DockerDaemonNotRunningError(Exception):
47
47
  """Raised when the Docker daemon is not running."""
48
48
 
49
- pass
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?"
50
52
 
51
53
 
52
54
  class DockerComposeInstallationError(BinaryInstallError):
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import argparse
4
4
  import atexit
5
+ import logging
5
6
  import os
6
7
  from importlib import metadata
7
8
 
@@ -17,8 +18,10 @@ from devservices.commands import status
17
18
  from devservices.commands import stop
18
19
  from devservices.commands import update
19
20
  from devservices.commands.check_for_update import check_for_update
21
+ from devservices.constants import LOGGER_NAME
20
22
  from devservices.exceptions import DockerComposeInstallationError
21
23
  from devservices.exceptions import DockerDaemonNotRunningError
24
+ from devservices.utils.console import Console
22
25
  from devservices.utils.docker_compose import check_docker_compose_version
23
26
 
24
27
  sentry_environment = (
@@ -26,6 +29,7 @@ sentry_environment = (
26
29
  )
27
30
 
28
31
  disable_sentry = os.environ.get("DISABLE_SENTRY", default=False)
32
+ logging.basicConfig(level=logging.INFO)
29
33
 
30
34
  if not disable_sentry:
31
35
  sentry_sdk.init(
@@ -44,13 +48,14 @@ def cleanup() -> None:
44
48
 
45
49
 
46
50
  def main() -> None:
51
+ console = Console()
47
52
  try:
48
53
  check_docker_compose_version()
49
54
  except DockerDaemonNotRunningError as e:
50
- print(e)
55
+ console.failure(str(e))
51
56
  exit(1)
52
57
  except DockerComposeInstallationError:
53
- print("Failed to ensure docker compose is installed and up-to-date")
58
+ console.failure("Failed to ensure docker compose is installed and up-to-date")
54
59
  exit(1)
55
60
  parser = argparse.ArgumentParser(
56
61
  prog="devservices",
@@ -75,6 +80,11 @@ def main() -> None:
75
80
 
76
81
  args = parser.parse_args()
77
82
 
83
+ # If the command has a debug flag, set the logger to debug
84
+ if "debug" in args and args.debug:
85
+ logger = logging.getLogger(LOGGER_NAME)
86
+ logger.setLevel(logging.DEBUG)
87
+
78
88
  if args.command:
79
89
  # Call the appropriate function based on the command
80
90
  with sentry_sdk.start_transaction(op="command", name=args.command):
@@ -85,10 +95,10 @@ def main() -> None:
85
95
  if args.command != "update":
86
96
  newest_version = check_for_update(metadata.version("devservices"))
87
97
  if newest_version != metadata.version("devservices"):
88
- print(
89
- f"\n\033[93mWARNING: A new version of devservices is available: {newest_version}\033[0m"
98
+ console.warning(
99
+ f"WARNING: A new version of devservices is available: {newest_version}"
90
100
  )
91
- print("To update, run: \033[1mdevservices update\033[0m")
101
+ console.warning('To update, run: "devservices update"')
92
102
 
93
103
 
94
104
  if __name__ == "__main__":