ndev-stack 0.1.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.
- ndev/__init__.py +8 -0
- ndev/__main__.py +4 -0
- ndev/cli.py +24 -0
- ndev/common/__init__.py +3 -0
- ndev/common/config.py +114 -0
- ndev/common/constants.py +51 -0
- ndev/common/github.py +13 -0
- ndev/common/logger.py +11 -0
- ndev/common/manifest.py +41 -0
- ndev/common/utils.py +96 -0
- ndev/linux/__init__.py +1 -0
- ndev/linux/chroot/manager.py +63 -0
- ndev/linux/chroot/packages.py +91 -0
- ndev/linux/chroot/shell.py +9 -0
- ndev/linux/cli.py +235 -0
- ndev/linux/commands/available.py +38 -0
- ndev/linux/commands/clean.py +25 -0
- ndev/linux/commands/ctl.py +192 -0
- ndev/linux/commands/current.py +11 -0
- ndev/linux/commands/db.py +319 -0
- ndev/linux/commands/doctor.py +56 -0
- ndev/linux/commands/grok.py +75 -0
- ndev/linux/commands/install.py +39 -0
- ndev/linux/commands/list.py +47 -0
- ndev/linux/commands/logs.py +36 -0
- ndev/linux/commands/mailpit.py +82 -0
- ndev/linux/commands/reload.py +26 -0
- ndev/linux/commands/restart.py +34 -0
- ndev/linux/commands/setup.py +113 -0
- ndev/linux/commands/start.py +34 -0
- ndev/linux/commands/status.py +81 -0
- ndev/linux/commands/stop.py +34 -0
- ndev/linux/commands/uninstall.py +69 -0
- ndev/linux/commands/update.py +57 -0
- ndev/linux/commands/upgrade.py +81 -0
- ndev/linux/commands/use.py +108 -0
- ndev/linux/commands/vhost.py +350 -0
- ndev/linux/php/builder.py +183 -0
- ndev/linux/php/downloader.py +58 -0
- ndev/linux/php/extensions.py +146 -0
- ndev/linux/php/installer.py +42 -0
- ndev/linux/php/resolver.py +59 -0
- ndev/linux/php/templates.py +128 -0
- ndev/linux/runtime/fpm.py +117 -0
- ndev/linux/runtime/mailpit.py +244 -0
- ndev/linux/runtime/pma.py +223 -0
- ndev/linux/runtime/process.py +37 -0
- ndev/linux/runtime/sockets.py +16 -0
- ndev/linux/runtime/upgrade.py +431 -0
- ndev/linux/tui.py +1423 -0
- ndev/main.py +52 -0
- ndev/tui.py +23 -0
- ndev/win/__init__.py +1 -0
- ndev/win/cli.py +1898 -0
- ndev/win/commands/__init__.py +0 -0
- ndev/win/core/__init__.py +0 -0
- ndev/win/core/db.py +265 -0
- ndev/win/core/elevate.py +94 -0
- ndev/win/core/ext.py +241 -0
- ndev/win/core/fcgi.py +216 -0
- ndev/win/core/grok.py +55 -0
- ndev/win/core/logs.py +66 -0
- ndev/win/core/mailpit.py +236 -0
- ndev/win/core/mkcert.py +65 -0
- ndev/win/core/paths.py +85 -0
- ndev/win/core/php.py +533 -0
- ndev/win/core/pma.py +190 -0
- ndev/win/core/services.py +349 -0
- ndev/win/core/setup.py +361 -0
- ndev/win/core/upgrade.py +513 -0
- ndev/win/core/vhost.py +289 -0
- ndev/win/templates/vhost.conf.tmpl +33 -0
- ndev/win/templates/vhost_ssl.conf.tmpl +43 -0
- ndev/win/tui.py +1313 -0
- ndev_stack-0.1.0.dist-info/METADATA +553 -0
- ndev_stack-0.1.0.dist-info/RECORD +79 -0
- ndev_stack-0.1.0.dist-info/WHEEL +5 -0
- ndev_stack-0.1.0.dist-info/entry_points.txt +4 -0
- ndev_stack-0.1.0.dist-info/top_level.txt +1 -0
ndev/linux/cli.py
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from ndev.common.logger import logger
|
|
3
|
+
from ndev.common.config import init_layout
|
|
4
|
+
|
|
5
|
+
app = typer.Typer(
|
|
6
|
+
help="ndev: Compile, install, and manage isolated PHP-FPM versions on Debian.",
|
|
7
|
+
no_args_is_help=True
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
ext_app = typer.Typer(
|
|
11
|
+
help="Manage PHP extensions for installed versions.",
|
|
12
|
+
no_args_is_help=True
|
|
13
|
+
)
|
|
14
|
+
app.add_typer(ext_app, name="ext")
|
|
15
|
+
|
|
16
|
+
# Register commands
|
|
17
|
+
from ndev.linux.commands.install import install_cmd
|
|
18
|
+
app.command("install")(install_cmd)
|
|
19
|
+
|
|
20
|
+
from ndev.linux.commands.uninstall import uninstall_cmd
|
|
21
|
+
app.command("uninstall")(uninstall_cmd)
|
|
22
|
+
|
|
23
|
+
from ndev.linux.commands.start import start_cmd
|
|
24
|
+
app.command("start")(start_cmd)
|
|
25
|
+
|
|
26
|
+
from ndev.linux.commands.stop import stop_cmd
|
|
27
|
+
app.command("stop")(stop_cmd)
|
|
28
|
+
|
|
29
|
+
from ndev.linux.commands.restart import restart_cmd
|
|
30
|
+
app.command("restart")(restart_cmd)
|
|
31
|
+
|
|
32
|
+
from ndev.linux.commands.reload import reload_cmd
|
|
33
|
+
app.command("reload")(reload_cmd)
|
|
34
|
+
|
|
35
|
+
from ndev.linux.commands.status import status_cmd
|
|
36
|
+
app.command("status")(status_cmd)
|
|
37
|
+
|
|
38
|
+
from ndev.linux.commands.list import list_cmd
|
|
39
|
+
app.command("list")(list_cmd)
|
|
40
|
+
|
|
41
|
+
from ndev.linux.commands.current import current_cmd
|
|
42
|
+
app.command("current")(current_cmd)
|
|
43
|
+
|
|
44
|
+
from ndev.linux.commands.use import use_cmd
|
|
45
|
+
app.command("use")(use_cmd)
|
|
46
|
+
|
|
47
|
+
from ndev.linux.commands.available import available_cmd
|
|
48
|
+
app.command("available")(available_cmd)
|
|
49
|
+
|
|
50
|
+
from ndev.linux.commands.doctor import doctor_cmd
|
|
51
|
+
app.command("doctor")(doctor_cmd)
|
|
52
|
+
|
|
53
|
+
from ndev.linux.commands.update import update_cmd
|
|
54
|
+
app.command("update")(update_cmd)
|
|
55
|
+
|
|
56
|
+
from ndev.linux.commands.clean import clean_cmd
|
|
57
|
+
app.command("clean")(clean_cmd)
|
|
58
|
+
|
|
59
|
+
from ndev.linux.commands.logs import logs_cmd
|
|
60
|
+
app.command("logs")(logs_cmd)
|
|
61
|
+
|
|
62
|
+
from ndev.linux.commands.grok import grok_cmd
|
|
63
|
+
app.command("grok")(grok_cmd)
|
|
64
|
+
|
|
65
|
+
from ndev.linux.commands.vhost import vhost_cmd
|
|
66
|
+
app.command("vhost")(vhost_cmd)
|
|
67
|
+
|
|
68
|
+
from ndev.linux.commands.ctl import ctl_cmd
|
|
69
|
+
app.command("ctl")(ctl_cmd)
|
|
70
|
+
|
|
71
|
+
from ndev.linux.commands.setup import setup_cmd
|
|
72
|
+
app.command("setup")(setup_cmd)
|
|
73
|
+
|
|
74
|
+
from ndev.linux.commands.upgrade import app as upgrade_app
|
|
75
|
+
app.add_typer(upgrade_app, name="upgrade")
|
|
76
|
+
|
|
77
|
+
from ndev.linux.commands.db import db_app
|
|
78
|
+
app.add_typer(db_app, name="db")
|
|
79
|
+
|
|
80
|
+
from ndev.linux.commands.mailpit import mailpit_app
|
|
81
|
+
app.add_typer(mailpit_app, name="mailpit")
|
|
82
|
+
|
|
83
|
+
@app.command("ui")
|
|
84
|
+
def ui_cmd():
|
|
85
|
+
"""Launch the interactive Textual TUI dashboard."""
|
|
86
|
+
from ndev.tui import run_dashboard
|
|
87
|
+
run_dashboard()
|
|
88
|
+
|
|
89
|
+
@app.command("tui", hidden=True)
|
|
90
|
+
def tui_cmd():
|
|
91
|
+
"""Alias for ui."""
|
|
92
|
+
from ndev.tui import run_dashboard
|
|
93
|
+
run_dashboard()
|
|
94
|
+
|
|
95
|
+
@app.command("dashboard", hidden=True)
|
|
96
|
+
def dashboard_cmd():
|
|
97
|
+
"""Alias for ui."""
|
|
98
|
+
from ndev.tui import run_dashboard
|
|
99
|
+
run_dashboard()
|
|
100
|
+
|
|
101
|
+
@app.command("shell")
|
|
102
|
+
def shell():
|
|
103
|
+
"""Enter the interactive bubblewrap build environment shell."""
|
|
104
|
+
from ndev.linux.chroot.shell import enter_sandbox_shell
|
|
105
|
+
enter_sandbox_shell()
|
|
106
|
+
|
|
107
|
+
# Extension command implementations
|
|
108
|
+
@ext_app.command("list")
|
|
109
|
+
def ext_list(version: str = typer.Argument(None, help="PHP version (e.g. 8.4.12)")):
|
|
110
|
+
"""List loaded extensions for a PHP version."""
|
|
111
|
+
from ndev.linux.php.extensions import list_extensions
|
|
112
|
+
if not version:
|
|
113
|
+
from ndev.common.utils import get_version_or_prompt
|
|
114
|
+
version = get_version_or_prompt(version, "PHP version")
|
|
115
|
+
if not version:
|
|
116
|
+
logger.error("PHP version is required.")
|
|
117
|
+
raise typer.Exit(code=1)
|
|
118
|
+
try:
|
|
119
|
+
exts = list_extensions(version)
|
|
120
|
+
for ext in exts:
|
|
121
|
+
print(ext)
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.error(f"Error listing extensions: {e}")
|
|
124
|
+
raise typer.Exit(code=1)
|
|
125
|
+
|
|
126
|
+
@ext_app.command("install")
|
|
127
|
+
def ext_install(
|
|
128
|
+
ext_name: str = typer.Argument(None, help="Extension name (e.g. redis)"),
|
|
129
|
+
version: str = typer.Argument(None, help="PHP version (e.g. 8.4.12)"),
|
|
130
|
+
show_logs: bool = typer.Option(
|
|
131
|
+
False,
|
|
132
|
+
"--show-logs",
|
|
133
|
+
"-s",
|
|
134
|
+
help="Show verbose compilation and installation logs"
|
|
135
|
+
)
|
|
136
|
+
):
|
|
137
|
+
"""Install and enable a PECL extension."""
|
|
138
|
+
from ndev.linux.php.extensions import install_extension
|
|
139
|
+
if not ext_name:
|
|
140
|
+
ext_name = typer.prompt("Extension name (e.g. redis)").strip()
|
|
141
|
+
if not ext_name:
|
|
142
|
+
logger.error("Extension name is required.")
|
|
143
|
+
raise typer.Exit(code=1)
|
|
144
|
+
if not version:
|
|
145
|
+
from ndev.common.utils import get_version_or_prompt
|
|
146
|
+
version = get_version_or_prompt(version, "PHP version")
|
|
147
|
+
if not version:
|
|
148
|
+
logger.error("PHP version is required.")
|
|
149
|
+
raise typer.Exit(code=1)
|
|
150
|
+
try:
|
|
151
|
+
install_extension(version, ext_name, show_logs=show_logs)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.error(f"Error installing extension: {e}")
|
|
154
|
+
raise typer.Exit(code=1)
|
|
155
|
+
|
|
156
|
+
@ext_app.command("uninstall")
|
|
157
|
+
def ext_uninstall(
|
|
158
|
+
ext_name: str = typer.Argument(None, help="Extension name (e.g. redis)"),
|
|
159
|
+
version: str = typer.Argument(None, help="PHP version (e.g. 8.4.12)")
|
|
160
|
+
):
|
|
161
|
+
"""Disable/uninstall an extension."""
|
|
162
|
+
from ndev.linux.php.extensions import disable_extension
|
|
163
|
+
if not ext_name:
|
|
164
|
+
ext_name = typer.prompt("Extension name (e.g. redis)").strip()
|
|
165
|
+
if not ext_name:
|
|
166
|
+
logger.error("Extension name is required.")
|
|
167
|
+
raise typer.Exit(code=1)
|
|
168
|
+
if not version:
|
|
169
|
+
from ndev.common.utils import get_version_or_prompt
|
|
170
|
+
version = get_version_or_prompt(version, "PHP version")
|
|
171
|
+
if not version:
|
|
172
|
+
logger.error("PHP version is required.")
|
|
173
|
+
raise typer.Exit(code=1)
|
|
174
|
+
try:
|
|
175
|
+
disable_extension(version, ext_name)
|
|
176
|
+
except Exception as e:
|
|
177
|
+
logger.error(f"Error uninstalling extension: {e}")
|
|
178
|
+
raise typer.Exit(code=1)
|
|
179
|
+
|
|
180
|
+
@ext_app.command("enable")
|
|
181
|
+
def ext_enable(
|
|
182
|
+
ext_name: str = typer.Argument(None, help="Extension name (e.g. redis)"),
|
|
183
|
+
version: str = typer.Argument(None, help="PHP version (e.g. 8.4.12)")
|
|
184
|
+
):
|
|
185
|
+
"""Enable an installed extension."""
|
|
186
|
+
from ndev.linux.php.extensions import enable_extension
|
|
187
|
+
if not ext_name:
|
|
188
|
+
ext_name = typer.prompt("Extension name (e.g. redis)").strip()
|
|
189
|
+
if not ext_name:
|
|
190
|
+
logger.error("Extension name is required.")
|
|
191
|
+
raise typer.Exit(code=1)
|
|
192
|
+
if not version:
|
|
193
|
+
from ndev.common.utils import get_version_or_prompt
|
|
194
|
+
version = get_version_or_prompt(version, "PHP version")
|
|
195
|
+
if not version:
|
|
196
|
+
logger.error("PHP version is required.")
|
|
197
|
+
raise typer.Exit(code=1)
|
|
198
|
+
try:
|
|
199
|
+
enable_extension(version, ext_name)
|
|
200
|
+
except Exception as e:
|
|
201
|
+
logger.error(f"Error enabling extension: {e}")
|
|
202
|
+
raise typer.Exit(code=1)
|
|
203
|
+
|
|
204
|
+
@ext_app.command("disable")
|
|
205
|
+
def ext_disable(
|
|
206
|
+
ext_name: str = typer.Argument(None, help="Extension name (e.g. redis)"),
|
|
207
|
+
version: str = typer.Argument(None, help="PHP version (e.g. 8.4.12)")
|
|
208
|
+
):
|
|
209
|
+
"""Disable an extension."""
|
|
210
|
+
from ndev.linux.php.extensions import disable_extension
|
|
211
|
+
if not ext_name:
|
|
212
|
+
ext_name = typer.prompt("Extension name (e.g. redis)").strip()
|
|
213
|
+
if not ext_name:
|
|
214
|
+
logger.error("Extension name is required.")
|
|
215
|
+
raise typer.Exit(code=1)
|
|
216
|
+
if not version:
|
|
217
|
+
from ndev.common.utils import get_version_or_prompt
|
|
218
|
+
version = get_version_or_prompt(version, "PHP version")
|
|
219
|
+
if not version:
|
|
220
|
+
logger.error("PHP version is required.")
|
|
221
|
+
raise typer.Exit(code=1)
|
|
222
|
+
try:
|
|
223
|
+
disable_extension(version, ext_name)
|
|
224
|
+
except Exception as e:
|
|
225
|
+
logger.error(f"Error disabling extension: {e}")
|
|
226
|
+
raise typer.Exit(code=1)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
@app.callback()
|
|
230
|
+
def main():
|
|
231
|
+
# Initialize the folder structure in ~/.ndev
|
|
232
|
+
init_layout()
|
|
233
|
+
|
|
234
|
+
if __name__ == "__main__":
|
|
235
|
+
app()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from rich.console import Console
|
|
3
|
+
from rich.table import Table
|
|
4
|
+
from ndev.common.github import fetch_releases
|
|
5
|
+
from ndev.common.logger import logger
|
|
6
|
+
|
|
7
|
+
console = Console()
|
|
8
|
+
|
|
9
|
+
def available_cmd():
|
|
10
|
+
"""List all available PHP versions from php.net."""
|
|
11
|
+
logger.info("Fetching available PHP versions from php.net...")
|
|
12
|
+
|
|
13
|
+
releases_8 = fetch_releases(8)
|
|
14
|
+
releases_7 = fetch_releases(7)
|
|
15
|
+
|
|
16
|
+
all_releases = {**releases_8, **releases_7}
|
|
17
|
+
|
|
18
|
+
if not all_releases:
|
|
19
|
+
logger.error("Could not fetch available releases. Check your network connection.")
|
|
20
|
+
raise typer.Exit(code=1)
|
|
21
|
+
|
|
22
|
+
from packaging.version import parse as parse_version
|
|
23
|
+
sorted_versions = sorted(all_releases.keys(), key=parse_version, reverse=True)
|
|
24
|
+
|
|
25
|
+
table = Table(title="Available PHP Releases")
|
|
26
|
+
table.add_column("Version", style="bold cyan")
|
|
27
|
+
table.add_column("Release Date")
|
|
28
|
+
table.add_column("Security Release")
|
|
29
|
+
|
|
30
|
+
for v in sorted_versions:
|
|
31
|
+
data = all_releases[v]
|
|
32
|
+
date = data.get("date", "N/A")
|
|
33
|
+
tags = data.get("tags", [])
|
|
34
|
+
is_security = "Yes" if "security" in tags else "No"
|
|
35
|
+
|
|
36
|
+
table.add_row(v, date, is_security)
|
|
37
|
+
|
|
38
|
+
console.print(table)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import typer
|
|
3
|
+
from ndev.common.constants import BUILDS_DIR, DOWNLOADS_DIR
|
|
4
|
+
from ndev.common.logger import logger
|
|
5
|
+
|
|
6
|
+
def clean_cmd(
|
|
7
|
+
builds: bool = typer.Option(True, "--builds/--no-builds", help="Clean extracted build source folders"),
|
|
8
|
+
downloads: bool = typer.Option(False, "--downloads", help="Clean cached download tarballs")
|
|
9
|
+
):
|
|
10
|
+
"""Clean up build files and optionally downloaded archives to free disk space."""
|
|
11
|
+
try:
|
|
12
|
+
if builds and BUILDS_DIR.exists():
|
|
13
|
+
logger.info(f"Cleaning build directory: {BUILDS_DIR}")
|
|
14
|
+
shutil.rmtree(BUILDS_DIR)
|
|
15
|
+
BUILDS_DIR.mkdir()
|
|
16
|
+
|
|
17
|
+
if downloads and DOWNLOADS_DIR.exists():
|
|
18
|
+
logger.info(f"Cleaning downloads cache: {DOWNLOADS_DIR}")
|
|
19
|
+
shutil.rmtree(DOWNLOADS_DIR)
|
|
20
|
+
DOWNLOADS_DIR.mkdir()
|
|
21
|
+
|
|
22
|
+
logger.info("Cleanup completed successfully.")
|
|
23
|
+
except Exception as e:
|
|
24
|
+
logger.error(f"Cleanup failed: {e}")
|
|
25
|
+
raise typer.Exit(code=1)
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
import shutil
|
|
6
|
+
import typer
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
from ndev.common.logger import logger
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
def get_user_ndev_dir() -> Path:
|
|
15
|
+
sudo_user = os.environ.get("SUDO_USER")
|
|
16
|
+
if sudo_user:
|
|
17
|
+
try:
|
|
18
|
+
import pwd
|
|
19
|
+
return Path(pwd.getpwnam(sudo_user).pw_dir) / ".ndev"
|
|
20
|
+
except Exception:
|
|
21
|
+
pass
|
|
22
|
+
return Path(os.path.expanduser("~/.ndev"))
|
|
23
|
+
|
|
24
|
+
def service_exists(service: str) -> bool:
|
|
25
|
+
if service == "pma":
|
|
26
|
+
return True
|
|
27
|
+
if service.startswith("ndev-"):
|
|
28
|
+
version = service[5:]
|
|
29
|
+
return (get_user_ndev_dir() / "php" / version).exists()
|
|
30
|
+
|
|
31
|
+
if shutil.which("systemctl"):
|
|
32
|
+
res = subprocess.run(["systemctl", "list-unit-files", f"{service}.service"], capture_output=True, text=True)
|
|
33
|
+
return service in res.stdout
|
|
34
|
+
else:
|
|
35
|
+
return Path(f"/etc/init.d/{service}").exists()
|
|
36
|
+
|
|
37
|
+
def service_status(service: str) -> str:
|
|
38
|
+
if not service_exists(service):
|
|
39
|
+
return "NOT INSTALLED"
|
|
40
|
+
|
|
41
|
+
if service == "pma":
|
|
42
|
+
from ndev.linux.runtime.pma import get_pma_status
|
|
43
|
+
st = get_pma_status()
|
|
44
|
+
if st["running"]:
|
|
45
|
+
return "RUNNING"
|
|
46
|
+
elif not st["installed"]:
|
|
47
|
+
return "NOT INSTALLED"
|
|
48
|
+
else:
|
|
49
|
+
return "STOPPED"
|
|
50
|
+
|
|
51
|
+
if service.startswith("ndev-"):
|
|
52
|
+
version = service[5:]
|
|
53
|
+
script_path = sys.argv[0]
|
|
54
|
+
res = subprocess.run([sys.executable, script_path, "status", version], capture_output=True, text=True)
|
|
55
|
+
if "Running" in res.stdout:
|
|
56
|
+
return "RUNNING"
|
|
57
|
+
else:
|
|
58
|
+
return "STOPPED"
|
|
59
|
+
|
|
60
|
+
if shutil.which("systemctl"):
|
|
61
|
+
res = subprocess.run(["systemctl", "is-active", "--quiet", service])
|
|
62
|
+
return "RUNNING" if res.returncode == 0 else "STOPPED"
|
|
63
|
+
return "UNKNOWN"
|
|
64
|
+
|
|
65
|
+
def get_php_versions() -> list[str]:
|
|
66
|
+
versions = []
|
|
67
|
+
ndev_dir = get_user_ndev_dir() / "php"
|
|
68
|
+
if ndev_dir.exists():
|
|
69
|
+
for d in ndev_dir.iterdir():
|
|
70
|
+
if d.is_dir():
|
|
71
|
+
versions.append(d.name)
|
|
72
|
+
return sorted(versions)
|
|
73
|
+
|
|
74
|
+
def manage_service(service: str, action: str):
|
|
75
|
+
console.print(f"\n[yellow]{action.upper()} -> {service}[/yellow]")
|
|
76
|
+
if service == "pma":
|
|
77
|
+
from ndev.linux.runtime.pma import start_pma, stop_pma, restart_pma
|
|
78
|
+
if action == "start":
|
|
79
|
+
start_pma()
|
|
80
|
+
elif action == "stop":
|
|
81
|
+
stop_pma()
|
|
82
|
+
elif action == "restart":
|
|
83
|
+
restart_pma()
|
|
84
|
+
elif service.startswith("ndev-"):
|
|
85
|
+
version = service[5:]
|
|
86
|
+
script_path = sys.argv[0]
|
|
87
|
+
subprocess.run([sys.executable, script_path, action, version])
|
|
88
|
+
else:
|
|
89
|
+
cmd = ["sudo"]
|
|
90
|
+
if shutil.which("systemctl"):
|
|
91
|
+
cmd.extend(["systemctl", action, service])
|
|
92
|
+
else:
|
|
93
|
+
cmd.extend(["service", service, action])
|
|
94
|
+
subprocess.run(cmd)
|
|
95
|
+
console.print("[green]Done[/green]")
|
|
96
|
+
|
|
97
|
+
def ctl_cmd():
|
|
98
|
+
"""Interactive dashboard to start, stop, or restart local web services."""
|
|
99
|
+
console.print("[bold blue]==================================================================[/bold blue]")
|
|
100
|
+
console.print("[bold blue] Web Service Management Tool [/bold blue]")
|
|
101
|
+
console.print("[bold blue]==================================================================[/bold blue]\n")
|
|
102
|
+
|
|
103
|
+
# 1. Detect base services status
|
|
104
|
+
base_services = [("nginx", "Base Service"), ("mariadb", "Base Service"), ("pma", "phpMyAdmin")]
|
|
105
|
+
php_versions = get_php_versions()
|
|
106
|
+
|
|
107
|
+
table = Table(title="Detected Services")
|
|
108
|
+
table.add_column("Service", style="cyan")
|
|
109
|
+
table.add_column("Type", style="magenta")
|
|
110
|
+
table.add_column("Status", style="bold")
|
|
111
|
+
|
|
112
|
+
for svc, svc_type in base_services:
|
|
113
|
+
if service_exists(svc):
|
|
114
|
+
status = service_status(svc)
|
|
115
|
+
color = "green" if status == "RUNNING" else ("yellow" if status == "NOT INSTALLED" else "red")
|
|
116
|
+
table.add_row(svc, svc_type, f"[{color}]{status}[/{color}]")
|
|
117
|
+
|
|
118
|
+
for version in php_versions:
|
|
119
|
+
svc = f"ndev-{version}"
|
|
120
|
+
status = service_status(svc)
|
|
121
|
+
color = "green" if status == "RUNNING" else "red"
|
|
122
|
+
table.add_row(svc, "PHP-FPM", f"[{color}]{status}[/{color}]")
|
|
123
|
+
|
|
124
|
+
console.print(table)
|
|
125
|
+
console.print("")
|
|
126
|
+
|
|
127
|
+
# 2. Select Action
|
|
128
|
+
console.print("[bold]Select Action:[/bold]")
|
|
129
|
+
console.print(" 1) Restart (Default)")
|
|
130
|
+
console.print(" 2) Start")
|
|
131
|
+
console.print(" 3) Stop")
|
|
132
|
+
choice = typer.prompt("Enter choice [1-3]", default=1)
|
|
133
|
+
|
|
134
|
+
action_map = {1: "restart", 2: "start", 3: "stop"}
|
|
135
|
+
action = action_map.get(choice, "restart")
|
|
136
|
+
|
|
137
|
+
# 3. Select Service
|
|
138
|
+
console.print("\n[bold]Select Service:[/bold]")
|
|
139
|
+
console.print(" 1) Nginx")
|
|
140
|
+
console.print(" 2) MariaDB")
|
|
141
|
+
console.print(" 3) phpMyAdmin (pma)")
|
|
142
|
+
console.print(" 4) PHP-FPM")
|
|
143
|
+
console.print(" 5) All Services")
|
|
144
|
+
svc_choice = typer.prompt("Enter choice [1-5]", default=5)
|
|
145
|
+
|
|
146
|
+
php_ver = None
|
|
147
|
+
if svc_choice in [4, 5]:
|
|
148
|
+
if php_versions:
|
|
149
|
+
console.print("\n[bold]Available PHP Versions[/bold]")
|
|
150
|
+
console.print("----------------------")
|
|
151
|
+
for i, version in enumerate(php_versions):
|
|
152
|
+
status = service_status(f"ndev-{version}")
|
|
153
|
+
console.print(f" {i + 1}) PHP {version:<12} {status}")
|
|
154
|
+
console.print(f" {len(php_versions) + 1}) All PHP-FPM Instances")
|
|
155
|
+
|
|
156
|
+
console.print("")
|
|
157
|
+
php_idx = typer.prompt("Select PHP version index", type=int)
|
|
158
|
+
if php_idx < 1 or php_idx > len(php_versions) + 1:
|
|
159
|
+
logger.error("Invalid selection.")
|
|
160
|
+
raise typer.Exit(code=1)
|
|
161
|
+
|
|
162
|
+
if php_idx == len(php_versions) + 1:
|
|
163
|
+
php_ver = "all"
|
|
164
|
+
else:
|
|
165
|
+
php_ver = php_versions[php_idx - 1]
|
|
166
|
+
|
|
167
|
+
services_to_manage = []
|
|
168
|
+
if svc_choice == 1:
|
|
169
|
+
services_to_manage = ["nginx"]
|
|
170
|
+
elif svc_choice == 2:
|
|
171
|
+
services_to_manage = ["mariadb"]
|
|
172
|
+
elif svc_choice == 3:
|
|
173
|
+
services_to_manage = ["pma"]
|
|
174
|
+
elif svc_choice == 4:
|
|
175
|
+
if php_ver == "all":
|
|
176
|
+
services_to_manage = [f"ndev-{v}" for v in php_versions]
|
|
177
|
+
elif php_ver:
|
|
178
|
+
services_to_manage = [f"ndev-{php_ver}"]
|
|
179
|
+
elif svc_choice == 5:
|
|
180
|
+
services_to_manage = ["nginx", "mariadb", "pma"]
|
|
181
|
+
if php_ver:
|
|
182
|
+
if php_ver == "all":
|
|
183
|
+
services_to_manage.extend([f"ndev-{v}" for v in php_versions])
|
|
184
|
+
else:
|
|
185
|
+
services_to_manage.append(f"ndev-{php_ver}")
|
|
186
|
+
|
|
187
|
+
console.print("\n[bold blue]Executing requested actions...[/bold blue]")
|
|
188
|
+
for svc in services_to_manage:
|
|
189
|
+
manage_service(svc, action)
|
|
190
|
+
|
|
191
|
+
console.print("\n[bold green]Completed successfully.[/bold green]")
|
|
192
|
+
console.print("[bold blue]==================================================================[/bold blue]")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from ndev.common.constants import CURRENT_LINK
|
|
3
|
+
from ndev.common.logger import logger
|
|
4
|
+
|
|
5
|
+
def current_cmd():
|
|
6
|
+
"""Show the currently active PHP version."""
|
|
7
|
+
if CURRENT_LINK.exists() and CURRENT_LINK.is_symlink():
|
|
8
|
+
version = CURRENT_LINK.resolve().name
|
|
9
|
+
logger.info(f"Current active PHP version: {version}")
|
|
10
|
+
else:
|
|
11
|
+
logger.info("No active PHP version set. Use 'ndev use <version>' to set one.")
|