devops-project-generator 1.2.0__tar.gz → 1.3.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.
Files changed (16) hide show
  1. {devops_project_generator-1.2.0/devops_project_generator.egg-info → devops_project_generator-1.3.0}/PKG-INFO +52 -3
  2. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/README.md +51 -2
  3. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/cli/__init__.py +1 -1
  4. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/cli/cli.py +1040 -1
  5. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0/devops_project_generator.egg-info}/PKG-INFO +52 -3
  6. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/pyproject.toml +1 -1
  7. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/LICENSE +0 -0
  8. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/devops_project_generator.egg-info/SOURCES.txt +0 -0
  9. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/devops_project_generator.egg-info/dependency_links.txt +0 -0
  10. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/devops_project_generator.egg-info/entry_points.txt +0 -0
  11. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/devops_project_generator.egg-info/requires.txt +0 -0
  12. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/devops_project_generator.egg-info/top_level.txt +0 -0
  13. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/generator/__init__.py +0 -0
  14. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/generator/config.py +0 -0
  15. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/generator/generator.py +0 -0
  16. {devops_project_generator-1.2.0 → devops_project_generator-1.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-project-generator
3
- Version: 1.2.0
3
+ Version: 1.3.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
@@ -167,6 +167,48 @@ devops-project-generator cleanup my-project --force
167
167
  devops-project-generator cleanup my-project --keep-config
168
168
  ```
169
169
 
170
+ ### Template Management (NEW v1.3.0)
171
+
172
+ ```bash
173
+ # List available templates
174
+ devops-project-generator template list
175
+
176
+ # Create custom template
177
+ devops-project-generator template create --name my-template
178
+
179
+ # Customize existing template
180
+ devops-project-generator template customize --name my-template
181
+
182
+ # Export templates
183
+ devops-project-generator template export --output ./templates
184
+ ```
185
+
186
+ ### Project Backup (NEW v1.3.0)
187
+
188
+ ```bash
189
+ # Create backup
190
+ devops-project-generator backup create my-project
191
+
192
+ # List available backups
193
+ devops-project-generator backup list
194
+
195
+ # Restore from backup
196
+ devops-project-generator backup restore my-project --file backup.tar.gz
197
+ ```
198
+
199
+ ### Health Check (NEW v1.3.0)
200
+
201
+ ```bash
202
+ # Basic health check
203
+ devops-project-generator health my-project
204
+
205
+ # Detailed health analysis
206
+ devops-project-generator health my-project --detailed
207
+
208
+ # Auto-fix health issues
209
+ devops-project-generator health my-project --fix
210
+ ```
211
+
170
212
  ## 🏗️ Generated Project Structure (Example)
171
213
 
172
214
  ```
@@ -410,7 +452,7 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
410
452
  - [x] Template caching and pre-loading
411
453
  - [x] Better user experience with improved messages
412
454
 
413
- ### v1.2 ✅ (Current)
455
+ ### v1.2 ✅
414
456
  - [x] Project validation and structure checking
415
457
  - [x] Configuration file management system
416
458
  - [x] Project cleanup and teardown utilities
@@ -418,7 +460,14 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
418
460
  - [x] DevOps maturity scoring
419
461
  - [x] Intelligent recommendations system
420
462
 
421
- ### v1.3
463
+ ### v1.3 ✅ (Current)
464
+ - [x] Template management and customization system
465
+ - [x] Project backup and restore functionality
466
+ - [x] Comprehensive health monitoring and scoring
467
+ - [x] Auto-fix capabilities for common issues
468
+ - [x] Advanced project analysis and recommendations
469
+
470
+ ### v1.4
422
471
  - [ ] Support for Azure DevOps
423
472
  - [ ] Additional cloud providers (GCP, Azure)
424
473
  - [ ] More deployment targets (AWS ECS, Fargate)
@@ -136,6 +136,48 @@ devops-project-generator cleanup my-project --force
136
136
  devops-project-generator cleanup my-project --keep-config
137
137
  ```
138
138
 
139
+ ### Template Management (NEW v1.3.0)
140
+
141
+ ```bash
142
+ # List available templates
143
+ devops-project-generator template list
144
+
145
+ # Create custom template
146
+ devops-project-generator template create --name my-template
147
+
148
+ # Customize existing template
149
+ devops-project-generator template customize --name my-template
150
+
151
+ # Export templates
152
+ devops-project-generator template export --output ./templates
153
+ ```
154
+
155
+ ### Project Backup (NEW v1.3.0)
156
+
157
+ ```bash
158
+ # Create backup
159
+ devops-project-generator backup create my-project
160
+
161
+ # List available backups
162
+ devops-project-generator backup list
163
+
164
+ # Restore from backup
165
+ devops-project-generator backup restore my-project --file backup.tar.gz
166
+ ```
167
+
168
+ ### Health Check (NEW v1.3.0)
169
+
170
+ ```bash
171
+ # Basic health check
172
+ devops-project-generator health my-project
173
+
174
+ # Detailed health analysis
175
+ devops-project-generator health my-project --detailed
176
+
177
+ # Auto-fix health issues
178
+ devops-project-generator health my-project --fix
179
+ ```
180
+
139
181
  ## 🏗️ Generated Project Structure (Example)
140
182
 
141
183
  ```
@@ -379,7 +421,7 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
379
421
  - [x] Template caching and pre-loading
380
422
  - [x] Better user experience with improved messages
381
423
 
382
- ### v1.2 ✅ (Current)
424
+ ### v1.2 ✅
383
425
  - [x] Project validation and structure checking
384
426
  - [x] Configuration file management system
385
427
  - [x] Project cleanup and teardown utilities
@@ -387,7 +429,14 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
387
429
  - [x] DevOps maturity scoring
388
430
  - [x] Intelligent recommendations system
389
431
 
390
- ### v1.3
432
+ ### v1.3 ✅ (Current)
433
+ - [x] Template management and customization system
434
+ - [x] Project backup and restore functionality
435
+ - [x] Comprehensive health monitoring and scoring
436
+ - [x] Auto-fix capabilities for common issues
437
+ - [x] Advanced project analysis and recommendations
438
+
439
+ ### v1.4
391
440
  - [ ] Support for Azure DevOps
392
441
  - [ ] Additional cloud providers (GCP, Azure)
393
442
  - [ ] More deployment targets (AWS ECS, Fargate)
@@ -2,7 +2,7 @@
2
2
  CLI module for DevOps Project Generator
3
3
  """
4
4
 
5
- __version__ = "1.2.0"
5
+ __version__ = "1.3.0"
6
6
 
7
7
  from .cli import app
8
8
 
@@ -6,6 +6,9 @@ CLI interface for DevOps Project Generator
6
6
  import os
7
7
  import sys
8
8
  import shutil
9
+ import json
10
+ import yaml
11
+ import datetime
9
12
  from pathlib import Path
10
13
  from typing import Optional, List
11
14
  import typer
@@ -1093,13 +1096,1049 @@ def _display_project_info(stats: dict, detailed: bool) -> None:
1093
1096
  console.print(f" {file_path} ({mod_time.strftime('%Y-%m-%d %H:%M')})")
1094
1097
 
1095
1098
 
1099
+ @app.command()
1100
+ def template(
1101
+ action: str = typer.Argument(
1102
+ "list",
1103
+ help="Action: list, create, customize, or export"
1104
+ ),
1105
+ template_name: Optional[str] = typer.Option(
1106
+ None,
1107
+ "--name",
1108
+ help="Template name for create/customize actions"
1109
+ ),
1110
+ output_dir: Optional[str] = typer.Option(
1111
+ None,
1112
+ "--output",
1113
+ help="Output directory for exported templates"
1114
+ ),
1115
+ ) -> None:
1116
+ """Manage and customize project templates"""
1117
+ if action == "list":
1118
+ _list_available_templates()
1119
+ elif action == "create":
1120
+ if not template_name:
1121
+ console.print("[red]❌ Template name required for create action[/red]")
1122
+ console.print("[yellow]Usage: devops-project-generator template create --name <template-name>[/yellow]")
1123
+ raise typer.Exit(1)
1124
+ _create_custom_template(template_name)
1125
+ elif action == "customize":
1126
+ if not template_name:
1127
+ console.print("[red]❌ Template name required for customize action[/red]")
1128
+ console.print("[yellow]Usage: devops-project-generator template customize --name <template-name>[/yellow]")
1129
+ raise typer.Exit(1)
1130
+ _customize_template(template_name)
1131
+ elif action == "export":
1132
+ if not output_dir:
1133
+ console.print("[red]❌ Output directory required for export action[/red]")
1134
+ console.print("[yellow]Usage: devops-project-generator template export --output <directory>[/yellow]")
1135
+ raise typer.Exit(1)
1136
+ _export_templates(output_dir)
1137
+ else:
1138
+ console.print(f"[red]❌ Unknown action: {action}[/red]")
1139
+ console.print("[yellow]Available actions: list, create, customize, export[/yellow]")
1140
+ raise typer.Exit(1)
1141
+
1142
+
1143
+ def _list_available_templates() -> None:
1144
+ """List all available templates"""
1145
+ console.print(Panel.fit(
1146
+ "[bold blue]📋 Available Templates[/bold blue]\n"
1147
+ "[dim]Built-in templates for different DevOps configurations[/dim]",
1148
+ border_style="blue"
1149
+ ))
1150
+
1151
+ template_categories = {
1152
+ "CI/CD": {
1153
+ "github-actions": "GitHub Actions workflows with multi-stage pipelines",
1154
+ "gitlab-ci": "GitLab CI/CD with auto-deployment",
1155
+ "jenkins": "Jenkins pipelines with Docker integration",
1156
+ "azure-pipelines": "Azure DevOps pipelines with YAML"
1157
+ },
1158
+ "Infrastructure": {
1159
+ "terraform": "Terraform modules for multi-cloud deployment",
1160
+ "cloudformation": "AWS CloudFormation templates",
1161
+ " pulumi": "Pulumi infrastructure as code",
1162
+ "ansible": "Ansible playbooks for configuration management"
1163
+ },
1164
+ "Deployment": {
1165
+ "kubernetes": "K8s manifests with Helm charts",
1166
+ "docker": "Docker containers with compose files",
1167
+ "serverless": "AWS Lambda and serverless functions",
1168
+ "static": "Static site deployment with CDN"
1169
+ },
1170
+ "Monitoring": {
1171
+ "prometheus": "Prometheus + Grafana monitoring stack",
1172
+ "datadog": "Datadog APM and monitoring",
1173
+ "elasticsearch": "ELK stack for logging and analytics",
1174
+ "cloudwatch": "AWS CloudWatch monitoring"
1175
+ },
1176
+ "Security": {
1177
+ "owasp": "OWASP security scanning and policies",
1178
+ "vault": "HashiCorp Vault secrets management",
1179
+ "cert-manager": "SSL certificate automation",
1180
+ "istio": "Service mesh security policies"
1181
+ }
1182
+ }
1183
+
1184
+ for category, templates in template_categories.items():
1185
+ console.print(f"\n[bold]{category}:[/bold]")
1186
+ table = Table()
1187
+ table.add_column("Template", style="cyan")
1188
+ table.add_column("Description")
1189
+
1190
+ for name, description in templates.items():
1191
+ table.add_row(name, description)
1192
+
1193
+ console.print(table)
1194
+
1195
+
1196
+ def _create_custom_template(template_name: str) -> None:
1197
+ """Create a new custom template"""
1198
+ console.print(f"[blue]📝 Creating custom template: {template_name}[/blue]")
1199
+
1200
+ template_dir = Path.home() / ".devops-generator" / "templates" / template_name
1201
+ template_dir.mkdir(parents=True, exist_ok=True)
1202
+
1203
+ # Create template structure
1204
+ subdirs = ["ci", "infra", "deploy", "monitoring", "security", "app"]
1205
+ for subdir in subdirs:
1206
+ (template_dir / subdir).mkdir(exist_ok=True)
1207
+
1208
+ # Create template metadata
1209
+ metadata = {
1210
+ "name": template_name,
1211
+ "version": "1.0.0",
1212
+ "description": f"Custom template: {template_name}",
1213
+ "author": "Custom User",
1214
+ "created": datetime.datetime.now().isoformat(),
1215
+ "components": {
1216
+ "ci": ["github-actions"],
1217
+ "infra": ["terraform"],
1218
+ "deploy": ["kubernetes"],
1219
+ "monitoring": ["prometheus"],
1220
+ "security": ["owasp"]
1221
+ },
1222
+ "variables": {
1223
+ "project_name": "string",
1224
+ "environment": "string",
1225
+ "cloud_provider": "string",
1226
+ "docker_registry": "string"
1227
+ }
1228
+ }
1229
+
1230
+ metadata_file = template_dir / "template.yaml"
1231
+ with open(metadata_file, "w", encoding="utf-8") as f:
1232
+ yaml.dump(metadata, f, default_flow_style=False)
1233
+
1234
+ # Create sample files
1235
+ sample_files = {
1236
+ "ci/github-actions.yml.j2": """# GitHub Actions Workflow for {{ project_name }}
1237
+ name: {{ environment }}-pipeline
1238
+
1239
+ on:
1240
+ push:
1241
+ branches: [ main, develop ]
1242
+ pull_request:
1243
+ branches: [ main ]
1244
+
1245
+ jobs:
1246
+ test:
1247
+ runs-on: ubuntu-latest
1248
+ steps:
1249
+ - uses: actions/checkout@v3
1250
+ - name: Run tests
1251
+ run: echo "Running tests for {{ project_name }}"
1252
+
1253
+ deploy:
1254
+ needs: test
1255
+ runs-on: ubuntu-latest
1256
+ if: github.ref == 'refs/heads/main'
1257
+ steps:
1258
+ - name: Deploy to {{ environment }}
1259
+ run: echo "Deploying to {{ environment }}"
1260
+
1261
+ """,
1262
+ "infra/main.tf.j2": """# Terraform configuration for {{ project_name }}
1263
+ provider "{{ cloud_provider }}" {
1264
+ region = var.region
1265
+ }
1266
+
1267
+ variable "project_name" {
1268
+ description = "Project name"
1269
+ default = "{{ project_name }}"
1270
+ }
1271
+
1272
+ variable "environment" {
1273
+ description = "Environment"
1274
+ default = "{{ environment }}"
1275
+ }
1276
+
1277
+ # Add your infrastructure resources here
1278
+ """,
1279
+ "deploy/k8s-deployment.yaml.j2": """# Kubernetes deployment for {{ project_name }}
1280
+ apiVersion: apps/v1
1281
+ kind: Deployment
1282
+ metadata:
1283
+ name: {{ project_name }}
1284
+ namespace: {{ environment }}
1285
+ spec:
1286
+ replicas: 3
1287
+ selector:
1288
+ matchLabels:
1289
+ app: {{ project_name }}
1290
+ template:
1291
+ metadata:
1292
+ labels:
1293
+ app: {{ project_name }}
1294
+ spec:
1295
+ containers:
1296
+ - name: {{ project_name }}
1297
+ image: {{ docker_registry }}/{{ project_name }}:latest
1298
+ ports:
1299
+ - containerPort: 8080
1300
+ """
1301
+ }
1302
+
1303
+ for file_path, content in sample_files.items():
1304
+ full_path = template_dir / file_path
1305
+ with open(full_path, "w", encoding="utf-8") as f:
1306
+ f.write(content)
1307
+
1308
+ console.print(f"[green]✅ Custom template created: {template_dir}[/green]")
1309
+ console.print("[yellow]💡 Edit the template files to customize your project structure[/yellow]")
1310
+
1311
+
1312
+ def _customize_template(template_name: str) -> None:
1313
+ """Customize an existing template"""
1314
+ template_dir = Path.home() / ".devops-generator" / "templates" / template_name
1315
+
1316
+ if not template_dir.exists():
1317
+ console.print(f"[red]❌ Template '{template_name}' not found[/red]")
1318
+ console.print(f"[yellow]Available templates in: {template_dir.parent}[/yellow]")
1319
+ raise typer.Exit(1)
1320
+
1321
+ console.print(f"[blue]🔧 Customizing template: {template_name}[/blue]")
1322
+
1323
+ # Show template structure
1324
+ console.print(f"\n[dim]Template structure:[/dim]")
1325
+ for item in template_dir.rglob("*"):
1326
+ if item.is_file():
1327
+ relative_path = item.relative_to(template_dir)
1328
+ console.print(f" 📄 {relative_path}")
1329
+
1330
+ console.print(f"\n[yellow]💡 Edit files in: {template_dir}[/yellow]")
1331
+ console.print("[dim]Use .j2 extension for Jinja2 templates[/dim]")
1332
+
1333
+
1334
+ def _export_templates(output_dir: str) -> None:
1335
+ """Export built-in templates to a directory"""
1336
+ output_path = Path(output_dir)
1337
+ output_path.mkdir(parents=True, exist_ok=True)
1338
+
1339
+ console.print(f"[blue]📤 Exporting templates to: {output_path}[/blue]")
1340
+
1341
+ # Get the built-in templates directory
1342
+ builtin_templates = Path(__file__).parent.parent / "templates"
1343
+
1344
+ if builtin_templates.exists():
1345
+ import shutil
1346
+ export_dir = output_path / "builtin-templates"
1347
+ shutil.copytree(builtin_templates, export_dir, dirs_exist_ok=True)
1348
+ console.print(f"[green]✅ Built-in templates exported to: {export_dir}[/green]")
1349
+ else:
1350
+ console.print("[yellow]⚠️ Built-in templates directory not found[/yellow]")
1351
+
1352
+ # Export custom templates if they exist
1353
+ custom_templates_dir = Path.home() / ".devops-generator" / "templates"
1354
+ if custom_templates_dir.exists():
1355
+ custom_export_dir = output_path / "custom-templates"
1356
+ shutil.copytree(custom_templates_dir, custom_export_dir, dirs_exist_ok=True)
1357
+ console.print(f"[green]✅ Custom templates exported to: {custom_export_dir}[/green]")
1358
+
1359
+
1360
+ @app.command()
1361
+ def backup(
1362
+ action: str = typer.Argument(
1363
+ "create",
1364
+ help="Action: create, restore, or list"
1365
+ ),
1366
+ project_path: str = typer.Argument(
1367
+ ".",
1368
+ help="Path to the DevOps project"
1369
+ ),
1370
+ backup_file: Optional[str] = typer.Option(
1371
+ None,
1372
+ "--file",
1373
+ help="Backup file path for restore action"
1374
+ ),
1375
+ include_config: bool = typer.Option(
1376
+ True,
1377
+ "--include-config/--no-config",
1378
+ help="Include configuration files in backup"
1379
+ ),
1380
+ compress: bool = typer.Option(
1381
+ True,
1382
+ "--compress/--no-compress",
1383
+ help="Compress backup file"
1384
+ ),
1385
+ ) -> None:
1386
+ """Create and restore project backups"""
1387
+ project_path = Path(project_path).resolve()
1388
+
1389
+ if action == "create":
1390
+ _create_backup(project_path, include_config, compress)
1391
+ elif action == "restore":
1392
+ if not backup_file:
1393
+ console.print("[red]❌ Backup file required for restore action[/red]")
1394
+ console.print("[yellow]Usage: devops-project-generator backup restore --file <backup-file>[/yellow]")
1395
+ raise typer.Exit(1)
1396
+ _restore_backup(project_path, backup_file)
1397
+ elif action == "list":
1398
+ _list_backups()
1399
+ else:
1400
+ console.print(f"[red]❌ Unknown action: {action}[/red]")
1401
+ console.print("[yellow]Available actions: create, restore, list[/yellow]")
1402
+ raise typer.Exit(1)
1403
+
1404
+
1405
+ def _create_backup(project_path: Path, include_config: bool, compress: bool) -> None:
1406
+ """Create a backup of the project"""
1407
+ if not project_path.exists():
1408
+ console.print(f"[red]❌ Project path '{project_path}' does not exist[/red]")
1409
+ raise typer.Exit(1)
1410
+
1411
+ console.print(Panel.fit(
1412
+ "[bold blue]💾 Creating Project Backup[/bold blue]\n"
1413
+ "[dim]Archive project files and configuration[/dim]",
1414
+ border_style="blue"
1415
+ ))
1416
+
1417
+ # Generate backup filename
1418
+ timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
1419
+ backup_name = f"{project_path.name}_backup_{timestamp}"
1420
+
1421
+ if compress:
1422
+ backup_file = project_path.parent / f"{backup_name}.tar.gz"
1423
+ else:
1424
+ backup_file = project_path.parent / f"{backup_name}.tar"
1425
+
1426
+ console.print(f"[blue]📦 Creating backup: {backup_file.name}[/blue]")
1427
+
1428
+ try:
1429
+ import tarfile
1430
+
1431
+ with tarfile.open(backup_file, "w:gz" if compress else "w") as tar:
1432
+ # Add project files
1433
+ for item in project_path.rglob("*"):
1434
+ if item.is_file():
1435
+ # Skip certain files if not including config
1436
+ if not include_config and any(pattern in item.name for pattern in [".env", "secret", "key"]):
1437
+ continue
1438
+
1439
+ arcname = str(item.relative_to(project_path.parent))
1440
+ tar.add(item, arcname=arcname)
1441
+
1442
+ # Create backup metadata
1443
+ backup_info = {
1444
+ "project_name": project_path.name,
1445
+ "created": datetime.datetime.now().isoformat(),
1446
+ "size_bytes": backup_file.stat().st_size,
1447
+ "include_config": include_config,
1448
+ "compressed": compress,
1449
+ "file_count": len(list(project_path.rglob("*"))),
1450
+ "version": "1.2.0"
1451
+ }
1452
+
1453
+ metadata_file = backup_file.with_suffix(".json")
1454
+ with open(metadata_file, "w", encoding="utf-8") as f:
1455
+ json.dump(backup_info, f, indent=2)
1456
+
1457
+ size_mb = backup_file.stat().st_size / (1024 * 1024)
1458
+ console.print(f"[green]✅ Backup created successfully![/green]")
1459
+ console.print(f" File: {backup_file.name}")
1460
+ console.print(f" Size: {size_mb:.2f} MB")
1461
+ console.print(f" Files: {backup_info['file_count']}")
1462
+
1463
+ except Exception as e:
1464
+ console.print(f"[red]❌ Backup failed: {str(e)}[/red]")
1465
+ raise typer.Exit(1)
1466
+
1467
+
1468
+ def _restore_backup(project_path: Path, backup_file: str) -> None:
1469
+ """Restore a project from backup"""
1470
+ backup_path = Path(backup_file)
1471
+
1472
+ if not backup_path.exists():
1473
+ console.print(f"[red]❌ Backup file '{backup_file}' does not exist[/red]")
1474
+ raise typer.Exit(1)
1475
+
1476
+ # Check for metadata file
1477
+ metadata_file = backup_path.with_suffix(".json")
1478
+ if metadata_file.exists():
1479
+ with open(metadata_file, "r", encoding="utf-8") as f:
1480
+ backup_info = json.load(f)
1481
+
1482
+ console.print(Panel.fit(
1483
+ f"[bold blue]🔄 Restoring Project Backup[/bold blue]\n"
1484
+ f"[dim]Project: {backup_info.get('project_name', 'Unknown')}[/dim]\n"
1485
+ f"[dim]Created: {backup_info.get('created', 'Unknown')}[/dim]",
1486
+ border_style="blue"
1487
+ ))
1488
+ else:
1489
+ console.print(Panel.fit(
1490
+ "[bold blue]🔄 Restoring Project Backup[/bold blue]\n"
1491
+ "[dim]Backup information not available[/dim]",
1492
+ border_style="blue"
1493
+ ))
1494
+
1495
+ # Check if project directory already exists
1496
+ if project_path.exists():
1497
+ console.print(f"[yellow]⚠️ Project directory '{project_path.name}' already exists[/yellow]")
1498
+ if not typer.confirm("Continue and overwrite?"):
1499
+ console.print("[dim]Operation cancelled.[/dim]")
1500
+ raise typer.Exit(0)
1501
+
1502
+ # Remove existing directory
1503
+ shutil.rmtree(project_path)
1504
+
1505
+ console.print(f"[blue]📦 Restoring from: {backup_path.name}[/blue]")
1506
+
1507
+ try:
1508
+ import tarfile
1509
+
1510
+ with tarfile.open(backup_path, "r:*") as tar:
1511
+ tar.extractall(project_path.parent)
1512
+
1513
+ console.print(f"[green]✅ Project restored successfully![/green]")
1514
+ console.print(f" Location: {project_path}")
1515
+ console.print(f"[yellow]💡 Run 'devops-project-generator validate {project_path.name}' to check the project[/yellow]")
1516
+
1517
+ except Exception as e:
1518
+ console.print(f"[red]❌ Restore failed: {str(e)}[/red]")
1519
+ raise typer.Exit(1)
1520
+
1521
+
1522
+ def _list_backups() -> None:
1523
+ """List all available backups"""
1524
+ console.print(Panel.fit(
1525
+ "[bold blue]📋 Available Backups[/bold blue]\n"
1526
+ "[dim]Project backups in current directory[/dim]",
1527
+ border_style="blue"
1528
+ ))
1529
+
1530
+ current_dir = Path.cwd()
1531
+ backup_files = []
1532
+
1533
+ # Find backup files
1534
+ for item in current_dir.glob("*backup*.tar*"):
1535
+ if item.is_file():
1536
+ backup_files.append(item)
1537
+
1538
+ if not backup_files:
1539
+ console.print("[yellow]No backup files found in current directory[/yellow]")
1540
+ return
1541
+
1542
+ console.print()
1543
+ table = Table()
1544
+ table.add_column("Backup File", style="cyan")
1545
+ table.add_column("Size", style="green")
1546
+ table.add_column("Created", style="blue")
1547
+ table.add_column("Project", style="yellow")
1548
+
1549
+ for backup_file in sorted(backup_files, key=lambda x: x.stat().st_mtime, reverse=True):
1550
+ size_mb = backup_file.stat().st_size / (1024 * 1024)
1551
+ mtime = datetime.datetime.fromtimestamp(backup_file.stat().st_mtime)
1552
+
1553
+ # Try to get project name from filename
1554
+ project_name = backup_file.name.split("_backup_")[0] if "_backup_" in backup_file.name else "Unknown"
1555
+
1556
+ # Check for metadata
1557
+ metadata_file = backup_file.with_suffix(".json")
1558
+ created = mtime.strftime("%Y-%m-%d %H:%M")
1559
+
1560
+ if metadata_file.exists():
1561
+ try:
1562
+ with open(metadata_file, "r", encoding="utf-8") as f:
1563
+ metadata = json.load(f)
1564
+ project_name = metadata.get("project_name", project_name)
1565
+ created = metadata.get("created", created)
1566
+ if isinstance(created, str):
1567
+ created = created[:16] # Just date and time
1568
+ except:
1569
+ pass
1570
+
1571
+ table.add_row(
1572
+ backup_file.name,
1573
+ f"{size_mb:.1f} MB",
1574
+ created,
1575
+ project_name
1576
+ )
1577
+
1578
+ console.print(table)
1579
+
1580
+
1581
+ @app.command()
1582
+ def health(
1583
+ project_path: str = typer.Argument(
1584
+ ".",
1585
+ help="Path to the DevOps project to check"
1586
+ ),
1587
+ detailed: bool = typer.Option(
1588
+ False,
1589
+ "--detailed",
1590
+ help="Show detailed health analysis"
1591
+ ),
1592
+ fix: bool = typer.Option(
1593
+ False,
1594
+ "--fix",
1595
+ help="Attempt to fix health issues automatically"
1596
+ ),
1597
+ ) -> None:
1598
+ """Perform comprehensive health check on DevOps project"""
1599
+ project_path = Path(project_path).resolve()
1600
+
1601
+ if not project_path.exists():
1602
+ console.print(f"[red]❌ Project path '{project_path}' does not exist[/red]")
1603
+ raise typer.Exit(1)
1604
+
1605
+ console.print(Panel.fit(
1606
+ "[bold blue]🏥 Project Health Check[/bold blue]\n"
1607
+ "[dim]Comprehensive analysis of project health and best practices[/dim]",
1608
+ border_style="blue"
1609
+ ))
1610
+
1611
+ # Perform health check
1612
+ health_report = _perform_health_check(project_path, detailed)
1613
+
1614
+ # Display results
1615
+ _display_health_report(health_report, detailed)
1616
+
1617
+ # Auto-fix if requested
1618
+ if fix and health_report["fixable_issues"]:
1619
+ console.print("\n[yellow]🔧 Attempting to fix health issues...[/yellow]")
1620
+ _fix_health_issues(project_path, health_report["fixable_issues"])
1621
+
1622
+ # Overall health score
1623
+ _display_health_score(health_report)
1624
+
1625
+
1626
+ def _perform_health_check(project_path: Path, detailed: bool) -> dict:
1627
+ """Perform comprehensive health check"""
1628
+ report = {
1629
+ "overall_score": 0,
1630
+ "categories": {
1631
+ "structure": {"score": 0, "issues": [], "fixable_issues": [], "checks_passed": []},
1632
+ "security": {"score": 0, "issues": [], "fixable_issues": [], "checks_passed": []},
1633
+ "performance": {"score": 0, "issues": [], "fixable_issues": [], "checks_passed": []},
1634
+ "maintenance": {"score": 0, "issues": [], "fixable_issues": [], "checks_passed": []},
1635
+ "documentation": {"score": 0, "issues": [], "fixable_issues": [], "checks_passed": []}
1636
+ },
1637
+ "recommendations": [],
1638
+ "critical_issues": [],
1639
+ "fixable_issues": []
1640
+ }
1641
+
1642
+ # Structure health
1643
+ _check_structure_health(project_path, report["categories"]["structure"])
1644
+
1645
+ # Security health
1646
+ _check_security_health(project_path, report["categories"]["security"])
1647
+
1648
+ # Performance health
1649
+ _check_performance_health(project_path, report["categories"]["performance"])
1650
+
1651
+ # Maintenance health
1652
+ _check_maintenance_health(project_path, report["categories"]["maintenance"])
1653
+
1654
+ # Documentation health
1655
+ _check_documentation_health(project_path, report["categories"]["documentation"])
1656
+
1657
+ # Calculate overall score
1658
+ total_score = sum(cat["score"] for cat in report["categories"].values())
1659
+ report["overall_score"] = total_score // len(report["categories"])
1660
+
1661
+ # Collect all issues
1662
+ for category in report["categories"].values():
1663
+ report["critical_issues"].extend([issue for issue in category["issues"] if "critical" in issue.lower()])
1664
+ report["fixable_issues"].extend(category["fixable_issues"])
1665
+
1666
+ # Generate recommendations
1667
+ report["recommendations"] = _generate_health_recommendations(report)
1668
+
1669
+ return report
1670
+
1671
+
1672
+ def _check_structure_health(project_path: Path, structure: dict) -> None:
1673
+ """Check project structure health"""
1674
+ checks = {
1675
+ "required_dirs": ["app", "ci", "infra", "deploy", "monitoring", "security"],
1676
+ "required_files": ["README.md", "Makefile", ".gitignore"],
1677
+ "recommended_dirs": ["scripts", "docs", "tests"],
1678
+ "recommended_files": ["Dockerfile", "docker-compose.yml"]
1679
+ }
1680
+
1681
+ score = 100
1682
+
1683
+ # Check required directories
1684
+ for dir_name in checks["required_dirs"]:
1685
+ dir_path = project_path / dir_name
1686
+ if dir_path.exists():
1687
+ structure["checks_passed"].append(f"✅ {dir_name}/ directory exists")
1688
+ else:
1689
+ structure["issues"].append(f"❌ Missing required {dir_name}/ directory")
1690
+ structure["fixable_issues"].append(("create_dir", dir_name))
1691
+ score -= 15
1692
+
1693
+ # Check required files
1694
+ for file_name in checks["required_files"]:
1695
+ file_path = project_path / file_name
1696
+ if file_path.exists():
1697
+ structure["checks_passed"].append(f"✅ {file_name} exists")
1698
+ else:
1699
+ structure["issues"].append(f"❌ Missing required {file_name}")
1700
+ if file_name == "README.md":
1701
+ structure["fixable_issues"].append(("create_readme", None))
1702
+ elif file_name == "Makefile":
1703
+ structure["fixable_issues"].append(("create_makefile", None))
1704
+ elif file_name == ".gitignore":
1705
+ structure["fixable_issues"].append(("create_gitignore", None))
1706
+ score -= 10
1707
+
1708
+ # Check recommended items
1709
+ for dir_name in checks["recommended_dirs"]:
1710
+ dir_path = project_path / dir_name
1711
+ if dir_path.exists():
1712
+ structure["checks_passed"].append(f"✅ {dir_name}/ directory exists")
1713
+ else:
1714
+ structure["issues"].append(f"⚠️ Consider adding {dir_name}/ directory")
1715
+ score -= 5
1716
+
1717
+ structure["score"] = max(0, score)
1718
+
1719
+
1720
+ def _check_security_health(project_path: Path, security: dict) -> None:
1721
+ """Check security health"""
1722
+ score = 100
1723
+
1724
+ # Check for secrets
1725
+ secret_patterns = [".env", "secret", "key", "password", "token"]
1726
+ for item in project_path.rglob("*"):
1727
+ if item.is_file() and any(pattern in item.name.lower() for pattern in secret_patterns):
1728
+ if item.suffix in [".txt", ".yml", ".yaml", ".json", ".env"]:
1729
+ security["issues"].append(f"🔒 Potential secret file: {item.relative_to(project_path)}")
1730
+ score -= 20
1731
+
1732
+ # Check for security directories
1733
+ security_dir = project_path / "security"
1734
+ if security_dir.exists():
1735
+ security["checks_passed"].append("✅ Security directory exists")
1736
+ security_files = list(security_dir.rglob("*.yml")) + list(security_dir.rglob("*.yaml"))
1737
+ if security_files:
1738
+ security["checks_passed"].append(f"✅ Found {len(security_files)} security configuration files")
1739
+ else:
1740
+ security["issues"].append("⚠️ Security directory exists but no configuration files")
1741
+ score -= 10
1742
+ else:
1743
+ security["issues"].append("❌ No security configuration found")
1744
+ score -= 25
1745
+
1746
+ # Check .gitignore for sensitive files
1747
+ gitignore_path = project_path / ".gitignore"
1748
+ if gitignore_path.exists():
1749
+ with open(gitignore_path, "r", encoding="utf-8") as f:
1750
+ gitignore_content = f.read().lower()
1751
+
1752
+ ignored_patterns = [".env", "secret", "key", "*.pem", "*.p12"]
1753
+ ignored_count = sum(1 for pattern in ignored_patterns if pattern in gitignore_content)
1754
+
1755
+ if ignored_count >= 3:
1756
+ security["checks_passed"].append("✅ .gitignore properly excludes sensitive files")
1757
+ else:
1758
+ security["issues"].append("⚠️ .gitignore may not exclude all sensitive files")
1759
+ security["fixable_issues"].append(("update_gitignore", None))
1760
+ score -= 15
1761
+ else:
1762
+ security["issues"].append("❌ No .gitignore file found")
1763
+ score -= 20
1764
+
1765
+ security["score"] = max(0, score)
1766
+
1767
+
1768
+ def _check_performance_health(project_path: Path, performance: dict) -> None:
1769
+ """Check performance-related health"""
1770
+ score = 100
1771
+
1772
+ # Check for Docker files
1773
+ docker_files = ["Dockerfile", "docker-compose.yml", "docker-compose.yaml"]
1774
+ docker_found = any((project_path / f).exists() for f in docker_files)
1775
+
1776
+ if docker_found:
1777
+ performance["checks_passed"].append("✅ Containerization files found")
1778
+ else:
1779
+ performance["issues"].append("⚠️ No containerization files found")
1780
+ performance["fixable_issues"].append(("create_dockerfile", None))
1781
+ score -= 15
1782
+
1783
+ # Check for CI/CD optimization
1784
+ ci_dir = project_path / "ci"
1785
+ if ci_dir.exists():
1786
+ ci_files = list(ci_dir.rglob("*.yml")) + list(ci_dir.rglob("*.yaml"))
1787
+ if ci_files:
1788
+ performance["checks_passed"].append("✅ CI/CD configuration found")
1789
+
1790
+ # Check for caching in CI files
1791
+ cache_found = False
1792
+ for ci_file in ci_files:
1793
+ try:
1794
+ with open(ci_file, "r", encoding="utf-8") as f:
1795
+ content = f.read().lower()
1796
+ if "cache" in content:
1797
+ cache_found = True
1798
+ break
1799
+ except:
1800
+ pass
1801
+
1802
+ if cache_found:
1803
+ performance["checks_passed"].append("✅ CI/CD caching configured")
1804
+ else:
1805
+ performance["issues"].append("⚠️ Consider adding CI/CD caching for better performance")
1806
+ score -= 10
1807
+ else:
1808
+ performance["issues"].append("❌ CI/CD directory exists but no configuration files")
1809
+ score -= 20
1810
+
1811
+ # Check project size
1812
+ total_size = sum(f.stat().st_size for f in project_path.rglob("*") if f.is_file())
1813
+ size_mb = total_size / (1024 * 1024)
1814
+
1815
+ if size_mb > 100:
1816
+ performance["issues"].append(f"⚠️ Large project size: {size_mb:.1f} MB")
1817
+ performance["fixable_issues"].append(("optimize_size", None))
1818
+ score -= 10
1819
+ else:
1820
+ performance["checks_passed"].append(f"✅ Reasonable project size: {size_mb:.1f} MB")
1821
+
1822
+ performance["score"] = max(0, score)
1823
+
1824
+
1825
+ def _check_maintenance_health(project_path: Path, maintenance: dict) -> None:
1826
+ """Check maintenance-related health"""
1827
+ score = 100
1828
+
1829
+ # Check for automation scripts
1830
+ scripts_dir = project_path / "scripts"
1831
+ if scripts_dir.exists():
1832
+ script_files = list(scripts_dir.rglob("*.sh")) + list(scripts_dir.rglob("*.py"))
1833
+ if script_files:
1834
+ maintenance["checks_passed"].append(f"✅ Found {len(script_files)} automation scripts")
1835
+ else:
1836
+ maintenance["issues"].append("⚠️ Scripts directory exists but no scripts found")
1837
+ score -= 10
1838
+ else:
1839
+ maintenance["issues"].append("⚠️ No automation scripts directory")
1840
+ maintenance["fixable_issues"].append(("create_scripts_dir", None))
1841
+ score -= 15
1842
+
1843
+ # Check for Makefile targets
1844
+ makefile_path = project_path / "Makefile"
1845
+ if makefile_path.exists():
1846
+ try:
1847
+ with open(makefile_path, "r", encoding="utf-8") as f:
1848
+ makefile_content = f.read()
1849
+
1850
+ common_targets = ["build", "deploy", "test", "clean"]
1851
+ found_targets = [target for target in common_targets if f"{target}:" in makefile_content]
1852
+
1853
+ if len(found_targets) >= 3:
1854
+ maintenance["checks_passed"].append(f"✅ Makefile has {len(found_targets)} common targets")
1855
+ else:
1856
+ maintenance["issues"].append(f"⚠️ Makefile has only {len(found_targets)} common targets")
1857
+ score -= 10
1858
+ except:
1859
+ maintenance["issues"].append("❌ Error reading Makefile")
1860
+ score -= 5
1861
+
1862
+ # Check for recent activity
1863
+ import time
1864
+ current_time = time.time()
1865
+ recent_files = []
1866
+
1867
+ for item in project_path.rglob("*"):
1868
+ if item.is_file():
1869
+ file_age = current_time - item.stat().st_mtime
1870
+ if file_age < 7 * 24 * 60 * 60: # Less than 7 days
1871
+ recent_files.append(item)
1872
+
1873
+ if len(recent_files) >= 3:
1874
+ maintenance["checks_passed"].append(f"✅ Recent activity: {len(recent_files)} files modified in last 7 days")
1875
+ else:
1876
+ maintenance["issues"].append("⚠️ Low recent activity - project may need maintenance")
1877
+ score -= 10
1878
+
1879
+ maintenance["score"] = max(0, score)
1880
+
1881
+
1882
+ def _check_documentation_health(project_path: Path, documentation: dict) -> None:
1883
+ """Check documentation health"""
1884
+ score = 100
1885
+
1886
+ # Check README quality
1887
+ readme_path = project_path / "README.md"
1888
+ if readme_path.exists():
1889
+ try:
1890
+ with open(readme_path, "r", encoding="utf-8") as f:
1891
+ readme_content = f.read()
1892
+
1893
+ readme_size = len(readme_content)
1894
+ sections = ["#", "##", "###"]
1895
+ section_count = sum(readme_content.count(section) for section in sections)
1896
+
1897
+ if readme_size > 1000 and section_count >= 5:
1898
+ documentation["checks_passed"].append("✅ Comprehensive README documentation")
1899
+ elif readme_size > 500:
1900
+ documentation["issues"].append("⚠️ README could be more detailed")
1901
+ score -= 10
1902
+ else:
1903
+ documentation["issues"].append("❌ README is too short")
1904
+ documentation["fixable_issues"].append(("enhance_readme", None))
1905
+ score -= 20
1906
+ except:
1907
+ documentation["issues"].append("❌ Error reading README")
1908
+ score -= 15
1909
+ else:
1910
+ documentation["issues"].append("❌ No README.md file found")
1911
+ score -= 30
1912
+
1913
+ # Check for additional documentation
1914
+ docs_dir = project_path / "docs"
1915
+ if docs_dir.exists():
1916
+ doc_files = list(docs_dir.rglob("*.md")) + list(docs_dir.rglob("*.txt"))
1917
+ if doc_files:
1918
+ documentation["checks_passed"].append(f"✅ Found {len(doc_files)} additional documentation files")
1919
+ else:
1920
+ documentation["issues"].append("⚠️ docs directory exists but no documentation files")
1921
+ score -= 5
1922
+ else:
1923
+ documentation["issues"].append("⚠️ No docs directory found")
1924
+ score -= 10
1925
+
1926
+ # Check for inline documentation
1927
+ code_files = []
1928
+ for ext in [".py", ".js", ".ts", ".go", ".java"]:
1929
+ code_files.extend(project_path.rglob(f"*{ext}"))
1930
+
1931
+ documented_files = 0
1932
+ for code_file in code_files[:20]: # Check first 20 files
1933
+ try:
1934
+ with open(code_file, "r", encoding="utf-8") as f:
1935
+ content = f.read()
1936
+ if any(marker in content for marker in ["#", "//", "/*", "*"]):
1937
+ documented_files += 1
1938
+ except:
1939
+ pass
1940
+
1941
+ if code_files and documented_files / len(code_files) > 0.7:
1942
+ documentation["checks_passed"].append("✅ Good inline documentation coverage")
1943
+ elif code_files:
1944
+ documentation["issues"].append("⚠️ Consider adding more inline documentation")
1945
+ score -= 10
1946
+
1947
+ documentation["score"] = max(0, score)
1948
+
1949
+
1950
+ def _generate_health_recommendations(report: dict) -> list:
1951
+ """Generate health improvement recommendations"""
1952
+ recommendations = []
1953
+
1954
+ if report["overall_score"] < 60:
1955
+ recommendations.append("🚨 Project needs significant improvement - focus on critical issues first")
1956
+ elif report["overall_score"] < 80:
1957
+ recommendations.append("⚡ Good foundation - address the identified issues to reach excellence")
1958
+ else:
1959
+ recommendations.append("🎉 Excellent project health! Consider sharing your practices")
1960
+
1961
+ # Category-specific recommendations
1962
+ for category_name, category_data in report["categories"].items():
1963
+ if category_data["score"] < 70:
1964
+ if category_name == "structure":
1965
+ recommendations.append("🏗️ Improve project structure with missing directories and files")
1966
+ elif category_name == "security":
1967
+ recommendations.append("🔒 Enhance security with proper secrets management and policies")
1968
+ elif category_name == "performance":
1969
+ recommendations.append("⚡ Optimize performance with caching and containerization")
1970
+ elif category_name == "maintenance":
1971
+ recommendations.append("🔧 Add automation scripts and improve maintainability")
1972
+ elif category_name == "documentation":
1973
+ recommendations.append("📚 Enhance documentation for better project understanding")
1974
+
1975
+ return recommendations
1976
+
1977
+
1978
+ def _display_health_report(report: dict, detailed: bool) -> None:
1979
+ """Display comprehensive health report"""
1980
+ console.print(f"\n[bold]🏥 Overall Health Score: {report['overall_score']}/100[/bold]")
1981
+
1982
+ # Health score color coding
1983
+ if report["overall_score"] >= 80:
1984
+ console.print("[green]✅ Excellent project health[/green]")
1985
+ elif report["overall_score"] >= 60:
1986
+ console.print("[yellow]⚠️ Good project health with room for improvement[/yellow]")
1987
+ else:
1988
+ console.print("[red]❌ Project needs attention[/red]")
1989
+
1990
+ # Category breakdown
1991
+ console.print(f"\n[bold]📊 Category Breakdown:[/bold]")
1992
+ for category_name, category_data in report["categories"].items():
1993
+ score = category_data["score"]
1994
+ icon = "🟢" if score >= 80 else "🟡" if score >= 60 else "🔴"
1995
+ console.print(f" {icon} {category_name.title()}: {score}/100")
1996
+
1997
+ # Critical issues
1998
+ if report["critical_issues"]:
1999
+ console.print(f"\n[bold red]🚨 Critical Issues:[/bold red]")
2000
+ for issue in report["critical_issues"]:
2001
+ console.print(f" {issue}")
2002
+
2003
+ # Category details if detailed
2004
+ if detailed:
2005
+ for category_name, category_data in report["categories"].items():
2006
+ console.print(f"\n[bold]{category_name.title()} Details:[/bold]")
2007
+
2008
+ if category_data["checks_passed"]:
2009
+ console.print("[green]✅ Passed Checks:[/green]")
2010
+ for check in category_data["checks_passed"][:5]:
2011
+ console.print(f" {check}")
2012
+ if len(category_data["checks_passed"]) > 5:
2013
+ console.print(f" ... and {len(category_data['checks_passed']) - 5} more")
2014
+
2015
+ if category_data["issues"]:
2016
+ console.print("[red]❌ Issues:[/red]")
2017
+ for issue in category_data["issues"]:
2018
+ console.print(f" {issue}")
2019
+
2020
+
2021
+ def _fix_health_issues(project_path: Path, fixable_issues: list) -> None:
2022
+ """Attempt to fix health issues automatically"""
2023
+ for issue_type, issue_data in fixable_issues:
2024
+ try:
2025
+ if issue_type == "create_dir":
2026
+ (project_path / issue_data).mkdir(parents=True, exist_ok=True)
2027
+ console.print(f"[green]✅ Created directory: {issue_data}/[/green]")
2028
+
2029
+ elif issue_type == "create_readme":
2030
+ readme_content = """# Project Name
2031
+
2032
+ ## Description
2033
+ Add your project description here.
2034
+
2035
+ ## Installation
2036
+ ```bash
2037
+ # Add installation instructions
2038
+ ```
2039
+
2040
+ ## Usage
2041
+ ```bash
2042
+ # Add usage instructions
2043
+ ```
2044
+
2045
+ ## Contributing
2046
+ Add contributing guidelines here.
2047
+
2048
+ ## License
2049
+ Add license information here.
2050
+ """
2051
+ with open(project_path / "README.md", "w", encoding="utf-8") as f:
2052
+ f.write(readme_content)
2053
+ console.print("[green]✅ Created README.md[/green]")
2054
+
2055
+ elif issue_type == "create_gitignore":
2056
+ gitignore_content = """# Python
2057
+ __pycache__/
2058
+ *.py[cod]
2059
+ *$py.class
2060
+ *.so
2061
+ .Python
2062
+ build/
2063
+ develop-eggs/
2064
+ dist/
2065
+ downloads/
2066
+ eggs/
2067
+ .eggs/
2068
+ lib/
2069
+ lib64/
2070
+ parts/
2071
+ sdist/
2072
+ var/
2073
+ wheels/
2074
+ *.egg-info/
2075
+ .installed.cfg
2076
+ *.egg
2077
+
2078
+ # Environment variables
2079
+ .env
2080
+ .env.local
2081
+ .env.*.local
2082
+
2083
+ # Secrets
2084
+ *.key
2085
+ *.pem
2086
+ *.p12
2087
+ secrets/
2088
+ *.secret
2089
+
2090
+ # IDE
2091
+ .vscode/
2092
+ .idea/
2093
+ *.swp
2094
+ *.swo
2095
+
2096
+ # OS
2097
+ .DS_Store
2098
+ Thumbs.db
2099
+ """
2100
+ with open(project_path / ".gitignore", "w", encoding="utf-8") as f:
2101
+ f.write(gitignore_content)
2102
+ console.print("[green]✅ Created .gitignore[/green]")
2103
+
2104
+ elif issue_type == "create_scripts_dir":
2105
+ scripts_dir = project_path / "scripts"
2106
+ scripts_dir.mkdir(exist_ok=True)
2107
+
2108
+ # Create sample script
2109
+ sample_script = """#!/bin/bash
2110
+ # Sample automation script
2111
+
2112
+ echo "Running automation..."
2113
+
2114
+ # Add your automation commands here
2115
+ """
2116
+ with open(scripts_dir / "setup.sh", "w", encoding="utf-8") as f:
2117
+ f.write(sample_script)
2118
+ os.chmod(scripts_dir / "setup.sh", 0o755)
2119
+ console.print("[green]✅ Created scripts directory with sample script[/green]")
2120
+
2121
+ except Exception as e:
2122
+ console.print(f"[red]❌ Could not fix {issue_type}: {str(e)}[/red]")
2123
+
2124
+
2125
+ def _display_health_score(report: dict) -> None:
2126
+ """Display final health score and recommendations"""
2127
+ console.print(f"\n[bold]🎯 Final Health Score: {report['overall_score']}/100[/bold]")
2128
+
2129
+ if report["recommendations"]:
2130
+ console.print(f"\n[bold]💡 Recommendations:[/bold]")
2131
+ for rec in report["recommendations"]:
2132
+ console.print(f" {rec}")
2133
+
2134
+
1096
2135
  @app.command()
1097
2136
  def version() -> None:
1098
2137
  """Show version information"""
1099
2138
  try:
1100
2139
  from . import __version__
1101
2140
  except ImportError:
1102
- __version__ = "1.2.0"
2141
+ __version__ = "1.3.0"
1103
2142
  console.print(f"[bold blue]DevOps Project Generator[/bold blue] v{__version__}")
1104
2143
 
1105
2144
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-project-generator
3
- Version: 1.2.0
3
+ Version: 1.3.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
@@ -167,6 +167,48 @@ devops-project-generator cleanup my-project --force
167
167
  devops-project-generator cleanup my-project --keep-config
168
168
  ```
169
169
 
170
+ ### Template Management (NEW v1.3.0)
171
+
172
+ ```bash
173
+ # List available templates
174
+ devops-project-generator template list
175
+
176
+ # Create custom template
177
+ devops-project-generator template create --name my-template
178
+
179
+ # Customize existing template
180
+ devops-project-generator template customize --name my-template
181
+
182
+ # Export templates
183
+ devops-project-generator template export --output ./templates
184
+ ```
185
+
186
+ ### Project Backup (NEW v1.3.0)
187
+
188
+ ```bash
189
+ # Create backup
190
+ devops-project-generator backup create my-project
191
+
192
+ # List available backups
193
+ devops-project-generator backup list
194
+
195
+ # Restore from backup
196
+ devops-project-generator backup restore my-project --file backup.tar.gz
197
+ ```
198
+
199
+ ### Health Check (NEW v1.3.0)
200
+
201
+ ```bash
202
+ # Basic health check
203
+ devops-project-generator health my-project
204
+
205
+ # Detailed health analysis
206
+ devops-project-generator health my-project --detailed
207
+
208
+ # Auto-fix health issues
209
+ devops-project-generator health my-project --fix
210
+ ```
211
+
170
212
  ## 🏗️ Generated Project Structure (Example)
171
213
 
172
214
  ```
@@ -410,7 +452,7 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
410
452
  - [x] Template caching and pre-loading
411
453
  - [x] Better user experience with improved messages
412
454
 
413
- ### v1.2 ✅ (Current)
455
+ ### v1.2 ✅
414
456
  - [x] Project validation and structure checking
415
457
  - [x] Configuration file management system
416
458
  - [x] Project cleanup and teardown utilities
@@ -418,7 +460,14 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
418
460
  - [x] DevOps maturity scoring
419
461
  - [x] Intelligent recommendations system
420
462
 
421
- ### v1.3
463
+ ### v1.3 ✅ (Current)
464
+ - [x] Template management and customization system
465
+ - [x] Project backup and restore functionality
466
+ - [x] Comprehensive health monitoring and scoring
467
+ - [x] Auto-fix capabilities for common issues
468
+ - [x] Advanced project analysis and recommendations
469
+
470
+ ### v1.4
422
471
  - [ ] Support for Azure DevOps
423
472
  - [ ] Additional cloud providers (GCP, Azure)
424
473
  - [ ] More deployment targets (AWS ECS, Fargate)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "devops-project-generator"
7
- version = "1.2.0"
7
+ version = "1.3.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"}