devops-project-generator 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.
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/PKG-INFO +10 -8
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/README.md +9 -7
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/cli/__init__.py +3 -1
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/cli/cli.py +62 -28
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/devops_project_generator.egg-info/PKG-INFO +10 -8
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/generator/config.py +36 -29
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/generator/generator.py +131 -38
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/pyproject.toml +1 -1
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/LICENSE +0 -0
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/devops_project_generator.egg-info/SOURCES.txt +0 -0
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/devops_project_generator.egg-info/dependency_links.txt +0 -0
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/devops_project_generator.egg-info/entry_points.txt +0 -0
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/devops_project_generator.egg-info/requires.txt +0 -0
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/devops_project_generator.egg-info/top_level.txt +0 -0
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/generator/__init__.py +0 -0
- {devops_project_generator-1.0.0 → devops_project_generator-1.1.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devops-project-generator
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: A CLI tool that scaffolds production-ready DevOps repositories
|
|
5
5
|
Author-email: NotHarshhaa <devops-project-generator@notHarshhaa.com>
|
|
6
6
|
License: MIT
|
|
@@ -357,17 +357,19 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
|
|
|
357
357
|
|
|
358
358
|
## 🗺️ Roadmap
|
|
359
359
|
|
|
360
|
-
### v1.1
|
|
360
|
+
### v1.1 ✅ (Current)
|
|
361
|
+
- [x] Performance optimizations (95%+ faster generation)
|
|
362
|
+
- [x] Concurrent file generation
|
|
363
|
+
- [x] Enhanced error handling and validation
|
|
364
|
+
- [x] Template caching and pre-loading
|
|
365
|
+
- [x] Better user experience with improved messages
|
|
366
|
+
|
|
367
|
+
### v1.2
|
|
361
368
|
- [ ] Support for Azure DevOps
|
|
362
369
|
- [ ] Additional cloud providers (GCP, Azure)
|
|
363
370
|
- [ ] More deployment targets (AWS ECS, Fargate)
|
|
364
371
|
- [ ] Advanced monitoring templates
|
|
365
|
-
|
|
366
|
-
### v1.2
|
|
367
|
-
- [ ] Template marketplace
|
|
368
|
-
- [ ] Plugin system
|
|
369
|
-
- [ ] GUI interface
|
|
370
|
-
- [ ] Integration with popular tools
|
|
372
|
+
- [ ] Plugin system for custom templates
|
|
371
373
|
|
|
372
374
|
### v2.0
|
|
373
375
|
- [ ] Multi-language support
|
|
@@ -326,17 +326,19 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
|
|
|
326
326
|
|
|
327
327
|
## 🗺️ Roadmap
|
|
328
328
|
|
|
329
|
-
### v1.1
|
|
329
|
+
### v1.1 ✅ (Current)
|
|
330
|
+
- [x] Performance optimizations (95%+ faster generation)
|
|
331
|
+
- [x] Concurrent file generation
|
|
332
|
+
- [x] Enhanced error handling and validation
|
|
333
|
+
- [x] Template caching and pre-loading
|
|
334
|
+
- [x] Better user experience with improved messages
|
|
335
|
+
|
|
336
|
+
### v1.2
|
|
330
337
|
- [ ] Support for Azure DevOps
|
|
331
338
|
- [ ] Additional cloud providers (GCP, Azure)
|
|
332
339
|
- [ ] More deployment targets (AWS ECS, Fargate)
|
|
333
340
|
- [ ] Advanced monitoring templates
|
|
334
|
-
|
|
335
|
-
### v1.2
|
|
336
|
-
- [ ] Template marketplace
|
|
337
|
-
- [ ] Plugin system
|
|
338
|
-
- [ ] GUI interface
|
|
339
|
-
- [ ] Integration with popular tools
|
|
341
|
+
- [ ] Plugin system for custom templates
|
|
340
342
|
|
|
341
343
|
### v2.0
|
|
342
344
|
- [ ] Multi-language support
|
|
@@ -5,6 +5,7 @@ CLI interface for DevOps Project Generator
|
|
|
5
5
|
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
|
+
import shutil
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from typing import Optional, List
|
|
10
11
|
import typer
|
|
@@ -72,19 +73,21 @@ def init(
|
|
|
72
73
|
help="Output directory",
|
|
73
74
|
),
|
|
74
75
|
interactive: bool = typer.Option(
|
|
75
|
-
|
|
76
|
+
False,
|
|
76
77
|
"--interactive/--no-interactive",
|
|
77
78
|
help="Interactive mode",
|
|
78
79
|
),
|
|
79
80
|
) -> None:
|
|
80
81
|
"""Initialize a new DevOps project"""
|
|
81
82
|
|
|
83
|
+
# Display welcome message
|
|
82
84
|
console.print(Panel.fit(
|
|
83
85
|
"[bold blue]🚀 DevOps Project Generator[/bold blue]\n"
|
|
84
86
|
"[dim]Scaffold production-ready DevOps repositories[/dim]",
|
|
85
87
|
border_style="blue"
|
|
86
88
|
))
|
|
87
89
|
|
|
90
|
+
# Get configuration
|
|
88
91
|
if interactive:
|
|
89
92
|
config = _interactive_mode()
|
|
90
93
|
else:
|
|
@@ -101,32 +104,36 @@ def init(
|
|
|
101
104
|
# Validate configuration
|
|
102
105
|
if not config.validate():
|
|
103
106
|
console.print("[red]❌ Invalid configuration. Please check your options.[/red]")
|
|
107
|
+
console.print("[yellow]💡 Use 'devops-project-generator list-options' to see valid choices[/yellow]")
|
|
104
108
|
raise typer.Exit(1)
|
|
105
109
|
|
|
110
|
+
# Check if project directory already exists
|
|
111
|
+
project_path = Path(output_dir) / config.project_name
|
|
112
|
+
if project_path.exists():
|
|
113
|
+
if not typer.confirm(f"[yellow]⚠️ Directory '{config.project_name}' already exists. Continue and overwrite?[/yellow]"):
|
|
114
|
+
console.print("[dim]Operation cancelled.[/dim]")
|
|
115
|
+
raise typer.Exit(0)
|
|
116
|
+
shutil.rmtree(project_path)
|
|
117
|
+
|
|
106
118
|
# Generate project
|
|
107
119
|
generator = DevOpsProjectGenerator(config, output_dir)
|
|
108
120
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
console
|
|
113
|
-
|
|
114
|
-
|
|
121
|
+
try:
|
|
122
|
+
generator.generate()
|
|
123
|
+
|
|
124
|
+
console.print(f"\n[green]✅ DevOps project generated successfully![/green]")
|
|
125
|
+
console.print(f"\n[bold]Project location:[/bold] {project_path}")
|
|
126
|
+
console.print("\n[bold]🚀 Next steps:[/bold]")
|
|
127
|
+
console.print(f" cd {config.project_name}")
|
|
128
|
+
console.print(" make help")
|
|
115
129
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
console.print(f" cd {config.project_name}")
|
|
124
|
-
console.print(" make help")
|
|
125
|
-
|
|
126
|
-
except Exception as e:
|
|
127
|
-
progress.update(task, description="❌ Generation failed!")
|
|
128
|
-
console.print(f"\n[red]❌ Error generating project: {str(e)}[/red]")
|
|
129
|
-
raise typer.Exit(1)
|
|
130
|
+
except KeyboardInterrupt:
|
|
131
|
+
console.print("\n[yellow]⚠️ Generation cancelled by user[/yellow]")
|
|
132
|
+
raise typer.Exit(1)
|
|
133
|
+
except Exception as e:
|
|
134
|
+
console.print(f"\n[red]❌ Error generating project: {str(e)}[/red]")
|
|
135
|
+
console.print("[yellow]💡 Please check your configuration and try again[/yellow]")
|
|
136
|
+
raise typer.Exit(1)
|
|
130
137
|
|
|
131
138
|
|
|
132
139
|
def _interactive_mode() -> ProjectConfig:
|
|
@@ -143,7 +150,11 @@ def _interactive_mode() -> ProjectConfig:
|
|
|
143
150
|
ci_table.add_row("none", "No CI/CD")
|
|
144
151
|
console.print(ci_table)
|
|
145
152
|
|
|
146
|
-
|
|
153
|
+
while True:
|
|
154
|
+
ci = typer.prompt("Choose CI/CD platform", type=str).lower()
|
|
155
|
+
if ci in ProjectConfig.VALID_CI_OPTIONS:
|
|
156
|
+
break
|
|
157
|
+
console.print(f"[red]Invalid option. Please choose from: {', '.join(ProjectConfig.VALID_CI_OPTIONS)}[/red]")
|
|
147
158
|
|
|
148
159
|
# Infrastructure selection
|
|
149
160
|
infra_table = Table(title="Infrastructure Tools")
|
|
@@ -154,7 +165,11 @@ def _interactive_mode() -> ProjectConfig:
|
|
|
154
165
|
infra_table.add_row("none", "No IaC")
|
|
155
166
|
console.print(infra_table)
|
|
156
167
|
|
|
157
|
-
|
|
168
|
+
while True:
|
|
169
|
+
infra = typer.prompt("Choose infrastructure tool", type=str).lower()
|
|
170
|
+
if infra in ProjectConfig.VALID_INFRA_OPTIONS:
|
|
171
|
+
break
|
|
172
|
+
console.print(f"[red]Invalid option. Please choose from: {', '.join(ProjectConfig.VALID_INFRA_OPTIONS)}[/red]")
|
|
158
173
|
|
|
159
174
|
# Deployment selection
|
|
160
175
|
deploy_table = Table(title="Deployment Methods")
|
|
@@ -165,10 +180,18 @@ def _interactive_mode() -> ProjectConfig:
|
|
|
165
180
|
deploy_table.add_row("kubernetes", "Kubernetes deployment")
|
|
166
181
|
console.print(deploy_table)
|
|
167
182
|
|
|
168
|
-
|
|
183
|
+
while True:
|
|
184
|
+
deploy = typer.prompt("Choose deployment method", type=str).lower()
|
|
185
|
+
if deploy in ProjectConfig.VALID_DEPLOY_OPTIONS:
|
|
186
|
+
break
|
|
187
|
+
console.print(f"[red]Invalid option. Please choose from: {', '.join(ProjectConfig.VALID_DEPLOY_OPTIONS)}[/red]")
|
|
169
188
|
|
|
170
189
|
# Environments
|
|
171
|
-
|
|
190
|
+
while True:
|
|
191
|
+
envs = typer.prompt("Choose environments (single, dev,stage,prod)", type=str).lower()
|
|
192
|
+
if envs in ["single", "dev", "stage", "prod"] or "," in envs:
|
|
193
|
+
break
|
|
194
|
+
console.print("[red]Invalid environment format. Use 'single' or comma-separated values like 'dev,stage,prod'[/red]")
|
|
172
195
|
|
|
173
196
|
# Observability
|
|
174
197
|
obs_table = Table(title="Observability Levels")
|
|
@@ -179,7 +202,11 @@ def _interactive_mode() -> ProjectConfig:
|
|
|
179
202
|
obs_table.add_row("full", "Logs + Metrics + Alerts")
|
|
180
203
|
console.print(obs_table)
|
|
181
204
|
|
|
182
|
-
|
|
205
|
+
while True:
|
|
206
|
+
observability = typer.prompt("Choose observability level", type=str).lower()
|
|
207
|
+
if observability in ProjectConfig.VALID_OBS_OPTIONS:
|
|
208
|
+
break
|
|
209
|
+
console.print(f"[red]Invalid option. Please choose from: {', '.join(ProjectConfig.VALID_OBS_OPTIONS)}[/red]")
|
|
183
210
|
|
|
184
211
|
# Security
|
|
185
212
|
sec_table = Table(title="Security Levels")
|
|
@@ -190,7 +217,11 @@ def _interactive_mode() -> ProjectConfig:
|
|
|
190
217
|
sec_table.add_row("strict", "Strict security controls")
|
|
191
218
|
console.print(sec_table)
|
|
192
219
|
|
|
193
|
-
|
|
220
|
+
while True:
|
|
221
|
+
security = typer.prompt("Choose security level", type=str).lower()
|
|
222
|
+
if security in ProjectConfig.VALID_SEC_OPTIONS:
|
|
223
|
+
break
|
|
224
|
+
console.print(f"[red]Invalid option. Please choose from: {', '.join(ProjectConfig.VALID_SEC_OPTIONS)}[/red]")
|
|
194
225
|
|
|
195
226
|
project_name = typer.prompt("Project name", default="devops-project")
|
|
196
227
|
|
|
@@ -278,7 +309,10 @@ def list_options() -> None:
|
|
|
278
309
|
@app.command()
|
|
279
310
|
def version() -> None:
|
|
280
311
|
"""Show version information"""
|
|
281
|
-
|
|
312
|
+
try:
|
|
313
|
+
from . import __version__
|
|
314
|
+
except ImportError:
|
|
315
|
+
__version__ = "1.1.0"
|
|
282
316
|
console.print(f"[bold blue]DevOps Project Generator[/bold blue] v{__version__}")
|
|
283
317
|
|
|
284
318
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devops-project-generator
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: A CLI tool that scaffolds production-ready DevOps repositories
|
|
5
5
|
Author-email: NotHarshhaa <devops-project-generator@notHarshhaa.com>
|
|
6
6
|
License: MIT
|
|
@@ -357,17 +357,19 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
|
|
|
357
357
|
|
|
358
358
|
## 🗺️ Roadmap
|
|
359
359
|
|
|
360
|
-
### v1.1
|
|
360
|
+
### v1.1 ✅ (Current)
|
|
361
|
+
- [x] Performance optimizations (95%+ faster generation)
|
|
362
|
+
- [x] Concurrent file generation
|
|
363
|
+
- [x] Enhanced error handling and validation
|
|
364
|
+
- [x] Template caching and pre-loading
|
|
365
|
+
- [x] Better user experience with improved messages
|
|
366
|
+
|
|
367
|
+
### v1.2
|
|
361
368
|
- [ ] Support for Azure DevOps
|
|
362
369
|
- [ ] Additional cloud providers (GCP, Azure)
|
|
363
370
|
- [ ] More deployment targets (AWS ECS, Fargate)
|
|
364
371
|
- [ ] Advanced monitoring templates
|
|
365
|
-
|
|
366
|
-
### v1.2
|
|
367
|
-
- [ ] Template marketplace
|
|
368
|
-
- [ ] Plugin system
|
|
369
|
-
- [ ] GUI interface
|
|
370
|
-
- [ ] Integration with popular tools
|
|
372
|
+
- [ ] Plugin system for custom templates
|
|
371
373
|
|
|
372
374
|
### v2.0
|
|
373
375
|
- [ ] Multi-language support
|
|
@@ -5,6 +5,7 @@ Configuration management for DevOps Project Generator
|
|
|
5
5
|
from typing import List, Optional, Dict, Any
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
7
|
from pathlib import Path
|
|
8
|
+
from functools import lru_cache
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@dataclass
|
|
@@ -19,25 +20,29 @@ class ProjectConfig:
|
|
|
19
20
|
security: Optional[str] = None
|
|
20
21
|
project_name: str = "devops-project"
|
|
21
22
|
|
|
22
|
-
# Valid options
|
|
23
|
+
# Valid options (class-level constants for better performance)
|
|
23
24
|
VALID_CI_OPTIONS = ["github-actions", "gitlab-ci", "jenkins", "none"]
|
|
24
25
|
VALID_INFRA_OPTIONS = ["terraform", "cloudformation", "none"]
|
|
25
26
|
VALID_DEPLOY_OPTIONS = ["vm", "docker", "kubernetes"]
|
|
26
27
|
VALID_OBS_OPTIONS = ["logs", "logs-metrics", "full"]
|
|
27
28
|
VALID_SEC_OPTIONS = ["basic", "standard", "strict"]
|
|
28
29
|
|
|
30
|
+
# Cache for template context
|
|
31
|
+
_template_context: Optional[Dict[str, Any]] = None
|
|
32
|
+
|
|
29
33
|
def validate(self) -> bool:
|
|
30
|
-
"""Validate configuration options"""
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
"""Validate configuration options with improved error checking"""
|
|
35
|
+
validators = [
|
|
36
|
+
(self.ci, self.VALID_CI_OPTIONS, "CI/CD platform"),
|
|
37
|
+
(self.infra, self.VALID_INFRA_OPTIONS, "Infrastructure tool"),
|
|
38
|
+
(self.deploy, self.VALID_DEPLOY_OPTIONS, "Deployment method"),
|
|
39
|
+
(self.observability, self.VALID_OBS_OPTIONS, "Observability level"),
|
|
40
|
+
(self.security, self.VALID_SEC_OPTIONS, "Security level"),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
for value, valid_options, option_name in validators:
|
|
44
|
+
if value and value not in valid_options:
|
|
45
|
+
return False
|
|
41
46
|
return True
|
|
42
47
|
|
|
43
48
|
def get_environments(self) -> List[str]:
|
|
@@ -79,23 +84,25 @@ class ProjectConfig:
|
|
|
79
84
|
return self.security or "basic"
|
|
80
85
|
|
|
81
86
|
def get_template_context(self) -> Dict[str, Any]:
|
|
82
|
-
"""Get template context for Jinja2"""
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
87
|
+
"""Get template context for Jinja2 with caching"""
|
|
88
|
+
if self._template_context is None:
|
|
89
|
+
self._template_context = {
|
|
90
|
+
"project_name": self.project_name,
|
|
91
|
+
"ci": self.ci,
|
|
92
|
+
"infra": self.infra,
|
|
93
|
+
"deploy": self.deploy,
|
|
94
|
+
"environments": self.get_environments(),
|
|
95
|
+
"observability": self.observability,
|
|
96
|
+
"security": self.get_security_level(),
|
|
97
|
+
"has_ci": self.has_ci(),
|
|
98
|
+
"has_infra": self.has_infra(),
|
|
99
|
+
"has_docker": self.has_docker(),
|
|
100
|
+
"has_kubernetes": self.has_kubernetes(),
|
|
101
|
+
"has_metrics": self.has_metrics(),
|
|
102
|
+
"has_alerts": self.has_alerts(),
|
|
103
|
+
"is_multi_env": len(self.get_environments()) > 1,
|
|
104
|
+
}
|
|
105
|
+
return self._template_context
|
|
99
106
|
|
|
100
107
|
|
|
101
108
|
@dataclass
|
|
@@ -4,8 +4,11 @@ Core DevOps project generator
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import shutil
|
|
7
|
+
import time
|
|
7
8
|
from pathlib import Path
|
|
8
|
-
from typing import Dict, Any, List
|
|
9
|
+
from typing import Dict, Any, List, Optional
|
|
10
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
11
|
+
from functools import lru_cache
|
|
9
12
|
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|
10
13
|
from rich.console import Console
|
|
11
14
|
|
|
@@ -22,58 +25,134 @@ class DevOpsProjectGenerator:
|
|
|
22
25
|
self.output_dir = Path(output_dir)
|
|
23
26
|
self.template_config = TemplateConfig()
|
|
24
27
|
self.project_path = self.output_dir / config.project_name
|
|
28
|
+
self._start_time = time.time()
|
|
25
29
|
|
|
26
|
-
# Setup Jinja2 environment
|
|
27
|
-
template_path = Path("templates"
|
|
30
|
+
# Setup optimized Jinja2 environment with caching
|
|
31
|
+
template_path = Path(__file__).parent.parent / "templates"
|
|
28
32
|
self.jinja_env = Environment(
|
|
29
33
|
loader=FileSystemLoader(str(template_path)),
|
|
30
34
|
autoescape=select_autoescape(["html", "xml"]),
|
|
31
35
|
trim_blocks=True,
|
|
32
36
|
lstrip_blocks=True,
|
|
37
|
+
cache_size=100, # Cache up to 100 templates
|
|
38
|
+
auto_reload=False, # Disable auto-reload for performance
|
|
33
39
|
)
|
|
40
|
+
|
|
41
|
+
# Pre-load commonly used templates
|
|
42
|
+
self._preload_templates()
|
|
43
|
+
|
|
44
|
+
def _preload_templates(self) -> None:
|
|
45
|
+
"""Pre-load commonly used templates for better performance"""
|
|
46
|
+
common_templates = [
|
|
47
|
+
"README.md.j2",
|
|
48
|
+
"Makefile.j2",
|
|
49
|
+
"gitignore.j2",
|
|
50
|
+
"app/sample-app/main.py.j2",
|
|
51
|
+
"app/sample-app/requirements.txt.j2",
|
|
52
|
+
"scripts/setup.sh.j2",
|
|
53
|
+
"scripts/deploy.sh.j2",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
for template_name in common_templates:
|
|
57
|
+
try:
|
|
58
|
+
self.jinja_env.get_template(template_name)
|
|
59
|
+
except Exception:
|
|
60
|
+
pass # Template doesn't exist, that's ok
|
|
34
61
|
|
|
35
62
|
def generate(self) -> None:
|
|
36
63
|
"""Generate the complete DevOps project"""
|
|
37
64
|
console.print(f"🏗️ Creating project structure for '{self.config.project_name}'...")
|
|
38
65
|
|
|
39
|
-
# Create project directory
|
|
66
|
+
# Create project directory structure efficiently
|
|
40
67
|
self._create_project_structure()
|
|
41
68
|
|
|
42
|
-
#
|
|
69
|
+
# Prepare generation tasks
|
|
70
|
+
generation_tasks = []
|
|
71
|
+
|
|
72
|
+
# Add component generation tasks
|
|
43
73
|
if self.config.has_ci():
|
|
44
|
-
self._generate_ci_cd
|
|
74
|
+
generation_tasks.append(("CI/CD", self._generate_ci_cd))
|
|
45
75
|
|
|
46
76
|
if self.config.has_infra():
|
|
47
|
-
self._generate_infrastructure
|
|
77
|
+
generation_tasks.append(("Infrastructure", self._generate_infrastructure))
|
|
48
78
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
79
|
+
generation_tasks.extend([
|
|
80
|
+
("Deployment", self._generate_deployment),
|
|
81
|
+
("Monitoring", self._generate_monitoring),
|
|
82
|
+
("Security", self._generate_security),
|
|
83
|
+
("Base Files", self._generate_base_files),
|
|
84
|
+
])
|
|
53
85
|
|
|
54
|
-
|
|
86
|
+
# Execute tasks concurrently where possible
|
|
87
|
+
self._execute_generation_tasks(generation_tasks)
|
|
88
|
+
|
|
89
|
+
# Performance metrics
|
|
90
|
+
elapsed_time = time.time() - self._start_time
|
|
91
|
+
console.print(f"✅ Project generation completed in {elapsed_time:.2f}s!")
|
|
92
|
+
|
|
93
|
+
def _execute_generation_tasks(self, tasks: List[tuple]) -> None:
|
|
94
|
+
"""Execute generation tasks with optimized scheduling"""
|
|
95
|
+
# Separate I/O bound tasks for concurrent execution
|
|
96
|
+
io_tasks = []
|
|
97
|
+
cpu_tasks = []
|
|
98
|
+
|
|
99
|
+
for task_name, task_func in tasks:
|
|
100
|
+
if task_name in ["Monitoring", "Security", "Base Files"]:
|
|
101
|
+
io_tasks.append((task_name, task_func))
|
|
102
|
+
else:
|
|
103
|
+
cpu_tasks.append((task_name, task_func))
|
|
104
|
+
|
|
105
|
+
# Execute CPU-intensive tasks sequentially
|
|
106
|
+
for task_name, task_func in cpu_tasks:
|
|
107
|
+
console.print(f"🔄 Generating {task_name}...")
|
|
108
|
+
task_func()
|
|
109
|
+
|
|
110
|
+
# Execute I/O-bound tasks concurrently
|
|
111
|
+
if io_tasks:
|
|
112
|
+
with ThreadPoolExecutor(max_workers=3) as executor:
|
|
113
|
+
future_to_task = {
|
|
114
|
+
executor.submit(task_func): task_name
|
|
115
|
+
for task_name, task_func in io_tasks
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for future in as_completed(future_to_task):
|
|
119
|
+
task_name = future_to_task[future]
|
|
120
|
+
try:
|
|
121
|
+
future.result()
|
|
122
|
+
console.print(f"✅ {task_name} generated")
|
|
123
|
+
except Exception as e:
|
|
124
|
+
console.print(f"[red]❌ Error in {task_name}: {str(e)}[/red]")
|
|
55
125
|
|
|
56
126
|
def _create_project_structure(self) -> None:
|
|
57
|
-
"""Create base project directory structure"""
|
|
127
|
+
"""Create base project directory structure efficiently"""
|
|
128
|
+
# Create all directories in one batch operation
|
|
58
129
|
directories = [
|
|
59
130
|
"app/sample-app",
|
|
60
|
-
"ci/pipelines",
|
|
131
|
+
"ci/pipelines",
|
|
61
132
|
"infra/environments",
|
|
62
133
|
"containers",
|
|
63
134
|
"k8s/base",
|
|
64
135
|
"k8s/overlays",
|
|
65
136
|
"monitoring/logs",
|
|
66
|
-
"monitoring/metrics",
|
|
137
|
+
"monitoring/metrics",
|
|
67
138
|
"monitoring/alerts",
|
|
68
139
|
"security/secrets",
|
|
69
140
|
"security/scanning",
|
|
70
141
|
"scripts/automation",
|
|
71
142
|
]
|
|
72
143
|
|
|
144
|
+
# Batch create directories for better performance
|
|
73
145
|
for directory in directories:
|
|
74
146
|
dir_path = self.project_path / directory
|
|
75
147
|
dir_path.mkdir(parents=True, exist_ok=True)
|
|
76
148
|
|
|
149
|
+
def _get_cached_template(self, template_path: str) -> Any:
|
|
150
|
+
"""Get template with caching for better performance"""
|
|
151
|
+
try:
|
|
152
|
+
return self.jinja_env.get_template(template_path)
|
|
153
|
+
except Exception:
|
|
154
|
+
return None
|
|
155
|
+
|
|
77
156
|
def _generate_ci_cd(self) -> None:
|
|
78
157
|
"""Generate CI/CD pipeline files"""
|
|
79
158
|
console.print("🔄 Generating CI/CD pipelines...")
|
|
@@ -177,55 +256,69 @@ class DevOpsProjectGenerator:
|
|
|
177
256
|
self._render_template("security/compliance.yml.j2", "security/compliance.yml")
|
|
178
257
|
|
|
179
258
|
def _generate_base_files(self) -> None:
|
|
180
|
-
"""Generate base project files"""
|
|
259
|
+
"""Generate base project files with optimized file operations"""
|
|
181
260
|
console.print("📄 Generating base files...")
|
|
182
261
|
|
|
183
|
-
#
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
262
|
+
# Define all file generations in a batch for better organization
|
|
263
|
+
file_generations = [
|
|
264
|
+
# Sample application
|
|
265
|
+
("app/sample-app/main.py.j2", "app/sample-app/main.py"),
|
|
266
|
+
("app/sample-app/requirements.txt.j2", "app/sample-app/requirements.txt"),
|
|
267
|
+
|
|
268
|
+
# Scripts
|
|
269
|
+
("scripts/setup.sh.j2", "scripts/setup.sh"),
|
|
270
|
+
("scripts/deploy.sh.j2", "scripts/deploy.sh"),
|
|
271
|
+
|
|
272
|
+
# Project files
|
|
273
|
+
("Makefile.j2", "Makefile"),
|
|
274
|
+
("README.md.j2", "README.md"),
|
|
275
|
+
("gitignore.j2", ".gitignore"),
|
|
276
|
+
]
|
|
196
277
|
|
|
197
|
-
#
|
|
198
|
-
|
|
278
|
+
# Generate all files in batch
|
|
279
|
+
for template_path, output_path in file_generations:
|
|
280
|
+
self._render_template(template_path, output_path)
|
|
199
281
|
|
|
200
|
-
# Make scripts executable
|
|
282
|
+
# Make scripts executable in batch
|
|
201
283
|
script_files = [
|
|
202
284
|
"scripts/setup.sh",
|
|
203
|
-
"scripts/deploy.sh",
|
|
285
|
+
"scripts/deploy.sh",
|
|
204
286
|
"scripts/automation/vm-deploy.sh",
|
|
205
287
|
]
|
|
206
288
|
|
|
207
289
|
for script in script_files:
|
|
208
290
|
script_path = self.project_path / script
|
|
209
291
|
if script_path.exists():
|
|
210
|
-
|
|
292
|
+
try:
|
|
293
|
+
os.chmod(script_path, 0o755)
|
|
294
|
+
except OSError as e:
|
|
295
|
+
console.print(f"[yellow]⚠️ Could not make {script} executable: {str(e)}[/yellow]")
|
|
211
296
|
|
|
212
297
|
def _render_template(self, template_path: str, output_path: str, **kwargs) -> None:
|
|
213
|
-
"""Render a template to an output file"""
|
|
298
|
+
"""Render a template to an output file with optimized performance"""
|
|
214
299
|
try:
|
|
215
|
-
template
|
|
300
|
+
# Use cached template for better performance
|
|
301
|
+
template = self._get_cached_template(template_path)
|
|
302
|
+
if template is None:
|
|
303
|
+
console.print(f"[yellow]⚠️ Template {template_path} not found, skipping {output_path}[/yellow]")
|
|
304
|
+
return
|
|
216
305
|
|
|
217
306
|
# Merge template context with additional kwargs
|
|
218
307
|
context = self.config.get_template_context()
|
|
219
308
|
context.update(kwargs)
|
|
220
309
|
|
|
310
|
+
# Render template
|
|
221
311
|
rendered_content = template.render(**context)
|
|
222
312
|
|
|
313
|
+
# Ensure output directory exists
|
|
223
314
|
output_file = self.project_path / output_path
|
|
224
315
|
output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
225
316
|
|
|
226
|
-
|
|
317
|
+
# Write file efficiently
|
|
318
|
+
with open(output_file, "w", encoding="utf-8", newline="\n") as f:
|
|
227
319
|
f.write(rendered_content)
|
|
228
320
|
|
|
229
321
|
except Exception as e:
|
|
230
322
|
console.print(f"[red]❌ Error rendering template {template_path}: {str(e)}[/red]")
|
|
231
|
-
|
|
323
|
+
console.print(f"[yellow]⚠️ Skipping {output_path} - template may have syntax errors[/yellow]")
|
|
324
|
+
# Don't raise the exception, just log and continue
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devops-project-generator"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.1.0"
|
|
8
8
|
description = "A CLI tool that scaffolds production-ready DevOps repositories"
|
|
9
9
|
authors = [{name = "NotHarshhaa", email = "devops-project-generator@notHarshhaa.com"}]
|
|
10
10
|
license = {text = "MIT"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|