cave-cli 3.5.5__tar.gz → 3.6.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.
- {cave_cli-3.5.5/cave_cli.egg-info → cave_cli-3.6.0}/PKG-INFO +29 -16
- {cave_cli-3.5.5 → cave_cli-3.6.0}/README.md +19 -15
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/cli.py +11 -3
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/create.py +6 -1
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/doctor.py +17 -10
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/list_cmd.py +9 -2
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/list_versions.py +9 -8
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/purge.py +13 -5
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/reset.py +3 -1
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/run.py +20 -8
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/sync_cmd.py +14 -8
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/uninstall.py +7 -8
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/update.py +21 -15
- cave_cli-3.6.0/cave_cli/commands/upgrade.py +352 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/version.py +24 -14
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/cache.py +3 -9
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/display.py +188 -49
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/docker.py +3 -7
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/env.py +1 -2
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/logger.py +2 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/subprocess.py +1 -21
- cave_cli-3.6.0/cave_cli/utils/sync.py +135 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/validate.py +60 -13
- cave_cli-3.6.0/cave_cli/utils/version.py +138 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0/cave_cli.egg-info}/PKG-INFO +29 -16
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli.egg-info/SOURCES.txt +10 -2
- cave_cli-3.6.0/cave_cli.egg-info/requires.txt +10 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/pyproject.toml +16 -1
- cave_cli-3.6.0/setup.cfg +4 -0
- cave_cli-3.6.0/test/test_display.py +116 -0
- cave_cli-3.6.0/test/test_env.py +118 -0
- cave_cli-3.6.0/test/test_net.py +33 -0
- cave_cli-3.6.0/test/test_sync.py +123 -0
- cave_cli-3.6.0/test/test_upgrade.py +66 -0
- cave_cli-3.6.0/test/test_validate.py +115 -0
- cave_cli-3.6.0/utils/prettify.py +25 -0
- cave_cli-3.5.5/cave_cli/commands/upgrade.py +0 -75
- cave_cli-3.5.5/cave_cli/utils/sync.py +0 -89
- cave_cli-3.5.5/setup.cfg +0 -10
- {cave_cli-3.5.5 → cave_cli-3.6.0}/LICENSE +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/NOTICE.md +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/__init__.py +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/__init__.py +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/kill.py +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/prettify.py +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/test.py +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/commands/theme.py +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/__init__.py +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/constants.py +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/git.py +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli/utils/net.py +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli.egg-info/dependency_links.txt +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli.egg-info/entry_points.txt +0 -0
- {cave_cli-3.5.5 → cave_cli-3.6.0}/cave_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cave_cli
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.6.0
|
|
4
4
|
Summary: CLI for creating and managing Docker-based CAVE web applications.
|
|
5
5
|
Author-email: MIT-CAVE <cave-contact@mit.edu>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -13,6 +13,15 @@ Requires-Python: >=3.11
|
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
License-File: NOTICE.md
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: pytest>=9.0.3; extra == "dev"
|
|
18
|
+
Requires-Dist: autoflake>=2.3.1; extra == "dev"
|
|
19
|
+
Requires-Dist: black>=25.1.0; extra == "dev"
|
|
20
|
+
Requires-Dist: build>=1.4.0; extra == "dev"
|
|
21
|
+
Requires-Dist: nox>=2026.4.10; extra == "dev"
|
|
22
|
+
Requires-Dist: nox-uv>=0.8.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pdoc>=15.0.1; extra == "dev"
|
|
24
|
+
Requires-Dist: twine>=6.2.0; extra == "dev"
|
|
16
25
|
Dynamic: license-file
|
|
17
26
|
|
|
18
27
|
# CAVE CLI
|
|
@@ -56,38 +65,42 @@ docker run hello-world
|
|
|
56
65
|
|
|
57
66
|
## Installation
|
|
58
67
|
|
|
59
|
-
Install with [
|
|
68
|
+
Install with [uv](https://docs.astral.sh/uv/) (required to run cave update and cave uninstall):
|
|
60
69
|
|
|
61
70
|
```sh
|
|
62
|
-
|
|
71
|
+
uv tool install cave_cli
|
|
63
72
|
```
|
|
64
73
|
|
|
65
|
-
If you don't have
|
|
74
|
+
If you don't have uv, install it first:
|
|
66
75
|
|
|
67
76
|
<details>
|
|
68
77
|
<summary>macOS</summary>
|
|
69
78
|
|
|
70
79
|
```sh
|
|
71
80
|
# With Homebrew (recommended)
|
|
72
|
-
brew install
|
|
73
|
-
pipx ensurepath
|
|
81
|
+
brew install uv
|
|
74
82
|
|
|
75
|
-
# Or with
|
|
76
|
-
|
|
77
|
-
pipx ensurepath
|
|
83
|
+
# Or with the official installer
|
|
84
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
78
85
|
```
|
|
79
86
|
|
|
80
87
|
</details>
|
|
81
88
|
<details>
|
|
82
|
-
<summary>
|
|
89
|
+
<summary>Linux</summary>
|
|
83
90
|
|
|
84
91
|
```sh
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
93
|
+
```
|
|
94
|
+
</details>
|
|
95
|
+
<details>
|
|
96
|
+
<summary>Windows</summary>
|
|
97
|
+
|
|
98
|
+
```powershell
|
|
99
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
87
100
|
```
|
|
88
101
|
</details>
|
|
89
102
|
|
|
90
|
-
For more options see the [
|
|
103
|
+
For more options see the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/).
|
|
91
104
|
|
|
92
105
|
Verify the installation and check your environment health:
|
|
93
106
|
|
|
@@ -116,7 +129,7 @@ cave --help
|
|
|
116
129
|
|---|---|
|
|
117
130
|
| `cave create <name>` | Create a new CAVE app from the template repository |
|
|
118
131
|
| `cave run` | Build Docker image and run the app with a live TUI dashboard |
|
|
119
|
-
| `cave doctor` | Check the health of your Docker, Git, and
|
|
132
|
+
| `cave doctor` | Check the health of your Docker, Git, and uv environment |
|
|
120
133
|
|
|
121
134
|
### Peripheral Commands
|
|
122
135
|
|
|
@@ -163,10 +176,10 @@ cave --help
|
|
|
163
176
|
cave update
|
|
164
177
|
```
|
|
165
178
|
|
|
166
|
-
Or directly via
|
|
179
|
+
Or directly via uv:
|
|
167
180
|
|
|
168
181
|
```sh
|
|
169
|
-
|
|
182
|
+
uv tool upgrade cave_cli
|
|
170
183
|
```
|
|
171
184
|
|
|
172
185
|
## License Notice
|
|
@@ -39,38 +39,42 @@ docker run hello-world
|
|
|
39
39
|
|
|
40
40
|
## Installation
|
|
41
41
|
|
|
42
|
-
Install with [
|
|
42
|
+
Install with [uv](https://docs.astral.sh/uv/) (required to run cave update and cave uninstall):
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
|
-
|
|
45
|
+
uv tool install cave_cli
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
If you don't have
|
|
48
|
+
If you don't have uv, install it first:
|
|
49
49
|
|
|
50
50
|
<details>
|
|
51
51
|
<summary>macOS</summary>
|
|
52
52
|
|
|
53
53
|
```sh
|
|
54
54
|
# With Homebrew (recommended)
|
|
55
|
-
brew install
|
|
56
|
-
pipx ensurepath
|
|
55
|
+
brew install uv
|
|
57
56
|
|
|
58
|
-
# Or with
|
|
59
|
-
|
|
60
|
-
pipx ensurepath
|
|
57
|
+
# Or with the official installer
|
|
58
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
61
59
|
```
|
|
62
60
|
|
|
63
61
|
</details>
|
|
64
62
|
<details>
|
|
65
|
-
<summary>
|
|
63
|
+
<summary>Linux</summary>
|
|
66
64
|
|
|
67
65
|
```sh
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
67
|
+
```
|
|
68
|
+
</details>
|
|
69
|
+
<details>
|
|
70
|
+
<summary>Windows</summary>
|
|
71
|
+
|
|
72
|
+
```powershell
|
|
73
|
+
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
70
74
|
```
|
|
71
75
|
</details>
|
|
72
76
|
|
|
73
|
-
For more options see the [
|
|
77
|
+
For more options see the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/).
|
|
74
78
|
|
|
75
79
|
Verify the installation and check your environment health:
|
|
76
80
|
|
|
@@ -99,7 +103,7 @@ cave --help
|
|
|
99
103
|
|---|---|
|
|
100
104
|
| `cave create <name>` | Create a new CAVE app from the template repository |
|
|
101
105
|
| `cave run` | Build Docker image and run the app with a live TUI dashboard |
|
|
102
|
-
| `cave doctor` | Check the health of your Docker, Git, and
|
|
106
|
+
| `cave doctor` | Check the health of your Docker, Git, and uv environment |
|
|
103
107
|
|
|
104
108
|
### Peripheral Commands
|
|
105
109
|
|
|
@@ -146,10 +150,10 @@ cave --help
|
|
|
146
150
|
cave update
|
|
147
151
|
```
|
|
148
152
|
|
|
149
|
-
Or directly via
|
|
153
|
+
Or directly via uv:
|
|
150
154
|
|
|
151
155
|
```sh
|
|
152
|
-
|
|
156
|
+
uv tool upgrade cave_cli
|
|
153
157
|
```
|
|
154
158
|
|
|
155
159
|
## License Notice
|
|
@@ -38,6 +38,13 @@ def add_global_args(parser: argparse.ArgumentParser) -> None:
|
|
|
38
38
|
default=False,
|
|
39
39
|
help="Enable verbose logging output (shorthand for --loglevel DEBUG)",
|
|
40
40
|
)
|
|
41
|
+
parser.add_argument(
|
|
42
|
+
"-q",
|
|
43
|
+
"--quiet",
|
|
44
|
+
action="store_true",
|
|
45
|
+
default=False,
|
|
46
|
+
help="Enable quiet mode (suppress most output, shorthand for --loglevel SILENT)",
|
|
47
|
+
)
|
|
41
48
|
parser.add_argument(
|
|
42
49
|
"--loglevel",
|
|
43
50
|
"--ll",
|
|
@@ -157,8 +164,7 @@ def main():
|
|
|
157
164
|
"reset",
|
|
158
165
|
aliases=["reset-db"],
|
|
159
166
|
help=(
|
|
160
|
-
"Remove Docker containers and volumes, "
|
|
161
|
-
"then rebuild from scratch"
|
|
167
|
+
"Remove Docker containers and volumes, " "then rebuild from scratch"
|
|
162
168
|
),
|
|
163
169
|
)
|
|
164
170
|
add_global_args(p_reset)
|
|
@@ -419,7 +425,9 @@ def main():
|
|
|
419
425
|
# Configure logging
|
|
420
426
|
from cave_cli.utils.logger import logger
|
|
421
427
|
|
|
422
|
-
if args
|
|
428
|
+
if getattr(args, "quiet", False):
|
|
429
|
+
logger.set_level("SILENT")
|
|
430
|
+
elif args.verbose:
|
|
423
431
|
logger.set_level("DEBUG")
|
|
424
432
|
else:
|
|
425
433
|
logger.set_level(args.loglevel)
|
|
@@ -8,7 +8,12 @@ from pathlib import Path
|
|
|
8
8
|
from cave_cli.commands.reset import reset
|
|
9
9
|
from cave_cli.commands.run import run_cave
|
|
10
10
|
from cave_cli.utils.constants import HTTPS_URL
|
|
11
|
-
from cave_cli.utils.display import
|
|
11
|
+
from cave_cli.utils.display import (
|
|
12
|
+
print_section,
|
|
13
|
+
step_done,
|
|
14
|
+
step_fail,
|
|
15
|
+
step_start,
|
|
16
|
+
)
|
|
12
17
|
from cave_cli.utils.docker import build_image, generate_secret_key
|
|
13
18
|
from cave_cli.utils.env import create_env_interactive
|
|
14
19
|
from cave_cli.utils.git import add, branch_rename, clone, commit, init
|
|
@@ -34,31 +34,38 @@ def check_git() -> tuple[bool, str]:
|
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def
|
|
37
|
+
def check_uv(return_path: bool = True) -> tuple[bool, str, str | None]:
|
|
38
38
|
"""
|
|
39
39
|
Usage:
|
|
40
40
|
|
|
41
|
-
- Validates that
|
|
41
|
+
- Validates that uv is installed
|
|
42
42
|
|
|
43
43
|
Returns:
|
|
44
44
|
|
|
45
45
|
- ``success``: bool
|
|
46
46
|
- ``message``: str remediation or version
|
|
47
|
+
- ``path``: str|None path to uv executable if found
|
|
47
48
|
"""
|
|
48
49
|
import shutil
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
if
|
|
51
|
+
uv_path = shutil.which("uv")
|
|
52
|
+
if uv_path:
|
|
52
53
|
try:
|
|
53
|
-
result = run([
|
|
54
|
+
result = run([uv_path, "--version"])
|
|
54
55
|
if result.returncode == 0:
|
|
55
|
-
|
|
56
|
+
output = True, f"uv version {result.stdout.strip()}"
|
|
57
|
+
if return_path:
|
|
58
|
+
return output + (uv_path,)
|
|
59
|
+
return output
|
|
56
60
|
except Exception:
|
|
57
61
|
pass
|
|
58
|
-
|
|
62
|
+
output = (
|
|
59
63
|
False,
|
|
60
|
-
"
|
|
64
|
+
"uv is not installed. Please install uv: https://github.com/astral-sh/uv",
|
|
61
65
|
)
|
|
66
|
+
if return_path:
|
|
67
|
+
return output + (None,)
|
|
68
|
+
return output
|
|
62
69
|
|
|
63
70
|
|
|
64
71
|
def check_docker() -> tuple[bool, str]:
|
|
@@ -126,7 +133,7 @@ def check_all(exit_on_fail: bool = True) -> dict[str, tuple[bool, str]]:
|
|
|
126
133
|
results = {
|
|
127
134
|
"Docker": check_docker(),
|
|
128
135
|
"Git": check_git(),
|
|
129
|
-
"
|
|
136
|
+
"uv": check_uv(return_path=False),
|
|
130
137
|
}
|
|
131
138
|
|
|
132
139
|
if exit_on_fail:
|
|
@@ -145,7 +152,7 @@ def doctor(args: argparse.Namespace) -> None:
|
|
|
145
152
|
"""
|
|
146
153
|
Usage:
|
|
147
154
|
|
|
148
|
-
- Checks the health of the CAVE environment (Docker, Git,
|
|
155
|
+
- Checks the health of the CAVE environment (Docker, Git, uv)
|
|
149
156
|
"""
|
|
150
157
|
print_section("CAVE Doctor")
|
|
151
158
|
|
|
@@ -20,9 +20,12 @@ def list_cmd(args: argparse.Namespace) -> None:
|
|
|
20
20
|
|
|
21
21
|
if show_all:
|
|
22
22
|
print_section("CAVE App Containers (All)")
|
|
23
|
+
from cave_cli.utils.display import _QUIET
|
|
24
|
+
|
|
23
25
|
for suffix in ("_django", "_db_host", "_redis_host", "_nginx_host"):
|
|
24
26
|
for name in get_all_containers(suffix):
|
|
25
|
-
|
|
27
|
+
if not _QUIET:
|
|
28
|
+
print(f" ● {name}")
|
|
26
29
|
else:
|
|
27
30
|
apps = get_running_apps()
|
|
28
31
|
if not apps:
|
|
@@ -40,4 +43,8 @@ def list_cmd(args: argparse.Namespace) -> None:
|
|
|
40
43
|
else:
|
|
41
44
|
port = get_container_host_port(f"{app_name}_django")
|
|
42
45
|
print_key_value(app_name, f"http://localhost:{port}")
|
|
43
|
-
|
|
46
|
+
|
|
47
|
+
from cave_cli.utils.display import _QUIET
|
|
48
|
+
|
|
49
|
+
if not _QUIET:
|
|
50
|
+
print("")
|
|
@@ -14,14 +14,11 @@ from cave_cli.utils.display import (
|
|
|
14
14
|
)
|
|
15
15
|
from cave_cli.utils.git import ls_remote_tags
|
|
16
16
|
from cave_cli.utils.logger import logger
|
|
17
|
+
from cave_cli.utils.version import version_tuple
|
|
17
18
|
|
|
18
19
|
RECENT_LIMIT = 5
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
def _version_sort_key(v: str) -> list[int]:
|
|
22
|
-
return [int(x) for x in re.findall(r"\d+", v)]
|
|
23
|
-
|
|
24
|
-
|
|
25
22
|
def list_versions(args: argparse.Namespace) -> None:
|
|
26
23
|
"""
|
|
27
24
|
Usage:
|
|
@@ -51,10 +48,11 @@ def list_versions(args: argparse.Namespace) -> None:
|
|
|
51
48
|
|
|
52
49
|
all_versions = sorted(
|
|
53
50
|
(
|
|
54
|
-
v
|
|
51
|
+
v
|
|
52
|
+
for v in set().union(*repo_tags.values())
|
|
55
53
|
if not pattern or fnmatch.fnmatch(v, pattern)
|
|
56
54
|
),
|
|
57
|
-
key=
|
|
55
|
+
key=version_tuple,
|
|
58
56
|
reverse=True,
|
|
59
57
|
)
|
|
60
58
|
|
|
@@ -64,7 +62,7 @@ def list_versions(args: argparse.Namespace) -> None:
|
|
|
64
62
|
|
|
65
63
|
by_major: dict[int, list[str]] = defaultdict(list)
|
|
66
64
|
for v in all_versions:
|
|
67
|
-
major =
|
|
65
|
+
major = version_tuple(v)[0]
|
|
68
66
|
by_major[major].append(v)
|
|
69
67
|
|
|
70
68
|
repos = list(VALID_REPOS)
|
|
@@ -91,7 +89,10 @@ def list_versions(args: argparse.Namespace) -> None:
|
|
|
91
89
|
row = indent + f"{v:<{version_w}}"
|
|
92
90
|
for r, w in zip(repos, col_w):
|
|
93
91
|
mark = f"{GREEN}✓{RESET}" if v in repo_tags[r] else ""
|
|
94
|
-
row +=
|
|
92
|
+
row += (
|
|
93
|
+
" " * pad
|
|
94
|
+
+ f"{mark:^{w + len(GREEN) + len(RESET) if mark else w}}"
|
|
95
|
+
)
|
|
95
96
|
print(row)
|
|
96
97
|
|
|
97
98
|
if hidden:
|
|
@@ -64,11 +64,19 @@ def purge(args: argparse.Namespace) -> None:
|
|
|
64
64
|
"Some files are owned by root (created by Docker). "
|
|
65
65
|
"Removing via Docker..."
|
|
66
66
|
)
|
|
67
|
-
run_and_log(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
run_and_log(
|
|
68
|
+
[
|
|
69
|
+
"docker",
|
|
70
|
+
"run",
|
|
71
|
+
"--rm",
|
|
72
|
+
"-v",
|
|
73
|
+
f"{abs_path}:/purge",
|
|
74
|
+
"alpine",
|
|
75
|
+
"rm",
|
|
76
|
+
"-rf",
|
|
77
|
+
"/purge",
|
|
78
|
+
]
|
|
79
|
+
)
|
|
72
80
|
if os.path.isdir(abs_path):
|
|
73
81
|
try:
|
|
74
82
|
shutil.rmtree(abs_path)
|
|
@@ -46,5 +46,7 @@ def reset(
|
|
|
46
46
|
verbose=getattr(args, "verbose", False),
|
|
47
47
|
loglevel=getattr(args, "loglevel", "INFO"),
|
|
48
48
|
)
|
|
49
|
-
run_cave(
|
|
49
|
+
run_cave(
|
|
50
|
+
app_dir, app_name, reset_args, skip_header=True, skip_build=skip_build
|
|
51
|
+
)
|
|
50
52
|
step_done("Reset complete")
|
|
@@ -49,13 +49,21 @@ def run_cave(
|
|
|
49
49
|
interactive = getattr(args, "interactive", False) or getattr(
|
|
50
50
|
args, "it", False
|
|
51
51
|
)
|
|
52
|
-
show_all =
|
|
52
|
+
show_all = (
|
|
53
|
+
getattr(args, "show_all", False)
|
|
54
|
+
or getattr(args, "verbose", False)
|
|
55
|
+
or getattr(args, "loglevel", "INFO").upper() == "DEBUG"
|
|
56
|
+
)
|
|
53
57
|
entrypoint = getattr(args, "entrypoint", None) or "./utils/run_server.sh"
|
|
54
58
|
docker_args_str = getattr(args, "docker_args", "") or ""
|
|
55
59
|
extra_docker_args = shlex.split(docker_args_str) if docker_args_str else []
|
|
56
60
|
ip_port_arg = getattr(args, "ip_port", None)
|
|
57
61
|
command_args = getattr(args, "command_args", []) or []
|
|
58
62
|
extra_env = getattr(args, "extra_env", {}) or {}
|
|
63
|
+
quiet = (
|
|
64
|
+
getattr(args, "quiet", False)
|
|
65
|
+
or getattr(args, "loglevel", "INFO").upper() == "SILENT"
|
|
66
|
+
)
|
|
59
67
|
|
|
60
68
|
is_server_run = entrypoint == "./utils/run_server.sh" and not interactive
|
|
61
69
|
use_tui = is_server_run and not show_all
|
|
@@ -179,7 +187,10 @@ def run_cave(
|
|
|
179
187
|
else:
|
|
180
188
|
step_done("Checking database")
|
|
181
189
|
else:
|
|
182
|
-
step_fail(
|
|
190
|
+
step_fail(
|
|
191
|
+
"Checking database",
|
|
192
|
+
"Database container failed to become ready.",
|
|
193
|
+
)
|
|
183
194
|
remove_containers(app_name)
|
|
184
195
|
sys.exit(1)
|
|
185
196
|
|
|
@@ -189,9 +200,7 @@ def run_cave(
|
|
|
189
200
|
if parsed:
|
|
190
201
|
ip, port = parsed
|
|
191
202
|
if not is_port_available(port):
|
|
192
|
-
logger.error(
|
|
193
|
-
"The specified port is in use. Please try another."
|
|
194
|
-
)
|
|
203
|
+
logger.error("The specified port is in use. Please try another.")
|
|
195
204
|
sys.exit(1)
|
|
196
205
|
|
|
197
206
|
if is_server_run and not interactive:
|
|
@@ -224,7 +233,7 @@ def run_cave(
|
|
|
224
233
|
url = f"https://{ip}:{port}"
|
|
225
234
|
|
|
226
235
|
if interactive:
|
|
227
|
-
logger.header("CAVE App
|
|
236
|
+
logger.header("CAVE App (Interactive)")
|
|
228
237
|
run_interactive(
|
|
229
238
|
name=django_container,
|
|
230
239
|
image=f"cave-app:{app_name}",
|
|
@@ -236,8 +245,8 @@ def run_cave(
|
|
|
236
245
|
command=server_command,
|
|
237
246
|
)
|
|
238
247
|
remove_containers(app_name)
|
|
239
|
-
|
|
240
248
|
elif use_tui:
|
|
249
|
+
logger.header("CAVE App (TUI)")
|
|
241
250
|
_run_tui(
|
|
242
251
|
app_name=app_name,
|
|
243
252
|
django_container=django_container,
|
|
@@ -252,6 +261,7 @@ def run_cave(
|
|
|
252
261
|
)
|
|
253
262
|
|
|
254
263
|
else:
|
|
264
|
+
logger.header("CAVE App (CLI)")
|
|
255
265
|
if is_server_run:
|
|
256
266
|
logger.info(
|
|
257
267
|
f"Your Cave App can be accessed from Chrome at:\n{url}"
|
|
@@ -273,7 +283,7 @@ def run_cave(
|
|
|
273
283
|
url = f"http://localhost:{port}"
|
|
274
284
|
|
|
275
285
|
if interactive:
|
|
276
|
-
logger.header("CAVE App
|
|
286
|
+
logger.header("CAVE App (Interactive)")
|
|
277
287
|
run_interactive(
|
|
278
288
|
name=django_container,
|
|
279
289
|
image=f"cave-app:{app_name}",
|
|
@@ -287,6 +297,7 @@ def run_cave(
|
|
|
287
297
|
remove_containers(app_name)
|
|
288
298
|
|
|
289
299
|
elif use_tui:
|
|
300
|
+
logger.header("CAVE App (TUI)")
|
|
290
301
|
_run_tui(
|
|
291
302
|
app_name=app_name,
|
|
292
303
|
django_container=django_container,
|
|
@@ -301,6 +312,7 @@ def run_cave(
|
|
|
301
312
|
)
|
|
302
313
|
|
|
303
314
|
else:
|
|
315
|
+
logger.header("CAVE App (CLI)")
|
|
304
316
|
if is_server_run:
|
|
305
317
|
logger.info(
|
|
306
318
|
f"Your Cave App can be accessed from Chrome at:\n{url}"
|
|
@@ -5,14 +5,19 @@ import tempfile
|
|
|
5
5
|
|
|
6
6
|
from cave_cli.commands.reset import reset
|
|
7
7
|
from cave_cli.utils.constants import HTTPS_URL
|
|
8
|
-
from cave_cli.utils.display import
|
|
8
|
+
from cave_cli.utils.display import (
|
|
9
|
+
print_section,
|
|
10
|
+
step_done,
|
|
11
|
+
step_start,
|
|
12
|
+
print_key_value,
|
|
13
|
+
)
|
|
9
14
|
from cave_cli.utils.git import clone
|
|
10
15
|
from cave_cli.utils.logger import logger
|
|
11
16
|
from cave_cli.utils.sync import sync_files
|
|
12
17
|
from cave_cli.utils.validate import confirm_action, find_app_dir
|
|
13
18
|
|
|
14
19
|
|
|
15
|
-
def sync_cmd(args: argparse.Namespace) -> None:
|
|
20
|
+
def sync_cmd(args: argparse.Namespace, do_reset: bool = True) -> None:
|
|
16
21
|
"""
|
|
17
22
|
Usage:
|
|
18
23
|
|
|
@@ -69,11 +74,12 @@ def sync_cmd(args: argparse.Namespace) -> None:
|
|
|
69
74
|
|
|
70
75
|
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
71
76
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
if do_reset:
|
|
78
|
+
reset_args = argparse.Namespace(
|
|
79
|
+
yes=True,
|
|
80
|
+
verbose=getattr(args, "verbose", False),
|
|
81
|
+
loglevel=getattr(args, "loglevel", "INFO"),
|
|
82
|
+
)
|
|
83
|
+
reset(reset_args)
|
|
78
84
|
|
|
79
85
|
logger.success("Sync complete.")
|
|
@@ -5,15 +5,14 @@ import sys
|
|
|
5
5
|
|
|
6
6
|
from cave_cli.utils.display import RED, RESET, step_done, step_start
|
|
7
7
|
from cave_cli.utils.logger import logger
|
|
8
|
-
|
|
9
|
-
PIPX_DOCS_URL = "https://pipx.pypa.io/stable/installation/"
|
|
8
|
+
from cave_cli.commands.doctor import check_uv
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
def uninstall(args: argparse.Namespace) -> None:
|
|
13
12
|
"""
|
|
14
13
|
Usage:
|
|
15
14
|
|
|
16
|
-
- Removes the CAVE CLI package via
|
|
15
|
+
- Removes the CAVE CLI package via uv.
|
|
17
16
|
"""
|
|
18
17
|
try:
|
|
19
18
|
response = input(
|
|
@@ -28,17 +27,17 @@ def uninstall(args: argparse.Namespace) -> None:
|
|
|
28
27
|
logger.error("Uninstall canceled")
|
|
29
28
|
return
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
if not
|
|
30
|
+
has_uv, uv_message, uv_path = check_uv()
|
|
31
|
+
if not has_uv:
|
|
32
|
+
logger.error(uv_message)
|
|
33
33
|
logger.error(
|
|
34
|
-
"
|
|
34
|
+
f"You may need to uninstall CAVE CLI manually based on how it was installed."
|
|
35
35
|
)
|
|
36
|
-
logger.error(f"See: {PIPX_DOCS_URL}")
|
|
37
36
|
sys.exit(1)
|
|
38
37
|
|
|
39
38
|
step_start("Removing installation")
|
|
40
39
|
result = subprocess.run(
|
|
41
|
-
[
|
|
40
|
+
[uv_path, "tool", "uninstall", "cave_cli"],
|
|
42
41
|
stdout=subprocess.PIPE,
|
|
43
42
|
stderr=subprocess.PIPE,
|
|
44
43
|
text=True,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import argparse
|
|
2
|
-
import shutil
|
|
3
2
|
import subprocess
|
|
4
3
|
import sys
|
|
5
4
|
import tempfile
|
|
6
5
|
|
|
7
6
|
from cave_cli.utils.display import step_done, step_start
|
|
8
7
|
from cave_cli.utils.logger import logger
|
|
8
|
+
from cave_cli.commands.doctor import check_uv
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
UV_DOCS_URL = "https://docs.astral.sh/uv/"
|
|
11
11
|
CLI_REPO_URL = "https://github.com/MIT-CAVE/cave_cli.git"
|
|
12
12
|
|
|
13
13
|
|
|
@@ -15,32 +15,38 @@ def update(args: argparse.Namespace) -> None:
|
|
|
15
15
|
"""
|
|
16
16
|
Usage:
|
|
17
17
|
|
|
18
|
-
- Updates the CAVE CLI via
|
|
18
|
+
- Updates the CAVE CLI via uv.
|
|
19
19
|
|
|
20
20
|
Notes:
|
|
21
21
|
|
|
22
|
-
- Without ``--version``,
|
|
23
|
-
``
|
|
24
|
-
- With ``--version``, reinstalls via ``
|
|
25
|
-
specified git tag or branch.
|
|
22
|
+
- Without ``--version``, upgrades to the latest version from PyPI via
|
|
23
|
+
``uv tool upgrade cave_cli``.
|
|
24
|
+
- With ``--version``, reinstalls via ``uv tool install --reinstall`` from
|
|
25
|
+
the specified git tag or branch.
|
|
26
26
|
- On Windows, the update runs in a new console window because Windows
|
|
27
27
|
locks running executables and cave.exe cannot replace itself.
|
|
28
28
|
"""
|
|
29
|
-
|
|
30
|
-
if not
|
|
29
|
+
has_uv, uv_message, uv_path = check_uv()
|
|
30
|
+
if not has_uv:
|
|
31
|
+
logger.error(uv_message)
|
|
31
32
|
logger.error(
|
|
32
|
-
"
|
|
33
|
+
f"The cave_cli package may need to be updated manually based on how it was installed."
|
|
33
34
|
)
|
|
34
|
-
logger.error(f"See: {PIPX_DOCS_URL}")
|
|
35
35
|
sys.exit(1)
|
|
36
36
|
|
|
37
37
|
version = getattr(args, "version", None)
|
|
38
38
|
if version:
|
|
39
39
|
label = f"Reinstalling CAVE CLI ({version})"
|
|
40
|
-
|
|
40
|
+
cmd = [
|
|
41
|
+
uv_path,
|
|
42
|
+
"tool",
|
|
43
|
+
"install",
|
|
44
|
+
"--reinstall",
|
|
45
|
+
f"cave_cli @ git+{CLI_REPO_URL}@{version}",
|
|
46
|
+
]
|
|
41
47
|
else:
|
|
42
48
|
label = "Updating CAVE CLI"
|
|
43
|
-
|
|
49
|
+
cmd = [uv_path, "tool", "upgrade", "cave_cli"]
|
|
44
50
|
|
|
45
51
|
step_start(label)
|
|
46
52
|
|
|
@@ -54,7 +60,7 @@ def update(args: argparse.Namespace) -> None:
|
|
|
54
60
|
) as f:
|
|
55
61
|
f.write("@echo off\r\n")
|
|
56
62
|
f.write("timeout /t 1 /nobreak >nul\r\n")
|
|
57
|
-
f.write(f'"{
|
|
63
|
+
f.write(" ".join(f'"{c}"' for c in cmd) + "\r\n")
|
|
58
64
|
f.write('del "%~f0"\r\n')
|
|
59
65
|
bat_path = f.name
|
|
60
66
|
subprocess.Popen(
|
|
@@ -66,7 +72,7 @@ def update(args: argparse.Namespace) -> None:
|
|
|
66
72
|
return
|
|
67
73
|
|
|
68
74
|
result = subprocess.run(
|
|
69
|
-
|
|
75
|
+
cmd,
|
|
70
76
|
stdout=subprocess.PIPE,
|
|
71
77
|
stderr=subprocess.PIPE,
|
|
72
78
|
text=True,
|