ndev-stack 0.3.6__tar.gz → 0.3.8__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.6 → ndev_stack-0.3.8}/PKG-INFO +1 -1
  2. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/__init__.py +1 -1
  3. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/utils.py +25 -1
  4. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/chroot/packages.py +27 -0
  5. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/available.py +38 -38
  6. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/ctl.py +6 -3
  7. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/current.py +10 -10
  8. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/db.py +319 -319
  9. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/install.py +39 -39
  10. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/list.py +45 -45
  11. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/mailpit.py +82 -82
  12. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/status.py +5 -2
  13. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/uninstall.py +69 -69
  14. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/update.py +56 -56
  15. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/upgrade.py +81 -81
  16. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/use.py +111 -108
  17. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/php/builder.py +9 -0
  18. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/php/installer.py +4 -0
  19. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/runtime/fpm.py +122 -117
  20. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/runtime/services.py +16 -8
  21. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev_stack.egg-info/PKG-INFO +1 -1
  22. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/pyproject.toml +1 -1
  23. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/tests/test_opensuse_compat.py +37 -0
  24. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/README.md +0 -0
  25. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/__main__.py +0 -0
  26. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/cli.py +0 -0
  27. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/__init__.py +0 -0
  28. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/config.py +0 -0
  29. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/constants.py +0 -0
  30. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/distro.py +0 -0
  31. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/github.py +0 -0
  32. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/logger.py +0 -0
  33. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/manifest.py +0 -0
  34. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/__init__.py +0 -0
  35. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/base.py +0 -0
  36. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/builtin/mailpit/manifest.json +0 -0
  37. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/builtin/mailpit/module.py +0 -0
  38. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/builtin/mongodb/manifest.json +0 -0
  39. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/builtin/mongodb/module.py +0 -0
  40. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/builtin/postgres/manifest.json +0 -0
  41. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/builtin/postgres/module.py +0 -0
  42. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/builtin/redis/manifest.json +0 -0
  43. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/builtin/redis/module.py +0 -0
  44. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/common/modules/manager.py +0 -0
  45. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/__init__.py +0 -0
  46. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/chroot/manager.py +0 -0
  47. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/chroot/shell.py +0 -0
  48. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/cli.py +0 -0
  49. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/clean.py +0 -0
  50. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/doctor.py +0 -0
  51. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/grok.py +0 -0
  52. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/logs.py +0 -0
  53. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/modules.py +0 -0
  54. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/mongodb.py +0 -0
  55. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/postgres.py +0 -0
  56. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/reload.py +0 -0
  57. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/restart.py +0 -0
  58. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/setup.py +0 -0
  59. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/start.py +0 -0
  60. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/stop.py +0 -0
  61. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/commands/vhost.py +0 -0
  62. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/php/downloader.py +0 -0
  63. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/php/extensions.py +0 -0
  64. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/php/resolver.py +0 -0
  65. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/php/templates.py +0 -0
  66. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/runtime/mailpit.py +0 -0
  67. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/runtime/nginx_helper.py +0 -0
  68. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/runtime/pma.py +0 -0
  69. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/runtime/process.py +0 -0
  70. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/runtime/sockets.py +0 -0
  71. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/runtime/upgrade.py +0 -0
  72. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/linux/tui.py +0 -0
  73. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/main.py +0 -0
  74. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/tui.py +0 -0
  75. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/__init__.py +0 -0
  76. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/bin/shim.exe +0 -0
  77. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/cli.py +0 -0
  78. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/commands/__init__.py +0 -0
  79. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/__init__.py +0 -0
  80. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/db.py +0 -0
  81. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/elevate.py +0 -0
  82. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/ext.py +0 -0
  83. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/fcgi.py +0 -0
  84. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/grok.py +0 -0
  85. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/logs.py +0 -0
  86. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/mailpit.py +0 -0
  87. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/mkcert.py +0 -0
  88. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/paths.py +0 -0
  89. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/php.py +0 -0
  90. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/pma.py +0 -0
  91. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/redis_core.py +0 -0
  92. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/services.py +0 -0
  93. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/setup.py +0 -0
  94. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/shim.py +0 -0
  95. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/upgrade.py +0 -0
  96. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/core/vhost.py +0 -0
  97. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/templates/vhost.conf.tmpl +0 -0
  98. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/templates/vhost_ssl.conf.tmpl +0 -0
  99. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev/win/tui.py +0 -0
  100. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev_stack.egg-info/SOURCES.txt +0 -0
  101. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev_stack.egg-info/dependency_links.txt +0 -0
  102. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev_stack.egg-info/entry_points.txt +0 -0
  103. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev_stack.egg-info/requires.txt +0 -0
  104. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/ndev_stack.egg-info/top_level.txt +0 -0
  105. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/setup.cfg +0 -0
  106. {ndev_stack-0.3.6 → ndev_stack-0.3.8}/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.6
3
+ Version: 0.3.8
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.6"
4
+ __version__ = "0.3.8"
5
5
 
6
6
  from . import common as common
7
7
  from .common.logger import logger as logger
@@ -92,7 +92,31 @@ def get_version_or_prompt(version: str = None, prompt_message: str = "PHP versio
92
92
  except Exception:
93
93
  pass
94
94
 
95
- return typer.prompt(prompt_message).strip()
95
+ return typer.prompt(prompt_message).strip()
96
+
97
+ def resolve_installed_version(version: str | None) -> str | None:
98
+ """Resolve an installed PHP version, matching exact name or major.minor prefix."""
99
+ if not version:
100
+ return version
101
+ from ndev.common.constants import PHP_DIR
102
+ if not PHP_DIR.exists():
103
+ return version
104
+ direct_path = PHP_DIR / version
105
+ if direct_path.exists() and direct_path.is_dir():
106
+ return version
107
+ # Try prefix matching (e.g. '8.4' -> '8.4.27')
108
+ matches = [
109
+ d.name for d in PHP_DIR.iterdir()
110
+ if d.is_dir() and (d.name == version or d.name.startswith(f"{version}."))
111
+ ]
112
+ if matches:
113
+ from packaging.version import parse as parse_version
114
+ try:
115
+ matches.sort(key=parse_version, reverse=True)
116
+ except Exception:
117
+ matches.sort(reverse=True)
118
+ return matches[0]
119
+ return version
96
120
 
97
121
  def which_binary(cmd: str) -> str | None:
98
122
  """
@@ -37,6 +37,33 @@ def install_host_packages(packages: list[str], show_logs: bool = False):
37
37
  logger.debug(f"zypper download failed: {e}")
38
38
 
39
39
  rpm_files = list(tmp_dir.rglob("*.rpm"))
40
+ # Auto-detect if any downloaded package is a metapackage requiring another -devel package
41
+ if rpm_files and shutil.which("rpm"):
42
+ downloaded_names = {rpm.stem.split("-")[0] for rpm in rpm_files}
43
+ secondary_pkgs = set()
44
+ for rpm in rpm_files:
45
+ try:
46
+ dep_res = subprocess.run(
47
+ ["rpm", "-qp", "--requires", str(rpm)],
48
+ capture_output=True,
49
+ text=True,
50
+ timeout=5
51
+ )
52
+ if dep_res.returncode == 0:
53
+ for line in dep_res.stdout.splitlines():
54
+ req = line.strip().split()[0]
55
+ if req.endswith("-devel") and not any(req.startswith(name) for name in downloaded_names):
56
+ secondary_pkgs.add(req)
57
+ except Exception:
58
+ pass
59
+ if secondary_pkgs:
60
+ try:
61
+ cmd2 = ["zypper", "--pkg-cache-dir", str(tmp_dir), "download"] + list(secondary_pkgs)
62
+ subprocess.run(cmd2, cwd=tmp_dir, capture_output=True, text=True)
63
+ except Exception as e:
64
+ logger.debug(f"zypper secondary download notice: {e}")
65
+ rpm_files = list(tmp_dir.rglob("*.rpm"))
66
+
40
67
  if rpm_files and shutil.which("rpm2cpio") and shutil.which("cpio"):
41
68
  for rpm in rpm_files:
42
69
  extract_dest = tmp_dir / rpm.stem
@@ -1,38 +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)
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)
@@ -42,12 +42,15 @@ def resolve_service_name(service: str) -> str:
42
42
  return "redis"
43
43
  return service
44
44
 
45
+ from ndev.common.utils import resolve_installed_version
46
+
45
47
  def service_exists(service: str) -> bool:
46
48
  if service == "pma":
47
49
  return True
48
50
  if service.startswith("ndev-"):
49
51
  version = service[5:]
50
- return (get_user_ndev_dir() / "php" / version).exists()
52
+ v = resolve_installed_version(version)
53
+ return (get_user_ndev_dir() / "php" / (v or version)).exists()
51
54
 
52
55
  resolved = resolve_service_name(service)
53
56
  if shutil.which("systemctl"):
@@ -71,7 +74,7 @@ def service_status(service: str) -> str:
71
74
  return "STOPPED"
72
75
 
73
76
  if service.startswith("ndev-"):
74
- version = service[5:]
77
+ version = resolve_installed_version(service[5:]) or service[5:]
75
78
  script_path = sys.argv[0]
76
79
  res = subprocess.run([sys.executable, script_path, "status", version], capture_output=True, text=True)
77
80
  if "Running" in res.stdout:
@@ -105,7 +108,7 @@ def manage_service(service: str, action: str):
105
108
  elif action == "restart":
106
109
  restart_pma()
107
110
  elif service.startswith("ndev-"):
108
- version = service[5:]
111
+ version = resolve_installed_version(service[5:]) or service[5:]
109
112
  script_path = sys.argv[0]
110
113
  subprocess.run([sys.executable, script_path, action, version])
111
114
  else:
@@ -1,10 +1,10 @@
1
- from ndev.common.constants import CURRENT_LINK
2
- from ndev.common.logger import logger
3
-
4
- def current_cmd():
5
- """Show the currently active PHP version."""
6
- if CURRENT_LINK.exists() and CURRENT_LINK.is_symlink():
7
- version = CURRENT_LINK.resolve().name
8
- logger.info(f"Current active PHP version: {version}")
9
- else:
10
- logger.info("No active PHP version set. Use 'ndev use <version>' to set one.")
1
+ from ndev.common.constants import CURRENT_LINK
2
+ from ndev.common.logger import logger
3
+
4
+ def current_cmd():
5
+ """Show the currently active PHP version."""
6
+ if CURRENT_LINK.exists() and CURRENT_LINK.is_symlink():
7
+ version = CURRENT_LINK.resolve().name
8
+ logger.info(f"Current active PHP version: {version}")
9
+ else:
10
+ logger.info("No active PHP version set. Use 'ndev use <version>' to set one.")