amen-cli 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.
- amen/__init__.py +25 -0
- amen/cli.py +230 -0
- amen/frameworks.py +46 -0
- amen/templates/__init__.py +3 -0
- amen/templates/manager.py +617 -0
- amen_cli-0.1.0.dist-info/METADATA +189 -0
- amen_cli-0.1.0.dist-info/RECORD +11 -0
- amen_cli-0.1.0.dist-info/WHEEL +5 -0
- amen_cli-0.1.0.dist-info/entry_points.txt +2 -0
- amen_cli-0.1.0.dist-info/licenses/LICENSE +43 -0
- amen_cli-0.1.0.dist-info/top_level.txt +1 -0
amen/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""AMEN CLI - Python Web Framework Scaffolding Tool"""
|
|
2
|
+
__version__ = "0.1.0"
|
|
3
|
+
__author__ = "Tanaka Chinengundu"
|
|
4
|
+
|
|
5
|
+
__description__ = "A composer-inspired Python Web Framework Scaffolding Tool"
|
|
6
|
+
__license__ = "Modified MIT with Attribution Requirements"
|
|
7
|
+
__copyright__ = "Copyright (c) 2025 Tanaka Chinengundu"
|
|
8
|
+
__email__ = "tanakah30@gmail.com"
|
|
9
|
+
__url__ = "https://github.com/taqsblaze/amen-cli"
|
|
10
|
+
__docs__ = "https://github.com/taqsblaze/amen-cli/#readme"
|
|
11
|
+
__python_requires__ = ">=3.7"
|
|
12
|
+
|
|
13
|
+
SUPPORTED_FRAMEWORKS = {
|
|
14
|
+
"flask": {"version": ">=2.0.0", "port": 5000},
|
|
15
|
+
"fastapi": {"version": ">=0.68.0", "port": 8000}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# Package status
|
|
19
|
+
__status__ = "Development"
|
|
20
|
+
__maintainer__ = "Tanaka Chinengundu"
|
|
21
|
+
__credits__ = ["Tanaka Chinengundu"]
|
|
22
|
+
|
|
23
|
+
# Feature flags
|
|
24
|
+
ENABLE_LOGGING = True
|
|
25
|
+
DEBUG_MODE = True
|
amen/cli.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import subprocess
|
|
4
|
+
import venv
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
import questionary
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
12
|
+
|
|
13
|
+
from .frameworks import FRAMEWORKS
|
|
14
|
+
from .templates import TemplateManager
|
|
15
|
+
|
|
16
|
+
console = Console()
|
|
17
|
+
|
|
18
|
+
VALID_FRAMEWORKS = ['flask', 'fastapi', 'bottle', 'pyramid']
|
|
19
|
+
VALID_PROJECT_TYPES = ['webapp', 'api']
|
|
20
|
+
|
|
21
|
+
def create_project(path, framework, project_type):
|
|
22
|
+
"""
|
|
23
|
+
Create a new project with the specified framework and type.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
path (str): Project directory path
|
|
27
|
+
framework (str): Web framework to use
|
|
28
|
+
project_type (str): Type of project (webapp/api)
|
|
29
|
+
"""
|
|
30
|
+
if framework not in VALID_FRAMEWORKS:
|
|
31
|
+
raise ValueError(f"Invalid framework. Choose from: {VALID_FRAMEWORKS}")
|
|
32
|
+
|
|
33
|
+
if project_type not in VALID_PROJECT_TYPES:
|
|
34
|
+
raise ValueError(f"Invalid project type. Choose from: {VALID_PROJECT_TYPES}")
|
|
35
|
+
|
|
36
|
+
# Create project directory
|
|
37
|
+
os.makedirs(path, exist_ok=True)
|
|
38
|
+
|
|
39
|
+
# Create basic structure
|
|
40
|
+
os.makedirs(os.path.join(path, "app"), exist_ok=True)
|
|
41
|
+
os.makedirs(os.path.join(path, "app", "templates"), exist_ok=True)
|
|
42
|
+
os.makedirs(os.path.join(path, "app", "static"), exist_ok=True)
|
|
43
|
+
|
|
44
|
+
# Create empty files
|
|
45
|
+
open(os.path.join(path, "requirements.txt"), 'a').close()
|
|
46
|
+
open(os.path.join(path, "README.md"), 'a').close()
|
|
47
|
+
|
|
48
|
+
class AmenCLI:
|
|
49
|
+
def __init__(self):
|
|
50
|
+
self.template_manager = TemplateManager()
|
|
51
|
+
|
|
52
|
+
def welcome_banner(self):
|
|
53
|
+
"""Display welcome banner"""
|
|
54
|
+
console.print(Panel.fit(
|
|
55
|
+
"""AMEN: composer-inspired Python Web Framework Scaffolding
|
|
56
|
+
Create your web applications with ease!
|
|
57
|
+
By [bold magenta]Tanaka Chinengundu[/bold magenta]
|
|
58
|
+
[bold blue]
|
|
59
|
+
""",
|
|
60
|
+
border_style="magenta"
|
|
61
|
+
))
|
|
62
|
+
console.print()
|
|
63
|
+
|
|
64
|
+
def select_framework(self) -> str:
|
|
65
|
+
"""Interactive framework selection"""
|
|
66
|
+
frameworks = list(FRAMEWORKS.keys())
|
|
67
|
+
|
|
68
|
+
choice = questionary.select(
|
|
69
|
+
"🚀 Select a web framework:",
|
|
70
|
+
choices=[
|
|
71
|
+
questionary.Choice(f"{FRAMEWORKS[fw]['name']} - {FRAMEWORKS[fw]['description']}", fw)
|
|
72
|
+
for fw in frameworks
|
|
73
|
+
]
|
|
74
|
+
).ask()
|
|
75
|
+
|
|
76
|
+
return choice
|
|
77
|
+
|
|
78
|
+
def select_app_type(self) -> str:
|
|
79
|
+
"""Select application type"""
|
|
80
|
+
return questionary.select(
|
|
81
|
+
"🏗️ What type of application?",
|
|
82
|
+
choices=[
|
|
83
|
+
questionary.Choice("Full Web Application (with frontend)", "webapp"),
|
|
84
|
+
questionary.Choice("API Only", "api"),
|
|
85
|
+
]
|
|
86
|
+
).ask()
|
|
87
|
+
|
|
88
|
+
def get_app_name(self) -> str:
|
|
89
|
+
"""Get application name"""
|
|
90
|
+
return questionary.text(
|
|
91
|
+
"📝 Enter your application name:",
|
|
92
|
+
validate=lambda x: len(x.strip()) > 0 or "Application name cannot be empty"
|
|
93
|
+
).ask().strip()
|
|
94
|
+
|
|
95
|
+
def create_virtual_environment(self, app_path: Path) -> bool:
|
|
96
|
+
"""Create virtual environment"""
|
|
97
|
+
venv_path = app_path / "venv"
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
with Progress(
|
|
101
|
+
SpinnerColumn(),
|
|
102
|
+
TextColumn("[progress.description]{task.description}"),
|
|
103
|
+
console=console,
|
|
104
|
+
) as progress:
|
|
105
|
+
task = progress.add_task("Creating virtual environment...", total=None)
|
|
106
|
+
venv.create(venv_path, with_pip=True)
|
|
107
|
+
progress.update(task, description="✅ Virtual environment created")
|
|
108
|
+
|
|
109
|
+
return True
|
|
110
|
+
except Exception as e:
|
|
111
|
+
console.print(f"❌ Error creating virtual environment: {e}", style="red")
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
def install_framework(self, app_path: Path, framework: str) -> bool:
|
|
115
|
+
"""Install selected framework in virtual environment"""
|
|
116
|
+
venv_path = app_path / "venv"
|
|
117
|
+
|
|
118
|
+
# Determine pip path based on OS
|
|
119
|
+
if sys.platform == "win32":
|
|
120
|
+
pip_path = venv_path / "Scripts" / "pip"
|
|
121
|
+
else:
|
|
122
|
+
pip_path = venv_path / "bin" / "pip"
|
|
123
|
+
|
|
124
|
+
framework_info = FRAMEWORKS[framework]
|
|
125
|
+
packages = framework_info['packages']
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
with Progress(
|
|
129
|
+
SpinnerColumn(),
|
|
130
|
+
TextColumn("[progress.description]{task.description}"),
|
|
131
|
+
console=console,
|
|
132
|
+
) as progress:
|
|
133
|
+
task = progress.add_task(f"Installing {framework_info['name']}...", total=None)
|
|
134
|
+
|
|
135
|
+
for package in packages:
|
|
136
|
+
subprocess.run([
|
|
137
|
+
str(pip_path), "install", package
|
|
138
|
+
], check=True, capture_output=True)
|
|
139
|
+
|
|
140
|
+
progress.update(task, description=f"✅ {framework_info['name']} installed")
|
|
141
|
+
|
|
142
|
+
return True
|
|
143
|
+
except subprocess.CalledProcessError as e:
|
|
144
|
+
console.print(f"❌ Error installing {framework_info['name']}: {e}", style="red")
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
def create_app(self):
|
|
148
|
+
"""Main app creation flow"""
|
|
149
|
+
self.welcome_banner()
|
|
150
|
+
|
|
151
|
+
# Get user choices
|
|
152
|
+
framework = self.select_framework()
|
|
153
|
+
if not framework:
|
|
154
|
+
console.print("❌ No framework selected. Exiting.", style="red")
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
app_type = self.select_app_type()
|
|
158
|
+
if not app_type:
|
|
159
|
+
console.print("❌ No application type selected. Exiting.", style="red")
|
|
160
|
+
return
|
|
161
|
+
|
|
162
|
+
app_name = self.get_app_name()
|
|
163
|
+
if not app_name:
|
|
164
|
+
console.print("❌ No application name provided. Exiting.", style="red")
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
# Create application directory
|
|
168
|
+
app_path = Path.cwd() / app_name
|
|
169
|
+
|
|
170
|
+
if app_path.exists():
|
|
171
|
+
overwrite = questionary.confirm(
|
|
172
|
+
f"Directory '{app_name}' already exists. Overwrite?"
|
|
173
|
+
).ask()
|
|
174
|
+
|
|
175
|
+
if not overwrite:
|
|
176
|
+
console.print("❌ Operation cancelled.", style="yellow")
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
import shutil
|
|
180
|
+
shutil.rmtree(app_path)
|
|
181
|
+
|
|
182
|
+
app_path.mkdir()
|
|
183
|
+
console.print(f"📁 Created directory: {app_path}", style="green")
|
|
184
|
+
|
|
185
|
+
# Create virtual environment
|
|
186
|
+
if not self.create_virtual_environment(app_path):
|
|
187
|
+
return
|
|
188
|
+
|
|
189
|
+
# Install framework
|
|
190
|
+
if not self.install_framework(app_path, framework):
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
# Generate project structure
|
|
194
|
+
with Progress(
|
|
195
|
+
SpinnerColumn(),
|
|
196
|
+
TextColumn("[progress.description]{task.description}"),
|
|
197
|
+
console=console,
|
|
198
|
+
) as progress:
|
|
199
|
+
task = progress.add_task("Generating project structure...", total=None)
|
|
200
|
+
self.template_manager.generate_structure(app_path, framework, app_type, app_name)
|
|
201
|
+
progress.update(task, description="✅ Project structure generated")
|
|
202
|
+
|
|
203
|
+
# Success message
|
|
204
|
+
console.print(Panel(
|
|
205
|
+
f"""🎉 Successfully created '{app_name}'!
|
|
206
|
+
|
|
207
|
+
📁 Next Steps:
|
|
208
|
+
1. cd {app_name}
|
|
209
|
+
2. source venv/bin/activate (Linux/Mac) or venv\\Scripts\\activate (Windows)
|
|
210
|
+
3. python run.py
|
|
211
|
+
|
|
212
|
+
Your app will be running at http://localhost:{FRAMEWORKS[framework]['default_port']}
|
|
213
|
+
""".strip(),
|
|
214
|
+
title="🎊 Project Created Successfully!",
|
|
215
|
+
border_style="green"
|
|
216
|
+
))
|
|
217
|
+
|
|
218
|
+
@click.group()
|
|
219
|
+
def main():
|
|
220
|
+
"""Amen - composer-inspired Python web framework scaffolding tool"""
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
@main.command()
|
|
224
|
+
def create():
|
|
225
|
+
"""Create a new web application"""
|
|
226
|
+
cli = AmenCLI()
|
|
227
|
+
cli.create_app()
|
|
228
|
+
|
|
229
|
+
if __name__ == "__main__":
|
|
230
|
+
main()
|
amen/frameworks.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Framework configurations and metadata"""
|
|
2
|
+
|
|
3
|
+
FRAMEWORKS = {
|
|
4
|
+
'flask': {
|
|
5
|
+
'name': 'Flask',
|
|
6
|
+
'description': 'Lightweight WSGI web application framework',
|
|
7
|
+
'packages': ['flask', 'flask-cors', 'python-dotenv'],
|
|
8
|
+
'default_port': 5000,
|
|
9
|
+
'entry_file': 'app.py',
|
|
10
|
+
},
|
|
11
|
+
# 'django': {
|
|
12
|
+
# 'name': 'Django',
|
|
13
|
+
# 'description': 'High-level Python web framework',
|
|
14
|
+
# 'packages': ['django', 'django-cors-headers', 'python-dotenv'],
|
|
15
|
+
# 'default_port': 8000,
|
|
16
|
+
# 'entry_file': 'manage.py',
|
|
17
|
+
# },
|
|
18
|
+
'fastapi': {
|
|
19
|
+
'name': 'FastAPI',
|
|
20
|
+
'description': 'Modern, fast web framework for building APIs',
|
|
21
|
+
'packages': ['fastapi', 'uvicorn[standard]', 'python-dotenv'],
|
|
22
|
+
'default_port': 8000,
|
|
23
|
+
'entry_file': 'main.py',
|
|
24
|
+
},
|
|
25
|
+
# 'bottle': {
|
|
26
|
+
# 'name': 'Bottle',
|
|
27
|
+
# 'description': 'Fast, simple micro web framework',
|
|
28
|
+
# 'packages': ['bottle', 'python-dotenv'],
|
|
29
|
+
# 'default_port': 8080,
|
|
30
|
+
# 'entry_file': 'app.py',
|
|
31
|
+
# },
|
|
32
|
+
# 'pyramid': {
|
|
33
|
+
# 'name': 'Pyramid',
|
|
34
|
+
# 'description': 'Flexible, open source web framework',
|
|
35
|
+
# 'packages': ['pyramid', 'waitress', 'python-dotenv'],
|
|
36
|
+
# 'default_port': 6543,
|
|
37
|
+
# 'entry_file': 'app.py',
|
|
38
|
+
# },
|
|
39
|
+
# 'django-hotsauce': {
|
|
40
|
+
# 'name': 'Django Hotsauce',
|
|
41
|
+
# 'description': 'Django-based rapid development framework',
|
|
42
|
+
# 'packages': ['django', 'django-hotsauce', 'python-dotenv'],
|
|
43
|
+
# 'default_port': 8000,
|
|
44
|
+
# 'entry_file': 'manage.py',
|
|
45
|
+
# }
|
|
46
|
+
}
|
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
"""Template management for different frameworks"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from ..frameworks import FRAMEWORKS
|
|
6
|
+
|
|
7
|
+
class TemplateManager:
|
|
8
|
+
def _write_file(self, path: Path, content: str):
|
|
9
|
+
"""Helper method to write files with UTF-8 encoding"""
|
|
10
|
+
# Ensure parent directory exists
|
|
11
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
12
|
+
path.write_text(content, encoding='utf-8')
|
|
13
|
+
|
|
14
|
+
def generate_structure(self, app_path: Path, framework: str, app_type: str, app_name: str):
|
|
15
|
+
"""Generate project structure based on framework and app type"""
|
|
16
|
+
|
|
17
|
+
# Create all necessary directories
|
|
18
|
+
(app_path / "app").mkdir(exist_ok=True)
|
|
19
|
+
(app_path / "app" / "templates").mkdir(exist_ok=True)
|
|
20
|
+
(app_path / "app" / "static").mkdir(exist_ok=True)
|
|
21
|
+
(app_path / "app" / "static" / "css").mkdir(exist_ok=True)
|
|
22
|
+
(app_path / "app" / "static" / "js").mkdir(exist_ok=True)
|
|
23
|
+
(app_path / "tests").mkdir(exist_ok=True) # Create tests directory
|
|
24
|
+
|
|
25
|
+
# Generate framework-specific files
|
|
26
|
+
if framework == 'flask':
|
|
27
|
+
self._generate_flask_files(app_path, app_type, app_name)
|
|
28
|
+
elif framework == 'fastapi':
|
|
29
|
+
self._generate_fastapi_files(app_path, app_type, app_name)
|
|
30
|
+
# Add other frameworks as needed
|
|
31
|
+
|
|
32
|
+
# Generate common files
|
|
33
|
+
self._generate_common_files(app_path, framework, app_name, app_type)
|
|
34
|
+
|
|
35
|
+
def _generate_flask_files(self, app_path: Path, app_type: str, app_name: str):
|
|
36
|
+
"""Generate Flask files"""
|
|
37
|
+
app_content = f"""from flask import Flask, render_template, jsonify
|
|
38
|
+
from flask_cors import CORS
|
|
39
|
+
from dotenv import load_dotenv
|
|
40
|
+
import os
|
|
41
|
+
|
|
42
|
+
load_dotenv()
|
|
43
|
+
|
|
44
|
+
app = Flask(__name__)
|
|
45
|
+
CORS(app)
|
|
46
|
+
|
|
47
|
+
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev-secret-key')
|
|
48
|
+
|
|
49
|
+
@app.route('/')
|
|
50
|
+
def index():
|
|
51
|
+
{"return render_template('index.html', title='" + app_name + "')" if app_type == 'webapp' else "return jsonify({'message': 'Welcome to " + app_name + " API!'})"}
|
|
52
|
+
|
|
53
|
+
@app.route('/health')
|
|
54
|
+
def health():
|
|
55
|
+
return jsonify({{'status': 'healthy', 'service': '{app_name}'}})
|
|
56
|
+
|
|
57
|
+
if __name__ == '__main__':
|
|
58
|
+
app.run(host='0.0.0.0', port=int(os.getenv('PORT', 5000)), debug=True)
|
|
59
|
+
"""
|
|
60
|
+
self._write_file(app_path / "app" / "app.py", app_content)
|
|
61
|
+
self._write_file(app_path / "run.py", "from app.app import app\n\nif __name__ == '__main__':\n app.run()")
|
|
62
|
+
|
|
63
|
+
if app_type == 'webapp':
|
|
64
|
+
self._generate_html_template(app_path, app_name)
|
|
65
|
+
|
|
66
|
+
def _generate_fastapi_files(self, app_path: Path, app_type: str, app_name: str):
|
|
67
|
+
"""Generate FastAPI files"""
|
|
68
|
+
main_content = f"""from fastapi import FastAPI, Request
|
|
69
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
70
|
+
{"from fastapi.templating import Jinja2Templates" if app_type == 'webapp' else ""}
|
|
71
|
+
{"from fastapi.staticfiles import StaticFiles" if app_type == 'webapp' else ""}
|
|
72
|
+
from dotenv import load_dotenv
|
|
73
|
+
import os
|
|
74
|
+
|
|
75
|
+
load_dotenv()
|
|
76
|
+
|
|
77
|
+
app = FastAPI(title="{app_name}")
|
|
78
|
+
|
|
79
|
+
app.add_middleware(
|
|
80
|
+
CORSMiddleware,
|
|
81
|
+
allow_origins=["*"],
|
|
82
|
+
allow_credentials=True,
|
|
83
|
+
allow_methods=["*"],
|
|
84
|
+
allow_headers=["*"],
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
{"app.mount('/static', StaticFiles(directory='app/static'), name='static')" if app_type == 'webapp' else ""}
|
|
88
|
+
{"templates = Jinja2Templates(directory='app/templates')" if app_type == 'webapp' else ""}
|
|
89
|
+
|
|
90
|
+
@app.get("/")
|
|
91
|
+
async def root({"request: Request" if app_type == 'webapp' else ""}):
|
|
92
|
+
{"return templates.TemplateResponse('index.html', {'request': request, 'title': '" + app_name + "'})" if app_type == 'webapp' else 'return {"message": "Welcome to ' + app_name + ' API!"}'}
|
|
93
|
+
|
|
94
|
+
@app.get("/health")
|
|
95
|
+
async def health():
|
|
96
|
+
return {{"status": "healthy", "service": "{app_name}"}}
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
import uvicorn
|
|
100
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
101
|
+
"""
|
|
102
|
+
self._write_file(app_path / "app" / "main.py", main_content)
|
|
103
|
+
self._write_file(app_path / "run.py", "import uvicorn\nfrom app.main import app\n\nif __name__ == '__main__':\n uvicorn.run(app, host='0.0.0.0', port=8000)")
|
|
104
|
+
|
|
105
|
+
if app_type == 'webapp':
|
|
106
|
+
self._generate_html_template(app_path, app_name)
|
|
107
|
+
|
|
108
|
+
def _generate_html_template(self, app_path: Path, app_name: str):
|
|
109
|
+
"""Generate HTML template"""
|
|
110
|
+
template_content = f"""<!DOCTYPE html>
|
|
111
|
+
<html lang="en">
|
|
112
|
+
<head>
|
|
113
|
+
<meta charset="UTF-8">
|
|
114
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
115
|
+
<title>{app_name}</title>
|
|
116
|
+
<link rel="stylesheet" href="/static/css/style.css">
|
|
117
|
+
</head>
|
|
118
|
+
<body>
|
|
119
|
+
<div class="container">
|
|
120
|
+
<h1>Welcome to {app_name}!</h1>
|
|
121
|
+
<p>Your application is running successfully.</p>
|
|
122
|
+
</div>
|
|
123
|
+
<script src="/static/js/app.js"></script>
|
|
124
|
+
</body>
|
|
125
|
+
</html>"""
|
|
126
|
+
self._write_file(app_path / "app" / "templates" / "index.html", template_content)
|
|
127
|
+
|
|
128
|
+
def _generate_common_files(self, app_path: Path, framework: str, app_name: str, app_type: str):
|
|
129
|
+
"""Generate common files for all projects"""
|
|
130
|
+
|
|
131
|
+
# requirements.txt
|
|
132
|
+
framework_info = FRAMEWORKS[framework]
|
|
133
|
+
requirements = "\n".join(framework_info['packages'])
|
|
134
|
+
self._write_file(app_path / "requirements.txt", requirements)
|
|
135
|
+
|
|
136
|
+
# .env
|
|
137
|
+
env_content = f"""SECRET_KEY=your-secret-key-here
|
|
138
|
+
DEBUG=True
|
|
139
|
+
PORT={framework_info['default_port']}
|
|
140
|
+
"""
|
|
141
|
+
self._write_file(app_path / ".env", env_content)
|
|
142
|
+
|
|
143
|
+
# README.md
|
|
144
|
+
readme_content = f"""# {app_name}
|
|
145
|
+
|
|
146
|
+
A web application built with {framework_info['name']}.
|
|
147
|
+
|
|
148
|
+
## Quick Start
|
|
149
|
+
|
|
150
|
+
1. Activate virtual environment:
|
|
151
|
+
```bash
|
|
152
|
+
source venv/bin/activate # Linux/Mac
|
|
153
|
+
venv\\Scripts\\activate # Windows
|
|
154
|
+
3. **Install dependencies** (if not already installed)
|
|
155
|
+
```bash
|
|
156
|
+
pip install -r requirements.txt
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
4. **Set up environment variables**
|
|
160
|
+
```bash
|
|
161
|
+
cp .env.example .env
|
|
162
|
+
# Edit .env file with your configuration
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
5. **Run the application**
|
|
166
|
+
```bash
|
|
167
|
+
python run.py
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Your application will be available at `http://localhost:{framework_info['default_port']}`
|
|
171
|
+
|
|
172
|
+
## 📁 Project Structure
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
{app_name}/
|
|
176
|
+
├── venv/ # Virtual environment
|
|
177
|
+
├── app/ # Main application code
|
|
178
|
+
│ ├── __init__.py
|
|
179
|
+
│ ├── {framework_info['entry_file']} # Main application file
|
|
180
|
+
│ ├── templates/ # HTML templates (if web app)
|
|
181
|
+
│ └── static/ # Static files (CSS, JS, images)
|
|
182
|
+
│ ├── css/
|
|
183
|
+
│ └── js/
|
|
184
|
+
├── tests/ # Test files
|
|
185
|
+
├── docs/ # Documentation
|
|
186
|
+
├── requirements.txt # Python dependencies
|
|
187
|
+
├── .env # Environment variables (local)
|
|
188
|
+
├── .env.example # Environment variables template
|
|
189
|
+
├── .gitignore # Git ignore rules
|
|
190
|
+
├── run.py # Application runner
|
|
191
|
+
└── README.md # This file
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## 🛠️ Development
|
|
195
|
+
|
|
196
|
+
### Framework: {FRAMEWORKS[framework]['name']}
|
|
197
|
+
{FRAMEWORKS[framework]['description']}
|
|
198
|
+
|
|
199
|
+
### Application Type: {"Full Web Application" if app_type == 'webapp' else "API Only"}
|
|
200
|
+
|
|
201
|
+
### Available Endpoints
|
|
202
|
+
- `GET /` - {"Main page" if app_type == 'webapp' else "API welcome message"}
|
|
203
|
+
- `GET /health` - Health check endpoint
|
|
204
|
+
|
|
205
|
+
### Adding New Routes
|
|
206
|
+
|
|
207
|
+
#### {FRAMEWORKS[framework]['name']} specific instructions:
|
|
208
|
+
'''
|
|
209
|
+
|
|
210
|
+
# Add framework-specific route examples
|
|
211
|
+
if framework == 'flask':
|
|
212
|
+
readme_content += '''
|
|
213
|
+
```python
|
|
214
|
+
from flask import Flask
|
|
215
|
+
|
|
216
|
+
@app.route('/new-route')
|
|
217
|
+
def new_route():
|
|
218
|
+
return 'Hello from new route!'
|
|
219
|
+
```
|
|
220
|
+
'''
|
|
221
|
+
elif framework == 'fastapi':
|
|
222
|
+
readme_content += '''
|
|
223
|
+
```python
|
|
224
|
+
from fastapi import FastAPI
|
|
225
|
+
|
|
226
|
+
@app.get("/new-route")
|
|
227
|
+
async def new_route():
|
|
228
|
+
return {{"message": "Hello from new route!"}}
|
|
229
|
+
```
|
|
230
|
+
'''
|
|
231
|
+
elif framework == 'django':
|
|
232
|
+
readme_content += '''
|
|
233
|
+
1. Add to `app/urls.py`:
|
|
234
|
+
```python
|
|
235
|
+
path('new-route/', views.new_route, name='new_route'),
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
2. Add to `app/views.py`:
|
|
239
|
+
```python
|
|
240
|
+
def new_route(request):
|
|
241
|
+
return JsonResponse({{'message': 'Hello from new route!'}})
|
|
242
|
+
```
|
|
243
|
+
'''
|
|
244
|
+
elif framework == 'bottle':
|
|
245
|
+
readme_content += '''
|
|
246
|
+
```python
|
|
247
|
+
@app.route('/new-route')
|
|
248
|
+
def new_route():
|
|
249
|
+
return {{'message': 'Hello from new route!'}}
|
|
250
|
+
```
|
|
251
|
+
'''
|
|
252
|
+
elif framework == 'pyramid':
|
|
253
|
+
readme_content += '''
|
|
254
|
+
1. Add route in `main()`:
|
|
255
|
+
```python
|
|
256
|
+
config.add_route('new_route', '/new-route')
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
2. Add view function:
|
|
260
|
+
```python
|
|
261
|
+
@view_config(route_name='new_route', renderer='json')
|
|
262
|
+
def new_route_view(request):
|
|
263
|
+
return {{'message': 'Hello from new route!'}}
|
|
264
|
+
```
|
|
265
|
+
"""
|
|
266
|
+
|
|
267
|
+
readme_content += f"""
|
|
268
|
+
|
|
269
|
+
## 🧪 Testing
|
|
270
|
+
|
|
271
|
+
Run tests with:
|
|
272
|
+
```bash
|
|
273
|
+
pytest tests/
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## 📦 Deployment
|
|
277
|
+
|
|
278
|
+
### Environment Variables
|
|
279
|
+
Make sure to set these in production:
|
|
280
|
+
- `SECRET_KEY`: A secure secret key
|
|
281
|
+
- `DEBUG`: Set to `False` in production
|
|
282
|
+
- `PORT`: Port number for the application
|
|
283
|
+
|
|
284
|
+
### Docker (Optional)
|
|
285
|
+
Create a `Dockerfile`:
|
|
286
|
+
```dockerfile
|
|
287
|
+
FROM python:3.9-slim
|
|
288
|
+
|
|
289
|
+
WORKDIR /app
|
|
290
|
+
|
|
291
|
+
COPY requirements.txt .`
|
|
292
|
+
RUN pip install -r requirements.txt
|
|
293
|
+
|
|
294
|
+
COPY . .
|
|
295
|
+
|
|
296
|
+
EXPOSE {framework_info['default_port']}
|
|
297
|
+
|
|
298
|
+
CMD ["python", "run.py"]
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## 🤝 Contributing
|
|
302
|
+
|
|
303
|
+
1. Fork the repository
|
|
304
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
305
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
306
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
307
|
+
5. Open a Pull Request
|
|
308
|
+
|
|
309
|
+
## 📄 License
|
|
310
|
+
|
|
311
|
+
This project is licensed under the MIT License.
|
|
312
|
+
|
|
313
|
+
## 🙏 Acknowledgments
|
|
314
|
+
|
|
315
|
+
- Built with [Amen CLI](https://github.com/your-username/amen-cli)
|
|
316
|
+
- Powered by {FRAMEWORKS[framework]['name']}
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
Happy coding! 🎉
|
|
321
|
+
"""
|
|
322
|
+
|
|
323
|
+
self._write_file(app_path / "README.md", readme_content)
|
|
324
|
+
|
|
325
|
+
# Generate CSS
|
|
326
|
+
css_content = """/* Reset and Base Styles */
|
|
327
|
+
* {
|
|
328
|
+
margin: 0;
|
|
329
|
+
padding: 0;
|
|
330
|
+
box-sizing: border-box;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
body {
|
|
334
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
335
|
+
line-height: 1.6;
|
|
336
|
+
color: #333;
|
|
337
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
338
|
+
min-height: 100vh;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.container {
|
|
342
|
+
max-width: 1200px;
|
|
343
|
+
margin: 0 auto;
|
|
344
|
+
padding: 2rem;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* Header */
|
|
348
|
+
header {
|
|
349
|
+
text-align: center;
|
|
350
|
+
margin-bottom: 3rem;
|
|
351
|
+
color: white;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
header h1 {
|
|
355
|
+
font-size: 3rem;
|
|
356
|
+
font-weight: 700;
|
|
357
|
+
margin-bottom: 0.5rem;
|
|
358
|
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.subtitle {
|
|
362
|
+
font-size: 1.2rem;
|
|
363
|
+
opacity: 0.9;
|
|
364
|
+
font-weight: 300;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* Hero Section */
|
|
368
|
+
.hero {
|
|
369
|
+
background: white;
|
|
370
|
+
padding: 3rem;
|
|
371
|
+
border-radius: 15px;
|
|
372
|
+
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
|
373
|
+
text-align: center;
|
|
374
|
+
margin-bottom: 3rem;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.hero h2 {
|
|
378
|
+
color: #667eea;
|
|
379
|
+
font-size: 2rem;
|
|
380
|
+
margin-bottom: 1rem;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.hero p {
|
|
384
|
+
font-size: 1.1rem;
|
|
385
|
+
color: #666;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/* Features Grid */
|
|
389
|
+
.features {
|
|
390
|
+
display: grid;
|
|
391
|
+
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
|
392
|
+
gap: 2rem;
|
|
393
|
+
margin-bottom: 3rem;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.feature {
|
|
397
|
+
background: white;
|
|
398
|
+
padding: 2rem;
|
|
399
|
+
border-radius: 10px;
|
|
400
|
+
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
|
|
401
|
+
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.feature:hover {
|
|
405
|
+
transform: translateY(-5px);
|
|
406
|
+
box-shadow: 0 15px 35px rgba(0,0,0,0.15);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.feature h3 {
|
|
410
|
+
color: #667eea;
|
|
411
|
+
font-size: 1.3rem;
|
|
412
|
+
margin-bottom: 1rem;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.feature p {
|
|
416
|
+
color: #666;
|
|
417
|
+
font-size: 1rem;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/* Footer */
|
|
421
|
+
footer {
|
|
422
|
+
text-align: center;
|
|
423
|
+
color: white;
|
|
424
|
+
opacity: 0.8;
|
|
425
|
+
padding: 2rem 0;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* Responsive Design */
|
|
429
|
+
@media (max-width: 768px) {
|
|
430
|
+
.container {
|
|
431
|
+
padding: 1rem;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
header h1 {
|
|
435
|
+
font-size: 2rem;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.hero {
|
|
439
|
+
padding: 2rem;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.hero h2 {
|
|
443
|
+
font-size: 1.5rem;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.features {
|
|
447
|
+
grid-template-columns: 1fr;
|
|
448
|
+
gap: 1rem;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.feature {
|
|
452
|
+
padding: 1.5rem;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/* Animation */
|
|
457
|
+
@keyframes fadeInUp {
|
|
458
|
+
from {
|
|
459
|
+
opacity: 0;
|
|
460
|
+
transform: translateY(30px);
|
|
461
|
+
}
|
|
462
|
+
to {
|
|
463
|
+
opacity: 1;
|
|
464
|
+
transform: translateY(0);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
.hero, .feature {
|
|
469
|
+
animation: fadeInUp 0.6s ease-out;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.feature:nth-child(2) {
|
|
473
|
+
animation-delay: 0.1s;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
.feature:nth-child(3) {
|
|
477
|
+
animation-delay: 0.2s;
|
|
478
|
+
}
|
|
479
|
+
"""
|
|
480
|
+
|
|
481
|
+
self._write_file(app_path / "app" / "static" / "css" / "style.css", css_content)
|
|
482
|
+
|
|
483
|
+
# Generate JavaScript
|
|
484
|
+
js_content = """// Main application JavaScript
|
|
485
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
486
|
+
console.log('Application loaded successfully!');
|
|
487
|
+
|
|
488
|
+
// Add smooth scrolling for anchor links
|
|
489
|
+
const links = document.querySelectorAll('a[href^="#"]');
|
|
490
|
+
links.forEach(link => {
|
|
491
|
+
link.addEventListener('click', function(e) {
|
|
492
|
+
e.preventDefault();
|
|
493
|
+
const target = document.querySelector(this.getAttribute('href'));
|
|
494
|
+
if (target) {
|
|
495
|
+
target.scrollIntoView({
|
|
496
|
+
behavior: 'smooth',
|
|
497
|
+
block: 'start'
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// Add click animation to features
|
|
504
|
+
const features = document.querySelectorAll('.feature');
|
|
505
|
+
features.forEach(feature => {
|
|
506
|
+
feature.addEventListener('click', function() {
|
|
507
|
+
this.style.transform = 'scale(0.98)';
|
|
508
|
+
setTimeout(() => {
|
|
509
|
+
this.style.transform = '';
|
|
510
|
+
}, 150);
|
|
511
|
+
});
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// Health check function (example API call)
|
|
515
|
+
async function checkHealth() {
|
|
516
|
+
try {
|
|
517
|
+
const response = await fetch('/health');
|
|
518
|
+
const data = await response.json();
|
|
519
|
+
console.log('Health check:', data);
|
|
520
|
+
} catch (error) {
|
|
521
|
+
console.error('Health check failed:', error);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Uncomment to perform health check on load
|
|
526
|
+
// checkHealth();
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// Utility functions
|
|
530
|
+
const utils = {
|
|
531
|
+
// Format date
|
|
532
|
+
formatDate: (date) => {
|
|
533
|
+
return new Intl.DateTimeFormat('en-US', {
|
|
534
|
+
year: 'numeric',
|
|
535
|
+
month: 'long',
|
|
536
|
+
day: 'numeric'
|
|
537
|
+
}).format(date);
|
|
538
|
+
},
|
|
539
|
+
|
|
540
|
+
// Debounce function
|
|
541
|
+
debounce: (func, wait) => {
|
|
542
|
+
let timeout;
|
|
543
|
+
return function executedFunction(...args) {
|
|
544
|
+
const later = () => {
|
|
545
|
+
clearTimeout(timeout);
|
|
546
|
+
func(...args);
|
|
547
|
+
};
|
|
548
|
+
clearTimeout(timeout);
|
|
549
|
+
timeout = setTimeout(later, wait);
|
|
550
|
+
};
|
|
551
|
+
},
|
|
552
|
+
|
|
553
|
+
// Show notification (you can integrate with a toast library)
|
|
554
|
+
showNotification: (message, type = 'info') => {
|
|
555
|
+
console.log(`[${type.toUpperCase()}] ${message}`);
|
|
556
|
+
// Implement your notification system here
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
// Export for use in other scripts
|
|
561
|
+
window.AppUtils = utils;
|
|
562
|
+
"""
|
|
563
|
+
self._write_file(app_path / "app" / "static" / "js" / "app.js", js_content)
|
|
564
|
+
|
|
565
|
+
# Generate test files
|
|
566
|
+
test_content = f"""import pytest
|
|
567
|
+
import sys
|
|
568
|
+
import os
|
|
569
|
+
|
|
570
|
+
# Add the app directory to the Python path
|
|
571
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'app'))
|
|
572
|
+
|
|
573
|
+
def test_basic_functionality():
|
|
574
|
+
'''Test basic functionality'''
|
|
575
|
+
assert True # Replace with actual tests
|
|
576
|
+
|
|
577
|
+
class Test{app_name.replace('-', '').replace('_', '').title()}:
|
|
578
|
+
'''Test class for {app_name}'''
|
|
579
|
+
|
|
580
|
+
def test_app_creation(self):
|
|
581
|
+
'''Test application creation'''
|
|
582
|
+
# Add your app-specific tests here
|
|
583
|
+
pass
|
|
584
|
+
|
|
585
|
+
def test_health_endpoint(self):
|
|
586
|
+
'''Test health endpoint'''
|
|
587
|
+
# Add health endpoint test
|
|
588
|
+
pass
|
|
589
|
+
"""
|
|
590
|
+
# Add more tests as needed
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
self._write_file(app_path / "tests" / "__init__.py", "")
|
|
594
|
+
self._write_file(app_path / "tests" / f"test_{app_name.replace('-', '_')}.py", test_content)
|
|
595
|
+
|
|
596
|
+
# Generate pytest configuration
|
|
597
|
+
pytest_ini = """
|
|
598
|
+
[tool:pytest]
|
|
599
|
+
testpaths = tests
|
|
600
|
+
python_files = test_*.py
|
|
601
|
+
python_functions = test_*
|
|
602
|
+
python_classes = Test*
|
|
603
|
+
addopts = -v --tb=short
|
|
604
|
+
"""
|
|
605
|
+
|
|
606
|
+
self._write_file(app_path / "pytest.ini", pytest_ini)
|
|
607
|
+
|
|
608
|
+
self._write_file(app_path / "pytest.ini", pytest_ini)
|
|
609
|
+
|
|
610
|
+
# MANIFEST.in - for including non-Python files in the package
|
|
611
|
+
MANIFEST_IN = """
|
|
612
|
+
include README.md
|
|
613
|
+
include LICENSE
|
|
614
|
+
recursive-include amen/templates *.py *.html *.css *.js *.md
|
|
615
|
+
recursive-exclude * __pycache__
|
|
616
|
+
recursive-exclude * *.py[co]
|
|
617
|
+
"""
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: amen-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: composer-inspired Python web framework scaffolding tool
|
|
5
|
+
Home-page: https://github.com/taqsblaze/amen-cli
|
|
6
|
+
Author: Tanaka Chinengundu
|
|
7
|
+
Author-email: tanakah30@gmail.com
|
|
8
|
+
Project-URL: Bug Reports, https://github.com/taqsblaze/amen-cli/issues
|
|
9
|
+
Project-URL: Source, https://github.com/taqsblaze/amen-cli
|
|
10
|
+
Project-URL: Documentation, https://github.com/taqsblaze/amen-cli#readme
|
|
11
|
+
Keywords: python,web-framework,cli,scaffold,amen,composer,laravel,flask,django,fastapi,bottle,pyramid
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: click>=8.0.0
|
|
23
|
+
Requires-Dist: rich>=12.0.0
|
|
24
|
+
Requires-Dist: questionary>=1.10.0
|
|
25
|
+
Requires-Dist: virtualenv>=20.0.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=6.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-cov>=2.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: black>=21.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: flake8>=3.9.0; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy>=0.900; extra == "dev"
|
|
32
|
+
Dynamic: author
|
|
33
|
+
Dynamic: author-email
|
|
34
|
+
Dynamic: classifier
|
|
35
|
+
Dynamic: description
|
|
36
|
+
Dynamic: description-content-type
|
|
37
|
+
Dynamic: home-page
|
|
38
|
+
Dynamic: keywords
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
Dynamic: project-url
|
|
41
|
+
Dynamic: provides-extra
|
|
42
|
+
Dynamic: requires-dist
|
|
43
|
+
Dynamic: requires-python
|
|
44
|
+
Dynamic: summary
|
|
45
|
+
|
|
46
|
+
# 🚀 AMEN CLI 
|
|
47
|
+
A composer-inspired Python Web Framework Scaffolding Tool that helps you create web applications with ease!
|
|
48
|
+
|
|
49
|
+
## ✨ Features
|
|
50
|
+
|
|
51
|
+
- 🎯 Interactive project setup wizard
|
|
52
|
+
- 🔧 Multiple framework support:
|
|
53
|
+
- Flask - Lightweight WSGI framework
|
|
54
|
+
- FastAPI - Modern, fast API framework
|
|
55
|
+
- Bottle - Simple micro web framework 🚧
|
|
56
|
+
- Pyramid - Flexible web framework 🚧
|
|
57
|
+
- 🎨 Project templates for both web apps and APIs
|
|
58
|
+
- 🔄 Automatic virtual environment setup
|
|
59
|
+
- 📦 Dependency management
|
|
60
|
+
- 🏗️ Structured project scaffolding
|
|
61
|
+
|
|
62
|
+
## 🛠️ Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install amen-cli
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 📖 Usage
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Create a new project
|
|
72
|
+
amen create
|
|
73
|
+
|
|
74
|
+
# Follow the interactive prompts to:
|
|
75
|
+
# 1. Select a framework
|
|
76
|
+
# 2. Choose application type (webapp/api)
|
|
77
|
+
# 3. Name your project
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## 🌟 Project Structure
|
|
81
|
+
|
|
82
|
+
When you create a project, AMEN generates:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
your-app/
|
|
86
|
+
├── venv/ # Virtual environment
|
|
87
|
+
├── app/ # Main application code
|
|
88
|
+
│ ├── templates/ # HTML templates (webapp)
|
|
89
|
+
│ └── static/ # Static files
|
|
90
|
+
│ ├── css/ # Stylesheets
|
|
91
|
+
│ └── js/ # JavaScript files
|
|
92
|
+
├── tests/ # Test directory
|
|
93
|
+
├── requirements.txt # Python dependencies
|
|
94
|
+
├── .env # Environment variables
|
|
95
|
+
└── README.md # Project documentation
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 🎯 Supported Frameworks
|
|
99
|
+
|
|
100
|
+
| Framework | Description | Default Port | Status |
|
|
101
|
+
|-----------|-------------|--------------|--------|
|
|
102
|
+
| Flask | Lightweight WSGI web framework | 5000 | ✅ |
|
|
103
|
+
| FastAPI | Modern, fast web framework | 8000 | ✅ |
|
|
104
|
+
| Django | High-level Python web framework | 8000 | ❌ |
|
|
105
|
+
| Bottle | Fast, simple micro framework | 8080 | 🚧 |
|
|
106
|
+
| Pyramid | Flexible web framework | 6543 | 🚧 |
|
|
107
|
+
|
|
108
|
+
## Work in Progress
|
|
109
|
+
Currently implementing support for additional web frameworks:
|
|
110
|
+
|
|
111
|
+
- **Bottle**: Integration in development
|
|
112
|
+
- **Pyramid**: Initial implementation phase
|
|
113
|
+
|
|
114
|
+
These frameworks will enable:
|
|
115
|
+
- Route mapping and handling
|
|
116
|
+
- Request/response processing
|
|
117
|
+
- Middleware integration
|
|
118
|
+
- Template rendering support
|
|
119
|
+
|
|
120
|
+
Check back for updates or follow the project's issues for implementation progress. Contributions are welcome!
|
|
121
|
+
|
|
122
|
+
> Note: For now, please use our stable implementations for Flask or FastAPI.
|
|
123
|
+
## 🚗 Quick Start
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Install AMEN CLI
|
|
127
|
+
pip install amen-cli
|
|
128
|
+
|
|
129
|
+
# Create a new project
|
|
130
|
+
amen create
|
|
131
|
+
|
|
132
|
+
# Follow the interactive prompts
|
|
133
|
+
|
|
134
|
+
# Navigate to your project
|
|
135
|
+
cd your-project-name
|
|
136
|
+
|
|
137
|
+
# Activate virtual environment
|
|
138
|
+
source venv/bin/activate # Linux/Mac
|
|
139
|
+
venv\Scripts\activate # Windows
|
|
140
|
+
|
|
141
|
+
# Run your application
|
|
142
|
+
python run.py
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## 🔧 Development
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Clone the repository
|
|
149
|
+
git clone https://github.com/taqsblaze/amen-cli.git
|
|
150
|
+
|
|
151
|
+
# Install for development and testing
|
|
152
|
+
pip install -e .
|
|
153
|
+
pip install pytest pytest-cov
|
|
154
|
+
|
|
155
|
+
# Run tests
|
|
156
|
+
pytest
|
|
157
|
+
|
|
158
|
+
# Run tests with coverage
|
|
159
|
+
pytest
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## 🤝 Contributing
|
|
163
|
+
|
|
164
|
+
Contributions are welcome! Here's how:
|
|
165
|
+
|
|
166
|
+
1. Fork the repository
|
|
167
|
+
2. Create your feature branch: `git checkout -b feature/amazing-feature`
|
|
168
|
+
3. Make your changes
|
|
169
|
+
4. Push to the branch: `git push origin feature/amazing-feature`
|
|
170
|
+
5. Open a Pull Request
|
|
171
|
+
|
|
172
|
+
## 📝 License
|
|
173
|
+
|
|
174
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
175
|
+
|
|
176
|
+
## 👥 Contact & Support
|
|
177
|
+
|
|
178
|
+
- 🌐 [GitHub Repository](https://github.com/taqsblaze/amen-cli)
|
|
179
|
+
- 🐛 [Issue Tracker](https://github.com/taqsblaze/amen-cli/issues)
|
|
180
|
+
- 📧 [Send Email](mailto:tanakah30@gmail.com)
|
|
181
|
+
|
|
182
|
+
## ⭐ Credits
|
|
183
|
+
|
|
184
|
+
Created by [Tanaka Chinengundu](https://www.linkedin.com/in/taqsblaze)
|
|
185
|
+
Inspired by Laravel's elegant development experience
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
Made with ❤️ by Tanaka Chinengundu
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
amen/__init__.py,sha256=7gWg66LLzl97AIWrsnSq0Y3OpGIWqQzkKLzwnuNr8LQ,810
|
|
2
|
+
amen/cli.py,sha256=BNeXzW2gjhLFx554WK_zZZOwQfJF30zVGLbeixq3pNE,7952
|
|
3
|
+
amen/frameworks.py,sha256=uarXCHkZgMwf9sfY9r2wim3rk8sNBwW7j8Cy8On3N7s,1624
|
|
4
|
+
amen/templates/__init__.py,sha256=2MvacuUyKD4x1-YnByMDi_hq90rH8wkNh0sKvUhhOAw,69
|
|
5
|
+
amen/templates/manager.py,sha256=PaGqpWivfet8DoV5UTMzS9A0XEwwXK9k7iD6DtOQ2kg,16958
|
|
6
|
+
amen_cli-0.1.0.dist-info/licenses/LICENSE,sha256=3A9c5OOecf_0logHhGvw85x-VtpTFPti1I1GBEJTkQk,2532
|
|
7
|
+
amen_cli-0.1.0.dist-info/METADATA,sha256=L8VJTDNIiXFTDpNi2y8c1SjqyECLMmq01jYDz9LaQek,5619
|
|
8
|
+
amen_cli-0.1.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
|
9
|
+
amen_cli-0.1.0.dist-info/entry_points.txt,sha256=US5E6fz5OI2kAwqz-zqBB7M-LeeQANqVlEvpx5a0yo4,39
|
|
10
|
+
amen_cli-0.1.0.dist-info/top_level.txt,sha256=Ot0LsWWgOO3jySIhly8qjAYj2sTASmVdbvE1LtBasdg,5
|
|
11
|
+
amen_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Modified MIT License with Attribution Requirements
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Tanaka Chinengundu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
1. The above copyright notice, this permission notice, and the following attribution
|
|
12
|
+
requirements shall be included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
2. Attribution Requirements:
|
|
15
|
+
a. All copies or substantial portions of the Software must retain clear and visible
|
|
16
|
+
attribution to the original author, Tanaka Chinengundu.
|
|
17
|
+
b. The original author's name (Tanaka Chinengundu) must not be removed, modified,
|
|
18
|
+
or obscured in any derivative works or modifications of the Software.
|
|
19
|
+
c. Any modifications or derivative works must clearly indicate that changes have
|
|
20
|
+
been made from the original Software and must not imply endorsement by the
|
|
21
|
+
original author.
|
|
22
|
+
|
|
23
|
+
3. Liability and Responsibility:
|
|
24
|
+
a. The original author shall not be held responsible or liable for any damages,
|
|
25
|
+
losses, or consequences arising from the use, misuse, or failure of the Software,
|
|
26
|
+
whether in its original or modified form.
|
|
27
|
+
b. Users and modifiers of the Software assume all risks associated with its use,
|
|
28
|
+
including but not limited to data loss, system failure, or other damages.
|
|
29
|
+
c. Any modifications made to the Software are the sole responsibility of the modifier,
|
|
30
|
+
and the original author bears no responsibility for derivative works.
|
|
31
|
+
|
|
32
|
+
4. Additional Restrictions:
|
|
33
|
+
a. The Software may not be used in any way that implies endorsement by the original author.
|
|
34
|
+
b. The Software may not be used for illegal or malicious purposes.
|
|
35
|
+
c. Users must notify end users if they are using a modified version of the Software.
|
|
36
|
+
|
|
37
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
38
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
39
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
40
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
41
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
42
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
43
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
amen
|