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.
Files changed (92) hide show
  1. devctl/__init__.py +3 -0
  2. devctl/commands/__init__.py +3 -0
  3. devctl/commands/add.py +166 -0
  4. devctl/commands/deploy.py +61 -0
  5. devctl/commands/docker.py +65 -0
  6. devctl/commands/init.py +193 -0
  7. devctl/commands/run.py +67 -0
  8. devctl/generators/__init__.py +3 -0
  9. devctl/generators/angular.py +112 -0
  10. devctl/generators/django.py +61 -0
  11. devctl/generators/docker_scaffold.py +656 -0
  12. devctl/generators/fastapi.py +67 -0
  13. devctl/generators/go_fiber.py +61 -0
  14. devctl/generators/nestjs.py +49 -0
  15. devctl/generators/nextjs.py +53 -0
  16. devctl/generators/nodejs.py +109 -0
  17. devctl/generators/react.py +43 -0
  18. devctl/generators/scaffold_angular.py +163 -0
  19. devctl/generators/scaffold_django.py +79 -0
  20. devctl/generators/scaffold_fastapi.py +83 -0
  21. devctl/generators/scaffold_go.py +67 -0
  22. devctl/generators/scaffold_nestjs.py +52 -0
  23. devctl/generators/scaffold_nextjs.py +73 -0
  24. devctl/generators/scaffold_nodejs.py +81 -0
  25. devctl/generators/scaffold_react.py +80 -0
  26. devctl/generators/scaffold_spring.py +166 -0
  27. devctl/generators/scaffold_svelte.py +73 -0
  28. devctl/generators/scaffold_vue.py +111 -0
  29. devctl/generators/spring.py +221 -0
  30. devctl/generators/svelte.py +52 -0
  31. devctl/generators/vue.py +105 -0
  32. devctl/main.py +45 -0
  33. devctl/orchestrator/__init__.py +3 -0
  34. devctl/orchestrator/config_builder.py +64 -0
  35. devctl/orchestrator/runner.py +219 -0
  36. devctl/orchestrator/scanner.py +155 -0
  37. devctl/templates/angular/config/environment.development.ts.j2 +4 -0
  38. devctl/templates/angular/config/environment.ts.j2 +5 -0
  39. devctl/templates/angular/config/proxy.conf.json.j2 +8 -0
  40. devctl/templates/angular/feature/models/request.model.ts.j2 +5 -0
  41. devctl/templates/angular/feature/models/response.model.ts.j2 +6 -0
  42. devctl/templates/angular/feature/pages/form/form.component.html.j2 +21 -0
  43. devctl/templates/angular/feature/pages/form/form.component.scss.j2 +0 -0
  44. devctl/templates/angular/feature/pages/form/form.component.ts.j2 +63 -0
  45. devctl/templates/angular/feature/pages/list/list.component.html.j2 +28 -0
  46. devctl/templates/angular/feature/pages/list/list.component.scss.j2 +0 -0
  47. devctl/templates/angular/feature/pages/list/list.component.ts.j2 +34 -0
  48. devctl/templates/angular/feature/routes.ts.j2 +9 -0
  49. devctl/templates/angular/feature/services/service.ts.j2 +34 -0
  50. devctl/templates/docker/deploy.yml.j2 +37 -0
  51. devctl/templates/docker/django/Dockerfile.j2 +21 -0
  52. devctl/templates/docker/fastapi/Dockerfile.j2 +15 -0
  53. devctl/templates/docker/frontend/Dockerfile.j2 +31 -0
  54. devctl/templates/docker/go/Dockerfile.j2 +24 -0
  55. devctl/templates/docker/nestjs/Dockerfile.j2 +26 -0
  56. devctl/templates/docker/nextjs/Dockerfile.j2 +43 -0
  57. devctl/templates/docker/nodejs/Dockerfile.j2 +24 -0
  58. devctl/templates/docker/spring/Dockerfile.j2 +24 -0
  59. devctl/templates/docker/svelte/Dockerfile.j2 +24 -0
  60. devctl/templates/proxy.conf.json.j2 +0 -0
  61. devctl/templates/spring/Controller.java.j2 +50 -0
  62. devctl/templates/spring/Entity.java.j2 +22 -0
  63. devctl/templates/spring/Repository.java.j2 +9 -0
  64. devctl/templates/spring/Service.java.j2 +20 -0
  65. devctl/templates/spring/ServiceImpl.java.j2 +62 -0
  66. devctl/templates/spring/application.properties.j2 +19 -0
  67. devctl/templates/spring/config/ApplicationConfig.java.j2 +46 -0
  68. devctl/templates/spring/config/JwtAuthenticationFilter.java.j2 +54 -0
  69. devctl/templates/spring/config/JwtService.java.j2 +68 -0
  70. devctl/templates/spring/config/SecurityConfig.java.j2 +42 -0
  71. devctl/templates/spring/docker-compose.yml.j2 +29 -0
  72. devctl/templates/spring/dto/Request.java.j2 +20 -0
  73. devctl/templates/spring/dto/Response.java.j2 +22 -0
  74. devctl/templates/spring/mapper/Mapper.java.j2 +30 -0
  75. devctl/templates/vue/config/App.vue.j2 +35 -0
  76. devctl/templates/vue/config/main.ts.j2 +9 -0
  77. devctl/templates/vue/config/router.ts.j2 +18 -0
  78. devctl/templates/vue/config/vite.config.ts.j2 +16 -0
  79. devctl/templates/vue/feature/Form.vue.j2 +162 -0
  80. devctl/templates/vue/feature/List.vue.j2 +155 -0
  81. devctl/templates/vue/feature/models.ts.j2 +12 -0
  82. devctl/templates/vue/feature/routes.ts.j2 +19 -0
  83. devctl/templates/vue/feature/service.ts.j2 +44 -0
  84. devctl/utils/__init__.py +3 -0
  85. devctl/utils/dependencies.py +36 -0
  86. devctl/utils/env_loader.py +57 -0
  87. devctl-1.0.0.dist-info/METADATA +127 -0
  88. devctl-1.0.0.dist-info/RECORD +92 -0
  89. devctl-1.0.0.dist-info/WHEEL +5 -0
  90. devctl-1.0.0.dist-info/entry_points.txt +2 -0
  91. devctl-1.0.0.dist-info/licenses/LICENSE +21 -0
  92. devctl-1.0.0.dist-info/top_level.txt +1 -0
devctl/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """
2
+ devctl: CLI tool to orchestrate Spring Boot and Frontend projects.
3
+ """
@@ -0,0 +1,3 @@
1
+ """
2
+ CLI command modules for devctl.
3
+ """
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
+ )
@@ -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,3 @@
1
+ """
2
+ Code and asset generators for various frameworks and tools.
3
+ """
@@ -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