cave-cli 3.5.0__py3-none-any.whl
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/__init__.py +3 -0
- cave_cli/cli.py +469 -0
- cave_cli/commands/__init__.py +0 -0
- cave_cli/commands/create.py +168 -0
- cave_cli/commands/kill.py +23 -0
- cave_cli/commands/list_cmd.py +37 -0
- cave_cli/commands/list_versions.py +93 -0
- cave_cli/commands/prettify.py +27 -0
- cave_cli/commands/purge.py +70 -0
- cave_cli/commands/reset.py +43 -0
- cave_cli/commands/run.py +189 -0
- cave_cli/commands/sync_cmd.py +79 -0
- cave_cli/commands/test.py +38 -0
- cave_cli/commands/uninstall.py +39 -0
- cave_cli/commands/update.py +38 -0
- cave_cli/commands/upgrade.py +70 -0
- cave_cli/commands/version.py +64 -0
- cave_cli/utils/__init__.py +0 -0
- cave_cli/utils/cache.py +235 -0
- cave_cli/utils/constants.py +43 -0
- cave_cli/utils/docker.py +530 -0
- cave_cli/utils/env.py +267 -0
- cave_cli/utils/git.py +240 -0
- cave_cli/utils/logger.py +77 -0
- cave_cli/utils/net.py +85 -0
- cave_cli/utils/subprocess.py +129 -0
- cave_cli/utils/sync.py +89 -0
- cave_cli/utils/validate.py +218 -0
- cave_cli-3.5.0.dist-info/METADATA +149 -0
- cave_cli-3.5.0.dist-info/RECORD +35 -0
- cave_cli-3.5.0.dist-info/WHEEL +5 -0
- cave_cli-3.5.0.dist-info/entry_points.txt +2 -0
- cave_cli-3.5.0.dist-info/licenses/LICENSE +201 -0
- cave_cli-3.5.0.dist-info/licenses/NOTICE.md +7 -0
- cave_cli-3.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import fnmatch
|
|
3
|
+
import re
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
|
|
6
|
+
from cave_cli.utils.constants import CHAR_LINE, VALID_REPOS
|
|
7
|
+
from cave_cli.utils.git import ls_remote_tags
|
|
8
|
+
from cave_cli.utils.logger import logger
|
|
9
|
+
|
|
10
|
+
RECENT_LIMIT = 5
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _version_sort_key(v: str) -> list[int]:
|
|
14
|
+
return [int(x) for x in re.findall(r"\d+", v)]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def list_versions(args: argparse.Namespace) -> None:
|
|
18
|
+
"""
|
|
19
|
+
Usage:
|
|
20
|
+
|
|
21
|
+
- Lists available stable versions across all CAVE repositories,
|
|
22
|
+
grouped by major version, with a presence check per repo
|
|
23
|
+
|
|
24
|
+
Optional:
|
|
25
|
+
|
|
26
|
+
- ``all``:
|
|
27
|
+
- Type: bool
|
|
28
|
+
- What: Show all versions instead of the 5 most recent per major
|
|
29
|
+
- Default: False
|
|
30
|
+
"""
|
|
31
|
+
show_all = getattr(args, "all", False)
|
|
32
|
+
pattern = getattr(args, "pattern", None)
|
|
33
|
+
|
|
34
|
+
repo_tags: dict[str, set[str]] = {}
|
|
35
|
+
for repo in VALID_REPOS:
|
|
36
|
+
logger.info(f"Fetching versions for {repo}...")
|
|
37
|
+
git_url = f"https://github.com/MIT-CAVE/{repo}.git"
|
|
38
|
+
raw = ls_remote_tags(git_url)
|
|
39
|
+
repo_tags[repo] = {
|
|
40
|
+
t for t in raw if re.match(r"^v[0-9]+\.[0-9]+\.[0-9]+$", t)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
all_versions = sorted(
|
|
44
|
+
(
|
|
45
|
+
v for v in set().union(*repo_tags.values())
|
|
46
|
+
if not pattern or fnmatch.fnmatch(v, pattern)
|
|
47
|
+
),
|
|
48
|
+
key=_version_sort_key,
|
|
49
|
+
reverse=True,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if not all_versions:
|
|
53
|
+
logger.info("No stable versions found.")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
by_major: dict[int, list[str]] = defaultdict(list)
|
|
57
|
+
for v in all_versions:
|
|
58
|
+
major = _version_sort_key(v)[0]
|
|
59
|
+
by_major[major].append(v)
|
|
60
|
+
|
|
61
|
+
repos = list(VALID_REPOS)
|
|
62
|
+
version_w = max(len("Version"), max(len(v) for v in all_versions))
|
|
63
|
+
col_w = [max(len(r), 3) for r in repos]
|
|
64
|
+
pad = 2
|
|
65
|
+
indent = " "
|
|
66
|
+
|
|
67
|
+
col_header = indent + f"{'Version':<{version_w}}"
|
|
68
|
+
for r, w in zip(repos, col_w):
|
|
69
|
+
col_header += " " * pad + f"{r:^{w}}"
|
|
70
|
+
col_sep = indent + "-" * (len(col_header) - len(indent))
|
|
71
|
+
|
|
72
|
+
for major in sorted(by_major.keys(), reverse=True):
|
|
73
|
+
versions = by_major[major]
|
|
74
|
+
shown = versions if show_all else versions[:RECENT_LIMIT]
|
|
75
|
+
hidden = len(versions) - len(shown)
|
|
76
|
+
|
|
77
|
+
print(CHAR_LINE)
|
|
78
|
+
print(f"Version {major}")
|
|
79
|
+
print(CHAR_LINE)
|
|
80
|
+
print(col_header)
|
|
81
|
+
print(col_sep)
|
|
82
|
+
|
|
83
|
+
for v in shown:
|
|
84
|
+
row = indent + f"{v:<{version_w}}"
|
|
85
|
+
for r, w in zip(repos, col_w):
|
|
86
|
+
mark = "✓" if v in repo_tags[r] else ""
|
|
87
|
+
row += " " * pad + f"{mark:^{w}}"
|
|
88
|
+
print(row)
|
|
89
|
+
|
|
90
|
+
if hidden:
|
|
91
|
+
print(f"\n +{hidden} older, pass --all to show")
|
|
92
|
+
|
|
93
|
+
print()
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from cave_cli.commands.run import run_cave
|
|
4
|
+
from cave_cli.utils.logger import logger
|
|
5
|
+
from cave_cli.utils.validate import get_app
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def prettify(args: argparse.Namespace) -> None:
|
|
9
|
+
"""
|
|
10
|
+
Usage:
|
|
11
|
+
|
|
12
|
+
- Runs code formatting on the CAVE app in the current directory
|
|
13
|
+
"""
|
|
14
|
+
app_dir, app_name = get_app()
|
|
15
|
+
logger.info("Prettifying cave_api...")
|
|
16
|
+
|
|
17
|
+
run_args = argparse.Namespace(
|
|
18
|
+
entrypoint="./utils/prettify.sh",
|
|
19
|
+
interactive=False,
|
|
20
|
+
it=False,
|
|
21
|
+
docker_args="",
|
|
22
|
+
ip_port=None,
|
|
23
|
+
yes=True,
|
|
24
|
+
verbose=getattr(args, "verbose", False),
|
|
25
|
+
loglevel=getattr(args, "loglevel", "INFO"),
|
|
26
|
+
)
|
|
27
|
+
run_cave(app_dir, app_name, run_args)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
from cave_cli.utils.docker import (
|
|
7
|
+
remove_containers,
|
|
8
|
+
remove_image,
|
|
9
|
+
remove_volume,
|
|
10
|
+
)
|
|
11
|
+
from cave_cli.utils.logger import logger
|
|
12
|
+
from cave_cli.utils.subprocess import run_and_log
|
|
13
|
+
from cave_cli.utils.validate import confirm_action, validate_app_dir
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def purge(args: argparse.Namespace) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Usage:
|
|
19
|
+
|
|
20
|
+
- Removes a CAVE app and all its Docker resources
|
|
21
|
+
"""
|
|
22
|
+
app_path = args.path
|
|
23
|
+
if not os.path.isdir(app_path):
|
|
24
|
+
logger.error(f"No directory {app_path}")
|
|
25
|
+
sys.exit(1)
|
|
26
|
+
|
|
27
|
+
abs_path = os.path.abspath(app_path)
|
|
28
|
+
errors = validate_app_dir(abs_path)
|
|
29
|
+
if errors:
|
|
30
|
+
logger.error("Ensure you specified a valid CAVE app directory")
|
|
31
|
+
sys.exit(1)
|
|
32
|
+
|
|
33
|
+
app_name = os.path.basename(abs_path)
|
|
34
|
+
logger.header(f"Purging CAVE App ({app_name}):")
|
|
35
|
+
|
|
36
|
+
auto_yes = getattr(args, "yes", False)
|
|
37
|
+
if not auto_yes:
|
|
38
|
+
confirm_action(
|
|
39
|
+
"This will permanently remove all data associated "
|
|
40
|
+
f"with your CAVE App ({app_name})"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
remove_containers(app_name)
|
|
44
|
+
remove_volume(app_name)
|
|
45
|
+
remove_image(app_name)
|
|
46
|
+
|
|
47
|
+
logger.info("Removing files...")
|
|
48
|
+
try:
|
|
49
|
+
shutil.rmtree(abs_path)
|
|
50
|
+
except PermissionError:
|
|
51
|
+
logger.debug(
|
|
52
|
+
"Some files are owned by root (created by Docker). "
|
|
53
|
+
"Removing via Docker..."
|
|
54
|
+
)
|
|
55
|
+
run_and_log([
|
|
56
|
+
"docker", "run", "--rm",
|
|
57
|
+
"-v", f"{abs_path}:/purge",
|
|
58
|
+
"alpine", "rm", "-rf", "/purge",
|
|
59
|
+
])
|
|
60
|
+
if os.path.isdir(abs_path):
|
|
61
|
+
try:
|
|
62
|
+
shutil.rmtree(abs_path)
|
|
63
|
+
except PermissionError:
|
|
64
|
+
logger.error(
|
|
65
|
+
"Couldn't remove files. "
|
|
66
|
+
"You may need elevated permissions."
|
|
67
|
+
)
|
|
68
|
+
sys.exit(1)
|
|
69
|
+
logger.info("Done")
|
|
70
|
+
logger.info("Purge complete.")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from cave_cli.commands.run import run_cave
|
|
4
|
+
from cave_cli.utils.docker import remove_containers, remove_volume
|
|
5
|
+
from cave_cli.utils.logger import logger
|
|
6
|
+
from cave_cli.utils.validate import confirm_action, get_app
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def reset(
|
|
10
|
+
args: argparse.Namespace,
|
|
11
|
+
app_dir: str | None = None,
|
|
12
|
+
app_name: str | None = None,
|
|
13
|
+
) -> None:
|
|
14
|
+
"""
|
|
15
|
+
Usage:
|
|
16
|
+
|
|
17
|
+
- Removes Docker containers and volumes, then rebuilds from scratch
|
|
18
|
+
"""
|
|
19
|
+
auto_yes = getattr(args, "yes", False)
|
|
20
|
+
if not auto_yes:
|
|
21
|
+
confirm_action(
|
|
22
|
+
"This will remove the Docker containers "
|
|
23
|
+
"(deleted and recreated from scratch) for this app. "
|
|
24
|
+
"All data in your database will be lost"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if app_dir is None or app_name is None:
|
|
28
|
+
app_dir, app_name = get_app()
|
|
29
|
+
remove_containers(app_name)
|
|
30
|
+
remove_volume(app_name)
|
|
31
|
+
|
|
32
|
+
reset_args = argparse.Namespace(
|
|
33
|
+
entrypoint="./utils/reset_db.sh",
|
|
34
|
+
interactive=False,
|
|
35
|
+
it=False,
|
|
36
|
+
docker_args="",
|
|
37
|
+
ip_port=None,
|
|
38
|
+
yes=True,
|
|
39
|
+
verbose=getattr(args, "verbose", False),
|
|
40
|
+
loglevel=getattr(args, "loglevel", "INFO"),
|
|
41
|
+
)
|
|
42
|
+
run_cave(app_dir, app_name, reset_args)
|
|
43
|
+
logger.info("DB reset complete.")
|
cave_cli/commands/run.py
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from cave_cli.utils.docker import (
|
|
5
|
+
build_image,
|
|
6
|
+
create_network,
|
|
7
|
+
remove_containers,
|
|
8
|
+
run_detached,
|
|
9
|
+
run_interactive,
|
|
10
|
+
)
|
|
11
|
+
from cave_cli.utils.env import parse_env
|
|
12
|
+
from cave_cli.utils.logger import logger
|
|
13
|
+
from cave_cli.utils.net import find_open_port, is_port_available, parse_ip_port
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run(args: argparse.Namespace) -> None:
|
|
17
|
+
"""
|
|
18
|
+
Usage:
|
|
19
|
+
|
|
20
|
+
- Builds and runs the CAVE app's Docker containers
|
|
21
|
+
"""
|
|
22
|
+
from cave_cli.utils.validate import get_app
|
|
23
|
+
|
|
24
|
+
app_dir, app_name = get_app()
|
|
25
|
+
run_cave(app_dir, app_name, args)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def run_cave(
|
|
29
|
+
app_dir: str,
|
|
30
|
+
app_name: str,
|
|
31
|
+
args: argparse.Namespace,
|
|
32
|
+
) -> None:
|
|
33
|
+
build_image(app_name, app_dir)
|
|
34
|
+
|
|
35
|
+
interactive = getattr(args, "interactive", False) or getattr(
|
|
36
|
+
args, "it", False
|
|
37
|
+
)
|
|
38
|
+
entrypoint = getattr(args, "entrypoint", None) or "./utils/run_server.sh"
|
|
39
|
+
docker_args_str = getattr(args, "docker_args", "") or ""
|
|
40
|
+
extra_docker_args = docker_args_str.split() if docker_args_str else []
|
|
41
|
+
ip_port_arg = getattr(args, "ip_port", None)
|
|
42
|
+
|
|
43
|
+
command_args = getattr(args, "command_args", []) or []
|
|
44
|
+
extra_env = getattr(args, "extra_env", {}) or {}
|
|
45
|
+
|
|
46
|
+
if interactive:
|
|
47
|
+
server_command = ["bash"]
|
|
48
|
+
logger.header("CAVE App: (Interactive)")
|
|
49
|
+
else:
|
|
50
|
+
server_command = [entrypoint] + command_args
|
|
51
|
+
logger.header(f"CAVE App: ({entrypoint})")
|
|
52
|
+
|
|
53
|
+
if extra_docker_args:
|
|
54
|
+
logger.info(f"docker-args: {docker_args_str}")
|
|
55
|
+
|
|
56
|
+
env_vars = parse_env(f"{app_dir}/.env")
|
|
57
|
+
db_password = env_vars.get("DATABASE_PASSWORD", "")
|
|
58
|
+
db_image = env_vars.get("DATABASE_IMAGE", "postgres:latest")
|
|
59
|
+
db_command_str = env_vars.get(
|
|
60
|
+
"DATABASE_COMMAND", "postgres -c listen_addresses=*"
|
|
61
|
+
)
|
|
62
|
+
cache_image = env_vars.get("CACHE_IMAGE", "")
|
|
63
|
+
|
|
64
|
+
if not db_command_str:
|
|
65
|
+
db_command_str = "postgres -c listen_addresses=*"
|
|
66
|
+
logger.debug(
|
|
67
|
+
"DATABASE_COMMAND not set in '.env' file. "
|
|
68
|
+
"Using 'postgres -c listen_addresses=*' as default."
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if not cache_image:
|
|
72
|
+
logger.warn(
|
|
73
|
+
"CACHE_IMAGE not set in '.env' file. "
|
|
74
|
+
"Using valkey/valkey:7 as default."
|
|
75
|
+
)
|
|
76
|
+
cache_image = "valkey/valkey:7"
|
|
77
|
+
|
|
78
|
+
network = f"cave-net:{app_name}"
|
|
79
|
+
create_network(app_name)
|
|
80
|
+
|
|
81
|
+
run_detached(
|
|
82
|
+
name=f"{app_name}_db_host",
|
|
83
|
+
image=db_image,
|
|
84
|
+
network=network,
|
|
85
|
+
volumes=[f"{app_name}_pg_volume:/var/lib/postgresql/data"],
|
|
86
|
+
env_vars={
|
|
87
|
+
"POSTGRES_PASSWORD": db_password,
|
|
88
|
+
"POSTGRES_USER": f"{app_name}_user",
|
|
89
|
+
"POSTGRES_DB": f"{app_name}_name",
|
|
90
|
+
},
|
|
91
|
+
extra_args=extra_docker_args or None,
|
|
92
|
+
command=db_command_str.split(),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
run_detached(
|
|
96
|
+
name=f"{app_name}_redis_host",
|
|
97
|
+
image=cache_image,
|
|
98
|
+
network=network,
|
|
99
|
+
volumes=[f"{app_name}_redis_volume:/data"],
|
|
100
|
+
extra_args=extra_docker_args or None,
|
|
101
|
+
command=["--save", "7200", "1"],
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
django_env = {
|
|
105
|
+
"DATABASE_HOST": f"{app_name}_db_host",
|
|
106
|
+
"DATABASE_USER": f"{app_name}_user",
|
|
107
|
+
"DATABASE_PASSWORD": db_password,
|
|
108
|
+
"DATABASE_NAME": f"{app_name}_name",
|
|
109
|
+
"DATABASE_PORT": "5432",
|
|
110
|
+
"REDIS_HOST": f"{app_name}_redis_host",
|
|
111
|
+
"REDIS_PORT": "6379",
|
|
112
|
+
**extra_env,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
django_volumes = [
|
|
116
|
+
f"{app_dir}:/app"
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
parsed = parse_ip_port(ip_port_arg) if ip_port_arg else None
|
|
120
|
+
|
|
121
|
+
if parsed:
|
|
122
|
+
ip, port = parsed
|
|
123
|
+
if not is_port_available(port):
|
|
124
|
+
logger.error(
|
|
125
|
+
"The specified port is in use. Please try another."
|
|
126
|
+
)
|
|
127
|
+
sys.exit(1)
|
|
128
|
+
|
|
129
|
+
if entrypoint == "./utils/run_server.sh" and not interactive:
|
|
130
|
+
logger.info(
|
|
131
|
+
f"Your Cave App can be accessed from Chrome at:\n"
|
|
132
|
+
f"https://{ip}:{port}\n"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
run_detached(
|
|
136
|
+
name=f"{app_name}_nginx_host",
|
|
137
|
+
image="nginx",
|
|
138
|
+
network=network,
|
|
139
|
+
extra_args=[
|
|
140
|
+
"--restart",
|
|
141
|
+
"unless-stopped",
|
|
142
|
+
"-p",
|
|
143
|
+
f"{ip}:{port}:8000",
|
|
144
|
+
]
|
|
145
|
+
+ (extra_docker_args or []),
|
|
146
|
+
volumes=[
|
|
147
|
+
f"{app_dir}/utils/lan_hosting:/certs",
|
|
148
|
+
f"{app_dir}/utils/nginx_ssl.conf.template:"
|
|
149
|
+
"/etc/nginx/templates/default.conf.template:ro",
|
|
150
|
+
],
|
|
151
|
+
env_vars={
|
|
152
|
+
"CAVE_HOST": f"{app_name}_django",
|
|
153
|
+
"CAVE_PORT": str(port),
|
|
154
|
+
"CAVE_IP": ip,
|
|
155
|
+
},
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
django_env["CSRF_TRUSTED_ORIGIN"] = f"{ip}:{port}"
|
|
159
|
+
run_interactive(
|
|
160
|
+
name=f"{app_name}_django",
|
|
161
|
+
image=f"cave-app:{app_name}",
|
|
162
|
+
network=network,
|
|
163
|
+
ports=["8000"],
|
|
164
|
+
volumes=django_volumes,
|
|
165
|
+
env_vars=django_env,
|
|
166
|
+
extra_args=extra_docker_args or None,
|
|
167
|
+
command=server_command,
|
|
168
|
+
)
|
|
169
|
+
else:
|
|
170
|
+
port = find_open_port(8000)
|
|
171
|
+
if entrypoint == "./utils/run_server.sh" and not interactive:
|
|
172
|
+
logger.info(
|
|
173
|
+
f"Your Cave App can be accessed from Chrome at:\n"
|
|
174
|
+
f"http://localhost:{port}\n"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
run_interactive(
|
|
178
|
+
name=f"{app_name}_django",
|
|
179
|
+
image=f"cave-app:{app_name}",
|
|
180
|
+
network=network,
|
|
181
|
+
ports=[f"{port}:8000"],
|
|
182
|
+
volumes=django_volumes,
|
|
183
|
+
env_vars=django_env,
|
|
184
|
+
extra_args=extra_docker_args or None,
|
|
185
|
+
command=server_command,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
logger.debug("Stopping Running Containers...")
|
|
189
|
+
remove_containers(app_name)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import tempfile
|
|
5
|
+
|
|
6
|
+
from cave_cli.commands.reset import reset
|
|
7
|
+
from cave_cli.utils.constants import HTTPS_URL
|
|
8
|
+
from cave_cli.utils.git import clone
|
|
9
|
+
from cave_cli.utils.logger import logger
|
|
10
|
+
from cave_cli.utils.sync import sync_files
|
|
11
|
+
from cave_cli.utils.validate import confirm_action, find_app_dir
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def sync_cmd(args: argparse.Namespace) -> None:
|
|
15
|
+
"""
|
|
16
|
+
Usage:
|
|
17
|
+
|
|
18
|
+
- Merges files from another git repository into the current CAVE app
|
|
19
|
+
"""
|
|
20
|
+
app_dir = find_app_dir()
|
|
21
|
+
auto_yes = getattr(args, "yes", False)
|
|
22
|
+
|
|
23
|
+
if not auto_yes:
|
|
24
|
+
confirm_action(
|
|
25
|
+
"This will reset your docker containers and database. "
|
|
26
|
+
"It will also potentially update your local files"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
logger.header("Sync:")
|
|
30
|
+
|
|
31
|
+
url = getattr(args, "url", None)
|
|
32
|
+
if not url:
|
|
33
|
+
logger.error("--url is required for sync")
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
branch = getattr(args, "branch", None)
|
|
37
|
+
includes = getattr(args, "include", None) or []
|
|
38
|
+
excludes = getattr(args, "exclude", None) or []
|
|
39
|
+
|
|
40
|
+
logger.info("Syncing files with the following parameters:\n")
|
|
41
|
+
logger.info(f"App Location: {app_dir}")
|
|
42
|
+
logger.info(f"Using Repo: {url}")
|
|
43
|
+
logger.info(f"Using Branch: {branch or 'default'}\n")
|
|
44
|
+
logger.info("Downloading repo to sync...")
|
|
45
|
+
|
|
46
|
+
temp_dir = tempfile.mkdtemp()
|
|
47
|
+
success = clone(url, temp_dir, branch=branch)
|
|
48
|
+
|
|
49
|
+
if not success or not os.listdir(temp_dir):
|
|
50
|
+
logger.error(
|
|
51
|
+
f"Failed!\n"
|
|
52
|
+
f"Ensure you have access rights to the repository: {url}\n"
|
|
53
|
+
f"Ensure you specified a valid branch: {branch}."
|
|
54
|
+
)
|
|
55
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
logger.info("Done")
|
|
59
|
+
logger.info("Syncing files...")
|
|
60
|
+
|
|
61
|
+
sync_files(
|
|
62
|
+
source=temp_dir,
|
|
63
|
+
dest=app_dir,
|
|
64
|
+
includes=includes,
|
|
65
|
+
excludes=excludes,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
logger.info("Done")
|
|
69
|
+
|
|
70
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
71
|
+
|
|
72
|
+
reset_args = argparse.Namespace(
|
|
73
|
+
yes=True,
|
|
74
|
+
verbose=getattr(args, "verbose", False),
|
|
75
|
+
loglevel=getattr(args, "loglevel", "INFO"),
|
|
76
|
+
)
|
|
77
|
+
reset(reset_args)
|
|
78
|
+
|
|
79
|
+
logger.info("Sync complete.")
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
|
|
3
|
+
from cave_cli.commands.run import run_cave
|
|
4
|
+
from cave_cli.utils.logger import logger
|
|
5
|
+
from cave_cli.utils.validate import get_app
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test(args: argparse.Namespace) -> None:
|
|
9
|
+
"""
|
|
10
|
+
Usage:
|
|
11
|
+
|
|
12
|
+
- Runs tests for the CAVE app in the current directory
|
|
13
|
+
"""
|
|
14
|
+
app_dir, app_name = get_app()
|
|
15
|
+
logger.info("Testing cave_api...")
|
|
16
|
+
|
|
17
|
+
remaining = getattr(args, "remaining", []) or []
|
|
18
|
+
|
|
19
|
+
if remaining:
|
|
20
|
+
command_args = [remaining[0]]
|
|
21
|
+
extra_env: dict[str, str] = {}
|
|
22
|
+
else:
|
|
23
|
+
command_args = []
|
|
24
|
+
extra_env = {"ALL_FLAG": "true"}
|
|
25
|
+
|
|
26
|
+
run_args = argparse.Namespace(
|
|
27
|
+
entrypoint="./utils/run_test.sh",
|
|
28
|
+
command_args=command_args,
|
|
29
|
+
extra_env=extra_env,
|
|
30
|
+
interactive=False,
|
|
31
|
+
it=False,
|
|
32
|
+
docker_args="",
|
|
33
|
+
ip_port=None,
|
|
34
|
+
yes=True,
|
|
35
|
+
verbose=getattr(args, "verbose", False),
|
|
36
|
+
loglevel=getattr(args, "loglevel", "INFO"),
|
|
37
|
+
)
|
|
38
|
+
run_cave(app_dir, app_name, run_args)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from cave_cli.utils.logger import logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def uninstall(args: argparse.Namespace) -> None:
|
|
9
|
+
"""
|
|
10
|
+
Usage:
|
|
11
|
+
|
|
12
|
+
- Removes the CAVE CLI package
|
|
13
|
+
"""
|
|
14
|
+
try:
|
|
15
|
+
response = input(
|
|
16
|
+
"Are you sure you want to uninstall CAVE CLI? [y/N] "
|
|
17
|
+
)
|
|
18
|
+
except (EOFError, KeyboardInterrupt):
|
|
19
|
+
print()
|
|
20
|
+
logger.error("Uninstall canceled")
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
if response.strip().lower() not in ("y", "yes"):
|
|
24
|
+
logger.error("Uninstall canceled")
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
logger.info("Removing installation...")
|
|
28
|
+
result = subprocess.run(
|
|
29
|
+
[sys.executable, "-m", "pip", "uninstall", "cave_cli", "-y"],
|
|
30
|
+
stdout=subprocess.PIPE,
|
|
31
|
+
stderr=subprocess.PIPE,
|
|
32
|
+
text=True,
|
|
33
|
+
)
|
|
34
|
+
if result.returncode == 0:
|
|
35
|
+
logger.info("Done.")
|
|
36
|
+
else:
|
|
37
|
+
logger.error("Failed to uninstall CAVE CLI.")
|
|
38
|
+
if result.stderr:
|
|
39
|
+
logger.error(result.stderr.strip())
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from cave_cli.utils.logger import logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def update(args: argparse.Namespace) -> None:
|
|
9
|
+
"""
|
|
10
|
+
Usage:
|
|
11
|
+
|
|
12
|
+
- Updates the CAVE CLI via pip install --upgrade
|
|
13
|
+
"""
|
|
14
|
+
logger.info("Updating CAVE CLI...")
|
|
15
|
+
version = getattr(args, "version", None)
|
|
16
|
+
if version:
|
|
17
|
+
spec = (
|
|
18
|
+
f"cave_cli @ "
|
|
19
|
+
f"git+https://github.com/MIT-CAVE/cave_cli.git@{version}"
|
|
20
|
+
)
|
|
21
|
+
else:
|
|
22
|
+
spec = (
|
|
23
|
+
"cave_cli @ "
|
|
24
|
+
"git+https://github.com/MIT-CAVE/cave_cli.git@main"
|
|
25
|
+
)
|
|
26
|
+
result = subprocess.run(
|
|
27
|
+
[sys.executable, "-m", "pip", "install", "--upgrade", spec],
|
|
28
|
+
stdout=subprocess.PIPE,
|
|
29
|
+
stderr=subprocess.PIPE,
|
|
30
|
+
text=True,
|
|
31
|
+
)
|
|
32
|
+
if result.returncode == 0:
|
|
33
|
+
logger.info("Done.")
|
|
34
|
+
logger.info("CAVE CLI updated.")
|
|
35
|
+
else:
|
|
36
|
+
logger.error("Failed to update CAVE CLI.")
|
|
37
|
+
if result.stderr:
|
|
38
|
+
logger.error(result.stderr.strip())
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import tempfile
|
|
5
|
+
|
|
6
|
+
from cave_cli.commands.create import remove_licence_info
|
|
7
|
+
from cave_cli.commands.run import run_cave
|
|
8
|
+
from cave_cli.commands.sync_cmd import sync_cmd
|
|
9
|
+
from cave_cli.utils.constants import HTTPS_URL
|
|
10
|
+
from cave_cli.utils.env import upgrade_env
|
|
11
|
+
from cave_cli.utils.git import clone
|
|
12
|
+
from cave_cli.utils.logger import logger
|
|
13
|
+
from cave_cli.utils.sync import sync_files
|
|
14
|
+
from cave_cli.utils.validate import confirm_action, find_app_dir, get_app
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def upgrade(args: argparse.Namespace) -> None:
|
|
18
|
+
"""
|
|
19
|
+
Usage:
|
|
20
|
+
|
|
21
|
+
- Upgrades the CAVE app in the current directory from the template repository
|
|
22
|
+
"""
|
|
23
|
+
auto_yes = getattr(args, "yes", False)
|
|
24
|
+
if not auto_yes:
|
|
25
|
+
confirm_action(
|
|
26
|
+
"This will potentially update all files not in "
|
|
27
|
+
"'cave_api/' or '.env' and reset your database"
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
logger.header("Upgrade:")
|
|
31
|
+
logger.info("Upgrading CAVE App via a Sync operation...\n")
|
|
32
|
+
|
|
33
|
+
app_dir, app_name = get_app()
|
|
34
|
+
url = getattr(args, "url", None) or HTTPS_URL
|
|
35
|
+
version = getattr(args, "version", None)
|
|
36
|
+
skip_env_upgrade = getattr(args, "skip_env_upgrade", False)
|
|
37
|
+
|
|
38
|
+
sync_args = argparse.Namespace(
|
|
39
|
+
url=url,
|
|
40
|
+
branch=version or "main",
|
|
41
|
+
include=["cave_api/docs"],
|
|
42
|
+
exclude=[".env", ".gitignore", "cave_api/*"],
|
|
43
|
+
yes=True,
|
|
44
|
+
verbose=getattr(args, "verbose", False),
|
|
45
|
+
loglevel=getattr(args, "loglevel", "INFO"),
|
|
46
|
+
)
|
|
47
|
+
sync_cmd(sync_args)
|
|
48
|
+
|
|
49
|
+
if not skip_env_upgrade:
|
|
50
|
+
temp_dir = tempfile.mkdtemp()
|
|
51
|
+
clone(url, temp_dir, branch=version or "main")
|
|
52
|
+
env_path = os.path.join(app_dir, ".env")
|
|
53
|
+
upgrade_env(env_path, temp_dir)
|
|
54
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
55
|
+
|
|
56
|
+
remove_licence_info(app_dir)
|
|
57
|
+
|
|
58
|
+
logger.info("\nUpdating Docs...")
|
|
59
|
+
docs_args = argparse.Namespace(
|
|
60
|
+
entrypoint="./utils/generate_docs.sh",
|
|
61
|
+
interactive=False,
|
|
62
|
+
it=False,
|
|
63
|
+
docker_args="",
|
|
64
|
+
ip_port=None,
|
|
65
|
+
yes=True,
|
|
66
|
+
verbose=getattr(args, "verbose", False),
|
|
67
|
+
loglevel=getattr(args, "loglevel", "INFO"),
|
|
68
|
+
)
|
|
69
|
+
run_cave(app_dir, app_name, docs_args)
|
|
70
|
+
logger.info("Upgrade complete.")
|