devctl 1.0.0__tar.gz → 1.1.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.
- {devctl-1.0.0 → devctl-1.1.0}/PKG-INFO +3 -2
- {devctl-1.0.0 → devctl-1.1.0}/README.md +2 -1
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/angular.py +9 -2
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/django.py +34 -6
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/fastapi.py +14 -3
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/go_fiber.py +9 -1
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/nestjs.py +3 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/nextjs.py +3 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/nodejs.py +14 -2
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/react.py +9 -1
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_nestjs.py +3 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/svelte.py +9 -1
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/vue.py +14 -2
- {devctl-1.0.0 → devctl-1.1.0}/devctl/orchestrator/runner.py +16 -12
- devctl-1.1.0/devctl/utils/__init__.py +5 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/utils/dependencies.py +11 -1
- devctl-1.1.0/devctl/utils/platform.py +120 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl.egg-info/PKG-INFO +3 -2
- {devctl-1.0.0 → devctl-1.1.0}/devctl.egg-info/SOURCES.txt +2 -0
- {devctl-1.0.0 → devctl-1.1.0}/pyproject.toml +1 -1
- devctl-1.1.0/test/test_platform.py +72 -0
- devctl-1.0.0/devctl/utils/__init__.py +0 -3
- {devctl-1.0.0 → devctl-1.1.0}/LICENSE +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/__init__.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/commands/__init__.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/commands/add.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/commands/deploy.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/commands/docker.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/commands/init.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/commands/run.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/__init__.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/docker_scaffold.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_angular.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_django.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_fastapi.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_go.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_nextjs.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_nodejs.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_react.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_spring.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_svelte.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/scaffold_vue.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/generators/spring.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/main.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/orchestrator/__init__.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/orchestrator/config_builder.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/orchestrator/scanner.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/config/environment.development.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/config/environment.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/config/proxy.conf.json.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/models/request.model.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/models/response.model.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/form/form.component.html.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/form/form.component.scss.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/form/form.component.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/list/list.component.html.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/list/list.component.scss.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/list/list.component.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/routes.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/services/service.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/docker/deploy.yml.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/docker/django/Dockerfile.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/docker/fastapi/Dockerfile.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/docker/frontend/Dockerfile.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/docker/go/Dockerfile.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/docker/nestjs/Dockerfile.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/docker/nextjs/Dockerfile.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/docker/nodejs/Dockerfile.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/docker/spring/Dockerfile.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/docker/svelte/Dockerfile.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/proxy.conf.json.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/Controller.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/Entity.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/Repository.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/Service.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/ServiceImpl.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/application.properties.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/config/ApplicationConfig.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/config/JwtAuthenticationFilter.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/config/JwtService.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/config/SecurityConfig.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/docker-compose.yml.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/dto/Request.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/dto/Response.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/mapper/Mapper.java.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/vue/config/App.vue.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/vue/config/main.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/vue/config/router.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/vue/config/vite.config.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/vue/feature/Form.vue.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/vue/feature/List.vue.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/vue/feature/models.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/vue/feature/routes.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/templates/vue/feature/service.ts.j2 +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl/utils/env_loader.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl.egg-info/dependency_links.txt +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl.egg-info/entry_points.txt +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl.egg-info/requires.txt +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/devctl.egg-info/top_level.txt +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/setup.cfg +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_add.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_commands.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_deploy.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_django.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_docker_scaffold.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_env_loader.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_fastapi.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_go.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_init.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_main.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_mongodb.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_nestjs.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_nextjs.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_nodejs.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_pom_patcher.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_react.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_runner.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_scanner.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_svelte.py +0 -0
- {devctl-1.0.0 → devctl-1.1.0}/test/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devctl
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Local development orchestrator for Spring Boot, Angular, and Vue.js
|
|
5
5
|
Author: Youssef Fellah
|
|
6
6
|
License: MIT License
|
|
@@ -45,10 +45,11 @@ Dynamic: license-file
|
|
|
45
45
|
orchestrate the development lifecycle for modern full-stack architectures. It
|
|
46
46
|
provides a unified workflow for managing backends (Spring Boot, NestJS, Go,
|
|
47
47
|
FastAPI), frontends (Angular, Vue, React, NextJS, Svelte), and their
|
|
48
|
-
containerization.
|
|
48
|
+
containerization across Linux, macOS, and Windows.
|
|
49
49
|
|
|
50
50
|
## Core Features
|
|
51
51
|
|
|
52
|
+
- **Cross-Platform Architecture**: Out-of-the-box support for Windows, Linux, and macOS utilizing a robust platform abstraction layer.
|
|
52
53
|
- **Multi-Stack Orchestration**: Launch databases, multiple microservices, and
|
|
53
54
|
frontend dev servers with a single command. Includes real-time, prefixed log
|
|
54
55
|
streaming.
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
orchestrate the development lifecycle for modern full-stack architectures. It
|
|
5
5
|
provides a unified workflow for managing backends (Spring Boot, NestJS, Go,
|
|
6
6
|
FastAPI), frontends (Angular, Vue, React, NextJS, Svelte), and their
|
|
7
|
-
containerization.
|
|
7
|
+
containerization across Linux, macOS, and Windows.
|
|
8
8
|
|
|
9
9
|
## Core Features
|
|
10
10
|
|
|
11
|
+
- **Cross-Platform Architecture**: Out-of-the-box support for Windows, Linux, and macOS utilizing a robust platform abstraction layer.
|
|
11
12
|
- **Multi-Stack Orchestration**: Launch databases, multiple microservices, and
|
|
12
13
|
frontend dev servers with a single command. Includes real-time, prefixed log
|
|
13
14
|
streaming.
|
|
@@ -79,8 +79,15 @@ def generate_angular_boilerplate(project_name: str) -> bool:
|
|
|
79
79
|
|
|
80
80
|
safe_name = project_name.lower().replace("_", "-")
|
|
81
81
|
|
|
82
|
+
from devctl.utils import get_platform
|
|
83
|
+
platform = get_platform()
|
|
82
84
|
try:
|
|
83
|
-
subprocess.run(
|
|
85
|
+
subprocess.run(
|
|
86
|
+
["ng", "version"],
|
|
87
|
+
capture_output=True,
|
|
88
|
+
check=True,
|
|
89
|
+
shell=platform.shell_required,
|
|
90
|
+
)
|
|
84
91
|
except FileNotFoundError:
|
|
85
92
|
typer.secho(
|
|
86
93
|
"Error: Angular CLI ('ng') not found on your system.",
|
|
@@ -95,7 +102,7 @@ def generate_angular_boilerplate(project_name: str) -> bool:
|
|
|
95
102
|
command = ["ng", "new", safe_name, "--routing=true", "--style=scss", "--skip-git=true"]
|
|
96
103
|
|
|
97
104
|
typer.secho("Downloading npm packages... (This may take 1-2 minutes)", fg=typer.colors.CYAN)
|
|
98
|
-
subprocess.run(command, check=True)
|
|
105
|
+
subprocess.run(command, check=True, shell=platform.shell_required)
|
|
99
106
|
|
|
100
107
|
# Post-installation configuration
|
|
101
108
|
project_full_path = os.path.join(os.getcwd(), safe_name)
|
|
@@ -28,28 +28,56 @@ django-cors-headers
|
|
|
28
28
|
with open(os.path.join(project_path, "requirements.txt"), "w") as f:
|
|
29
29
|
f.write(requirements)
|
|
30
30
|
|
|
31
|
+
import sys
|
|
32
|
+
python_exe = "python" if sys.platform == "win32" else "python3"
|
|
33
|
+
|
|
31
34
|
# 2. Create virtual environment
|
|
32
35
|
typer.secho("Creating virtual environment...", fg=typer.colors.CYAN)
|
|
33
|
-
subprocess.run(
|
|
36
|
+
subprocess.run(
|
|
37
|
+
[python_exe, "-m", "venv", ".venv"],
|
|
38
|
+
cwd=project_path,
|
|
39
|
+
check=True,
|
|
40
|
+
shell=(sys.platform == "win32"),
|
|
41
|
+
)
|
|
34
42
|
|
|
35
43
|
# 3. Install Django
|
|
36
44
|
typer.secho("Installing Django and DRF...", fg=typer.colors.CYAN)
|
|
37
|
-
|
|
45
|
+
if sys.platform == "win32":
|
|
46
|
+
pip_path = os.path.join(".venv", "Scripts", "pip.exe")
|
|
47
|
+
else:
|
|
48
|
+
pip_path = os.path.join(".venv", "bin", "pip")
|
|
38
49
|
subprocess.run(
|
|
39
50
|
[pip_path, "install", "django", "djangorestframework"],
|
|
40
51
|
cwd=project_path,
|
|
41
52
|
check=True,
|
|
42
53
|
stdout=subprocess.DEVNULL,
|
|
54
|
+
shell=(sys.platform == "win32"),
|
|
43
55
|
)
|
|
44
56
|
|
|
45
57
|
# 4. Start Project
|
|
46
58
|
typer.secho("Scaffolding Django project structure...", fg=typer.colors.CYAN)
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
if sys.platform == "win32":
|
|
60
|
+
django_admin = os.path.join(".venv", "Scripts", "django-admin.exe")
|
|
61
|
+
else:
|
|
62
|
+
django_admin = os.path.join(".venv", "bin", "django-admin")
|
|
63
|
+
subprocess.run(
|
|
64
|
+
[django_admin, "startproject", safe_name, "."],
|
|
65
|
+
cwd=project_path,
|
|
66
|
+
check=True,
|
|
67
|
+
shell=(sys.platform == "win32"),
|
|
68
|
+
)
|
|
49
69
|
|
|
50
70
|
# 5. Create core app
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
if sys.platform == "win32":
|
|
72
|
+
python_path = os.path.join(".venv", "Scripts", "python.exe")
|
|
73
|
+
else:
|
|
74
|
+
python_path = os.path.join(".venv", "bin", "python")
|
|
75
|
+
subprocess.run(
|
|
76
|
+
[python_path, "manage.py", "startapp", "core"],
|
|
77
|
+
cwd=project_path,
|
|
78
|
+
check=True,
|
|
79
|
+
shell=(sys.platform == "win32"),
|
|
80
|
+
)
|
|
53
81
|
|
|
54
82
|
typer.secho(
|
|
55
83
|
f"Django project '{project_name}' successfully generated!", fg=typer.colors.GREEN
|
|
@@ -44,19 +44,30 @@ pydantic
|
|
|
44
44
|
with open(os.path.join(project_path, "requirements.txt"), "w") as f:
|
|
45
45
|
f.write(requirements)
|
|
46
46
|
|
|
47
|
+
import sys
|
|
48
|
+
python_exe = "python" if sys.platform == "win32" else "python3"
|
|
49
|
+
|
|
47
50
|
# 3. Create virtual environment
|
|
48
51
|
typer.secho("Creating virtual environment...", fg=typer.colors.CYAN)
|
|
49
|
-
subprocess.run(
|
|
52
|
+
subprocess.run(
|
|
53
|
+
[python_exe, "-m", "venv", ".venv"],
|
|
54
|
+
cwd=project_path,
|
|
55
|
+
check=True,
|
|
56
|
+
shell=(sys.platform == "win32"),
|
|
57
|
+
)
|
|
50
58
|
|
|
51
59
|
# 4. Install dependencies
|
|
52
60
|
typer.secho("Installing dependencies (fastapi, uvicorn)...", fg=typer.colors.CYAN)
|
|
53
|
-
|
|
54
|
-
|
|
61
|
+
if sys.platform == "win32":
|
|
62
|
+
pip_path = os.path.join(".venv", "Scripts", "pip.exe")
|
|
63
|
+
else:
|
|
64
|
+
pip_path = os.path.join(".venv", "bin", "pip")
|
|
55
65
|
subprocess.run(
|
|
56
66
|
[pip_path, "install", "-r", "requirements.txt"],
|
|
57
67
|
cwd=project_path,
|
|
58
68
|
check=True,
|
|
59
69
|
stdout=subprocess.DEVNULL,
|
|
70
|
+
shell=(sys.platform == "win32"),
|
|
60
71
|
)
|
|
61
72
|
|
|
62
73
|
typer.secho(f"FastAPI project '{safe_name}' successfully generated!", fg=typer.colors.GREEN)
|
|
@@ -19,9 +19,16 @@ def generate_go_boilerplate(project_name: str) -> bool:
|
|
|
19
19
|
try:
|
|
20
20
|
os.makedirs(project_path, exist_ok=True)
|
|
21
21
|
|
|
22
|
+
from devctl.utils import get_platform
|
|
23
|
+
platform = get_platform()
|
|
22
24
|
# 1. Go mod init
|
|
23
25
|
typer.secho("Initializing Go module...", fg=typer.colors.CYAN)
|
|
24
|
-
subprocess.run(
|
|
26
|
+
subprocess.run(
|
|
27
|
+
["go", "mod", "init", project_name],
|
|
28
|
+
cwd=project_path,
|
|
29
|
+
check=True,
|
|
30
|
+
shell=platform.shell_required,
|
|
31
|
+
)
|
|
25
32
|
|
|
26
33
|
# 2. Install Fiber
|
|
27
34
|
typer.secho("Installing Fiber framework...", fg=typer.colors.CYAN)
|
|
@@ -30,6 +37,7 @@ def generate_go_boilerplate(project_name: str) -> bool:
|
|
|
30
37
|
cwd=project_path,
|
|
31
38
|
check=True,
|
|
32
39
|
stdout=subprocess.DEVNULL,
|
|
40
|
+
shell=platform.shell_required,
|
|
33
41
|
)
|
|
34
42
|
|
|
35
43
|
# 3. Create main.go
|
|
@@ -21,6 +21,8 @@ def generate_nest_boilerplate(project_name: str) -> bool:
|
|
|
21
21
|
# --strict: enables strict mode
|
|
22
22
|
# --skip-git: devctl might be in a git repo already
|
|
23
23
|
typer.secho("Scaffolding NestJS project (this may take a minute)...", fg=typer.colors.CYAN)
|
|
24
|
+
from devctl.utils import get_platform
|
|
25
|
+
platform = get_platform()
|
|
24
26
|
subprocess.run(
|
|
25
27
|
[
|
|
26
28
|
"npx",
|
|
@@ -34,6 +36,7 @@ def generate_nest_boilerplate(project_name: str) -> bool:
|
|
|
34
36
|
"--skip-git",
|
|
35
37
|
],
|
|
36
38
|
check=True,
|
|
39
|
+
shell=platform.shell_required,
|
|
37
40
|
)
|
|
38
41
|
|
|
39
42
|
typer.secho(
|
|
@@ -23,6 +23,8 @@ def generate_nextjs_boilerplate(project_name: str) -> bool:
|
|
|
23
23
|
# --src-dir: Use src/ directory
|
|
24
24
|
# --app: Use App Router
|
|
25
25
|
# --import-alias: alias for imports
|
|
26
|
+
from devctl.utils import get_platform
|
|
27
|
+
platform = get_platform()
|
|
26
28
|
subprocess.run(
|
|
27
29
|
[
|
|
28
30
|
"npx",
|
|
@@ -38,6 +40,7 @@ def generate_nextjs_boilerplate(project_name: str) -> bool:
|
|
|
38
40
|
"--use-npm",
|
|
39
41
|
],
|
|
40
42
|
check=True,
|
|
43
|
+
shell=platform.shell_required,
|
|
41
44
|
)
|
|
42
45
|
|
|
43
46
|
typer.secho(
|
|
@@ -21,10 +21,16 @@ def generate_nodejs_boilerplate(project_name: str) -> bool:
|
|
|
21
21
|
try:
|
|
22
22
|
os.makedirs(project_path, exist_ok=True)
|
|
23
23
|
|
|
24
|
+
from devctl.utils import get_platform
|
|
25
|
+
platform = get_platform()
|
|
24
26
|
# 1. Initialize package.json
|
|
25
27
|
typer.secho("Initializing package.json...", fg=typer.colors.CYAN)
|
|
26
28
|
subprocess.run(
|
|
27
|
-
["npm", "init", "-y"],
|
|
29
|
+
["npm", "init", "-y"],
|
|
30
|
+
cwd=project_path,
|
|
31
|
+
check=True,
|
|
32
|
+
stdout=subprocess.DEVNULL,
|
|
33
|
+
shell=platform.shell_required,
|
|
28
34
|
)
|
|
29
35
|
|
|
30
36
|
# 2. Install dependencies
|
|
@@ -37,6 +43,7 @@ def generate_nodejs_boilerplate(project_name: str) -> bool:
|
|
|
37
43
|
cwd=project_path,
|
|
38
44
|
check=True,
|
|
39
45
|
stdout=subprocess.DEVNULL,
|
|
46
|
+
shell=platform.shell_required,
|
|
40
47
|
)
|
|
41
48
|
subprocess.run(
|
|
42
49
|
[
|
|
@@ -53,12 +60,17 @@ def generate_nodejs_boilerplate(project_name: str) -> bool:
|
|
|
53
60
|
cwd=project_path,
|
|
54
61
|
check=True,
|
|
55
62
|
stdout=subprocess.DEVNULL,
|
|
63
|
+
shell=platform.shell_required,
|
|
56
64
|
)
|
|
57
65
|
|
|
58
66
|
# 3. Initialize TypeScript
|
|
59
67
|
typer.secho("Configuring TypeScript...", fg=typer.colors.CYAN)
|
|
60
68
|
subprocess.run(
|
|
61
|
-
["npx", "tsc", "--init"],
|
|
69
|
+
["npx", "tsc", "--init"],
|
|
70
|
+
cwd=project_path,
|
|
71
|
+
check=True,
|
|
72
|
+
stdout=subprocess.DEVNULL,
|
|
73
|
+
shell=platform.shell_required,
|
|
62
74
|
)
|
|
63
75
|
|
|
64
76
|
# 4. Create folder structure
|
|
@@ -17,16 +17,24 @@ def generate_react_boilerplate(project_name: str) -> bool:
|
|
|
17
17
|
safe_name = project_name.lower().replace("_", "-")
|
|
18
18
|
|
|
19
19
|
try:
|
|
20
|
+
from devctl.utils import get_platform
|
|
21
|
+
platform = get_platform()
|
|
20
22
|
typer.secho("Scaffolding React project...", fg=typer.colors.CYAN)
|
|
21
23
|
subprocess.run(
|
|
22
24
|
["npm", "create", "vite@latest", safe_name, "--", "--template", "react-ts"],
|
|
23
25
|
check=True,
|
|
26
|
+
shell=platform.shell_required,
|
|
24
27
|
)
|
|
25
28
|
|
|
26
29
|
project_full_path = os.path.join(os.getcwd(), safe_name)
|
|
27
30
|
|
|
28
31
|
typer.secho("Installing npm dependencies...", fg=typer.colors.CYAN)
|
|
29
|
-
subprocess.run(
|
|
32
|
+
subprocess.run(
|
|
33
|
+
["npm", "install"],
|
|
34
|
+
cwd=project_full_path,
|
|
35
|
+
check=True,
|
|
36
|
+
shell=platform.shell_required,
|
|
37
|
+
)
|
|
30
38
|
|
|
31
39
|
typer.secho(
|
|
32
40
|
f"ReactJS frontend '{safe_name}' successfully generated!", fg=typer.colors.GREEN
|
|
@@ -25,6 +25,8 @@ def generate_nest_resource(resource_name: str, fields_str: str, root_path: str =
|
|
|
25
25
|
|
|
26
26
|
typer.secho(f"⚙️ Generating NestJS resource '{resource_name}'...", fg=typer.colors.CYAN)
|
|
27
27
|
|
|
28
|
+
from devctl.utils import get_platform
|
|
29
|
+
platform = get_platform()
|
|
28
30
|
try:
|
|
29
31
|
# Use npx to run Nest CLI
|
|
30
32
|
# 'g res' is shorthand for 'generate resource'
|
|
@@ -33,6 +35,7 @@ def generate_nest_resource(resource_name: str, fields_str: str, root_path: str =
|
|
|
33
35
|
["npx", "@nestjs/cli", "g", "resource", resource_lower, "--no-spec"],
|
|
34
36
|
cwd=nest_root,
|
|
35
37
|
check=True,
|
|
38
|
+
shell=platform.shell_required,
|
|
36
39
|
)
|
|
37
40
|
|
|
38
41
|
typer.secho(
|
|
@@ -18,6 +18,8 @@ def generate_svelte_boilerplate(project_name: str) -> bool:
|
|
|
18
18
|
|
|
19
19
|
try:
|
|
20
20
|
typer.secho("Scaffolding SvelteKit project...", fg=typer.colors.CYAN)
|
|
21
|
+
from devctl.utils import get_platform
|
|
22
|
+
platform = get_platform()
|
|
21
23
|
# Using a non-interactive way to scaffold svelte
|
|
22
24
|
# We'll use the 'skeleton' template with TypeScript
|
|
23
25
|
subprocess.run(
|
|
@@ -34,12 +36,18 @@ def generate_svelte_boilerplate(project_name: str) -> bool:
|
|
|
34
36
|
"--no-git",
|
|
35
37
|
],
|
|
36
38
|
check=True,
|
|
39
|
+
shell=platform.shell_required,
|
|
37
40
|
)
|
|
38
41
|
|
|
39
42
|
project_full_path = os.path.join(os.getcwd(), safe_name)
|
|
40
43
|
|
|
41
44
|
typer.secho("Installing npm dependencies...", fg=typer.colors.CYAN)
|
|
42
|
-
subprocess.run(
|
|
45
|
+
subprocess.run(
|
|
46
|
+
["npm", "install"],
|
|
47
|
+
cwd=project_full_path,
|
|
48
|
+
check=True,
|
|
49
|
+
shell=platform.shell_required,
|
|
50
|
+
)
|
|
43
51
|
|
|
44
52
|
typer.secho(f"Svelte project '{safe_name}' successfully generated!", fg=typer.colors.GREEN)
|
|
45
53
|
return True
|
|
@@ -37,6 +37,8 @@ def setup_vue_router(project_path: str):
|
|
|
37
37
|
"""
|
|
38
38
|
typer.secho("Installing and configuring vue-router...", fg=typer.colors.CYAN)
|
|
39
39
|
|
|
40
|
+
from devctl.utils import get_platform
|
|
41
|
+
platform = get_platform()
|
|
40
42
|
try:
|
|
41
43
|
# 1. NPM package installation
|
|
42
44
|
subprocess.run(
|
|
@@ -44,6 +46,7 @@ def setup_vue_router(project_path: str):
|
|
|
44
46
|
cwd=project_path,
|
|
45
47
|
check=True,
|
|
46
48
|
stdout=subprocess.DEVNULL,
|
|
49
|
+
shell=platform.shell_required,
|
|
47
50
|
)
|
|
48
51
|
|
|
49
52
|
# 2. Router directory creation
|
|
@@ -79,16 +82,25 @@ def generate_vue_boilerplate(project_name: str) -> bool:
|
|
|
79
82
|
typer.secho(f"Generating Vue.js frontend '{project_name}' via Vite...", fg=typer.colors.CYAN)
|
|
80
83
|
safe_name = project_name.lower().replace("_", "-")
|
|
81
84
|
|
|
85
|
+
from devctl.utils import get_platform
|
|
86
|
+
platform = get_platform()
|
|
82
87
|
try:
|
|
83
88
|
typer.secho("Scaffolding Vite project...", fg=typer.colors.CYAN)
|
|
84
89
|
subprocess.run(
|
|
85
|
-
["npm", "create", "vite@latest", safe_name, "--", "--template", "vue-ts"],
|
|
90
|
+
["npm", "create", "vite@latest", safe_name, "--", "--template", "vue-ts"],
|
|
91
|
+
check=True,
|
|
92
|
+
shell=platform.shell_required,
|
|
86
93
|
)
|
|
87
94
|
|
|
88
95
|
project_full_path = os.path.join(os.getcwd(), safe_name)
|
|
89
96
|
|
|
90
97
|
typer.secho("Installing npm dependencies...", fg=typer.colors.CYAN)
|
|
91
|
-
subprocess.run(
|
|
98
|
+
subprocess.run(
|
|
99
|
+
["npm", "install"],
|
|
100
|
+
cwd=project_full_path,
|
|
101
|
+
check=True,
|
|
102
|
+
shell=platform.shell_required,
|
|
103
|
+
)
|
|
92
104
|
|
|
93
105
|
# --- CALL OUR TWO CONFIGURATORS ---
|
|
94
106
|
setup_vue_proxy(project_full_path)
|
|
@@ -17,6 +17,7 @@ from rich.console import Console
|
|
|
17
17
|
from rich.markup import escape
|
|
18
18
|
|
|
19
19
|
from devctl.generators.docker_scaffold import DockerProject
|
|
20
|
+
from devctl.utils import get_platform
|
|
20
21
|
from devctl.utils.env_loader import get_project_env
|
|
21
22
|
|
|
22
23
|
console = Console()
|
|
@@ -56,6 +57,7 @@ def _launch_process(p: DockerProject, cmd: List[str], color: str, label: str):
|
|
|
56
57
|
# Load environment variables including .env if present
|
|
57
58
|
env = get_project_env(p.path)
|
|
58
59
|
|
|
60
|
+
platform = get_platform()
|
|
59
61
|
proc = subprocess.Popen(
|
|
60
62
|
cmd,
|
|
61
63
|
cwd=str(p.path),
|
|
@@ -63,6 +65,7 @@ def _launch_process(p: DockerProject, cmd: List[str], color: str, label: str):
|
|
|
63
65
|
stderr=subprocess.STDOUT,
|
|
64
66
|
bufsize=1,
|
|
65
67
|
env=env,
|
|
68
|
+
shell=platform.shell_required,
|
|
66
69
|
)
|
|
67
70
|
active_processes.append((p.name, proc))
|
|
68
71
|
|
|
@@ -82,7 +85,11 @@ def launch_dev_environment(projects: List[DockerProject], docker_composes: List[
|
|
|
82
85
|
cleanup_and_exit(docker_composes)
|
|
83
86
|
|
|
84
87
|
signal.signal(signal.SIGINT, signal_handler)
|
|
85
|
-
|
|
88
|
+
if sys.platform != "win32":
|
|
89
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
|
90
|
+
else:
|
|
91
|
+
if hasattr(signal, "SIGBREAK"):
|
|
92
|
+
signal.signal(signal.SIGBREAK, signal_handler)
|
|
86
93
|
|
|
87
94
|
try:
|
|
88
95
|
# 1. Start Databases
|
|
@@ -105,10 +112,11 @@ def launch_dev_environment(projects: List[DockerProject], docker_composes: List[
|
|
|
105
112
|
typer.echo("Waiting 5s for databases to initialize...")
|
|
106
113
|
time.sleep(5)
|
|
107
114
|
|
|
115
|
+
platform = get_platform()
|
|
108
116
|
# 2. Start Projects
|
|
109
117
|
for p in projects:
|
|
110
118
|
if p.kind == "spring":
|
|
111
|
-
_launch_process(p, [
|
|
119
|
+
_launch_process(p, [platform.mvnw_cmd, "spring-boot:run"], "green", "Spring Boot")
|
|
112
120
|
|
|
113
121
|
elif p.kind == "angular":
|
|
114
122
|
_launch_process(p, ["npx", "ng", "serve"], "cyan", "Angular")
|
|
@@ -132,17 +140,17 @@ def launch_dev_environment(projects: List[DockerProject], docker_composes: List[
|
|
|
132
140
|
_launch_process(p, ["npm", "run", "dev"], "green", "NodeJS")
|
|
133
141
|
|
|
134
142
|
elif p.kind == "fastapi":
|
|
135
|
-
venv_python =
|
|
143
|
+
venv_python = platform.get_venv_python(p.path)
|
|
136
144
|
if not os.path.exists(venv_python):
|
|
137
|
-
venv_python =
|
|
145
|
+
venv_python = platform.python_exe
|
|
138
146
|
_launch_process(
|
|
139
147
|
p, [venv_python, "-m", "uvicorn", "main:app", "--reload"], "cyan", "FastAPI"
|
|
140
148
|
)
|
|
141
149
|
|
|
142
150
|
elif p.kind == "django":
|
|
143
|
-
venv_python =
|
|
151
|
+
venv_python = platform.get_venv_python(p.path)
|
|
144
152
|
if not os.path.exists(venv_python):
|
|
145
|
-
venv_python =
|
|
153
|
+
venv_python = platform.python_exe
|
|
146
154
|
_launch_process(p, [venv_python, "manage.py", "runserver"], "green", "Django")
|
|
147
155
|
|
|
148
156
|
elif p.kind == "go":
|
|
@@ -193,14 +201,10 @@ def launch_dev_environment(projects: List[DockerProject], docker_composes: List[
|
|
|
193
201
|
|
|
194
202
|
def cleanup_and_exit(docker_composes: List[Path]):
|
|
195
203
|
"""Stops all active processes and docker containers."""
|
|
204
|
+
platform = get_platform()
|
|
196
205
|
for name, proc in active_processes:
|
|
197
206
|
typer.echo(f"Closing {name}...")
|
|
198
|
-
|
|
199
|
-
try:
|
|
200
|
-
proc.wait(timeout=5)
|
|
201
|
-
except subprocess.TimeoutExpired:
|
|
202
|
-
typer.echo(f"Force killing {name}...")
|
|
203
|
-
proc.kill()
|
|
207
|
+
platform.kill_process_tree(proc)
|
|
204
208
|
|
|
205
209
|
for compose_path in docker_composes:
|
|
206
210
|
typer.echo(f"Stopping Docker Compose DB in {compose_path}...")
|
|
@@ -13,7 +13,17 @@ def check_tool(tool_name: str, required_for: str = "this operation"):
|
|
|
13
13
|
Check if a tool is available in the system PATH.
|
|
14
14
|
If not, print a friendly error message and exit.
|
|
15
15
|
"""
|
|
16
|
-
|
|
16
|
+
from devctl.utils import get_platform
|
|
17
|
+
platform = get_platform()
|
|
18
|
+
|
|
19
|
+
actual_tool = tool_name
|
|
20
|
+
if tool_name == "python3" and platform.python_exe == "python":
|
|
21
|
+
actual_tool = "python"
|
|
22
|
+
|
|
23
|
+
if shutil.which(actual_tool) is None:
|
|
24
|
+
if tool_name == "python3" and shutil.which("python") is not None:
|
|
25
|
+
return
|
|
26
|
+
|
|
17
27
|
typer.secho(
|
|
18
28
|
f"\nError: '{tool_name}' is not installed or not in your PATH.",
|
|
19
29
|
fg=typer.colors.RED,
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Platform abstraction layer to handle OS-specific command execution, paths, and process management.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BasePlatform(ABC):
|
|
13
|
+
@property
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def python_exe(self) -> str:
|
|
16
|
+
"""Global python executable name."""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def mvnw_cmd(self) -> str:
|
|
22
|
+
"""Maven wrapper command/script name."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def shell_required(self) -> bool:
|
|
28
|
+
"""Indicates if shell execution is required for subprocess commands."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def get_venv_python(self, project_path: Path) -> str:
|
|
33
|
+
"""Returns the path to the python executable in the virtual environment."""
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
@abstractmethod
|
|
37
|
+
def get_venv_pip(self, project_path: Path) -> str:
|
|
38
|
+
"""Returns the path to the pip executable in the virtual environment."""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
@abstractmethod
|
|
42
|
+
def get_venv_django_admin(self, project_path: Path) -> str:
|
|
43
|
+
"""Returns the path to the django-admin executable in the virtual environment."""
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
@abstractmethod
|
|
47
|
+
def kill_process_tree(self, proc: subprocess.Popen) -> None:
|
|
48
|
+
"""Terminates the process and all its descendants."""
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class WindowsPlatform(BasePlatform):
|
|
53
|
+
@property
|
|
54
|
+
def python_exe(self) -> str:
|
|
55
|
+
return "python"
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def mvnw_cmd(self) -> str:
|
|
59
|
+
return "mvnw.cmd"
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def shell_required(self) -> bool:
|
|
63
|
+
return True
|
|
64
|
+
|
|
65
|
+
def get_venv_python(self, project_path: Path) -> str:
|
|
66
|
+
return os.path.join(str(project_path), ".venv", "Scripts", "python.exe")
|
|
67
|
+
|
|
68
|
+
def get_venv_pip(self, project_path: Path) -> str:
|
|
69
|
+
return os.path.join(str(project_path), ".venv", "Scripts", "pip.exe")
|
|
70
|
+
|
|
71
|
+
def get_venv_django_admin(self, project_path: Path) -> str:
|
|
72
|
+
return os.path.join(str(project_path), ".venv", "Scripts", "django-admin.exe")
|
|
73
|
+
|
|
74
|
+
def kill_process_tree(self, proc: subprocess.Popen) -> None:
|
|
75
|
+
try:
|
|
76
|
+
subprocess.run(
|
|
77
|
+
["taskkill", "/F", "/T", "/PID", str(proc.pid)],
|
|
78
|
+
stdout=subprocess.DEVNULL,
|
|
79
|
+
stderr=subprocess.DEVNULL,
|
|
80
|
+
check=False,
|
|
81
|
+
)
|
|
82
|
+
except Exception:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class UnixPlatform(BasePlatform):
|
|
87
|
+
@property
|
|
88
|
+
def python_exe(self) -> str:
|
|
89
|
+
return "python3"
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def mvnw_cmd(self) -> str:
|
|
93
|
+
return "./mvnw"
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def shell_required(self) -> bool:
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
def get_venv_python(self, project_path: Path) -> str:
|
|
100
|
+
return os.path.join(str(project_path), ".venv", "bin", "python3")
|
|
101
|
+
|
|
102
|
+
def get_venv_pip(self, project_path: Path) -> str:
|
|
103
|
+
return os.path.join(str(project_path), ".venv", "bin", "pip")
|
|
104
|
+
|
|
105
|
+
def get_venv_django_admin(self, project_path: Path) -> str:
|
|
106
|
+
return os.path.join(str(project_path), ".venv", "bin", "django-admin")
|
|
107
|
+
|
|
108
|
+
def kill_process_tree(self, proc: subprocess.Popen) -> None:
|
|
109
|
+
proc.terminate()
|
|
110
|
+
try:
|
|
111
|
+
proc.wait(timeout=5)
|
|
112
|
+
except subprocess.TimeoutExpired:
|
|
113
|
+
proc.kill()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def get_platform() -> BasePlatform:
|
|
117
|
+
"""Factory function to return the correct Platform implementation."""
|
|
118
|
+
if sys.platform == "win32":
|
|
119
|
+
return WindowsPlatform()
|
|
120
|
+
return UnixPlatform()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devctl
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Local development orchestrator for Spring Boot, Angular, and Vue.js
|
|
5
5
|
Author: Youssef Fellah
|
|
6
6
|
License: MIT License
|
|
@@ -45,10 +45,11 @@ Dynamic: license-file
|
|
|
45
45
|
orchestrate the development lifecycle for modern full-stack architectures. It
|
|
46
46
|
provides a unified workflow for managing backends (Spring Boot, NestJS, Go,
|
|
47
47
|
FastAPI), frontends (Angular, Vue, React, NextJS, Svelte), and their
|
|
48
|
-
containerization.
|
|
48
|
+
containerization across Linux, macOS, and Windows.
|
|
49
49
|
|
|
50
50
|
## Core Features
|
|
51
51
|
|
|
52
|
+
- **Cross-Platform Architecture**: Out-of-the-box support for Windows, Linux, and macOS utilizing a robust platform abstraction layer.
|
|
52
53
|
- **Multi-Stack Orchestration**: Launch databases, multiple microservices, and
|
|
53
54
|
frontend dev servers with a single command. Includes real-time, prefixed log
|
|
54
55
|
streaming.
|
|
@@ -93,6 +93,7 @@ devctl/templates/vue/feature/service.ts.j2
|
|
|
93
93
|
devctl/utils/__init__.py
|
|
94
94
|
devctl/utils/dependencies.py
|
|
95
95
|
devctl/utils/env_loader.py
|
|
96
|
+
devctl/utils/platform.py
|
|
96
97
|
test/test_add.py
|
|
97
98
|
test/test_commands.py
|
|
98
99
|
test/test_deploy.py
|
|
@@ -107,6 +108,7 @@ test/test_mongodb.py
|
|
|
107
108
|
test/test_nestjs.py
|
|
108
109
|
test/test_nextjs.py
|
|
109
110
|
test/test_nodejs.py
|
|
111
|
+
test/test_platform.py
|
|
110
112
|
test/test_pom_patcher.py
|
|
111
113
|
test/test_react.py
|
|
112
114
|
test/test_runner.py
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from unittest.mock import MagicMock, patch
|
|
5
|
+
|
|
6
|
+
from devctl.utils.platform import (
|
|
7
|
+
UnixPlatform,
|
|
8
|
+
WindowsPlatform,
|
|
9
|
+
get_platform,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_get_platform():
|
|
14
|
+
"""Ensure get_platform returns the correct implementation based on sys.platform."""
|
|
15
|
+
with patch("sys.platform", "win32"):
|
|
16
|
+
p = get_platform()
|
|
17
|
+
assert isinstance(p, WindowsPlatform)
|
|
18
|
+
|
|
19
|
+
with patch("sys.platform", "linux"):
|
|
20
|
+
p = get_platform()
|
|
21
|
+
assert isinstance(p, UnixPlatform)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_windows_platform():
|
|
25
|
+
"""Ensure WindowsPlatform returns the correct values and calls taskkill."""
|
|
26
|
+
p = WindowsPlatform()
|
|
27
|
+
assert p.python_exe == "python"
|
|
28
|
+
assert p.mvnw_cmd == "mvnw.cmd"
|
|
29
|
+
assert p.shell_required is True
|
|
30
|
+
|
|
31
|
+
path = Path("proj")
|
|
32
|
+
assert p.get_venv_python(path) == os.path.join(
|
|
33
|
+
"proj", ".venv", "Scripts", "python.exe"
|
|
34
|
+
)
|
|
35
|
+
assert p.get_venv_pip(path) == os.path.join(
|
|
36
|
+
"proj", ".venv", "Scripts", "pip.exe"
|
|
37
|
+
)
|
|
38
|
+
assert p.get_venv_django_admin(path) == os.path.join(
|
|
39
|
+
"proj", ".venv", "Scripts", "django-admin.exe"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Test kill_process_tree
|
|
43
|
+
mock_proc = MagicMock()
|
|
44
|
+
mock_proc.pid = 1234
|
|
45
|
+
with patch("subprocess.run") as mock_run:
|
|
46
|
+
p.kill_process_tree(mock_proc)
|
|
47
|
+
mock_run.assert_called_once_with(
|
|
48
|
+
["taskkill", "/F", "/T", "/PID", "1234"],
|
|
49
|
+
stdout=subprocess.DEVNULL,
|
|
50
|
+
stderr=subprocess.DEVNULL,
|
|
51
|
+
check=False,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_unix_platform():
|
|
56
|
+
"""Ensure UnixPlatform returns the correct values and terminates/kills process."""
|
|
57
|
+
p = UnixPlatform()
|
|
58
|
+
assert p.python_exe == "python3"
|
|
59
|
+
assert p.mvnw_cmd == "./mvnw"
|
|
60
|
+
assert p.shell_required is False
|
|
61
|
+
|
|
62
|
+
path = Path("proj")
|
|
63
|
+
assert p.get_venv_python(path) == os.path.join("proj", ".venv", "bin", "python3")
|
|
64
|
+
assert p.get_venv_pip(path) == os.path.join("proj", ".venv", "bin", "pip")
|
|
65
|
+
assert p.get_venv_django_admin(path) == os.path.join("proj", ".venv", "bin", "django-admin")
|
|
66
|
+
|
|
67
|
+
# Test kill_process_tree with normal termination
|
|
68
|
+
mock_proc = MagicMock()
|
|
69
|
+
p.kill_process_tree(mock_proc)
|
|
70
|
+
mock_proc.terminate.assert_called_once()
|
|
71
|
+
mock_proc.wait.assert_called_once_with(timeout=5)
|
|
72
|
+
mock_proc.kill.assert_not_called()
|
|
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
|
{devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/form/form.component.html.j2
RENAMED
|
File without changes
|
{devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/form/form.component.scss.j2
RENAMED
|
File without changes
|
{devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/form/form.component.ts.j2
RENAMED
|
File without changes
|
{devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/list/list.component.html.j2
RENAMED
|
File without changes
|
{devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/list/list.component.scss.j2
RENAMED
|
File without changes
|
{devctl-1.0.0 → devctl-1.1.0}/devctl/templates/angular/feature/pages/list/list.component.ts.j2
RENAMED
|
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
|
{devctl-1.0.0 → devctl-1.1.0}/devctl/templates/spring/config/JwtAuthenticationFilter.java.j2
RENAMED
|
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
|