ndev-stack 0.3.13__tar.gz → 0.3.15__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.
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/PKG-INFO +1 -1
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/__init__.py +1 -1
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/db.py +61 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/doctor.py +19 -1
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/setup.py +80 -22
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/runtime/nginx_helper.py +75 -2
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev_stack.egg-info/PKG-INFO +1 -1
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/pyproject.toml +1 -1
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/tests/test_opensuse_compat.py +11 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/README.md +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/__main__.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/cli.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/__init__.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/config.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/constants.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/distro.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/github.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/logger.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/manifest.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/__init__.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/base.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/builtin/mailpit/manifest.json +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/builtin/mailpit/module.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/builtin/mongodb/manifest.json +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/builtin/mongodb/module.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/builtin/postgres/manifest.json +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/builtin/postgres/module.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/builtin/redis/manifest.json +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/builtin/redis/module.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/modules/manager.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/common/utils.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/__init__.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/chroot/manager.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/chroot/packages.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/chroot/shell.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/cli.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/available.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/clean.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/ctl.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/current.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/grok.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/install.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/list.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/logs.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/mailpit.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/modules.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/mongodb.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/postgres.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/reload.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/restart.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/start.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/status.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/stop.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/uninstall.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/update.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/upgrade.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/use.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/commands/vhost.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/php/builder.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/php/downloader.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/php/extensions.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/php/installer.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/php/resolver.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/php/templates.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/runtime/fpm.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/runtime/mailpit.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/runtime/pma.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/runtime/process.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/runtime/services.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/runtime/sockets.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/runtime/upgrade.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/linux/tui.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/main.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/tui.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/__init__.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/bin/shim.exe +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/cli.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/commands/__init__.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/__init__.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/db.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/elevate.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/ext.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/fcgi.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/grok.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/logs.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/mailpit.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/mkcert.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/paths.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/php.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/pma.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/redis_core.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/services.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/setup.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/shim.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/upgrade.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/core/vhost.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/templates/vhost.conf.tmpl +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/templates/vhost_ssl.conf.tmpl +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev/win/tui.py +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev_stack.egg-info/SOURCES.txt +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev_stack.egg-info/dependency_links.txt +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev_stack.egg-info/entry_points.txt +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev_stack.egg-info/requires.txt +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/ndev_stack.egg-info/top_level.txt +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/setup.cfg +0 -0
- {ndev_stack-0.3.13 → ndev_stack-0.3.15}/tests/test_shim.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ndev-stack
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.15
|
|
4
4
|
Summary: Cross-platform developer stack manager for PHP, FastCGI/PHP-FPM, MariaDB, Redis, PostgreSQL, MongoDB, Nginx, SSL, and Mailpit on Linux and Windows
|
|
5
5
|
Author-email: Ankit Karan <ankit.karan99@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/ankitkaran99/ndev
|
|
@@ -317,3 +317,64 @@ def drop_user_cmd(
|
|
|
317
317
|
confirm_destructive(force, username, f"You are about to DROP user '{username}' permanently.")
|
|
318
318
|
execute_drop_user(host, port, user, password, username, user_host)
|
|
319
319
|
|
|
320
|
+
|
|
321
|
+
def configure_mariadb_root_password(new_password: str = "root", sudo_password: Optional[str] = None) -> bool:
|
|
322
|
+
"""
|
|
323
|
+
Configure MariaDB root account to allow password authentication (e.g. root/root)
|
|
324
|
+
across localhost, 127.0.0.1, and %.
|
|
325
|
+
Fixes ERROR 1698 (28000): Access denied for user 'root'@'localhost'
|
|
326
|
+
caused by default unix_socket authentication on Linux.
|
|
327
|
+
"""
|
|
328
|
+
from ndev.linux.runtime.nginx_helper import run_privileged_command
|
|
329
|
+
from ndev.common.utils import which_binary
|
|
330
|
+
|
|
331
|
+
sql_statements = [
|
|
332
|
+
f"ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('{new_password}') OR unix_socket;",
|
|
333
|
+
f"ALTER USER 'root'@'localhost' IDENTIFIED BY '{new_password}';",
|
|
334
|
+
f"CREATE USER IF NOT EXISTS 'root'@'127.0.0.1' IDENTIFIED BY '{new_password}';",
|
|
335
|
+
f"ALTER USER 'root'@'127.0.0.1' IDENTIFIED BY '{new_password}';",
|
|
336
|
+
f"CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '{new_password}';",
|
|
337
|
+
f"ALTER USER 'root'@'%' IDENTIFIED BY '{new_password}';",
|
|
338
|
+
"GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;",
|
|
339
|
+
"GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' WITH GRANT OPTION;",
|
|
340
|
+
"GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;",
|
|
341
|
+
"FLUSH PRIVILEGES;",
|
|
342
|
+
]
|
|
343
|
+
|
|
344
|
+
client_bin = which_binary("mariadb") or which_binary("mysql") or "mariadb"
|
|
345
|
+
|
|
346
|
+
for sql in sql_statements:
|
|
347
|
+
try:
|
|
348
|
+
cmd = [client_bin, "-u", "root", "-e", sql]
|
|
349
|
+
run_privileged_command(cmd, password=sudo_password, check=False)
|
|
350
|
+
except Exception:
|
|
351
|
+
pass
|
|
352
|
+
|
|
353
|
+
# Verify if connection succeeds via TCP
|
|
354
|
+
for h in ["127.0.0.1", "localhost"]:
|
|
355
|
+
try:
|
|
356
|
+
test_cmd = [client_bin, "-h", h, "-P", "3306", "-u", "root", f"--password={new_password}", "-e", "SELECT 1;"]
|
|
357
|
+
res = subprocess.run(test_cmd, capture_output=True, text=True, timeout=5)
|
|
358
|
+
if res.returncode == 0:
|
|
359
|
+
return True
|
|
360
|
+
except Exception:
|
|
361
|
+
pass
|
|
362
|
+
|
|
363
|
+
return False
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@db_app.command("set-root-password")
|
|
367
|
+
@db_app.command("reset-root")
|
|
368
|
+
def set_root_password_cmd(
|
|
369
|
+
password: str = typer.Option("root", "--password", "-p", help="New root password (default: root)"),
|
|
370
|
+
):
|
|
371
|
+
"""Set or reset MariaDB root password and enable password login across localhost and 127.0.0.1."""
|
|
372
|
+
console.print(f"[bold yellow]Configuring MariaDB root access with password '{password}'...[/bold yellow]")
|
|
373
|
+
ok = configure_mariadb_root_password(new_password=password)
|
|
374
|
+
if ok:
|
|
375
|
+
console.print(f"[bold green]✓ MariaDB root password configured successfully! (user: root, password: {password})[/bold green]")
|
|
376
|
+
console.print(f"[dim]You can now connect via phpMyAdmin, Laravel, or CLI using user 'root' and password '{password}'.[/dim]")
|
|
377
|
+
else:
|
|
378
|
+
logger.error("Failed to configure MariaDB root password. Ensure MariaDB is running and you have administrator privileges.")
|
|
379
|
+
raise typer.Exit(code=1)
|
|
380
|
+
|
|
@@ -64,7 +64,25 @@ def doctor_cmd():
|
|
|
64
64
|
|
|
65
65
|
db_path = which_binary("mariadb") or which_binary("mysql") or which_binary("mysqld")
|
|
66
66
|
if db_path:
|
|
67
|
-
|
|
67
|
+
db_details = f"Found at {db_path}"
|
|
68
|
+
connected = False
|
|
69
|
+
client_bin = which_binary("mariadb") or which_binary("mysql") or db_path
|
|
70
|
+
for test_pwd in ["root", ""]:
|
|
71
|
+
try:
|
|
72
|
+
cmd = [client_bin, "-h", "127.0.0.1", "-P", "3306", "-u", "root", f"--password={test_pwd}", "-e", "SELECT 1;"]
|
|
73
|
+
r = subprocess.run(cmd, capture_output=True, timeout=2)
|
|
74
|
+
if r.returncode == 0:
|
|
75
|
+
connected = True
|
|
76
|
+
pwd_label = f"password: '{test_pwd}'" if test_pwd else "no password"
|
|
77
|
+
db_details += f" (127.0.0.1 root access: {pwd_label})"
|
|
78
|
+
break
|
|
79
|
+
except Exception:
|
|
80
|
+
pass
|
|
81
|
+
if not connected:
|
|
82
|
+
db_details += " (127.0.0.1 root access restricted: run 'ndev db set-root-password')"
|
|
83
|
+
table.add_row("MariaDB / MySQL", "[yellow]RESTRICTED[/yellow]", db_details)
|
|
84
|
+
else:
|
|
85
|
+
table.add_row("MariaDB / MySQL", "[green]OK[/green]", db_details)
|
|
68
86
|
else:
|
|
69
87
|
table.add_row("MariaDB / MySQL", "[yellow]NOT INSTALLED[/yellow]", f"Run 'ndev setup' or: {stack_install_cmd}")
|
|
70
88
|
|
|
@@ -51,9 +51,9 @@ def setup_cmd():
|
|
|
51
51
|
console.print("[bold yellow]Refreshing openSUSE repositories (sudo zypper refresh)...[/bold yellow]")
|
|
52
52
|
subprocess.run(["sudo", "zypper", "--non-interactive", "refresh"], check=True)
|
|
53
53
|
|
|
54
|
-
# 2. Install MariaDB, Nginx, curl, and
|
|
55
|
-
console.print("\n[bold yellow]Installing MariaDB, Nginx, curl, and
|
|
56
|
-
packages = ["mariadb", "mariadb-client", "nginx", "curl", "bubblewrap"]
|
|
54
|
+
# 2. Install MariaDB, Nginx, curl, Bubblewrap, and SELinux tools
|
|
55
|
+
console.print("\n[bold yellow]Installing MariaDB, Nginx, curl, Bubblewrap, and system tools (sudo zypper install)...[/bold yellow]")
|
|
56
|
+
packages = ["mariadb", "mariadb-client", "nginx", "curl", "bubblewrap", "policycoreutils-python-utils"]
|
|
57
57
|
subprocess.run(["sudo", "zypper", "--non-interactive", "install", "-y"] + packages, check=True)
|
|
58
58
|
|
|
59
59
|
# Optional mkcert package
|
|
@@ -65,27 +65,16 @@ def setup_cmd():
|
|
|
65
65
|
except Exception:
|
|
66
66
|
pass
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
subprocess.run(["sudo", "systemctl", "enable", "--now", "mariadb"], check=False, capture_output=True)
|
|
70
|
-
subprocess.run(["sudo", "systemctl", "enable", "--now", "nginx"], check=False, capture_output=True)
|
|
71
|
-
except Exception:
|
|
72
|
-
pass
|
|
73
|
-
|
|
74
|
-
try:
|
|
75
|
-
from ndev.linux.runtime.nginx_helper import ensure_selinux_httpd_permissions
|
|
76
|
-
ensure_selinux_httpd_permissions()
|
|
77
|
-
except Exception:
|
|
78
|
-
pass
|
|
79
|
-
|
|
80
|
-
console.print("[bold green]MariaDB, Nginx, and Bubblewrap installed and configured successfully![/bold green]\n")
|
|
68
|
+
console.print("[bold green]System packages installed successfully![/bold green]\n")
|
|
81
69
|
else:
|
|
82
70
|
# 1. Update Package Lists
|
|
83
71
|
console.print("[bold yellow]Updating package lists (sudo apt-get update)...[/bold yellow]")
|
|
84
72
|
subprocess.run(["sudo", "apt-get", "update"], check=True)
|
|
85
73
|
|
|
86
74
|
# 2. Install MariaDB and Nginx
|
|
87
|
-
console.print("\n[bold yellow]Installing MariaDB and
|
|
88
|
-
|
|
75
|
+
console.print("\n[bold yellow]Installing MariaDB, Nginx, curl, Bubblewrap, and system tools (sudo apt-get install)...[/bold yellow]")
|
|
76
|
+
packages = ["mariadb-server", "nginx", "curl", "bubblewrap", "policycoreutils-python-utils"]
|
|
77
|
+
subprocess.run(["sudo", "apt-get", "install", "-y"] + packages, check=True)
|
|
89
78
|
|
|
90
79
|
# Optional mkcert package
|
|
91
80
|
try:
|
|
@@ -96,9 +85,73 @@ def setup_cmd():
|
|
|
96
85
|
except Exception:
|
|
97
86
|
pass
|
|
98
87
|
|
|
99
|
-
console.print("[bold green]
|
|
100
|
-
|
|
101
|
-
# 3.
|
|
88
|
+
console.print("[bold green]System packages installed successfully![/bold green]\n")
|
|
89
|
+
|
|
90
|
+
# 3. Auto-configure Stack Services, Permissions, SSL, and SELinux
|
|
91
|
+
console.print("[bold yellow]Auto-configuring stack services, directories, SSL, and SELinux...[/bold yellow]")
|
|
92
|
+
from ndev.linux.runtime.nginx_helper import (
|
|
93
|
+
ensure_selinux_httpd_permissions,
|
|
94
|
+
ensure_nginx_ssl_dir,
|
|
95
|
+
ensure_nginx_sites_enabled_included,
|
|
96
|
+
ensure_development_directories,
|
|
97
|
+
reload_nginx,
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Enable & start system services
|
|
101
|
+
try:
|
|
102
|
+
subprocess.run(["sudo", "systemctl", "enable", "--now", "mariadb"], check=False, capture_output=True)
|
|
103
|
+
subprocess.run(["sudo", "systemctl", "enable", "--now", "nginx"], check=False, capture_output=True)
|
|
104
|
+
console.print(" [bold green]✓[/bold green] Enabled and started MariaDB and Nginx system services.")
|
|
105
|
+
except Exception:
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
# SELinux Permissions
|
|
109
|
+
try:
|
|
110
|
+
ok = ensure_selinux_httpd_permissions()
|
|
111
|
+
if ok:
|
|
112
|
+
console.print(" [bold green]✓[/bold green] Configured SELinux permissions for Nginx (httpd_t set to permissive).")
|
|
113
|
+
except Exception as e:
|
|
114
|
+
logger.warning(f"SELinux configuration notice: {e}")
|
|
115
|
+
|
|
116
|
+
# Nginx SSL Directory (/etc/nginx/ssl)
|
|
117
|
+
try:
|
|
118
|
+
ensure_nginx_ssl_dir()
|
|
119
|
+
console.print(" [bold green]✓[/bold green] Configured Nginx SSL certificate directory (/etc/nginx/ssl).")
|
|
120
|
+
except Exception as e:
|
|
121
|
+
logger.warning(f"SSL directory configuration notice: {e}")
|
|
122
|
+
|
|
123
|
+
# Nginx Sites-Enabled / Vhosts Bridge
|
|
124
|
+
try:
|
|
125
|
+
ensure_nginx_sites_enabled_included()
|
|
126
|
+
console.print(" [bold green]✓[/bold green] Configured Nginx sites-available/sites-enabled directory bridge.")
|
|
127
|
+
except Exception as e:
|
|
128
|
+
logger.warning(f"Nginx bridge configuration notice: {e}")
|
|
129
|
+
|
|
130
|
+
# Web development root permissions and docroot traversal
|
|
131
|
+
try:
|
|
132
|
+
ensure_development_directories()
|
|
133
|
+
console.print(" [bold green]✓[/bold green] Configured web development root permissions (~/Sites).")
|
|
134
|
+
except Exception as e:
|
|
135
|
+
logger.warning(f"Docroot permission notice: {e}")
|
|
136
|
+
|
|
137
|
+
# Reload Nginx
|
|
138
|
+
try:
|
|
139
|
+
reload_nginx()
|
|
140
|
+
console.print(" [bold green]✓[/bold green] Nginx web server configuration reloaded.")
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
# MariaDB Root User Authentication (root / root)
|
|
145
|
+
try:
|
|
146
|
+
from ndev.linux.commands.db import configure_mariadb_root_password
|
|
147
|
+
if configure_mariadb_root_password(new_password="root"):
|
|
148
|
+
console.print(" [bold green]✓[/bold green] Configured MariaDB root access (user: root, password: root).")
|
|
149
|
+
else:
|
|
150
|
+
console.print(" [yellow]Notice: MariaDB root access could not be automatically set to root/root.[/yellow]")
|
|
151
|
+
except Exception as e:
|
|
152
|
+
logger.warning(f"MariaDB root configuration notice: {e}")
|
|
153
|
+
|
|
154
|
+
# 4. Install Composer
|
|
102
155
|
local_bin = get_user_local_bin_dir()
|
|
103
156
|
composer_bin = local_bin / "composer"
|
|
104
157
|
if composer_bin.exists():
|
|
@@ -150,7 +203,12 @@ def setup_cmd():
|
|
|
150
203
|
chown_to_sudo_user(ndev_link)
|
|
151
204
|
console.print(f"[bold green]Created symlink {ndev_link} -> {ndev_venv_path}[/bold green]")
|
|
152
205
|
|
|
153
|
-
console.print("\n[bold green]System setup completed successfully![/bold green]")
|
|
206
|
+
console.print("\n[bold green]System setup completed successfully![/bold green]\n")
|
|
207
|
+
try:
|
|
208
|
+
from ndev.linux.commands.doctor import doctor_cmd
|
|
209
|
+
doctor_cmd()
|
|
210
|
+
except Exception:
|
|
211
|
+
pass
|
|
154
212
|
|
|
155
213
|
except subprocess.CalledProcessError as e:
|
|
156
214
|
logger.error(f"Command execution failed: {e}")
|
|
@@ -74,14 +74,26 @@ def run_privileged_command(
|
|
|
74
74
|
capture_output: bool = True
|
|
75
75
|
) -> subprocess.CompletedProcess:
|
|
76
76
|
"""
|
|
77
|
-
Run a command with elevated privileges using sudo
|
|
77
|
+
Run a command with elevated privileges using sudo if non-root.
|
|
78
78
|
Validates sudo password first if provided to refresh the sudo credential timestamp.
|
|
79
|
+
If no password provided:
|
|
80
|
+
- Uses sudo -n if not in an interactive terminal or if sudo timestamp is active.
|
|
81
|
+
- Falls back to interactive sudo if in an interactive terminal.
|
|
79
82
|
"""
|
|
80
83
|
full_cmd = list(cmd)
|
|
81
84
|
if os.geteuid() != 0:
|
|
82
85
|
if password:
|
|
83
86
|
validate_sudo_password(password)
|
|
84
|
-
|
|
87
|
+
full_cmd = ["sudo", "-n"] + full_cmd
|
|
88
|
+
else:
|
|
89
|
+
if not check_sudo_needs_password():
|
|
90
|
+
full_cmd = ["sudo", "-n"] + full_cmd
|
|
91
|
+
elif sys.stdin.isatty():
|
|
92
|
+
full_cmd = ["sudo"] + full_cmd
|
|
93
|
+
capture_output = False
|
|
94
|
+
else:
|
|
95
|
+
full_cmd = ["sudo", "-n"] + full_cmd
|
|
96
|
+
|
|
85
97
|
return subprocess.run(
|
|
86
98
|
full_cmd,
|
|
87
99
|
check=check,
|
|
@@ -156,6 +168,67 @@ def ensure_selinux_httpd_permissions(password: Optional[str] = None) -> bool:
|
|
|
156
168
|
return success or is_selinux_httpd_permissive()
|
|
157
169
|
|
|
158
170
|
|
|
171
|
+
def ensure_nginx_ssl_dir(password: Optional[str] = None) -> Path:
|
|
172
|
+
"""
|
|
173
|
+
Ensure /etc/nginx/ssl directory exists with 0755 permissions and proper SELinux context.
|
|
174
|
+
"""
|
|
175
|
+
ssl_dir = Path("/etc/nginx/ssl")
|
|
176
|
+
if os.geteuid() == 0:
|
|
177
|
+
ssl_dir.mkdir(parents=True, exist_ok=True)
|
|
178
|
+
try:
|
|
179
|
+
os.chmod(str(ssl_dir), 0o755)
|
|
180
|
+
except Exception:
|
|
181
|
+
pass
|
|
182
|
+
else:
|
|
183
|
+
run_privileged_command(["mkdir", "-p", str(ssl_dir)], password=password, check=False)
|
|
184
|
+
run_privileged_command(["chmod", "755", str(ssl_dir)], password=password, check=False)
|
|
185
|
+
|
|
186
|
+
if shutil.which("restorecon"):
|
|
187
|
+
run_privileged_command(["restorecon", "-Rv", str(ssl_dir)], password=password, check=False)
|
|
188
|
+
|
|
189
|
+
return ssl_dir
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def ensure_development_directories(password: Optional[str] = None) -> None:
|
|
193
|
+
"""
|
|
194
|
+
Ensure the user home directory has +x traversal permissions,
|
|
195
|
+
~/Sites directory exists with 0755 permissions, and any configured vhost docroots are accessible.
|
|
196
|
+
"""
|
|
197
|
+
home = Path.home()
|
|
198
|
+
sudo_user = os.environ.get("SUDO_USER")
|
|
199
|
+
if sudo_user:
|
|
200
|
+
try:
|
|
201
|
+
import pwd
|
|
202
|
+
home = Path(pwd.getpwnam(sudo_user).pw_dir)
|
|
203
|
+
except Exception:
|
|
204
|
+
pass
|
|
205
|
+
|
|
206
|
+
# Ensure home has traversal (+x)
|
|
207
|
+
try:
|
|
208
|
+
st = home.stat()
|
|
209
|
+
if not (st.st_mode & 0o001):
|
|
210
|
+
os.chmod(str(home), st.st_mode | 0o001)
|
|
211
|
+
except Exception:
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
# Ensure ~/Sites exists
|
|
215
|
+
sites_dir = home / "Sites"
|
|
216
|
+
try:
|
|
217
|
+
sites_dir.mkdir(parents=True, exist_ok=True)
|
|
218
|
+
os.chmod(str(sites_dir), 0o755)
|
|
219
|
+
except Exception:
|
|
220
|
+
pass
|
|
221
|
+
|
|
222
|
+
# Ensure all configured vhost docroots are accessible
|
|
223
|
+
try:
|
|
224
|
+
for vhost in list_vhosts():
|
|
225
|
+
docroot = vhost.get("root")
|
|
226
|
+
if docroot and Path(docroot).exists():
|
|
227
|
+
ensure_docroot_accessible(Path(docroot))
|
|
228
|
+
except Exception:
|
|
229
|
+
pass
|
|
230
|
+
|
|
231
|
+
|
|
159
232
|
def write_privileged_file(target: Path, content: str, password: Optional[str] = None) -> None:
|
|
160
233
|
"""
|
|
161
234
|
Write content to a file, using sudo tee if current process lacks permissions.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ndev-stack
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.15
|
|
4
4
|
Summary: Cross-platform developer stack manager for PHP, FastCGI/PHP-FPM, MariaDB, Redis, PostgreSQL, MongoDB, Nginx, SSL, and Mailpit on Linux and Windows
|
|
5
5
|
Author-email: Ankit Karan <ankit.karan99@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/ankitkaran99/ndev
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ndev-stack"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.15"
|
|
8
8
|
description = "Cross-platform developer stack manager for PHP, FastCGI/PHP-FPM, MariaDB, Redis, PostgreSQL, MongoDB, Nginx, SSL, and Mailpit on Linux and Windows"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -549,6 +549,17 @@ class TestTuiSelectEventHandling(unittest.TestCase):
|
|
|
549
549
|
app._switch_php_version.assert_called_once_with("8.4.27")
|
|
550
550
|
|
|
551
551
|
|
|
552
|
+
class TestDatabaseCommands(unittest.TestCase):
|
|
553
|
+
def test_configure_mariadb_root_password(self):
|
|
554
|
+
from ndev.linux.commands.db import configure_mariadb_root_password
|
|
555
|
+
with patch("ndev.linux.runtime.nginx_helper.run_privileged_command") as mock_priv:
|
|
556
|
+
with patch("subprocess.run") as mock_run:
|
|
557
|
+
mock_run.return_value = subprocess.CompletedProcess(args=[], returncode=0)
|
|
558
|
+
ok = configure_mariadb_root_password(new_password="root")
|
|
559
|
+
self.assertTrue(ok)
|
|
560
|
+
mock_priv.assert_called()
|
|
561
|
+
|
|
562
|
+
|
|
552
563
|
class TestTuiEnsureSudo(unittest.TestCase):
|
|
553
564
|
def test_ensure_sudo_when_not_needed(self):
|
|
554
565
|
from ndev.linux.tui import NdevDashboard
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|