ndev-stack 0.3.14__tar.gz → 0.3.16__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.
Files changed (106) hide show
  1. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/PKG-INFO +1 -1
  2. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/__init__.py +1 -1
  3. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/db.py +138 -1
  4. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/doctor.py +19 -1
  5. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/setup.py +10 -0
  6. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/runtime/nginx_helper.py +1 -0
  7. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev_stack.egg-info/PKG-INFO +1 -1
  8. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/pyproject.toml +1 -1
  9. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/tests/test_opensuse_compat.py +25 -0
  10. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/README.md +0 -0
  11. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/__main__.py +0 -0
  12. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/cli.py +0 -0
  13. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/__init__.py +0 -0
  14. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/config.py +0 -0
  15. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/constants.py +0 -0
  16. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/distro.py +0 -0
  17. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/github.py +0 -0
  18. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/logger.py +0 -0
  19. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/manifest.py +0 -0
  20. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/__init__.py +0 -0
  21. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/base.py +0 -0
  22. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/builtin/mailpit/manifest.json +0 -0
  23. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/builtin/mailpit/module.py +0 -0
  24. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/builtin/mongodb/manifest.json +0 -0
  25. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/builtin/mongodb/module.py +0 -0
  26. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/builtin/postgres/manifest.json +0 -0
  27. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/builtin/postgres/module.py +0 -0
  28. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/builtin/redis/manifest.json +0 -0
  29. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/builtin/redis/module.py +0 -0
  30. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/modules/manager.py +0 -0
  31. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/common/utils.py +0 -0
  32. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/__init__.py +0 -0
  33. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/chroot/manager.py +0 -0
  34. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/chroot/packages.py +0 -0
  35. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/chroot/shell.py +0 -0
  36. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/cli.py +0 -0
  37. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/available.py +0 -0
  38. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/clean.py +0 -0
  39. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/ctl.py +0 -0
  40. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/current.py +0 -0
  41. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/grok.py +0 -0
  42. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/install.py +0 -0
  43. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/list.py +0 -0
  44. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/logs.py +0 -0
  45. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/mailpit.py +0 -0
  46. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/modules.py +0 -0
  47. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/mongodb.py +0 -0
  48. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/postgres.py +0 -0
  49. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/reload.py +0 -0
  50. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/restart.py +0 -0
  51. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/start.py +0 -0
  52. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/status.py +0 -0
  53. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/stop.py +0 -0
  54. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/uninstall.py +0 -0
  55. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/update.py +0 -0
  56. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/upgrade.py +0 -0
  57. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/use.py +0 -0
  58. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/commands/vhost.py +0 -0
  59. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/php/builder.py +0 -0
  60. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/php/downloader.py +0 -0
  61. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/php/extensions.py +0 -0
  62. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/php/installer.py +0 -0
  63. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/php/resolver.py +0 -0
  64. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/php/templates.py +0 -0
  65. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/runtime/fpm.py +0 -0
  66. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/runtime/mailpit.py +0 -0
  67. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/runtime/pma.py +0 -0
  68. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/runtime/process.py +0 -0
  69. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/runtime/services.py +0 -0
  70. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/runtime/sockets.py +0 -0
  71. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/runtime/upgrade.py +0 -0
  72. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/linux/tui.py +0 -0
  73. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/main.py +0 -0
  74. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/tui.py +0 -0
  75. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/__init__.py +0 -0
  76. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/bin/shim.exe +0 -0
  77. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/cli.py +0 -0
  78. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/commands/__init__.py +0 -0
  79. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/__init__.py +0 -0
  80. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/db.py +0 -0
  81. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/elevate.py +0 -0
  82. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/ext.py +0 -0
  83. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/fcgi.py +0 -0
  84. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/grok.py +0 -0
  85. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/logs.py +0 -0
  86. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/mailpit.py +0 -0
  87. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/mkcert.py +0 -0
  88. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/paths.py +0 -0
  89. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/php.py +0 -0
  90. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/pma.py +0 -0
  91. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/redis_core.py +0 -0
  92. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/services.py +0 -0
  93. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/setup.py +0 -0
  94. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/shim.py +0 -0
  95. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/upgrade.py +0 -0
  96. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/core/vhost.py +0 -0
  97. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/templates/vhost.conf.tmpl +0 -0
  98. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/templates/vhost_ssl.conf.tmpl +0 -0
  99. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev/win/tui.py +0 -0
  100. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev_stack.egg-info/SOURCES.txt +0 -0
  101. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev_stack.egg-info/dependency_links.txt +0 -0
  102. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev_stack.egg-info/entry_points.txt +0 -0
  103. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev_stack.egg-info/requires.txt +0 -0
  104. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/ndev_stack.egg-info/top_level.txt +0 -0
  105. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/setup.cfg +0 -0
  106. {ndev_stack-0.3.14 → ndev_stack-0.3.16}/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.14
3
+ Version: 0.3.16
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
@@ -1,7 +1,7 @@
1
1
  """
2
2
  ndev: Fast, cross-platform PHP developer environment for Windows and Linux.
3
3
  """
4
- __version__ = "0.3.14"
4
+ __version__ = "0.3.16"
5
5
 
6
6
  from . import common as common
7
7
  from .common.logger import logger as logger
@@ -3,9 +3,11 @@ import sys
3
3
  import shutil
4
4
  import subprocess
5
5
  import typer
6
+ from pathlib import Path
6
7
  from typing import Optional
7
8
  from rich.console import Console
8
9
  from ndev.common.logger import logger
10
+ from ndev.common.utils import which_binary
9
11
 
10
12
  console = Console()
11
13
 
@@ -13,7 +15,8 @@ db_app = typer.Typer(help="Manage MySQL databases and users", invoke_without_com
13
15
 
14
16
  # Shared options for commands
15
17
  def execute_sql_mysql(host: str, port: int, user: str, password: Optional[str], sql: str) -> subprocess.CompletedProcess:
16
- cmd = ["mysql", "-h", host, "-P", str(port), "-u", user, "--batch", "--skip-column-names"]
18
+ client = which_binary("mariadb") or which_binary("mysql") or "mysql"
19
+ cmd = [client, "-h", host, "-P", str(port), "-u", user, "--batch", "--skip-column-names"]
17
20
  if password:
18
21
  cmd.append(f"--password={password}")
19
22
  return subprocess.run(cmd, input=sql, capture_output=True, text=True)
@@ -317,3 +320,137 @@ def drop_user_cmd(
317
320
  confirm_destructive(force, username, f"You are about to DROP user '{username}' permanently.")
318
321
  execute_drop_user(host, port, user, password, username, user_host)
319
322
 
323
+
324
+ def configure_mariadb_root_password(new_password: str = "root", sudo_password: Optional[str] = None) -> bool:
325
+ """
326
+ Configure MariaDB root account to allow password authentication (e.g. root/root)
327
+ across localhost, 127.0.0.1, and %.
328
+ Fixes ERROR 1698 (28000): Access denied for user 'root'@'localhost'
329
+ caused by default unix_socket authentication on Linux.
330
+ """
331
+ from ndev.linux.runtime.nginx_helper import run_privileged_command
332
+ from ndev.common.utils import which_binary
333
+
334
+ client_bin = which_binary("mariadb") or which_binary("mysql") or "mariadb"
335
+
336
+ # 1. Ensure MariaDB service is active
337
+ try:
338
+ run_privileged_command(["systemctl", "start", "mariadb"], password=sudo_password, check=False)
339
+ except Exception:
340
+ pass
341
+
342
+ # 2. Detect MariaDB unix socket path
343
+ socket_candidates = [
344
+ Path("/run/mysql/mysql.sock"),
345
+ Path("/run/mysqld/mysqld.sock"),
346
+ Path("/var/run/mysqld/mysqld.sock"),
347
+ Path("/var/lib/mysql/mysql.sock"),
348
+ ]
349
+ socket_args: list[str] = []
350
+ for s in socket_candidates:
351
+ if s.exists():
352
+ socket_args = ["-S", str(s)]
353
+ break
354
+
355
+ # 3. Dual auth (unix_socket + password) attempt for localhost
356
+ dual_auth_sql = (
357
+ f"ALTER USER 'root'@'localhost' IDENTIFIED VIA mysql_native_password USING PASSWORD('{new_password}') OR unix_socket; "
358
+ "FLUSH PRIVILEGES;"
359
+ )
360
+ try:
361
+ run_privileged_command([client_bin] + socket_args + ["-u", "root", "-e", dual_auth_sql], password=sudo_password, check=False)
362
+ except Exception as e:
363
+ logger.debug(f"Dual auth attempt: {e}")
364
+
365
+ # 4. Standard root password & network user creation
366
+ batch_sql = (
367
+ f"ALTER USER 'root'@'localhost' IDENTIFIED BY '{new_password}'; "
368
+ f"CREATE USER IF NOT EXISTS 'root'@'127.0.0.1' IDENTIFIED BY '{new_password}'; "
369
+ f"ALTER USER 'root'@'127.0.0.1' IDENTIFIED BY '{new_password}'; "
370
+ f"CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '{new_password}'; "
371
+ f"ALTER USER 'root'@'%' IDENTIFIED BY '{new_password}'; "
372
+ "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION; "
373
+ "GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' WITH GRANT OPTION; "
374
+ "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION; "
375
+ "FLUSH PRIVILEGES;"
376
+ )
377
+
378
+ try:
379
+ run_privileged_command([client_bin] + socket_args + ["-u", "root", "-f", "-e", batch_sql], password=sudo_password, check=False)
380
+ except Exception as e:
381
+ logger.debug(f"Batch SQL config: {e}")
382
+
383
+ # Also try if root already has password set
384
+ try:
385
+ run_privileged_command([client_bin] + socket_args + ["-u", "root", f"--password={new_password}", "-f", "-e", batch_sql], password=sudo_password, check=False)
386
+ except Exception:
387
+ pass
388
+
389
+ # 5. Fallback individual statements
390
+ sql_statements = [
391
+ f"ALTER USER 'root'@'localhost' IDENTIFIED BY '{new_password}';",
392
+ f"SET PASSWORD FOR 'root'@'localhost' = PASSWORD('{new_password}');",
393
+ f"CREATE USER IF NOT EXISTS 'root'@'127.0.0.1' IDENTIFIED BY '{new_password}';",
394
+ f"ALTER USER 'root'@'127.0.0.1' IDENTIFIED BY '{new_password}';",
395
+ f"CREATE USER IF NOT EXISTS 'root'@'%' IDENTIFIED BY '{new_password}';",
396
+ f"ALTER USER 'root'@'%' IDENTIFIED BY '{new_password}';",
397
+ "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION;",
398
+ "GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' WITH GRANT OPTION;",
399
+ "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;",
400
+ "FLUSH PRIVILEGES;",
401
+ ]
402
+
403
+ for sql in sql_statements:
404
+ try:
405
+ cmd = [client_bin] + socket_args + ["-u", "root", "-e", sql]
406
+ run_privileged_command(cmd, password=sudo_password, check=False)
407
+ except Exception:
408
+ pass
409
+
410
+ # 6. Verify if connection succeeds via TCP
411
+ for h in ["127.0.0.1", "localhost"]:
412
+ try:
413
+ test_cmd = [client_bin, "-h", h, "-P", "3306", "-u", "root", f"--password={new_password}", "-e", "SELECT 1;"]
414
+ res = subprocess.run(test_cmd, capture_output=True, text=True, timeout=5)
415
+ if res.returncode == 0:
416
+ return True
417
+ except Exception:
418
+ pass
419
+
420
+ return False
421
+
422
+
423
+ @db_app.command("set-root-password")
424
+ @db_app.command("reset-root")
425
+ def set_root_password_cmd(
426
+ password: str = typer.Option("root", "--password", "-p", help="New root password (default: root)"),
427
+ ):
428
+ """Set or reset MariaDB root password and enable password login across localhost and 127.0.0.1."""
429
+ if os.geteuid() != 0:
430
+ console.print("[bold yellow]MariaDB root configuration requires administrator privileges. Elevating via sudo...[/bold yellow]")
431
+ cmd = [
432
+ "sudo",
433
+ sys.executable,
434
+ "-m",
435
+ "ndev",
436
+ "db",
437
+ "set-root-password",
438
+ "--password",
439
+ password,
440
+ ]
441
+ try:
442
+ res = subprocess.run(cmd)
443
+ raise typer.Exit(code=res.returncode)
444
+ except KeyboardInterrupt:
445
+ console.print("\n[red]Cancelled by user.[/red]")
446
+ raise typer.Exit(code=130)
447
+
448
+ console.print(f"[bold yellow]Configuring MariaDB root access with password '{password}'...[/bold yellow]")
449
+ ok = configure_mariadb_root_password(new_password=password)
450
+ if ok:
451
+ console.print(f"[bold green]✓ MariaDB root password configured successfully! (user: root, password: {password})[/bold green]")
452
+ console.print(f"[dim]You can now connect via phpMyAdmin, Laravel, or CLI using user 'root' and password '{password}'.[/dim]")
453
+ else:
454
+ logger.error("Failed to configure MariaDB root password. Ensure MariaDB is running and you have administrator privileges.")
455
+ raise typer.Exit(code=1)
456
+
@@ -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
- table.add_row("MariaDB / MySQL", "[green]OK[/green]", f"Found at {db_path}")
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
 
@@ -141,6 +141,16 @@ def setup_cmd():
141
141
  except Exception:
142
142
  pass
143
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
+
144
154
  # 4. Install Composer
145
155
  local_bin = get_user_local_bin_dir()
146
156
  composer_bin = local_bin / "composer"
@@ -7,6 +7,7 @@ from __future__ import annotations
7
7
 
8
8
  import os
9
9
  import re
10
+ import sys
10
11
  import socket
11
12
  import shutil
12
13
  import subprocess
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ndev-stack
3
- Version: 0.3.14
3
+ Version: 0.3.16
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.14"
7
+ version = "0.3.16"
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,31 @@ 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
+ def test_set_root_password_cmd_elevates_when_non_root(self):
563
+ import typer
564
+ from ndev.linux.commands.db import set_root_password_cmd
565
+ with patch("os.geteuid", return_value=1000):
566
+ with patch("subprocess.run") as mock_run:
567
+ mock_run.return_value = subprocess.CompletedProcess(args=[], returncode=0)
568
+ with self.assertRaises((SystemExit, typer.Exit)) as cm:
569
+ set_root_password_cmd(password="root")
570
+ exit_code = getattr(cm.exception, "exit_code", getattr(cm.exception, "code", 0))
571
+ self.assertEqual(exit_code, 0)
572
+ mock_run.assert_called_once()
573
+ self.assertIn("sudo", mock_run.call_args[0][0])
574
+ self.assertIn("set-root-password", mock_run.call_args[0][0])
575
+
576
+
552
577
  class TestTuiEnsureSudo(unittest.TestCase):
553
578
  def test_ensure_sudo_when_not_needed(self):
554
579
  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