quickscale-cli 0.83.0__tar.gz → 0.84.0__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.
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/PKG-INFO +4 -3
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/pyproject.toml +5 -4
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/_version.py +1 -1
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/development_commands.py +33 -8
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/dependency_utils.py +8 -25
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/docker_utils.py +12 -10
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/README.md +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/__init__.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/analytics_contract.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/auth_contract.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/backups_contract.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/beta_migration.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/__init__.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/apply_command.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/deployment_commands.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/dr_commands.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/module_commands.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/module_config.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/module_wiring_specs.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/plan_command.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/remove_command.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/status_command.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/crm_contract.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/main.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/module_catalog.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/notifications_contract.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/schema/__init__.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/schema/config_schema.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/schema/delta.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/schema/state_schema.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/social_contract.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/__init__.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/module_dependency_sync.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/module_wiring_manager.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/project_identity.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/project_manager.py +0 -0
- {quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/railway_utils.py +0 -0
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quickscale-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.84.0
|
|
4
4
|
Summary: Command-line interface for QuickScale Django project generator
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Keywords: django,cli,project-generator,saas
|
|
7
7
|
Author: Experto AI
|
|
8
8
|
Author-email: victor@experto.ai
|
|
9
|
-
Requires-Python: >=3.
|
|
9
|
+
Requires-Python: >=3.13,<3.15
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
11
|
Classifier: Intended Audience :: Developers
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
14
|
Classifier: Programming Language :: Python :: 3.14
|
|
14
15
|
Classifier: Framework :: Django
|
|
15
16
|
Classifier: Framework :: Django :: 6.0
|
|
16
17
|
Requires-Dist: click (>=8.3.1,<9.0.0)
|
|
17
|
-
Requires-Dist: quickscale-core (>=0.
|
|
18
|
+
Requires-Dist: quickscale-core (>=0.84.0,<0.85.0)
|
|
18
19
|
Project-URL: Documentation, https://github.com/Experto-AI/quickscale/tree/main/docs
|
|
19
20
|
Project-URL: Homepage, https://github.com/Experto-AI/quickscale
|
|
20
21
|
Project-URL: Repository, https://github.com/Experto-AI/quickscale
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "quickscale-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.84.0"
|
|
4
4
|
description = "Command-line interface for QuickScale Django project generator"
|
|
5
|
-
requires-python = ">=3.
|
|
5
|
+
requires-python = ">=3.13,<3.15"
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
license = "Apache-2.0"
|
|
8
8
|
authors = [{name = "Experto AI", email = "victor@experto.ai"}]
|
|
@@ -11,6 +11,7 @@ classifiers = [
|
|
|
11
11
|
"Development Status :: 3 - Alpha",
|
|
12
12
|
"Intended Audience :: Developers",
|
|
13
13
|
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.13",
|
|
14
15
|
"Programming Language :: Python :: 3.14",
|
|
15
16
|
"Framework :: Django",
|
|
16
17
|
"Framework :: Django :: 6.0",
|
|
@@ -29,9 +30,9 @@ quickscale = "quickscale_cli.main:cli"
|
|
|
29
30
|
packages = [{include = "quickscale_cli", from = "src"}]
|
|
30
31
|
|
|
31
32
|
[tool.poetry.dependencies]
|
|
32
|
-
python = ">=3.
|
|
33
|
+
python = ">=3.13,<3.15"
|
|
33
34
|
click = "^8.3.1"
|
|
34
|
-
quickscale-core = "^0.
|
|
35
|
+
quickscale-core = "^0.84.0"
|
|
35
36
|
|
|
36
37
|
[tool.poetry.group.dev.dependencies]
|
|
37
38
|
# Dev dependencies centralized in root pyproject.toml
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# Auto-generated by scripts/version_tool.sh
|
|
2
|
-
__version__ = "0.
|
|
2
|
+
__version__ = "0.84.0"
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/development_commands.py
RENAMED
|
@@ -10,6 +10,7 @@ import click
|
|
|
10
10
|
|
|
11
11
|
from quickscale_cli.schema.config_schema import QuickScaleConfig
|
|
12
12
|
from quickscale_cli.utils.docker_utils import (
|
|
13
|
+
DockerComposePluginRequiredError,
|
|
13
14
|
get_docker_compose_command,
|
|
14
15
|
get_port_from_env,
|
|
15
16
|
is_docker_running,
|
|
@@ -74,8 +75,32 @@ def _show_port_conflict_error(port: int | str) -> None:
|
|
|
74
75
|
)
|
|
75
76
|
|
|
76
77
|
|
|
78
|
+
def _show_compose_v2_required_error() -> None:
|
|
79
|
+
"""Display a user-friendly error when Docker Compose v2 is unavailable."""
|
|
80
|
+
click.secho(
|
|
81
|
+
"❌ Error: Docker Compose v2 plugin is required",
|
|
82
|
+
fg="red",
|
|
83
|
+
err=True,
|
|
84
|
+
)
|
|
85
|
+
click.echo(
|
|
86
|
+
"💡 Tip: Install or update Docker so the 'docker compose' command is "
|
|
87
|
+
"available. QuickScale keeps the generated 'docker-compose.yml' file "
|
|
88
|
+
"name, but the CLI uses the Docker Compose v2 plugin.",
|
|
89
|
+
err=True,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _require_docker_compose_command() -> list[str]:
|
|
94
|
+
"""Return the Docker Compose v2 command or exit with remediation text."""
|
|
95
|
+
try:
|
|
96
|
+
return get_docker_compose_command()
|
|
97
|
+
except DockerComposePluginRequiredError:
|
|
98
|
+
_show_compose_v2_required_error()
|
|
99
|
+
sys.exit(1)
|
|
100
|
+
|
|
101
|
+
|
|
77
102
|
def _run_docker_compose_up(compose_cmd: list, build: bool, no_cache: bool) -> None:
|
|
78
|
-
"""Execute docker
|
|
103
|
+
"""Execute docker compose up with appropriate flags."""
|
|
79
104
|
if build or no_cache:
|
|
80
105
|
cmd = compose_cmd + ["--progress", "plain", "up", "-d"]
|
|
81
106
|
else:
|
|
@@ -101,7 +126,7 @@ def _run_docker_compose_up(compose_cmd: list, build: bool, no_cache: bool) -> No
|
|
|
101
126
|
|
|
102
127
|
|
|
103
128
|
def _handle_up_error(error: subprocess.CalledProcessError) -> None:
|
|
104
|
-
"""Handle docker
|
|
129
|
+
"""Handle docker compose up errors with user-friendly messages."""
|
|
105
130
|
error_output = error.stderr if error.stderr else ""
|
|
106
131
|
stdout_output = error.stdout if error.stdout else ""
|
|
107
132
|
full_output = error_output + stdout_output
|
|
@@ -122,7 +147,7 @@ def _handle_up_error(error: subprocess.CalledProcessError) -> None:
|
|
|
122
147
|
click.echo(
|
|
123
148
|
f"\n💡 To resolve this issue, try one of the following:\n"
|
|
124
149
|
f" 1. Stop existing containers: quickscale down\n"
|
|
125
|
-
f" 2. Remove orphaned containers: docker
|
|
150
|
+
f" 2. Remove orphaned containers: docker compose down --remove-orphans\n"
|
|
126
151
|
f" 3. Find and kill the process: lsof -ti:{conflict_port} | xargs kill -9\n"
|
|
127
152
|
f" 4. Find process details: sudo lsof -i:{conflict_port}\n"
|
|
128
153
|
f" 5. Or use: sudo fuser -k {conflict_port}/tcp",
|
|
@@ -272,14 +297,14 @@ def up(build: bool, no_cache: bool) -> None:
|
|
|
272
297
|
)
|
|
273
298
|
click.secho(" quickscale down && quickscale up --build\n", fg="cyan")
|
|
274
299
|
|
|
275
|
-
# Check if required port is available BEFORE calling docker
|
|
300
|
+
# Check if required port is available BEFORE calling docker compose.
|
|
276
301
|
port = get_port_from_env()
|
|
277
302
|
if not is_port_available(port):
|
|
278
303
|
_show_port_conflict_error(port)
|
|
279
304
|
sys.exit(1)
|
|
280
305
|
|
|
281
306
|
try:
|
|
282
|
-
compose_cmd =
|
|
307
|
+
compose_cmd = _require_docker_compose_command()
|
|
283
308
|
_run_docker_compose_up(compose_cmd, should_build, no_cache)
|
|
284
309
|
|
|
285
310
|
# Update build timestamp if build was performed
|
|
@@ -326,7 +351,7 @@ def down(volumes: bool) -> None:
|
|
|
326
351
|
_validate_project_and_docker()
|
|
327
352
|
|
|
328
353
|
try:
|
|
329
|
-
compose_cmd =
|
|
354
|
+
compose_cmd = _require_docker_compose_command()
|
|
330
355
|
cmd = compose_cmd + ["down", "--remove-orphans"]
|
|
331
356
|
|
|
332
357
|
if volumes:
|
|
@@ -468,7 +493,7 @@ def logs(service: str | None, follow: bool, tail: str | None, timestamps: bool)
|
|
|
468
493
|
_validate_project_and_docker()
|
|
469
494
|
|
|
470
495
|
try:
|
|
471
|
-
compose_cmd =
|
|
496
|
+
compose_cmd = _require_docker_compose_command()
|
|
472
497
|
cmd = compose_cmd + ["logs"]
|
|
473
498
|
|
|
474
499
|
if follow:
|
|
@@ -503,7 +528,7 @@ def ps() -> None:
|
|
|
503
528
|
_validate_project_and_docker()
|
|
504
529
|
|
|
505
530
|
try:
|
|
506
|
-
compose_cmd =
|
|
531
|
+
compose_cmd = _require_docker_compose_command()
|
|
507
532
|
cmd = compose_cmd + ["ps"]
|
|
508
533
|
subprocess.run(cmd, check=True)
|
|
509
534
|
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/dependency_utils.py
RENAMED
|
@@ -42,13 +42,13 @@ class DependencyStatus(NamedTuple):
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def check_python_version() -> DependencyStatus:
|
|
45
|
-
"""Check Python version (3.
|
|
45
|
+
"""Check Python version (3.13+ required)."""
|
|
46
46
|
import sys
|
|
47
47
|
|
|
48
48
|
version = (
|
|
49
49
|
f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
50
50
|
)
|
|
51
|
-
meets_requirement = sys.version_info >= (3,
|
|
51
|
+
meets_requirement = sys.version_info >= (3, 13)
|
|
52
52
|
|
|
53
53
|
return DependencyStatus(
|
|
54
54
|
name="Python",
|
|
@@ -151,7 +151,7 @@ def check_git_installed() -> DependencyStatus:
|
|
|
151
151
|
|
|
152
152
|
|
|
153
153
|
def check_docker_installed() -> DependencyStatus:
|
|
154
|
-
"""Check if Docker and Docker Compose are installed."""
|
|
154
|
+
"""Check if Docker and the Docker Compose v2 plugin are installed."""
|
|
155
155
|
|
|
156
156
|
if _should_skip_dependency_checks():
|
|
157
157
|
return _skipped_dependency(
|
|
@@ -176,45 +176,28 @@ def check_docker_installed() -> DependencyStatus:
|
|
|
176
176
|
parts = version_str.split()
|
|
177
177
|
version = parts[2].rstrip(",") if len(parts) >= 3 else None
|
|
178
178
|
|
|
179
|
-
#
|
|
180
|
-
compose_available = False
|
|
179
|
+
# Docker-backed workflows require the Compose v2 plugin.
|
|
181
180
|
try:
|
|
182
|
-
# Try docker compose v2 (plugin)
|
|
183
181
|
subprocess.run(
|
|
184
182
|
["docker", "compose", "version"],
|
|
185
183
|
capture_output=True,
|
|
186
184
|
check=True,
|
|
187
185
|
timeout=2,
|
|
188
186
|
)
|
|
189
|
-
compose_available = True
|
|
190
187
|
except (
|
|
191
188
|
subprocess.SubprocessError,
|
|
192
189
|
FileNotFoundError,
|
|
193
190
|
subprocess.TimeoutExpired,
|
|
194
191
|
):
|
|
195
|
-
# Try docker-compose v1 (standalone)
|
|
196
|
-
try:
|
|
197
|
-
subprocess.run(
|
|
198
|
-
["docker-compose", "--version"],
|
|
199
|
-
capture_output=True,
|
|
200
|
-
check=True,
|
|
201
|
-
timeout=2,
|
|
202
|
-
)
|
|
203
|
-
compose_available = True
|
|
204
|
-
except (
|
|
205
|
-
subprocess.SubprocessError,
|
|
206
|
-
FileNotFoundError,
|
|
207
|
-
subprocess.TimeoutExpired,
|
|
208
|
-
):
|
|
209
|
-
pass
|
|
210
|
-
|
|
211
|
-
if not compose_available:
|
|
212
192
|
return DependencyStatus(
|
|
213
193
|
name="Docker",
|
|
214
194
|
installed=False,
|
|
215
195
|
version=version,
|
|
216
196
|
required=False,
|
|
217
|
-
purpose=
|
|
197
|
+
purpose=(
|
|
198
|
+
"Containerized development (quickscale up/down) - "
|
|
199
|
+
"Docker Compose v2 plugin not found"
|
|
200
|
+
),
|
|
218
201
|
)
|
|
219
202
|
|
|
220
203
|
return DependencyStatus(
|
|
@@ -7,6 +7,10 @@ import time
|
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
class DockerComposePluginRequiredError(RuntimeError):
|
|
11
|
+
"""Raised when the Docker Compose v2 plugin is unavailable."""
|
|
12
|
+
|
|
13
|
+
|
|
10
14
|
def is_interactive() -> bool:
|
|
11
15
|
"""Check if running in an interactive terminal (has TTY)."""
|
|
12
16
|
return sys.stdout.isatty() and sys.stdin.isatty()
|
|
@@ -32,24 +36,22 @@ def find_docker_compose() -> Path | None:
|
|
|
32
36
|
|
|
33
37
|
|
|
34
38
|
def get_docker_compose_command() -> list[str]:
|
|
35
|
-
"""Get the
|
|
36
|
-
|
|
37
|
-
Tries 'docker compose' (v2 plugin) first, falls back to 'docker-compose' (v1 standalone).
|
|
38
|
-
Both are fully supported.
|
|
39
|
-
"""
|
|
40
|
-
# Try docker compose first (v2 plugin)
|
|
39
|
+
"""Get the Docker Compose v2 command."""
|
|
41
40
|
try:
|
|
42
41
|
subprocess.run(
|
|
43
42
|
["docker", "compose", "version"], capture_output=True, check=True, timeout=2
|
|
44
43
|
)
|
|
45
|
-
return ["docker", "compose"]
|
|
46
44
|
except (
|
|
47
45
|
subprocess.SubprocessError,
|
|
48
46
|
FileNotFoundError,
|
|
49
47
|
subprocess.TimeoutExpired,
|
|
50
|
-
):
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
) as error:
|
|
49
|
+
raise DockerComposePluginRequiredError(
|
|
50
|
+
"Docker Compose v2 is required. Install or update Docker so the "
|
|
51
|
+
"'docker compose' command is available."
|
|
52
|
+
) from error
|
|
53
|
+
|
|
54
|
+
return ["docker", "compose"]
|
|
53
55
|
|
|
54
56
|
|
|
55
57
|
def get_container_status(container_name: str) -> str | None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/apply_command.py
RENAMED
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/deployment_commands.py
RENAMED
|
File without changes
|
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/module_commands.py
RENAMED
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/module_config.py
RENAMED
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/module_wiring_specs.py
RENAMED
|
File without changes
|
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/remove_command.py
RENAMED
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/commands/status_command.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/notifications_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/module_dependency_sync.py
RENAMED
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/module_wiring_manager.py
RENAMED
|
File without changes
|
{quickscale_cli-0.83.0 → quickscale_cli-0.84.0}/src/quickscale_cli/utils/project_identity.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|