cicd-cli-tool 0.1.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 (49) hide show
  1. cicd_cli_tool-0.1.0.dist-info/METADATA +41 -0
  2. cicd_cli_tool-0.1.0.dist-info/RECORD +49 -0
  3. cicd_cli_tool-0.1.0.dist-info/WHEEL +5 -0
  4. cicd_cli_tool-0.1.0.dist-info/entry_points.txt +2 -0
  5. cicd_cli_tool-0.1.0.dist-info/top_level.txt +3 -0
  6. cli/main.py +384 -0
  7. core/__init__.py +3 -0
  8. core/base_detector.py +113 -0
  9. core/base_generator.py +190 -0
  10. frameworks/__init__.py +39 -0
  11. frameworks/dotnet/__init__.py +7 -0
  12. frameworks/dotnet/detector.py +146 -0
  13. frameworks/dotnet/generator.py +157 -0
  14. frameworks/dotnet/templates/Dockerfile.j2 +29 -0
  15. frameworks/dotnet/templates/Jenkinsfile.j2 +8 -0
  16. frameworks/dotnet/templates/github-actions.yml.j2 +77 -0
  17. frameworks/dotnet/templates/gitlab-ci.yml.j2 +68 -0
  18. frameworks/gradle/__init__.py +7 -0
  19. frameworks/gradle/detector.py +115 -0
  20. frameworks/gradle/generator.py +149 -0
  21. frameworks/gradle/templates/Dockerfile.j2 +31 -0
  22. frameworks/gradle/templates/Jenkinsfile.j2 +8 -0
  23. frameworks/gradle/templates/github-actions.yml.j2 +79 -0
  24. frameworks/gradle/templates/gitlab-ci.yml.j2 +63 -0
  25. frameworks/java/__init__.py +7 -0
  26. frameworks/java/detector.py +29 -0
  27. frameworks/java/generator.py +29 -0
  28. frameworks/maven/__init__.py +7 -0
  29. frameworks/maven/detector.py +104 -0
  30. frameworks/maven/generator.py +155 -0
  31. frameworks/maven/templates/Dockerfile.j2 +35 -0
  32. frameworks/maven/templates/Jenkinsfile.j2 +8 -0
  33. frameworks/maven/templates/github-actions.yml.j2 +28 -0
  34. frameworks/maven/templates/gitlab-ci.yml.j2 +73 -0
  35. frameworks/node/__init__.py +3 -0
  36. frameworks/node/detector.py +110 -0
  37. frameworks/node/generator.py +149 -0
  38. frameworks/node/templates/Dockerfile.j2 +31 -0
  39. frameworks/node/templates/Jenkinsfile.j2 +8 -0
  40. frameworks/node/templates/github-actions.yml.j2 +191 -0
  41. frameworks/node/templates/gitlab-ci.yml.j2 +74 -0
  42. frameworks/python/__init__.py +5 -0
  43. frameworks/python/detector.py +189 -0
  44. frameworks/python/generator.py +254 -0
  45. frameworks/python/templates/Dockerfile.j2 +34 -0
  46. frameworks/python/templates/Jenkinsfile.j2 +8 -0
  47. frameworks/python/templates/README.md.j2 +195 -0
  48. frameworks/python/templates/github-actions.yml.j2 +91 -0
  49. frameworks/python/templates/gitlab-ci.yml.j2 +64 -0
@@ -0,0 +1,41 @@
1
+ Metadata-Version: 2.4
2
+ Name: cicd-cli-tool
3
+ Version: 0.1.0
4
+ Summary: Auto-detect project frameworks and generate CI/CD pipeline files
5
+ Author: Your Name
6
+ Author-email: Ayoub Jedidi <ayoub.ejedidi@gmail.com>
7
+ Keywords: ci,cd,jenkins,gitlab,github,pipeline,devops,cicd
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Topic :: Software Development :: Build Tools
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.8
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: typer[all]>=0.9.0
20
+ Requires-Dist: jinja2>=3.1.2
21
+ Requires-Dist: rich>=13.7.0
22
+ Requires-Dist: pydantic<2
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
25
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
26
+ Requires-Dist: black>=23.0.0; extra == "dev"
27
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
28
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
29
+ Dynamic: author
30
+ Dynamic: requires-python
31
+
32
+ # CLI Client
33
+ this cli provides every setup you need for these frameworks (nodejs,pytohn,java,gradle,maven,dotnet)
34
+ after installing the cli with pip install -e
35
+ you'll use these commands :
36
+ cicd--help : to see all commands you can use with cicd-cli
37
+ cicd detect : detects everything is related to your project dependencies , version , framewokr, language etc ...
38
+ you should provide the folder your project to work
39
+ cicd init : includes cicd detect , it will detect auto and will generate jenkinsfile and dockerfile for your project
40
+ this all you need to know about the cli
41
+ if you need more example you find the tests folder ive used to test , simple example running
@@ -0,0 +1,49 @@
1
+ cli/main.py,sha256=PyX1gxNic6LZ96yx1rTHFG9rub8i5s3Y3oPH8REtCAU,13870
2
+ core/__init__.py,sha256=4k9y0t_moGgaw2bzmbC-maaPJ5tM2mFlTnJj_NW7Cs4,126
3
+ core/base_detector.py,sha256=yIYgr1WRJNNZ-Fef5YlB7KDyNM3bpp9PuK5F3x2wS9E,3608
4
+ core/base_generator.py,sha256=U20eIAd9DSOrBltHSrHYVW-4nh77Z16BfwnAfXDYAEM,6445
5
+ frameworks/__init__.py,sha256=D3rVWmuKN7te5qn41dcMD6GhdLEQv0OdlVx28K4izds,1051
6
+ frameworks/dotnet/__init__.py,sha256=Nj8Fh8Fd_sOXrCl2rU0JW-Y_AIIeFyGIkGq70hdnzck,170
7
+ frameworks/dotnet/detector.py,sha256=uuwpcbNQqz7YOJ7QhVvGE4bptcOWSCXXnbPT9boSFuU,4874
8
+ frameworks/dotnet/generator.py,sha256=ScrCMg-aER9EtJb_32q383Xs7SiiinBSjUUGRoZw9vg,5754
9
+ frameworks/dotnet/templates/Dockerfile.j2,sha256=YK7IaZfsAaB7mfNt1G2979upEaHEw7TXpD5OwqXT9as,542
10
+ frameworks/dotnet/templates/Jenkinsfile.j2,sha256=j4ac3a5OkISiBLlruPy9F13_QMe310Tb4c1pk2iXIWU,163
11
+ frameworks/dotnet/templates/github-actions.yml.j2,sha256=s8ytAfZK6Y6zb6K1d_wf6JcxBQaiVRtgCRvSUSXHj7c,1968
12
+ frameworks/dotnet/templates/gitlab-ci.yml.j2,sha256=e2qcByOH6pPzQ29I8HoPAEu5mh1b7iBmFuo1-zXVto4,1347
13
+ frameworks/gradle/__init__.py,sha256=uHycl82gkj4zjy_fFZZw4MEhett7PpJli1NoMxqNX30,172
14
+ frameworks/gradle/detector.py,sha256=OI7yi_BOi3EDlC9m1SGycJjodeFj9M90EUrIpblcb1k,3969
15
+ frameworks/gradle/generator.py,sha256=1c7nRj_H7aAtcp8ZrZ4wXJDavDa4WMcXrGlUjIjXYfA,5388
16
+ frameworks/gradle/templates/Dockerfile.j2,sha256=qS4VZv98OyzALMDTgfCO_eshgM8AHIQqMwCworzzAdk,634
17
+ frameworks/gradle/templates/Jenkinsfile.j2,sha256=Rr7AcYjf2l33SZVaDWW4D6YSbtqSViweIYhxtXG4f44,163
18
+ frameworks/gradle/templates/github-actions.yml.j2,sha256=Vgfi8-REzplZv9DM1K34x7bEIp3STj3JenaEo-4uFZM,1966
19
+ frameworks/gradle/templates/gitlab-ci.yml.j2,sha256=Fy2Z_zmkjptbZ89AH14LluEyRTsP60Bv3iLSPH2u9rw,1055
20
+ frameworks/java/__init__.py,sha256=MhBeJxNdQyZKeDp5Lz4LEgCtSoCeO8zLLU4j-VXQkxk,158
21
+ frameworks/java/detector.py,sha256=BD_Whan0twNB-kv3pnDqcPDwkUi39QGf55hjsN6t9oI,923
22
+ frameworks/java/generator.py,sha256=sASHMg1WxTAszkTJroQv5mMBpJ9wNXEkTL9qfRqVBt0,929
23
+ frameworks/maven/__init__.py,sha256=BflBSotd-iLNN1MDIqmQodZroAprzNnFF5PQXGHkrZ0,167
24
+ frameworks/maven/detector.py,sha256=vFqWLhLFTxzMsj6i4cV6DT4aanpoa-MwuIAdfW-2K6U,3397
25
+ frameworks/maven/generator.py,sha256=Pt8TDmioX_xd9--vFGxsCeirZ7mk0gvb-fpFIkN88kY,5617
26
+ frameworks/maven/templates/Dockerfile.j2,sha256=0rSTYspIUZnSBPF0a01SwW2UcNgYbvicUVJ_hltH3-4,836
27
+ frameworks/maven/templates/Jenkinsfile.j2,sha256=cXiCJmf_tSlbvfwJtAXj7JalHVAMdFWATTaquLLIV4o,162
28
+ frameworks/maven/templates/github-actions.yml.j2,sha256=0bvN6ROsrRTByBAjcHPoBpXlSMdDgwh6kRMTQp6sjlI,564
29
+ frameworks/maven/templates/gitlab-ci.yml.j2,sha256=1Dd8APErz-R-7k4Za8fPMtMsAvZy66fn5UQswAY9Cpg,1161
30
+ frameworks/node/__init__.py,sha256=88s2aFa7FGELa2j7tlkmUWhkLfu8IrPjk0bwWyFDMTU,116
31
+ frameworks/node/detector.py,sha256=GhxfkRuSWaznPnVbJNkPvrOQDwo44qv2yaElzWFEqi8,3258
32
+ frameworks/node/generator.py,sha256=b-_wQL13hjhJKxwSkbtL1VNN3wloerP-rkZRViRVbfA,5393
33
+ frameworks/node/templates/Dockerfile.j2,sha256=91z9TxWQOey0-XAbZW4cnx0Yl0rcSIdNrZVmLSLbjAM,636
34
+ frameworks/node/templates/Jenkinsfile.j2,sha256=G6UG9KS6JIbExmsCT-HlZAkD6fGfZZE0YTff41_lOmA,160
35
+ frameworks/node/templates/github-actions.yml.j2,sha256=TznC_ki8RYwudbRdB0LMB7OvuFBDXKRXszGg9v4jQic,6619
36
+ frameworks/node/templates/gitlab-ci.yml.j2,sha256=rApavtOLnA5LdvnhQ4EW8kcqVYfS3Tudzacw6h3nl58,1334
37
+ frameworks/python/__init__.py,sha256=Kl3eOjpv_tyNbBN1E9IABQFN0hjUOKspwTrdYxTnCkw,147
38
+ frameworks/python/detector.py,sha256=67NL1VtGFqQ1wOdF0BcOdtlxzP3hU_Q0r5HyY005z-c,6187
39
+ frameworks/python/generator.py,sha256=EgBcaOTCQxTMd7uXq7d0P1qsK3x8wOjc73jKM5-ORw4,9512
40
+ frameworks/python/templates/Dockerfile.j2,sha256=fppi68xMCE9cBcHiaCrmX0mRPe1w_m7LRk-PSCbCwt0,817
41
+ frameworks/python/templates/Jenkinsfile.j2,sha256=bfoTBiHVXLyGVCG5Ias-EwzJtC1gG3nrtreecQGz7Ak,163
42
+ frameworks/python/templates/README.md.j2,sha256=ScPwDQYq2XkrHmvhaU27bvK3XJbi7sNgqBBc1tkkCfY,5493
43
+ frameworks/python/templates/github-actions.yml.j2,sha256=tOFL7VTi90r-hDdm8cjYCkMFyekBsGyKjfBiOr7g4eE,2339
44
+ frameworks/python/templates/gitlab-ci.yml.j2,sha256=5gvlepYPUuK6ZAALZ96OsRBCHzWA48WF0CXNx8uL4r0,1343
45
+ cicd_cli_tool-0.1.0.dist-info/METADATA,sha256=XvcexTUZsnXJuXj-Ujqxq7KeBTD9ethGYL7DfCmUGT4,1838
46
+ cicd_cli_tool-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
47
+ cicd_cli_tool-0.1.0.dist-info/entry_points.txt,sha256=WuJa44_vrSvNg8LaRDOBEeB2Q5g1_VSozoVZfMb0Wac,39
48
+ cicd_cli_tool-0.1.0.dist-info/top_level.txt,sha256=iTFuqReac6OeWK02FqIhPrTzd4t9xBxk270SYfFrp4g,20
49
+ cicd_cli_tool-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cicd = cli.main:main
@@ -0,0 +1,3 @@
1
+ cli
2
+ core
3
+ frameworks
cli/main.py ADDED
@@ -0,0 +1,384 @@
1
+ """
2
+ CLI for cicd-framework tool
3
+ Auto-detects project framework and generates CI/CD files
4
+ """
5
+ from typing import Optional, List
6
+ import typer
7
+ from pathlib import Path
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+ from frameworks import AVAILABLE_FRAMEWORKS
11
+
12
+ app = typer.Typer(
13
+ help="CI/CD Framework Generator - Auto-detect and generate pipeline files",
14
+ add_completion=False
15
+ )
16
+ console = Console()
17
+
18
+
19
+ @app.command()
20
+ def init(
21
+ path: Path = typer.Argument(
22
+ Path("."),
23
+ help="Project path to analyze",
24
+ exists=True,
25
+ file_okay=False,
26
+ dir_okay=True,
27
+ resolve_path=True
28
+ ),
29
+ framework: Optional[str] = typer.Option(
30
+ None,
31
+ "--framework",
32
+ "-f",
33
+ help="Force specific framework (python, node, java, etc.)"
34
+ ),
35
+ platforms: str = typer.Option(
36
+ "jenkins",
37
+ "--platforms",
38
+ "-p",
39
+ help="CI/CD platforms to generate (comma-separated). Options: jenkins | gitlab | github"
40
+ ),
41
+ deployment_type: str = typer.Option(
42
+ "webapp",
43
+ "--deployment-type",
44
+ "-dt",
45
+ help="Type of deployment. Options: webapp | instance"
46
+ ),
47
+ cloud_provider: str = typer.Option(
48
+ "local",
49
+ "--cloud-provider",
50
+ "-cp",
51
+ help="Cloud provider for deployment. Options: local | aws | azure | gcp"
52
+ ),
53
+ ):
54
+ """
55
+ Initialize CI/CD pipeline for a project.
56
+
57
+ Auto-detects project framework and generates pipeline files.
58
+
59
+ Examples:
60
+
61
+ # Generate Jenkins pipeline (default)
62
+ $ cicd-framework init
63
+
64
+ # Generate GitLab CI only
65
+ $ cicd-framework init --platforms gitlab
66
+
67
+ # Generate all platforms
68
+ $ cicd-framework init --platforms jenkins,gitlab,github
69
+
70
+ # Force Python framework with multiple platforms
71
+ $ cicd-framework init --framework python --platforms gitlab,github
72
+
73
+ # Deploy to AWS as a webapp
74
+ $ cicd-framework init --cloud-provider aws --deployment-type webapp
75
+
76
+ # Deploy to Azure as a VM instance
77
+ $ cicd-framework init --cloud-provider azure --deployment-type instance
78
+ """
79
+ console.print("🔍 Detecting project framework...\n")
80
+
81
+ project_path = Path(path).resolve()
82
+
83
+ # Parse and validate platforms
84
+ requested_platforms = [p.strip().lower() for p in platforms.split(',')]
85
+ valid_platforms = ['jenkins', 'gitlab', 'github']
86
+ invalid = [p for p in requested_platforms if p not in valid_platforms]
87
+
88
+ if invalid:
89
+ console.print(f"[red]✗ Invalid platforms: {', '.join(invalid)}[/red]")
90
+ console.print(f"[yellow]Valid platforms: {', '.join(valid_platforms)}[/yellow]")
91
+ console.print("\n[dim]Tip: Use comma-separated values like --platforms jenkins,gitlab,github[/dim]")
92
+ raise typer.Exit(code=1)
93
+
94
+ # Validate deployment type
95
+ deployment_type = deployment_type.lower()
96
+ valid_deployment_types = ['webapp', 'instance']
97
+ if deployment_type not in valid_deployment_types:
98
+ console.print(f"[red]✗ Invalid deployment type: {deployment_type}[/red]")
99
+ console.print(f"[yellow]Valid deployment types: {', '.join(valid_deployment_types)}[/yellow]")
100
+ raise typer.Exit(code=1)
101
+
102
+ # Validate cloud provider
103
+ cloud_provider = cloud_provider.lower()
104
+ valid_cloud_providers = ['local', 'aws', 'azure', 'gcp']
105
+ if cloud_provider not in valid_cloud_providers:
106
+ console.print(f"[red]✗ Invalid cloud provider: {cloud_provider}[/red]")
107
+ console.print(f"[yellow]Valid cloud providers: {', '.join(valid_cloud_providers)}[/yellow]")
108
+ raise typer.Exit(code=1)
109
+
110
+ # If user specified framework, use only that
111
+ if framework:
112
+ if framework not in AVAILABLE_FRAMEWORKS:
113
+ console.print(f"[red]✗ Unknown framework: {framework}[/red]")
114
+ console.print(f"[yellow]Available frameworks: {', '.join(AVAILABLE_FRAMEWORKS.keys())}[/yellow]")
115
+ raise typer.Exit(code=1)
116
+
117
+ frameworks_to_try = [framework]
118
+ else:
119
+ # Try all available frameworks
120
+ frameworks_to_try = list(AVAILABLE_FRAMEWORKS.keys())
121
+
122
+ # Try each framework detector
123
+ detection_result = None
124
+ detected_framework = None
125
+
126
+ for fw_name in frameworks_to_try:
127
+ fw_config = AVAILABLE_FRAMEWORKS[fw_name]
128
+ DetectorClass = fw_config['detector']
129
+
130
+ console.print(f" Trying {fw_name}...", style="dim")
131
+
132
+ # Initialize detector and try detection
133
+ detector = DetectorClass(project_path)
134
+ result = detector.detect()
135
+
136
+ if result is not None:
137
+ detection_result = result
138
+ detected_framework = fw_name
139
+ console.print(f"[green]✓ Detected: {fw_name}[/green]")
140
+ break
141
+
142
+ # Check if detection failed
143
+ if detection_result is None:
144
+ console.print("[red]✗ No supported framework detected![/red]")
145
+ console.print(f"[yellow]Supported frameworks: {', '.join(AVAILABLE_FRAMEWORKS.keys())}[/yellow]")
146
+ console.print("\n[dim]Tip: Use --framework to force a specific framework[/dim]")
147
+ raise typer.Exit(code=1)
148
+
149
+ # Add project path to result
150
+ detection_result['project_path'] = str(project_path)
151
+
152
+ # Add deployment configuration
153
+ detection_result['deployment_type'] = deployment_type
154
+ detection_result['cloud_provider'] = cloud_provider
155
+
156
+ # Display detection results
157
+ console.print()
158
+ table = Table(title="📊 Detection Results", show_header=True, header_style="bold cyan")
159
+ table.add_column("Property", style="cyan", no_wrap=True)
160
+ table.add_column("Value", style="green")
161
+
162
+ table.add_row("Framework", detected_framework)
163
+ table.add_row("Language", detection_result.get("language", "N/A"))
164
+ table.add_row("Cloud Provider", cloud_provider.upper() if cloud_provider != "local" else "Local Docker")
165
+ table.add_row("Deployment Type", deployment_type.capitalize())
166
+
167
+ # Add framework-specific details
168
+ if detected_framework == 'python':
169
+ table.add_row("Python Version", detection_result.get("python_version", "N/A"))
170
+ table.add_row("Package Manager", detection_result.get("package_manager", "N/A"))
171
+ table.add_row("Test Framework", detection_result.get("test_framework", "N/A"))
172
+ if detection_result.get("framework"):
173
+ table.add_row("Web Framework", detection_result.get("framework"))
174
+ elif detected_framework == 'node':
175
+ table.add_row("Node Version", detection_result.get("node_version", "N/A"))
176
+ table.add_row("Package Manager", detection_result.get("package_manager", "N/A"))
177
+ if detection_result.get("framework"):
178
+ table.add_row("Framework", detection_result.get("framework"))
179
+
180
+ console.print(table)
181
+ console.print()
182
+
183
+ # Display platforms to generate
184
+ platforms_display = ", ".join([f"[cyan]{p}[/cyan]" for p in requested_platforms])
185
+ console.print(f"[bold]📦 Generating CI/CD files for: {platforms_display}[/bold]\n")
186
+
187
+ # Get the appropriate generator
188
+ GeneratorClass = AVAILABLE_FRAMEWORKS[detected_framework]['generator']
189
+ generator = GeneratorClass()
190
+
191
+ try:
192
+ files = generator.generate(detection_result, project_path, requested_platforms)
193
+
194
+ console.print("[green]✅ Generation successful![/green]\n")
195
+
196
+ # Display generated files
197
+ files_table = Table(title="📄 Generated Files", show_header=True, header_style="bold green")
198
+ files_table.add_column("Platform", style="cyan", no_wrap=True)
199
+ files_table.add_column("File Path", style="yellow")
200
+
201
+ for platform, file_path in files['generated_files'].items():
202
+ # Make path relative to project for cleaner display
203
+ try:
204
+ rel_path = Path(file_path).relative_to(project_path)
205
+ files_table.add_row(platform.upper(), str(rel_path))
206
+ except ValueError:
207
+ files_table.add_row(platform.upper(), str(file_path))
208
+
209
+ console.print(files_table)
210
+ console.print()
211
+
212
+ return files
213
+ except Exception as e:
214
+ console.print(f"[red]✗ Generation failed: {e}[/red]")
215
+ import traceback
216
+ console.print("\n[dim]Stack trace:[/dim]")
217
+ traceback.print_exc()
218
+ raise typer.Exit(code=1)
219
+
220
+
221
+ @app.command()
222
+ def detect(
223
+ path: Path = typer.Argument(
224
+ Path("."),
225
+ help="Project path to analyze",
226
+ exists=True,
227
+ file_okay=False,
228
+ dir_okay=True,
229
+ resolve_path=True
230
+ ),
231
+ framework: Optional[str] = typer.Option(
232
+ None,
233
+ "--framework",
234
+ "-f",
235
+ help="Force specific framework detection"
236
+ ),
237
+ ):
238
+ """
239
+ Detect project configuration without generating files.
240
+
241
+ Useful for testing framework detection.
242
+
243
+ Examples:
244
+
245
+ # Auto-detect framework
246
+ $ cicd-framework detect
247
+
248
+ # Force Python detection
249
+ $ cicd-framework detect --framework python
250
+ """
251
+ project_path = Path(path).resolve()
252
+
253
+ console.print("🔍 Detecting project framework...\n")
254
+
255
+ # If user specified framework, use only that
256
+ if framework:
257
+ if framework not in AVAILABLE_FRAMEWORKS:
258
+ console.print(f"[red]✗ Unknown framework: {framework}[/red]")
259
+ console.print(f"[yellow]Available frameworks: {', '.join(AVAILABLE_FRAMEWORKS.keys())}[/yellow]")
260
+ raise typer.Exit(code=1)
261
+ frameworks_to_try = [framework]
262
+ else:
263
+ frameworks_to_try = list(AVAILABLE_FRAMEWORKS.keys())
264
+
265
+ # Try each framework
266
+ for fw_name in frameworks_to_try:
267
+ fw_config = AVAILABLE_FRAMEWORKS[fw_name]
268
+ DetectorClass = fw_config['detector']
269
+
270
+ console.print(f" Trying {fw_name}...", style="dim")
271
+ detector = DetectorClass(project_path)
272
+ result = detector.detect()
273
+
274
+ if result is not None:
275
+ result['project_path'] = str(project_path)
276
+ result['detected_framework'] = fw_name
277
+ console.print(f"\n[green]✓ Detected: {fw_name}[/green]\n")
278
+ console.print_json(data=result)
279
+ return result
280
+
281
+ console.print("[red]✗ No supported framework detected![/red]")
282
+ console.print(f"[yellow]Supported frameworks: {', '.join(AVAILABLE_FRAMEWORKS.keys())}[/yellow]")
283
+ raise typer.Exit(code=1)
284
+
285
+
286
+ @app.command(name="list")
287
+ def list_frameworks():
288
+ """
289
+ List all supported frameworks and platforms.
290
+
291
+ Shows available framework detectors and generators.
292
+ """
293
+ console.print("\n[bold cyan]🚀 CI/CD Framework Generator[/bold cyan]\n")
294
+
295
+ # Frameworks table
296
+ frameworks_table = Table(title="Supported Frameworks", show_header=True, header_style="bold cyan")
297
+ frameworks_table.add_column("Framework", style="cyan", no_wrap=True)
298
+ frameworks_table.add_column("Detector", style="yellow")
299
+ frameworks_table.add_column("Generator", style="green")
300
+
301
+ for fw_name, fw_config in AVAILABLE_FRAMEWORKS.items():
302
+ frameworks_table.add_row(
303
+ fw_name.upper(),
304
+ fw_config['detector'].__name__,
305
+ fw_config['generator'].__name__
306
+ )
307
+
308
+ console.print(frameworks_table)
309
+ console.print()
310
+
311
+ # Platforms table
312
+ platforms_table = Table(title="Supported CI/CD Platforms", show_header=True, header_style="bold green")
313
+ platforms_table.add_column("Platform", style="green", no_wrap=True)
314
+ platforms_table.add_column("File Generated", style="yellow")
315
+ platforms_table.add_column("Description", style="dim")
316
+
317
+ platforms_table.add_row("Jenkins", "Jenkinsfile", "Declarative pipeline for Jenkins")
318
+ platforms_table.add_row("GitLab CI", ".gitlab-ci.yml", "GitLab CI/CD configuration")
319
+ platforms_table.add_row("GitHub Actions", ".github/workflows/ci.yml", "GitHub Actions workflow")
320
+
321
+ console.print(platforms_table)
322
+ console.print()
323
+
324
+
325
+ @app.command()
326
+ def version():
327
+ """
328
+ Show version information.
329
+ """
330
+ console.print("\n[bold cyan]CI/CD Framework Generator[/bold cyan]")
331
+ console.print("[dim]Version:[/dim] [green]1.0.0[/green]")
332
+ console.print("[dim]Author:[/dim] [yellow]Ayoub Jedidi[/yellow]")
333
+ console.print()
334
+
335
+
336
+ @app.command()
337
+ def options():
338
+ """
339
+ Show all available deployment options and their values.
340
+ """
341
+ console.print("\n[bold cyan]🚀 Available Deployment Options[/bold cyan]\n")
342
+
343
+ # Cloud Providers table
344
+ providers_table = Table(title="Cloud Providers", show_header=True, header_style="bold cyan")
345
+ providers_table.add_column("Value", style="green", no_wrap=True)
346
+ providers_table.add_column("Description", style="yellow")
347
+
348
+ providers_table.add_row("local", "Local Docker Registry (default)")
349
+ providers_table.add_row("aws", "Amazon Web Services")
350
+ providers_table.add_row("azure", "Microsoft Azure")
351
+ providers_table.add_row("gcp", "Google Cloud Platform")
352
+
353
+ console.print(providers_table)
354
+ console.print()
355
+
356
+ # Deployment Types table
357
+ types_table = Table(title="Deployment Types", show_header=True, header_style="bold green")
358
+ types_table.add_column("Value", style="green", no_wrap=True)
359
+ types_table.add_column("Description", style="yellow")
360
+
361
+ types_table.add_row("webapp", "Web Application/Service (default)")
362
+ types_table.add_row("instance", "VM/Container Instance")
363
+
364
+ console.print(types_table)
365
+ console.print()
366
+
367
+ # Usage examples
368
+ console.print("[bold]Usage Examples:[/bold]\n")
369
+ console.print(" [dim]#[/dim] Deploy to AWS as a web application:")
370
+ console.print(" [cyan]cicd init --cloud-provider aws --deployment-type webapp[/cyan]\n")
371
+ console.print(" [dim]#[/dim] Deploy to Azure as a VM instance:")
372
+ console.print(" [cyan]cicd init --cloud-provider azure --deployment-type instance[/cyan]\n")
373
+ console.print(" [dim]#[/dim] Local Docker deployment:")
374
+ console.print(" [cyan]cicd init --cloud-provider local[/cyan]\n")
375
+
376
+
377
+ def main():
378
+ """Entry point for the CLI"""
379
+ app()
380
+
381
+
382
+ if __name__ == "__main__":
383
+ main()
384
+
core/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .base_detector import BaseDetector
2
+ from .base_generator import BaseGenerator
3
+ __all__ = ['BaseDetector', 'BaseGenerator']
core/base_detector.py ADDED
@@ -0,0 +1,113 @@
1
+ """
2
+ Base Detector class for all framework detectors
3
+ Provides common utilities for file checking, reading, and dependency parsing
4
+ """
5
+ import re
6
+ from pathlib import Path
7
+ from typing import Optional, Set, Dict
8
+
9
+
10
+ class BaseDetector:
11
+ """Base class for framework-specific detectors"""
12
+
13
+ def __init__(self, project_path: Path):
14
+ """
15
+ Initialize detector with project path
16
+
17
+ Args:
18
+ project_path: Path to the project to detect
19
+ """
20
+ self.project_path = Path(project_path)
21
+
22
+ def detect(self) -> Optional[Dict]:
23
+ """
24
+ Main detection method - must be overridden by child classes
25
+
26
+ Returns:
27
+ Dict with detection results or None if not detected
28
+ """
29
+ raise NotImplementedError("Subclasses must implement detect()")
30
+
31
+ # ============ Shared File Utilities ============
32
+
33
+ def file_exists(self, filename: str) -> bool:
34
+ """Check if a file exists in project"""
35
+ return (self.project_path / filename).exists()
36
+
37
+ def read_file(self, filename: str) -> str:
38
+ """Read file content safely"""
39
+ try:
40
+ file_path = self.project_path / filename
41
+ if file_path.exists():
42
+ return file_path.read_text()
43
+ except Exception:
44
+ pass
45
+ return ""
46
+
47
+ def has_files_with_extension(self, extension: str) -> bool:
48
+ """Check if project has files with given extension"""
49
+ pattern = f"*.{extension}" if not extension.startswith('*') else extension
50
+ return bool(list(self.project_path.rglob(pattern)))
51
+
52
+ # ============ Shared Version Detection ============
53
+
54
+ def normalize_version(self, version: str) -> str:
55
+ """
56
+ Normalize version to X.Y format
57
+ Example: 3.11.2 -> 3.11
58
+ """
59
+ parts = version.split('.')
60
+ if len(parts) >= 2:
61
+ return f"{parts[0]}.{parts[1]}"
62
+ return version
63
+
64
+ def extract_version_from_content(self, content: str, pattern: str) -> Optional[str]:
65
+ """
66
+ Extract version using regex pattern
67
+
68
+ Args:
69
+ content: File content to search
70
+ pattern: Regex pattern with one capture group for version
71
+ """
72
+ match = re.search(pattern, content)
73
+ if match:
74
+ return self.normalize_version(match.group(1))
75
+ return None
76
+
77
+ # ============ Shared Dependency Parsing ============
78
+
79
+ def parse_dependencies_from_file(self, filename: str) -> Set[str]:
80
+ """
81
+ Parse dependencies from a requirements-style file
82
+ Returns set of lowercase package names
83
+ """
84
+ deps = set()
85
+ content = self.read_file(filename)
86
+
87
+ for line in content.split('\n'):
88
+ line = line.strip().lower()
89
+ # Skip comments and empty lines
90
+ if not line or line.startswith('#') or line.startswith('-r'):
91
+ continue
92
+
93
+ # Extract package name (before ==, >=, etc.)
94
+ pkg = re.split(r'[=<>~!]', line)[0].strip()
95
+ if pkg:
96
+ deps.add(pkg)
97
+
98
+ return deps
99
+
100
+ def check_dependency_in_files(self, dependency: str, *filenames) -> bool:
101
+ """
102
+ Check if a dependency exists in any of the given files
103
+
104
+ Args:
105
+ dependency: Package name to look for (case-insensitive)
106
+ *filenames: Variable number of filenames to check
107
+ """
108
+ dependency = dependency.lower()
109
+ for filename in filenames:
110
+ deps = self.parse_dependencies_from_file(filename)
111
+ if dependency in deps:
112
+ return True
113
+ return False