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.
- {devservices-0.0.5 → devservices-1.0.1}/PKG-INFO +1 -1
- {devservices-0.0.5 → devservices-1.0.1}/README.md +4 -9
- {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/list_dependencies.py +6 -4
- {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/list_services.py +9 -8
- {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/logs.py +19 -10
- devservices-1.0.1/devservices/commands/purge.py +50 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/start.py +24 -8
- {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/status.py +17 -10
- {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/stop.py +30 -6
- {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/update.py +13 -9
- {devservices-0.0.5 → devservices-1.0.1}/devservices/constants.py +1 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/exceptions.py +3 -1
- {devservices-0.0.5 → devservices-1.0.1}/devservices/main.py +15 -5
- devservices-1.0.1/devservices/utils/console.py +125 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/dependencies.py +21 -12
- devservices-1.0.1/devservices/utils/docker.py +36 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/docker_compose.py +13 -26
- {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/install_binary.py +9 -5
- {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/services.py +4 -1
- {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/state.py +9 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/PKG-INFO +1 -1
- {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/SOURCES.txt +1 -0
- {devservices-0.0.5 → devservices-1.0.1}/pyproject.toml +1 -1
- devservices-1.0.1/tests/commands/test_purge.py +132 -0
- {devservices-0.0.5 → devservices-1.0.1}/tests/commands/test_start.py +51 -2
- {devservices-0.0.5 → devservices-1.0.1}/tests/commands/test_stop.py +2 -2
- {devservices-0.0.5 → devservices-1.0.1}/tests/commands/test_update.py +1 -3
- devservices-1.0.1/tests/utils/test_docker.py +47 -0
- devservices-0.0.5/devservices/commands/purge.py +0 -25
- devservices-0.0.5/devservices/utils/console.py +0 -67
- devservices-0.0.5/devservices/utils/docker.py +0 -19
- devservices-0.0.5/tests/commands/test_purge.py +0 -35
- {devservices-0.0.5 → devservices-1.0.1}/LICENSE.md +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/__init__.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/__init__.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/commands/check_for_update.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/configs/service_config.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/__init__.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/devenv.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices/utils/file_lock.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/dependency_links.txt +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/entry_points.txt +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/requires.txt +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/devservices.egg-info/top_level.txt +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/setup.cfg +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/testing/__init__.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/testing/utils.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/tests/__init__.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/tests/commands/test_list_services.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/tests/configs/test_service_config.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/tests/conftest.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/tests/utils/test_dependencies.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/tests/utils/test_docker_compose.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/tests/utils/test_install_binary.py +0 -0
- {devservices-0.0.5 → devservices-1.0.1}/tests/utils/test_state.py +0 -0
|
@@ -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
|
-
|
|
11
|
+
The recommended way to install devservices is through a virtualenv in the requirements.txt.
|
|
12
12
|
|
|
13
13
|
```
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38
|
+
console.info(f"No dependencies found for {service.name}")
|
|
37
39
|
return
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
console.info(f"Dependencies of {service.name}:")
|
|
40
42
|
for dependency_key, dependency_info in dependencies.items():
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
+
console.info("Services installed locally:")
|
|
44
45
|
else:
|
|
45
|
-
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
46
|
+
console.warning(f"Service {service_name} is not running")
|
|
45
47
|
return
|
|
46
48
|
|
|
47
49
|
try:
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
44
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
service, "ps", mode_dependencies, options=["--format", "json"]
|
|
79
|
-
)
|
|
79
|
+
remote_dependencies = install_and_verify_dependencies(service)
|
|
80
80
|
except DependencyError as de:
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
50
|
+
console.warning(f"{service.name} is not running")
|
|
40
51
|
exit(0)
|
|
41
52
|
|
|
42
|
-
with 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
|
-
|
|
58
|
+
remote_dependencies = install_and_verify_dependencies(service)
|
|
45
59
|
except DependencyError as de:
|
|
46
|
-
status.
|
|
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.
|
|
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
|
-
|
|
31
|
+
console.failure(f"Failed to update devservices: {e}")
|
|
32
|
+
exit(1)
|
|
30
33
|
|
|
31
|
-
|
|
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
|
-
|
|
53
|
+
console.warning("You're already on the latest version.")
|
|
50
54
|
return
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
console.warning(f"A new version of devservices is available: {latest_version}")
|
|
53
57
|
|
|
54
58
|
if is_in_virtualenv():
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
63
|
+
console.warning(
|
|
60
64
|
f"For example, update the line in requirements.txt to: devservices=={latest_version}"
|
|
61
65
|
)
|
|
62
|
-
|
|
66
|
+
console.warning("Then, run: pip install --update -r requirements.txt")
|
|
63
67
|
return
|
|
64
68
|
|
|
65
|
-
|
|
69
|
+
console.info("Upgrading to the latest version...")
|
|
66
70
|
update_version(sys.executable, latest_version)
|
|
@@ -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
|
-
|
|
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
|
-
|
|
55
|
+
console.failure(str(e))
|
|
51
56
|
exit(1)
|
|
52
57
|
except DockerComposeInstallationError:
|
|
53
|
-
|
|
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
|
-
|
|
89
|
-
f"
|
|
98
|
+
console.warning(
|
|
99
|
+
f"WARNING: A new version of devservices is available: {newest_version}"
|
|
90
100
|
)
|
|
91
|
-
|
|
101
|
+
console.warning('To update, run: "devservices update"')
|
|
92
102
|
|
|
93
103
|
|
|
94
104
|
if __name__ == "__main__":
|