cave-cli 3.5.4__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.4/cave_cli.egg-info → cave_cli-3.6.0}/PKG-INFO +29 -16
- {cave_cli-3.5.4 → cave_cli-3.6.0}/README.md +19 -15
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/cli.py +29 -3
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/create.py +6 -1
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/doctor.py +17 -10
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/list_cmd.py +9 -2
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/list_versions.py +9 -8
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/prettify.py +1 -1
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/purge.py +13 -5
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/reset.py +4 -2
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/run.py +67 -8
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/sync_cmd.py +14 -8
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/test.py +1 -1
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/uninstall.py +7 -8
- {cave_cli-3.5.4 → 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.4 → cave_cli-3.6.0}/cave_cli/commands/version.py +24 -14
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/utils/cache.py +3 -9
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/utils/display.py +188 -49
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/utils/docker.py +76 -7
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/utils/env.py +1 -2
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/utils/logger.py +2 -0
- {cave_cli-3.5.4 → 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.4 → 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.4 → cave_cli-3.6.0/cave_cli.egg-info}/PKG-INFO +29 -16
- {cave_cli-3.5.4 → 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.4 → 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.4/cave_cli/commands/upgrade.py +0 -75
- cave_cli-3.5.4/cave_cli/utils/sync.py +0 -89
- cave_cli-3.5.4/setup.cfg +0 -10
- {cave_cli-3.5.4 → cave_cli-3.6.0}/LICENSE +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/NOTICE.md +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/__init__.py +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/__init__.py +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/kill.py +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/commands/theme.py +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/utils/__init__.py +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/utils/constants.py +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/utils/git.py +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli/utils/net.py +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli.egg-info/dependency_links.txt +0 -0
- {cave_cli-3.5.4 → cave_cli-3.6.0}/cave_cli.egg-info/entry_points.txt +0 -0
- {cave_cli-3.5.4 → 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,11 +164,16 @@ 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)
|
|
171
|
+
p_reset.add_argument(
|
|
172
|
+
"--docker-args",
|
|
173
|
+
default="",
|
|
174
|
+
metavar="ARGS",
|
|
175
|
+
help="Additional arguments to pass to docker run",
|
|
176
|
+
)
|
|
165
177
|
|
|
166
178
|
# ------------------------------------------------------------------ #
|
|
167
179
|
# upgrade #
|
|
@@ -233,6 +245,12 @@ def main():
|
|
|
233
245
|
help="Run tests in cave_api/tests/",
|
|
234
246
|
)
|
|
235
247
|
add_global_args(p_test)
|
|
248
|
+
p_test.add_argument(
|
|
249
|
+
"--docker-args",
|
|
250
|
+
default="",
|
|
251
|
+
metavar="ARGS",
|
|
252
|
+
help="Additional arguments to pass to docker run",
|
|
253
|
+
)
|
|
236
254
|
p_test.add_argument(
|
|
237
255
|
"remaining",
|
|
238
256
|
nargs="*",
|
|
@@ -247,6 +265,12 @@ def main():
|
|
|
247
265
|
help="Format code with autoflake and black",
|
|
248
266
|
)
|
|
249
267
|
add_global_args(p_prettify)
|
|
268
|
+
p_prettify.add_argument(
|
|
269
|
+
"--docker-args",
|
|
270
|
+
default="",
|
|
271
|
+
metavar="ARGS",
|
|
272
|
+
help="Additional arguments to pass to docker run",
|
|
273
|
+
)
|
|
250
274
|
|
|
251
275
|
# ------------------------------------------------------------------ #
|
|
252
276
|
# list #
|
|
@@ -401,7 +425,9 @@ def main():
|
|
|
401
425
|
# Configure logging
|
|
402
426
|
from cave_cli.utils.logger import logger
|
|
403
427
|
|
|
404
|
-
if args
|
|
428
|
+
if getattr(args, "quiet", False):
|
|
429
|
+
logger.set_level("SILENT")
|
|
430
|
+
elif args.verbose:
|
|
405
431
|
logger.set_level("DEBUG")
|
|
406
432
|
else:
|
|
407
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:
|
|
@@ -18,7 +18,7 @@ def prettify(args: argparse.Namespace) -> None:
|
|
|
18
18
|
entrypoint="./utils/prettify.sh",
|
|
19
19
|
interactive=False,
|
|
20
20
|
it=False,
|
|
21
|
-
docker_args="",
|
|
21
|
+
docker_args=getattr(args, "docker_args", "") or "",
|
|
22
22
|
ip_port=None,
|
|
23
23
|
yes=True,
|
|
24
24
|
verbose=getattr(args, "verbose", False),
|
|
@@ -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)
|
|
@@ -40,11 +40,13 @@ def reset(
|
|
|
40
40
|
entrypoint="./utils/reset_db.sh",
|
|
41
41
|
interactive=False,
|
|
42
42
|
it=False,
|
|
43
|
-
docker_args="",
|
|
43
|
+
docker_args=getattr(args, "docker_args", "") or "",
|
|
44
44
|
ip_port=None,
|
|
45
45
|
yes=True,
|
|
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")
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import argparse
|
|
2
|
+
import shlex
|
|
2
3
|
import signal
|
|
3
4
|
import sys
|
|
4
5
|
import threading
|
|
@@ -8,12 +9,14 @@ from cave_cli.utils.display import (
|
|
|
8
9
|
RunDashboard,
|
|
9
10
|
print_section,
|
|
10
11
|
step_done,
|
|
12
|
+
step_fail,
|
|
11
13
|
step_start,
|
|
12
14
|
)
|
|
13
15
|
from cave_cli.utils.docker import (
|
|
14
16
|
build_image,
|
|
15
17
|
create_network,
|
|
16
18
|
remove_containers,
|
|
19
|
+
run_and_log,
|
|
17
20
|
run_detached,
|
|
18
21
|
run_detached_logged,
|
|
19
22
|
run_interactive,
|
|
@@ -46,13 +49,21 @@ def run_cave(
|
|
|
46
49
|
interactive = getattr(args, "interactive", False) or getattr(
|
|
47
50
|
args, "it", False
|
|
48
51
|
)
|
|
49
|
-
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
|
+
)
|
|
50
57
|
entrypoint = getattr(args, "entrypoint", None) or "./utils/run_server.sh"
|
|
51
58
|
docker_args_str = getattr(args, "docker_args", "") or ""
|
|
52
|
-
extra_docker_args =
|
|
59
|
+
extra_docker_args = shlex.split(docker_args_str) if docker_args_str else []
|
|
53
60
|
ip_port_arg = getattr(args, "ip_port", None)
|
|
54
61
|
command_args = getattr(args, "command_args", []) or []
|
|
55
62
|
extra_env = getattr(args, "extra_env", {}) or {}
|
|
63
|
+
quiet = (
|
|
64
|
+
getattr(args, "quiet", False)
|
|
65
|
+
or getattr(args, "loglevel", "INFO").upper() == "SILENT"
|
|
66
|
+
)
|
|
56
67
|
|
|
57
68
|
is_server_run = entrypoint == "./utils/run_server.sh" and not interactive
|
|
58
69
|
use_tui = is_server_run and not show_all
|
|
@@ -136,15 +147,60 @@ def run_cave(
|
|
|
136
147
|
|
|
137
148
|
django_volumes = [f"{app_dir}:/app"]
|
|
138
149
|
|
|
150
|
+
# Check if database needs initialization
|
|
151
|
+
if is_server_run and "postgres" in db_image.lower():
|
|
152
|
+
from cave_cli.utils.docker import is_container_ready, is_db_initialized
|
|
153
|
+
from cave_cli.utils.validate import confirm_action
|
|
154
|
+
|
|
155
|
+
db_container = f"{app_name}_db_host"
|
|
156
|
+
db_user = f"{app_name}_user"
|
|
157
|
+
db_name = f"{app_name}_name"
|
|
158
|
+
|
|
159
|
+
step_start("Checking database")
|
|
160
|
+
import time
|
|
161
|
+
|
|
162
|
+
ready = False
|
|
163
|
+
for _ in range(30):
|
|
164
|
+
if is_container_ready(db_container, db_user):
|
|
165
|
+
ready = True
|
|
166
|
+
break
|
|
167
|
+
time.sleep(1)
|
|
168
|
+
|
|
169
|
+
if ready:
|
|
170
|
+
if not is_db_initialized(db_container, db_user, db_name):
|
|
171
|
+
step_done("Checking database")
|
|
172
|
+
confirm_action(
|
|
173
|
+
"The database for this app is not set up. "
|
|
174
|
+
"Would you like to set it up now?"
|
|
175
|
+
)
|
|
176
|
+
step_start("Initializing database")
|
|
177
|
+
run_interactive(
|
|
178
|
+
name=f"{app_name}_django_setup",
|
|
179
|
+
image=f"cave-app:{app_name}",
|
|
180
|
+
network=network,
|
|
181
|
+
volumes=django_volumes,
|
|
182
|
+
env_vars=django_env,
|
|
183
|
+
command=["./utils/reset_db.sh"],
|
|
184
|
+
)
|
|
185
|
+
run_and_log(["docker", "rm", f"{app_name}_django_setup"])
|
|
186
|
+
step_done("Initializing database")
|
|
187
|
+
else:
|
|
188
|
+
step_done("Checking database")
|
|
189
|
+
else:
|
|
190
|
+
step_fail(
|
|
191
|
+
"Checking database",
|
|
192
|
+
"Database container failed to become ready.",
|
|
193
|
+
)
|
|
194
|
+
remove_containers(app_name)
|
|
195
|
+
sys.exit(1)
|
|
196
|
+
|
|
139
197
|
parsed = parse_ip_port(ip_port_arg) if ip_port_arg else None
|
|
140
198
|
django_container = f"{app_name}_django"
|
|
141
199
|
|
|
142
200
|
if parsed:
|
|
143
201
|
ip, port = parsed
|
|
144
202
|
if not is_port_available(port):
|
|
145
|
-
logger.error(
|
|
146
|
-
"The specified port is in use. Please try another."
|
|
147
|
-
)
|
|
203
|
+
logger.error("The specified port is in use. Please try another.")
|
|
148
204
|
sys.exit(1)
|
|
149
205
|
|
|
150
206
|
if is_server_run and not interactive:
|
|
@@ -177,7 +233,7 @@ def run_cave(
|
|
|
177
233
|
url = f"https://{ip}:{port}"
|
|
178
234
|
|
|
179
235
|
if interactive:
|
|
180
|
-
logger.header("CAVE App
|
|
236
|
+
logger.header("CAVE App (Interactive)")
|
|
181
237
|
run_interactive(
|
|
182
238
|
name=django_container,
|
|
183
239
|
image=f"cave-app:{app_name}",
|
|
@@ -189,8 +245,8 @@ def run_cave(
|
|
|
189
245
|
command=server_command,
|
|
190
246
|
)
|
|
191
247
|
remove_containers(app_name)
|
|
192
|
-
|
|
193
248
|
elif use_tui:
|
|
249
|
+
logger.header("CAVE App (TUI)")
|
|
194
250
|
_run_tui(
|
|
195
251
|
app_name=app_name,
|
|
196
252
|
django_container=django_container,
|
|
@@ -205,6 +261,7 @@ def run_cave(
|
|
|
205
261
|
)
|
|
206
262
|
|
|
207
263
|
else:
|
|
264
|
+
logger.header("CAVE App (CLI)")
|
|
208
265
|
if is_server_run:
|
|
209
266
|
logger.info(
|
|
210
267
|
f"Your Cave App can be accessed from Chrome at:\n{url}"
|
|
@@ -226,7 +283,7 @@ def run_cave(
|
|
|
226
283
|
url = f"http://localhost:{port}"
|
|
227
284
|
|
|
228
285
|
if interactive:
|
|
229
|
-
logger.header("CAVE App
|
|
286
|
+
logger.header("CAVE App (Interactive)")
|
|
230
287
|
run_interactive(
|
|
231
288
|
name=django_container,
|
|
232
289
|
image=f"cave-app:{app_name}",
|
|
@@ -240,6 +297,7 @@ def run_cave(
|
|
|
240
297
|
remove_containers(app_name)
|
|
241
298
|
|
|
242
299
|
elif use_tui:
|
|
300
|
+
logger.header("CAVE App (TUI)")
|
|
243
301
|
_run_tui(
|
|
244
302
|
app_name=app_name,
|
|
245
303
|
django_container=django_container,
|
|
@@ -254,6 +312,7 @@ def run_cave(
|
|
|
254
312
|
)
|
|
255
313
|
|
|
256
314
|
else:
|
|
315
|
+
logger.header("CAVE App (CLI)")
|
|
257
316
|
if is_server_run:
|
|
258
317
|
logger.info(
|
|
259
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.")
|