devctl 1.0.0__py3-none-any.whl
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/__init__.py +3 -0
- devctl/commands/__init__.py +3 -0
- devctl/commands/add.py +166 -0
- devctl/commands/deploy.py +61 -0
- devctl/commands/docker.py +65 -0
- devctl/commands/init.py +193 -0
- devctl/commands/run.py +67 -0
- devctl/generators/__init__.py +3 -0
- devctl/generators/angular.py +112 -0
- devctl/generators/django.py +61 -0
- devctl/generators/docker_scaffold.py +656 -0
- devctl/generators/fastapi.py +67 -0
- devctl/generators/go_fiber.py +61 -0
- devctl/generators/nestjs.py +49 -0
- devctl/generators/nextjs.py +53 -0
- devctl/generators/nodejs.py +109 -0
- devctl/generators/react.py +43 -0
- devctl/generators/scaffold_angular.py +163 -0
- devctl/generators/scaffold_django.py +79 -0
- devctl/generators/scaffold_fastapi.py +83 -0
- devctl/generators/scaffold_go.py +67 -0
- devctl/generators/scaffold_nestjs.py +52 -0
- devctl/generators/scaffold_nextjs.py +73 -0
- devctl/generators/scaffold_nodejs.py +81 -0
- devctl/generators/scaffold_react.py +80 -0
- devctl/generators/scaffold_spring.py +166 -0
- devctl/generators/scaffold_svelte.py +73 -0
- devctl/generators/scaffold_vue.py +111 -0
- devctl/generators/spring.py +221 -0
- devctl/generators/svelte.py +52 -0
- devctl/generators/vue.py +105 -0
- devctl/main.py +45 -0
- devctl/orchestrator/__init__.py +3 -0
- devctl/orchestrator/config_builder.py +64 -0
- devctl/orchestrator/runner.py +219 -0
- devctl/orchestrator/scanner.py +155 -0
- devctl/templates/angular/config/environment.development.ts.j2 +4 -0
- devctl/templates/angular/config/environment.ts.j2 +5 -0
- devctl/templates/angular/config/proxy.conf.json.j2 +8 -0
- devctl/templates/angular/feature/models/request.model.ts.j2 +5 -0
- devctl/templates/angular/feature/models/response.model.ts.j2 +6 -0
- devctl/templates/angular/feature/pages/form/form.component.html.j2 +21 -0
- devctl/templates/angular/feature/pages/form/form.component.scss.j2 +0 -0
- devctl/templates/angular/feature/pages/form/form.component.ts.j2 +63 -0
- devctl/templates/angular/feature/pages/list/list.component.html.j2 +28 -0
- devctl/templates/angular/feature/pages/list/list.component.scss.j2 +0 -0
- devctl/templates/angular/feature/pages/list/list.component.ts.j2 +34 -0
- devctl/templates/angular/feature/routes.ts.j2 +9 -0
- devctl/templates/angular/feature/services/service.ts.j2 +34 -0
- devctl/templates/docker/deploy.yml.j2 +37 -0
- devctl/templates/docker/django/Dockerfile.j2 +21 -0
- devctl/templates/docker/fastapi/Dockerfile.j2 +15 -0
- devctl/templates/docker/frontend/Dockerfile.j2 +31 -0
- devctl/templates/docker/go/Dockerfile.j2 +24 -0
- devctl/templates/docker/nestjs/Dockerfile.j2 +26 -0
- devctl/templates/docker/nextjs/Dockerfile.j2 +43 -0
- devctl/templates/docker/nodejs/Dockerfile.j2 +24 -0
- devctl/templates/docker/spring/Dockerfile.j2 +24 -0
- devctl/templates/docker/svelte/Dockerfile.j2 +24 -0
- devctl/templates/proxy.conf.json.j2 +0 -0
- devctl/templates/spring/Controller.java.j2 +50 -0
- devctl/templates/spring/Entity.java.j2 +22 -0
- devctl/templates/spring/Repository.java.j2 +9 -0
- devctl/templates/spring/Service.java.j2 +20 -0
- devctl/templates/spring/ServiceImpl.java.j2 +62 -0
- devctl/templates/spring/application.properties.j2 +19 -0
- devctl/templates/spring/config/ApplicationConfig.java.j2 +46 -0
- devctl/templates/spring/config/JwtAuthenticationFilter.java.j2 +54 -0
- devctl/templates/spring/config/JwtService.java.j2 +68 -0
- devctl/templates/spring/config/SecurityConfig.java.j2 +42 -0
- devctl/templates/spring/docker-compose.yml.j2 +29 -0
- devctl/templates/spring/dto/Request.java.j2 +20 -0
- devctl/templates/spring/dto/Response.java.j2 +22 -0
- devctl/templates/spring/mapper/Mapper.java.j2 +30 -0
- devctl/templates/vue/config/App.vue.j2 +35 -0
- devctl/templates/vue/config/main.ts.j2 +9 -0
- devctl/templates/vue/config/router.ts.j2 +18 -0
- devctl/templates/vue/config/vite.config.ts.j2 +16 -0
- devctl/templates/vue/feature/Form.vue.j2 +162 -0
- devctl/templates/vue/feature/List.vue.j2 +155 -0
- devctl/templates/vue/feature/models.ts.j2 +12 -0
- devctl/templates/vue/feature/routes.ts.j2 +19 -0
- devctl/templates/vue/feature/service.ts.j2 +44 -0
- devctl/utils/__init__.py +3 -0
- devctl/utils/dependencies.py +36 -0
- devctl/utils/env_loader.py +57 -0
- devctl-1.0.0.dist-info/METADATA +127 -0
- devctl-1.0.0.dist-info/RECORD +92 -0
- devctl-1.0.0.dist-info/WHEEL +5 -0
- devctl-1.0.0.dist-info/entry_points.txt +2 -0
- devctl-1.0.0.dist-info/licenses/LICENSE +21 -0
- devctl-1.0.0.dist-info/top_level.txt +1 -0
devctl/__init__.py
ADDED
devctl/commands/add.py
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI command group for adding new resources to existing projects.
|
|
3
|
+
Supports Spring Boot, Angular, Vue.js, NestJS, NodeJS, React, NextJS, FastAPI,
|
|
4
|
+
Django, Svelte, and Go scaffolding.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
from devctl.generators.scaffold_angular import generate_angular_resource
|
|
12
|
+
from devctl.generators.scaffold_django import generate_django_resource
|
|
13
|
+
from devctl.generators.scaffold_fastapi import generate_fastapi_resource
|
|
14
|
+
from devctl.generators.scaffold_go import generate_go_resource
|
|
15
|
+
from devctl.generators.scaffold_nestjs import generate_nest_resource
|
|
16
|
+
from devctl.generators.scaffold_nextjs import generate_nextjs_resource
|
|
17
|
+
from devctl.generators.scaffold_nodejs import generate_nodejs_resource
|
|
18
|
+
from devctl.generators.scaffold_react import generate_react_resource
|
|
19
|
+
from devctl.generators.scaffold_spring import generate_spring_resource
|
|
20
|
+
from devctl.generators.scaffold_svelte import generate_svelte_resource
|
|
21
|
+
from devctl.generators.scaffold_vue import generate_vue_resource
|
|
22
|
+
from devctl.orchestrator.scanner import detect_environment
|
|
23
|
+
from devctl.utils.dependencies import check_tool
|
|
24
|
+
|
|
25
|
+
app = typer.Typer(help="Adds resources to the current project (Scaffolding).")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@app.command()
|
|
29
|
+
def resource(
|
|
30
|
+
name: str = typer.Argument(..., help="The name of the resource (e.g., Client, Product)"),
|
|
31
|
+
fields: str = typer.Option(
|
|
32
|
+
"", "--fields", "-f", help="Fields in the format 'name:type, age:int'"
|
|
33
|
+
),
|
|
34
|
+
):
|
|
35
|
+
"""
|
|
36
|
+
Scans the current folder and generates a suitable business architecture.
|
|
37
|
+
"""
|
|
38
|
+
typer.secho("Analyzing current context...", fg=typer.colors.CYAN)
|
|
39
|
+
env_state = detect_environment(".")
|
|
40
|
+
|
|
41
|
+
original_dir = os.getcwd()
|
|
42
|
+
project_detected = False
|
|
43
|
+
|
|
44
|
+
# Check for Spring Boot project
|
|
45
|
+
if env_state["has_spring"]:
|
|
46
|
+
check_tool("java", "generating Spring Boot resources")
|
|
47
|
+
project_detected = True
|
|
48
|
+
typer.secho(
|
|
49
|
+
"Spring Boot project detected. Launching Java generator...", fg=typer.colors.GREEN
|
|
50
|
+
)
|
|
51
|
+
os.chdir(env_state["spring_path"])
|
|
52
|
+
try:
|
|
53
|
+
generate_spring_resource(name, fields)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
typer.secho(f"Error: Spring generation failed: {e}", fg=typer.colors.RED)
|
|
56
|
+
finally:
|
|
57
|
+
os.chdir(original_dir)
|
|
58
|
+
|
|
59
|
+
# Check for Angular project
|
|
60
|
+
if env_state["has_angular"]:
|
|
61
|
+
check_tool("npm", "generating Angular resources")
|
|
62
|
+
project_detected = True
|
|
63
|
+
typer.secho(
|
|
64
|
+
"Angular project detected. Launching TypeScript generator...", fg=typer.colors.CYAN
|
|
65
|
+
)
|
|
66
|
+
try:
|
|
67
|
+
generate_angular_resource(name, fields, root_path=".")
|
|
68
|
+
except Exception as e:
|
|
69
|
+
typer.secho(f"Error: Angular generation failed: {e}", fg=typer.colors.RED)
|
|
70
|
+
|
|
71
|
+
# Check for Vue.js project
|
|
72
|
+
if env_state.get("has_vue"):
|
|
73
|
+
check_tool("npm", "generating Vue.js resources")
|
|
74
|
+
project_detected = True
|
|
75
|
+
typer.secho("Vue.js project detected. Launching Vue generator...", fg=typer.colors.GREEN)
|
|
76
|
+
try:
|
|
77
|
+
generate_vue_resource(name, fields, root_path=".")
|
|
78
|
+
except Exception as e:
|
|
79
|
+
typer.secho(f"Error: Vue generation failed: {e}", fg=typer.colors.RED)
|
|
80
|
+
|
|
81
|
+
# Check for NestJS project
|
|
82
|
+
if env_state.get("has_nest"):
|
|
83
|
+
project_detected = True
|
|
84
|
+
typer.secho("NestJS project detected. Launching Nest generator...", fg=typer.colors.CYAN)
|
|
85
|
+
try:
|
|
86
|
+
generate_nest_resource(name, fields, root_path=".")
|
|
87
|
+
except Exception as e:
|
|
88
|
+
typer.secho(f"Error: Nest generation failed: {e}", fg=typer.colors.RED)
|
|
89
|
+
|
|
90
|
+
# Check for React project
|
|
91
|
+
if env_state.get("has_react"):
|
|
92
|
+
project_detected = True
|
|
93
|
+
typer.secho("React project detected. Launching React generator...", fg=typer.colors.BLUE)
|
|
94
|
+
try:
|
|
95
|
+
generate_react_resource(name, fields, root_path=".")
|
|
96
|
+
except Exception as e:
|
|
97
|
+
typer.secho(f"Error: React generation failed: {e}", fg=typer.colors.RED)
|
|
98
|
+
|
|
99
|
+
# Check for NextJS project
|
|
100
|
+
if env_state.get("has_nextjs"):
|
|
101
|
+
project_detected = True
|
|
102
|
+
typer.secho(
|
|
103
|
+
"NextJS project detected. Launching NextJS generator...", fg=typer.colors.YELLOW
|
|
104
|
+
)
|
|
105
|
+
try:
|
|
106
|
+
generate_nextjs_resource(name, fields, root_path=".")
|
|
107
|
+
except Exception as e:
|
|
108
|
+
typer.secho(f"Error: NextJS generation failed: {e}", fg=typer.colors.RED)
|
|
109
|
+
|
|
110
|
+
# Check for FastAPI project
|
|
111
|
+
if env_state.get("has_fastapi"):
|
|
112
|
+
project_detected = True
|
|
113
|
+
typer.secho(
|
|
114
|
+
"FastAPI project detected. Launching FastAPI generator...", fg=typer.colors.CYAN
|
|
115
|
+
)
|
|
116
|
+
try:
|
|
117
|
+
generate_fastapi_resource(name, fields, root_path=".")
|
|
118
|
+
except Exception as e:
|
|
119
|
+
typer.secho(f"Error: FastAPI generation failed: {e}", fg=typer.colors.RED)
|
|
120
|
+
|
|
121
|
+
# Check for Django project
|
|
122
|
+
if env_state.get("has_django"):
|
|
123
|
+
project_detected = True
|
|
124
|
+
typer.secho("Django project detected. Launching Django generator...", fg=typer.colors.GREEN)
|
|
125
|
+
try:
|
|
126
|
+
generate_django_resource(name, fields, root_path=".")
|
|
127
|
+
except Exception as e:
|
|
128
|
+
typer.secho(f"Error: Django generation failed: {e}", fg=typer.colors.RED)
|
|
129
|
+
|
|
130
|
+
# Check for Svelte project
|
|
131
|
+
if env_state.get("has_svelte"):
|
|
132
|
+
project_detected = True
|
|
133
|
+
typer.secho("Svelte project detected. Launching Svelte generator...", fg=typer.colors.RED)
|
|
134
|
+
try:
|
|
135
|
+
generate_svelte_resource(name, fields, root_path=".")
|
|
136
|
+
except Exception as e:
|
|
137
|
+
typer.secho(f"Error: Svelte generation failed: {e}", fg=typer.colors.RED)
|
|
138
|
+
|
|
139
|
+
# Check for Go project
|
|
140
|
+
if env_state.get("has_go"):
|
|
141
|
+
project_detected = True
|
|
142
|
+
typer.secho("Go project detected. Launching Go generator...", fg=typer.colors.CYAN)
|
|
143
|
+
try:
|
|
144
|
+
generate_go_resource(name, fields, root_path=".")
|
|
145
|
+
except Exception as e:
|
|
146
|
+
typer.secho(f"Error: Go generation failed: {e}", fg=typer.colors.RED)
|
|
147
|
+
|
|
148
|
+
# Check for NodeJS project
|
|
149
|
+
if os.path.exists("package.json") and not project_detected:
|
|
150
|
+
# Heuristic for generic nodejs project if not already caught by others
|
|
151
|
+
project_detected = True
|
|
152
|
+
typer.secho("NodeJS project detected. Launching NodeJS generator...", fg=typer.colors.GREEN)
|
|
153
|
+
try:
|
|
154
|
+
generate_nodejs_resource(name, fields, root_path=".")
|
|
155
|
+
except Exception as e:
|
|
156
|
+
typer.secho(f"Error: NodeJS generation failed: {e}", fg=typer.colors.RED)
|
|
157
|
+
|
|
158
|
+
# Error message only if NO project detected
|
|
159
|
+
if not project_detected:
|
|
160
|
+
typer.secho(
|
|
161
|
+
"Error: Unable to determine project type. "
|
|
162
|
+
"Please run from within a supported project directory "
|
|
163
|
+
"(Spring, Angular, React, NextJS, FastAPI, Django, Svelte, Go, or Vue.js).",
|
|
164
|
+
fg=typer.colors.RED,
|
|
165
|
+
)
|
|
166
|
+
raise typer.Exit(code=1)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from devctl.generators.docker_scaffold import (
|
|
6
|
+
DockerScaffoldError,
|
|
7
|
+
scaffold_docker_assets,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
app = typer.Typer(help="Deployment and orchestration commands.")
|
|
11
|
+
|
|
12
|
+
PATH_ARGUMENT = typer.Argument(
|
|
13
|
+
Path("."),
|
|
14
|
+
help="Repository or project directory to scan for services.",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
FORCE_OPTION = typer.Option(
|
|
18
|
+
False,
|
|
19
|
+
"--force",
|
|
20
|
+
help="Overwrite generated Docker files that already exist.",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
@app.command()
|
|
24
|
+
def deploy(
|
|
25
|
+
path: Path = PATH_ARGUMENT,
|
|
26
|
+
force: bool = FORCE_OPTION,
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Generate a global docker-compose-prod.yml by scanning subdirectories.
|
|
30
|
+
"""
|
|
31
|
+
typer.secho(f"Preparing deployment for {path.resolve()}...", fg=typer.colors.CYAN, bold=True)
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
result = scaffold_docker_assets(
|
|
35
|
+
path,
|
|
36
|
+
force=force,
|
|
37
|
+
)
|
|
38
|
+
except DockerScaffoldError as exc:
|
|
39
|
+
typer.secho(f"Error: {exc}", fg=typer.colors.RED)
|
|
40
|
+
raise typer.Exit(code=1) from exc
|
|
41
|
+
|
|
42
|
+
typer.secho(
|
|
43
|
+
f"Deployment scaffolding complete for {result.root_path}",
|
|
44
|
+
fg=typer.colors.CYAN,
|
|
45
|
+
bold=True,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
typer.echo("\nDetected services:")
|
|
49
|
+
for service in result.services:
|
|
50
|
+
typer.echo(f" - {service.kind}: {service.service_name} ({service.relative_context})")
|
|
51
|
+
|
|
52
|
+
typer.echo("\nFiles:")
|
|
53
|
+
for operation in result.operations:
|
|
54
|
+
if "docker-compose-prod.yml" in str(operation.path):
|
|
55
|
+
relative_path = operation.path.relative_to(result.root_path)
|
|
56
|
+
typer.echo(f" - {operation.action}: {relative_path}")
|
|
57
|
+
|
|
58
|
+
typer.secho(
|
|
59
|
+
f"\nSummary: {result.created_count} created, {result.skipped_count} skipped.",
|
|
60
|
+
fg=typer.colors.GREEN,
|
|
61
|
+
)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI command group for Docker-related operations.
|
|
3
|
+
Includes scaffolding Dockerfiles for detected services.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from devctl.generators.docker_scaffold import (
|
|
11
|
+
DockerScaffoldError,
|
|
12
|
+
scaffold_docker_assets,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
PATH_ARGUMENT = typer.Argument(
|
|
16
|
+
Path("."),
|
|
17
|
+
help="Repository or project directory to scan for Dockerizable services.",
|
|
18
|
+
)
|
|
19
|
+
FORCE_OPTION = typer.Option(
|
|
20
|
+
False,
|
|
21
|
+
"--force",
|
|
22
|
+
help="Overwrite generated Docker files that already exist.",
|
|
23
|
+
)
|
|
24
|
+
DRY_RUN_OPTION = typer.Option(
|
|
25
|
+
False,
|
|
26
|
+
"--dry-run",
|
|
27
|
+
help="Show what would be generated without writing files.",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def dockerize(
|
|
32
|
+
path: Path = PATH_ARGUMENT,
|
|
33
|
+
force: bool = FORCE_OPTION,
|
|
34
|
+
dry_run: bool = DRY_RUN_OPTION,
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
Scaffold Dockerfiles for Spring Boot, Angular, and Vue/Vite projects.
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
result = scaffold_docker_assets(
|
|
41
|
+
path,
|
|
42
|
+
force=force,
|
|
43
|
+
dry_run=dry_run,
|
|
44
|
+
)
|
|
45
|
+
except DockerScaffoldError as exc:
|
|
46
|
+
typer.secho(f"Error: {exc}", fg=typer.colors.RED)
|
|
47
|
+
raise typer.Exit(code=1) from exc
|
|
48
|
+
|
|
49
|
+
mode = "Dry run" if dry_run else "Docker scaffolding"
|
|
50
|
+
typer.secho(f"{mode} complete for {result.root_path}", fg=typer.colors.CYAN, bold=True)
|
|
51
|
+
|
|
52
|
+
typer.echo("\nDetected services:")
|
|
53
|
+
for service in result.services:
|
|
54
|
+
typer.echo(f" - {service.kind}: {service.service_name} ({service.relative_context})")
|
|
55
|
+
|
|
56
|
+
typer.echo("\nFiles:")
|
|
57
|
+
for operation in result.operations:
|
|
58
|
+
relative_path = operation.path.relative_to(result.root_path)
|
|
59
|
+
typer.echo(f" - {operation.action}: {relative_path}")
|
|
60
|
+
|
|
61
|
+
typer.secho(
|
|
62
|
+
f"\nSummary: {result.created_count} created, "
|
|
63
|
+
f"{result.skipped_count} skipped, {result.planned_count} planned.",
|
|
64
|
+
fg=typer.colors.GREEN,
|
|
65
|
+
)
|
devctl/commands/init.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI command group for initializing new projects.
|
|
3
|
+
Supports Spring Boot, Angular, Vue.js, NestJS, NodeJS, React, NextJS, FastAPI,
|
|
4
|
+
Django, Svelte, and Go.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
# Angular generator
|
|
10
|
+
from devctl.generators.angular import generate_angular_boilerplate
|
|
11
|
+
from devctl.generators.django import generate_django_boilerplate
|
|
12
|
+
from devctl.generators.fastapi import generate_fastapi_boilerplate
|
|
13
|
+
from devctl.generators.go_fiber import generate_go_boilerplate
|
|
14
|
+
from devctl.generators.nestjs import generate_nest_boilerplate
|
|
15
|
+
from devctl.generators.nextjs import generate_nextjs_boilerplate
|
|
16
|
+
from devctl.generators.nodejs import generate_nodejs_boilerplate
|
|
17
|
+
from devctl.generators.react import generate_react_boilerplate
|
|
18
|
+
|
|
19
|
+
# Spring generator
|
|
20
|
+
from devctl.generators.spring import download_spring_boilerplate
|
|
21
|
+
from devctl.generators.svelte import generate_svelte_boilerplate
|
|
22
|
+
from devctl.generators.vue import generate_vue_boilerplate
|
|
23
|
+
from devctl.orchestrator.config_builder import generate_config
|
|
24
|
+
from devctl.utils.dependencies import check_tool
|
|
25
|
+
|
|
26
|
+
# Local Typer application for "init" command group
|
|
27
|
+
app = typer.Typer(help="Initializes a new project based on the chosen framework.")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@app.command("spring")
|
|
31
|
+
def init_spring(
|
|
32
|
+
name: str,
|
|
33
|
+
db: str = typer.Option("postgres", help="Database type (postgres, mysql, or mongodb)"),
|
|
34
|
+
port: int = typer.Option(None, help="Local port (optional)"),
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
Initializes a new Spring Boot backend project with its database.
|
|
38
|
+
"""
|
|
39
|
+
check_tool("java", "initializing a Spring Boot project")
|
|
40
|
+
|
|
41
|
+
# Strict input validation
|
|
42
|
+
if db not in ["postgres", "mysql", "mongodb"]:
|
|
43
|
+
typer.secho(f"Error: Database '{db}' is not supported.", fg=typer.colors.RED)
|
|
44
|
+
raise typer.Exit(code=1)
|
|
45
|
+
|
|
46
|
+
typer.secho(f"Initializing Spring Boot project: '{name}'...", fg=typer.colors.CYAN)
|
|
47
|
+
|
|
48
|
+
success_download = download_spring_boilerplate(name, db_type=db)
|
|
49
|
+
|
|
50
|
+
if success_download:
|
|
51
|
+
generate_config(name, db_type=db, custom_port=port)
|
|
52
|
+
typer.secho("\nSpring project ready!", fg=typer.colors.GREEN)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@app.command("angular")
|
|
56
|
+
def init_angular(name: str):
|
|
57
|
+
"""
|
|
58
|
+
Initializes a new Angular frontend project.
|
|
59
|
+
"""
|
|
60
|
+
check_tool("npm", "initializing an Angular project")
|
|
61
|
+
check_tool("ng", "initializing an Angular project")
|
|
62
|
+
|
|
63
|
+
typer.secho(f"Initializing Angular project: '{name}'...", fg=typer.colors.CYAN)
|
|
64
|
+
success = generate_angular_boilerplate(name)
|
|
65
|
+
|
|
66
|
+
if success:
|
|
67
|
+
typer.secho("\nAngular project ready!", fg=typer.colors.GREEN)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@app.command("vue")
|
|
71
|
+
def init_vue(name: str):
|
|
72
|
+
"""
|
|
73
|
+
Initializes a new Vue.js frontend project (Vite + TS).
|
|
74
|
+
"""
|
|
75
|
+
check_tool("npm", "initializing a Vue.js project")
|
|
76
|
+
|
|
77
|
+
typer.secho(f"Initializing Vue.js project: '{name}'...", fg=typer.colors.CYAN)
|
|
78
|
+
success = generate_vue_boilerplate(name)
|
|
79
|
+
|
|
80
|
+
if success:
|
|
81
|
+
typer.secho("\nVue.js project ready!", fg=typer.colors.GREEN)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@app.command("nest")
|
|
85
|
+
def init_nest(name: str):
|
|
86
|
+
"""
|
|
87
|
+
Initializes a new NestJS backend project.
|
|
88
|
+
"""
|
|
89
|
+
check_tool("npm", "initializing a NestJS project")
|
|
90
|
+
|
|
91
|
+
typer.secho(f"Initializing NestJS project: '{name}'...", fg=typer.colors.CYAN)
|
|
92
|
+
success = generate_nest_boilerplate(name)
|
|
93
|
+
|
|
94
|
+
if success:
|
|
95
|
+
typer.secho("\nNestJS project ready!", fg=typer.colors.GREEN)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@app.command("nodejs")
|
|
99
|
+
def init_nodejs(name: str):
|
|
100
|
+
"""
|
|
101
|
+
Initializes a new NodeJS/Express backend project.
|
|
102
|
+
"""
|
|
103
|
+
check_tool("npm", "initializing a NodeJS project")
|
|
104
|
+
|
|
105
|
+
typer.secho(f"Initializing NodeJS project: '{name}'...", fg=typer.colors.CYAN)
|
|
106
|
+
success = generate_nodejs_boilerplate(name)
|
|
107
|
+
|
|
108
|
+
if success:
|
|
109
|
+
typer.secho("\nNodeJS project ready!", fg=typer.colors.GREEN)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@app.command("react")
|
|
113
|
+
def init_react(name: str):
|
|
114
|
+
"""
|
|
115
|
+
Initializes a new ReactJS frontend project (Vite + TS).
|
|
116
|
+
"""
|
|
117
|
+
check_tool("npm", "initializing a ReactJS project")
|
|
118
|
+
|
|
119
|
+
typer.secho(f"Initializing ReactJS project: '{name}'...", fg=typer.colors.CYAN)
|
|
120
|
+
success = generate_react_boilerplate(name)
|
|
121
|
+
|
|
122
|
+
if success:
|
|
123
|
+
typer.secho("\nReactJS project ready!", fg=typer.colors.GREEN)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@app.command("nextjs")
|
|
127
|
+
def init_nextjs(name: str):
|
|
128
|
+
"""
|
|
129
|
+
Initializes a new NextJS frontend project.
|
|
130
|
+
"""
|
|
131
|
+
check_tool("npm", "initializing a NextJS project")
|
|
132
|
+
|
|
133
|
+
typer.secho(f"Initializing NextJS project: '{name}'...", fg=typer.colors.CYAN)
|
|
134
|
+
success = generate_nextjs_boilerplate(name)
|
|
135
|
+
|
|
136
|
+
if success:
|
|
137
|
+
typer.secho("\nNextJS project ready!", fg=typer.colors.GREEN)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@app.command("fastapi")
|
|
141
|
+
def init_fastapi(name: str):
|
|
142
|
+
"""
|
|
143
|
+
Initializes a new FastAPI backend project.
|
|
144
|
+
"""
|
|
145
|
+
check_tool("python3", "initializing a FastAPI project")
|
|
146
|
+
|
|
147
|
+
typer.secho(f"Initializing FastAPI project: '{name}'...", fg=typer.colors.CYAN)
|
|
148
|
+
success = generate_fastapi_boilerplate(name)
|
|
149
|
+
|
|
150
|
+
if success:
|
|
151
|
+
typer.secho("\nFastAPI project ready!", fg=typer.colors.GREEN)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@app.command("django")
|
|
155
|
+
def init_django(name: str):
|
|
156
|
+
"""
|
|
157
|
+
Initializes a new Django backend project.
|
|
158
|
+
"""
|
|
159
|
+
check_tool("python3", "initializing a Django project")
|
|
160
|
+
|
|
161
|
+
typer.secho(f"Initializing Django project: '{name}'...", fg=typer.colors.CYAN)
|
|
162
|
+
success = generate_django_boilerplate(name)
|
|
163
|
+
|
|
164
|
+
if success:
|
|
165
|
+
typer.secho("\nDjango project ready!", fg=typer.colors.GREEN)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@app.command("svelte")
|
|
169
|
+
def init_svelte(name: str):
|
|
170
|
+
"""
|
|
171
|
+
Initializes a new SvelteKit frontend project.
|
|
172
|
+
"""
|
|
173
|
+
check_tool("npm", "initializing a Svelte project")
|
|
174
|
+
|
|
175
|
+
typer.secho(f"Initializing Svelte project: '{name}'...", fg=typer.colors.CYAN)
|
|
176
|
+
success = generate_svelte_boilerplate(name)
|
|
177
|
+
|
|
178
|
+
if success:
|
|
179
|
+
typer.secho("\nSvelte project ready!", fg=typer.colors.GREEN)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@app.command("go")
|
|
183
|
+
def init_go(name: str):
|
|
184
|
+
"""
|
|
185
|
+
Initializes a new Go/Fiber backend project.
|
|
186
|
+
"""
|
|
187
|
+
check_tool("go", "initializing a Go project")
|
|
188
|
+
|
|
189
|
+
typer.secho(f"Initializing Go project: '{name}'...", fg=typer.colors.CYAN)
|
|
190
|
+
success = generate_go_boilerplate(name)
|
|
191
|
+
|
|
192
|
+
if success:
|
|
193
|
+
typer.secho("\nGo project ready!", fg=typer.colors.GREEN)
|
devctl/commands/run.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI command group for running the local development environment.
|
|
3
|
+
Automatically detects and launches backend, frontend, and database services.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from devctl.generators.docker_scaffold import discover_docker_projects
|
|
11
|
+
from devctl.orchestrator.runner import launch_dev_environment
|
|
12
|
+
from devctl.utils.dependencies import check_tool
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(help="Local execution and development commands.")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.callback(invoke_without_command=True)
|
|
18
|
+
def run_env(ctx: typer.Context):
|
|
19
|
+
"""
|
|
20
|
+
Scans the current tree and automatically launches Spring, Angular, Vue, and Databases.
|
|
21
|
+
"""
|
|
22
|
+
if ctx.invoked_subcommand is not None:
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
typer.secho("Analyzing the current directory tree...", fg=typer.colors.CYAN)
|
|
26
|
+
|
|
27
|
+
projects = discover_docker_projects(".")
|
|
28
|
+
|
|
29
|
+
# Check for docker-compose-db.yml files
|
|
30
|
+
docker_composes = []
|
|
31
|
+
for p in Path(".").rglob("docker-compose-db.yml"):
|
|
32
|
+
if "node_modules" not in str(p) and "target" not in str(p) and ".git" not in str(p):
|
|
33
|
+
docker_composes.append(p.parent)
|
|
34
|
+
|
|
35
|
+
any(p.kind == "spring" for p in projects)
|
|
36
|
+
any(p.kind == "angular" for p in projects)
|
|
37
|
+
any(p.kind == "vue" for p in projects)
|
|
38
|
+
has_docker = len(docker_composes) > 0
|
|
39
|
+
|
|
40
|
+
# Check dependencies based on detection
|
|
41
|
+
if has_docker:
|
|
42
|
+
check_tool("docker", "running the database environment")
|
|
43
|
+
|
|
44
|
+
# Group counts
|
|
45
|
+
counts = {}
|
|
46
|
+
for p in projects:
|
|
47
|
+
counts[p.kind] = counts.get(p.kind, 0) + 1
|
|
48
|
+
|
|
49
|
+
# Visual summary of detection for the user
|
|
50
|
+
def get_status(condition: bool):
|
|
51
|
+
return (
|
|
52
|
+
typer.style("FOUND", fg=typer.colors.GREEN)
|
|
53
|
+
if condition
|
|
54
|
+
else typer.style("MISSING", fg=typer.colors.RED)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
typer.echo(f" - Docker Compose DB ({len(docker_composes)}) : {get_status(has_docker)}")
|
|
58
|
+
|
|
59
|
+
for kind in sorted(counts.keys()):
|
|
60
|
+
typer.echo(f" - {kind.capitalize()} ({counts[kind]}) : {get_status(True)}")
|
|
61
|
+
|
|
62
|
+
if not projects and not docker_composes:
|
|
63
|
+
typer.secho("\nError: No valid development environment detected here.", fg=typer.colors.RED)
|
|
64
|
+
raise typer.Exit(code=1)
|
|
65
|
+
|
|
66
|
+
# Transfer control to the system orchestration layer
|
|
67
|
+
launch_dev_environment(projects, docker_composes)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generators for Angular projects.
|
|
3
|
+
Includes boilerplate generation and environment configuration.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
import subprocess
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
from jinja2 import Environment, FileSystemLoader
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def setup_angular_environments(project_path: str):
|
|
15
|
+
"""
|
|
16
|
+
Generates environments and proxy for an Angular project.
|
|
17
|
+
"""
|
|
18
|
+
typer.secho("Configuring Proxy and Environments...", fg=typer.colors.CYAN)
|
|
19
|
+
|
|
20
|
+
# 1. Target paths
|
|
21
|
+
src_dir = os.path.join(project_path, "src")
|
|
22
|
+
env_dir = os.path.join(src_dir, "environments")
|
|
23
|
+
os.makedirs(env_dir, exist_ok=True)
|
|
24
|
+
|
|
25
|
+
# 2. Template rendering via Jinja2
|
|
26
|
+
templates_dir = os.path.join(os.path.dirname(__file__), "..", "templates", "angular", "config")
|
|
27
|
+
env = Environment(loader=FileSystemLoader(templates_dir))
|
|
28
|
+
|
|
29
|
+
files_to_generate = {
|
|
30
|
+
"proxy.conf.json.j2": os.path.join(project_path, "src", "proxy.conf.json"),
|
|
31
|
+
"environment.ts.j2": os.path.join(env_dir, "environment.ts"),
|
|
32
|
+
"environment.development.ts.j2": os.path.join(env_dir, "environment.development.ts"),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for tpl_name, target_path in files_to_generate.items():
|
|
36
|
+
try:
|
|
37
|
+
template = env.get_template(tpl_name)
|
|
38
|
+
content = template.render()
|
|
39
|
+
with open(target_path, "w", encoding="utf-8") as f:
|
|
40
|
+
f.write(content)
|
|
41
|
+
except Exception as e:
|
|
42
|
+
typer.secho(f"Warning: Failed to generate {tpl_name}: {e}", fg=typer.colors.YELLOW)
|
|
43
|
+
|
|
44
|
+
# 3. Modify angular.json to enable the proxy
|
|
45
|
+
angular_json_path = os.path.join(project_path, "angular.json")
|
|
46
|
+
if os.path.exists(angular_json_path):
|
|
47
|
+
with open(angular_json_path, "r", encoding="utf-8") as f:
|
|
48
|
+
angular_config = json.load(f)
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
# Find the default project name (usually the same name as the folder)
|
|
52
|
+
project_name = list(angular_config["projects"].keys())[0]
|
|
53
|
+
|
|
54
|
+
# Injection of proxyConfig into the "serve" architect
|
|
55
|
+
serve_target = angular_config["projects"][project_name]["architect"]["serve"]
|
|
56
|
+
|
|
57
|
+
# Ensure "options" exists
|
|
58
|
+
if "options" not in serve_target:
|
|
59
|
+
serve_target["options"] = {}
|
|
60
|
+
|
|
61
|
+
serve_target["options"]["proxyConfig"] = "src/proxy.conf.json"
|
|
62
|
+
|
|
63
|
+
with open(angular_json_path, "w", encoding="utf-8") as f:
|
|
64
|
+
json.dump(angular_config, f, indent=2)
|
|
65
|
+
|
|
66
|
+
typer.echo(" - angular.json updated with proxyConfig.")
|
|
67
|
+
except Exception as e:
|
|
68
|
+
typer.secho(
|
|
69
|
+
f"Warning: Unable to modify angular.json automatically: {e}",
|
|
70
|
+
fg=typer.colors.YELLOW,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def generate_angular_boilerplate(project_name: str) -> bool:
|
|
75
|
+
"""
|
|
76
|
+
Generates an Angular project via the native CLI (@angular/cli) and configures it.
|
|
77
|
+
"""
|
|
78
|
+
typer.secho(f"Generating Angular frontend '{project_name}'...", fg=typer.colors.CYAN)
|
|
79
|
+
|
|
80
|
+
safe_name = project_name.lower().replace("_", "-")
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
subprocess.run(["ng", "version"], capture_output=True, check=True)
|
|
84
|
+
except FileNotFoundError:
|
|
85
|
+
typer.secho(
|
|
86
|
+
"Error: Angular CLI ('ng') not found on your system.",
|
|
87
|
+
fg=typer.colors.RED,
|
|
88
|
+
)
|
|
89
|
+
return False
|
|
90
|
+
except subprocess.CalledProcessError:
|
|
91
|
+
typer.secho("Error: Angular CLI is installed but not responding.", fg=typer.colors.RED)
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
command = ["ng", "new", safe_name, "--routing=true", "--style=scss", "--skip-git=true"]
|
|
96
|
+
|
|
97
|
+
typer.secho("Downloading npm packages... (This may take 1-2 minutes)", fg=typer.colors.CYAN)
|
|
98
|
+
subprocess.run(command, check=True)
|
|
99
|
+
|
|
100
|
+
# Post-installation configuration
|
|
101
|
+
project_full_path = os.path.join(os.getcwd(), safe_name)
|
|
102
|
+
setup_angular_environments(project_full_path)
|
|
103
|
+
|
|
104
|
+
typer.secho(
|
|
105
|
+
f"Frontend '{safe_name}' successfully generated and configured!",
|
|
106
|
+
fg=typer.colors.GREEN,
|
|
107
|
+
)
|
|
108
|
+
return True
|
|
109
|
+
|
|
110
|
+
except subprocess.CalledProcessError as e:
|
|
111
|
+
typer.secho(f"Error: Angular process failed with code: {e.returncode}", fg=typer.colors.RED)
|
|
112
|
+
return False
|