quickscale-core 0.57.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.
- quickscale_core-0.57.0/PKG-INFO +23 -0
- quickscale_core-0.57.0/pyproject.toml +111 -0
- quickscale_core-0.57.0/src/quickscale_core/__init__.py +5 -0
- quickscale_core-0.57.0/src/quickscale_core/_version.py +2 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/__init__.py +10 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/generator.py +181 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/.dockerignore.j2 +61 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/.editorconfig.j2 +33 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/.env.example.j2 +38 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/.gitignore.j2 +74 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/.pre-commit-config.yaml.j2 +22 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/Dockerfile.j2 +82 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/README.md.j2 +299 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/docker-compose.yml.j2 +49 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/github/workflows/ci.yml.j2 +62 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/manage.py.j2 +51 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/poetry.lock.j2 +797 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/project_name/__init__.py.j2 +5 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/project_name/asgi.py.j2 +16 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/project_name/settings/__init__.py.j2 +10 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/project_name/settings/base.py.j2 +189 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/project_name/settings/local.py.j2 +86 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/project_name/settings/production.py.j2 +197 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/project_name/urls.py.j2 +42 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/project_name/wsgi.py.j2 +16 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/pyproject.toml.j2 +78 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/static/css/style.css.j2 +123 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/static/images/.gitkeep +0 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/static/images/favicon.svg +4 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/static/js/.gitkeep +0 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/templates/base.html.j2 +32 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/templates/index.html.j2 +30 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/tests/__init__.py.j2 +0 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/tests/conftest.py.j2 +44 -0
- quickscale_core-0.57.0/src/quickscale_core/generator/templates/tests/test_example.py.j2 +51 -0
- quickscale_core-0.57.0/src/quickscale_core/py.typed +0 -0
- quickscale_core-0.57.0/src/quickscale_core/utils/__init__.py +17 -0
- quickscale_core-0.57.0/src/quickscale_core/utils/file_utils.py +70 -0
- quickscale_core-0.57.0/src/quickscale_core/version.py +36 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: quickscale-core
|
|
3
|
+
Version: 0.57.0
|
|
4
|
+
Summary: Core scaffolding and utilities for QuickScale Django project generator
|
|
5
|
+
Home-page: https://github.com/Experto-AI/quickscale
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Keywords: django,scaffolding,project-generator,saas
|
|
8
|
+
Author: Experto AI
|
|
9
|
+
Author-email: victor@experto.ai
|
|
10
|
+
Requires-Python: >=3.10,<4.0
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Framework :: Django
|
|
13
|
+
Classifier: Framework :: Django :: 4.2
|
|
14
|
+
Classifier: Framework :: Django :: 5.0
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Requires-Dist: Jinja2 (>=3.1.0,<4.0.0)
|
|
22
|
+
Project-URL: Documentation, https://github.com/Experto-AI/quickscale/tree/main/docs
|
|
23
|
+
Project-URL: Repository, https://github.com/Experto-AI/quickscale
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "quickscale-core"
|
|
3
|
+
version = "0.57.0"
|
|
4
|
+
description = "Core scaffolding and utilities for QuickScale Django project generator"
|
|
5
|
+
authors = ["Experto AI <victor@experto.ai>"]
|
|
6
|
+
license = "Apache-2.0"
|
|
7
|
+
keywords = ["django", "scaffolding", "project-generator", "saas"]
|
|
8
|
+
homepage = "https://github.com/Experto-AI/quickscale"
|
|
9
|
+
repository = "https://github.com/Experto-AI/quickscale"
|
|
10
|
+
documentation = "https://github.com/Experto-AI/quickscale/tree/main/docs"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Development Status :: 3 - Alpha",
|
|
13
|
+
"Intended Audience :: Developers",
|
|
14
|
+
"License :: OSI Approved :: Apache Software License",
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Framework :: Django",
|
|
20
|
+
"Framework :: Django :: 4.2",
|
|
21
|
+
"Framework :: Django :: 5.0",
|
|
22
|
+
]
|
|
23
|
+
packages = [{include = "quickscale_core", from = "src"}]
|
|
24
|
+
include = [
|
|
25
|
+
"src/quickscale_core/generator/templates/**/*",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[tool.poetry.dependencies]
|
|
29
|
+
python = "^3.10"
|
|
30
|
+
Jinja2 = "^3.1.0"
|
|
31
|
+
|
|
32
|
+
[tool.poetry.group.dev.dependencies]
|
|
33
|
+
pytest = "^7.4.0"
|
|
34
|
+
pytest-cov = "^4.1.0"
|
|
35
|
+
ruff = "^0.6.0"
|
|
36
|
+
mypy = "^1.8.0"
|
|
37
|
+
|
|
38
|
+
[build-system]
|
|
39
|
+
requires = ["poetry-core"]
|
|
40
|
+
build-backend = "poetry.core.masonry.api"
|
|
41
|
+
|
|
42
|
+
[tool.pytest.ini_options]
|
|
43
|
+
testpaths = ["tests"]
|
|
44
|
+
python_files = ["test_*.py"]
|
|
45
|
+
python_classes = ["Test*"]
|
|
46
|
+
python_functions = ["test_*"]
|
|
47
|
+
addopts = [
|
|
48
|
+
"--strict-markers",
|
|
49
|
+
"--strict-config",
|
|
50
|
+
"--cov=quickscale_core",
|
|
51
|
+
"--cov-report=term-missing",
|
|
52
|
+
"--cov-report=html",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[tool.coverage.run]
|
|
56
|
+
omit = [
|
|
57
|
+
"*/templates/*.j2",
|
|
58
|
+
"*/templates/**/*.j2",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
[tool.ruff]
|
|
62
|
+
line-length = 100
|
|
63
|
+
target-version = "py310"
|
|
64
|
+
|
|
65
|
+
[tool.ruff.lint]
|
|
66
|
+
select = [
|
|
67
|
+
"E", # pycodestyle errors
|
|
68
|
+
"W", # pycodestyle warnings
|
|
69
|
+
"F", # pyflakes
|
|
70
|
+
"I", # isort
|
|
71
|
+
"N", # pep8-naming
|
|
72
|
+
"UP", # pyupgrade
|
|
73
|
+
"D", # docstring rules
|
|
74
|
+
]
|
|
75
|
+
ignore = [
|
|
76
|
+
"D100", # Missing docstring in public module
|
|
77
|
+
"D101", # Missing docstring in public class
|
|
78
|
+
"D102", # Missing docstring in public method
|
|
79
|
+
"D103", # Missing docstring in public function
|
|
80
|
+
"D105", # Missing docstring in magic method
|
|
81
|
+
"D107", # Missing docstring in __init__
|
|
82
|
+
"D203", # 1 blank line required before class docstring (incompatible with D211)
|
|
83
|
+
"D212", # Multi-line docstring summary should start at the first line (incompatible with D213)
|
|
84
|
+
"D400", # First line should end with a period
|
|
85
|
+
"D415", # First line should end with a period, question mark, or exclamation point
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
[tool.ruff.lint.isort]
|
|
89
|
+
known-first-party = ["quickscale_core"]
|
|
90
|
+
|
|
91
|
+
[tool.mypy]
|
|
92
|
+
python_version = "3.10"
|
|
93
|
+
warn_return_any = true
|
|
94
|
+
warn_unused_configs = true
|
|
95
|
+
disallow_untyped_defs = true
|
|
96
|
+
disallow_incomplete_defs = true
|
|
97
|
+
check_untyped_defs = true
|
|
98
|
+
disallow_untyped_decorators = true
|
|
99
|
+
no_implicit_optional = true
|
|
100
|
+
warn_redundant_casts = true
|
|
101
|
+
warn_unused_ignores = true
|
|
102
|
+
warn_no_return = true
|
|
103
|
+
warn_unreachable = true
|
|
104
|
+
strict_equality = true
|
|
105
|
+
show_error_codes = true
|
|
106
|
+
|
|
107
|
+
[[tool.mypy.overrides]]
|
|
108
|
+
module = "tests.*"
|
|
109
|
+
disallow_untyped_defs = false
|
|
110
|
+
check_untyped_defs = false
|
|
111
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""
|
|
2
|
+
QuickScale Generator Package
|
|
3
|
+
|
|
4
|
+
Template-based Django project generator for QuickScale.
|
|
5
|
+
MVP: Simple Jinja2 template rendering for production-ready Django starters.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from quickscale_core.generator.generator import ProjectGenerator
|
|
9
|
+
|
|
10
|
+
__all__ = ["ProjectGenerator"]
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Project generator implementation"""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import shutil
|
|
5
|
+
import tempfile
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from jinja2 import Environment, FileSystemLoader
|
|
9
|
+
|
|
10
|
+
from quickscale_core.utils.file_utils import (
|
|
11
|
+
ensure_directory,
|
|
12
|
+
validate_project_name,
|
|
13
|
+
write_file,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ProjectGenerator:
|
|
18
|
+
"""Generate Django projects from templates"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, template_dir: Path | None = None):
|
|
21
|
+
"""Initialize generator with template directory"""
|
|
22
|
+
if template_dir is None:
|
|
23
|
+
# Try to find templates in development environment first
|
|
24
|
+
import quickscale_core
|
|
25
|
+
|
|
26
|
+
package_dir = Path(quickscale_core.__file__).parent
|
|
27
|
+
|
|
28
|
+
# Check if we're in development (source directory exists)
|
|
29
|
+
dev_template_dir = package_dir / "generator" / "templates"
|
|
30
|
+
if dev_template_dir.exists():
|
|
31
|
+
template_dir = dev_template_dir
|
|
32
|
+
else:
|
|
33
|
+
# Fall back to package templates (should be included)
|
|
34
|
+
template_dir = package_dir / "templates"
|
|
35
|
+
|
|
36
|
+
# If package templates don't exist, try to find source templates
|
|
37
|
+
# by walking up from the current file location
|
|
38
|
+
if not template_dir.exists():
|
|
39
|
+
current_file = Path(__file__)
|
|
40
|
+
# Try common development layouts
|
|
41
|
+
possible_paths = [
|
|
42
|
+
current_file.parent / "templates", # Same directory
|
|
43
|
+
current_file.parent.parent / "generator" / "templates", # Parent
|
|
44
|
+
Path.cwd()
|
|
45
|
+
/ "quickscale_core"
|
|
46
|
+
/ "src"
|
|
47
|
+
/ "quickscale_core"
|
|
48
|
+
/ "generator"
|
|
49
|
+
/ "templates", # From repo root
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
for path in possible_paths:
|
|
53
|
+
if path.exists():
|
|
54
|
+
template_dir = path
|
|
55
|
+
break
|
|
56
|
+
|
|
57
|
+
# Validate template directory exists
|
|
58
|
+
if not template_dir.exists():
|
|
59
|
+
raise FileNotFoundError(f"Template directory not found: {template_dir}")
|
|
60
|
+
|
|
61
|
+
self.template_dir = template_dir
|
|
62
|
+
self.env = Environment(
|
|
63
|
+
loader=FileSystemLoader(str(template_dir), followlinks=True),
|
|
64
|
+
keep_trailing_newline=True,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def generate(self, project_name: str, output_path: Path) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Generate Django project from templates
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
project_name: Name of the project (must be valid Python identifier)
|
|
73
|
+
output_path: Path where project will be created
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
ValueError: If project_name is invalid
|
|
77
|
+
FileExistsError: If output_path already exists
|
|
78
|
+
PermissionError: If output_path is not writable
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
# Validate project name
|
|
82
|
+
is_valid, error_msg = validate_project_name(project_name)
|
|
83
|
+
if not is_valid:
|
|
84
|
+
raise ValueError(f"Invalid project name: {error_msg}")
|
|
85
|
+
|
|
86
|
+
# Check if output path already exists
|
|
87
|
+
if output_path.exists():
|
|
88
|
+
raise FileExistsError(
|
|
89
|
+
f"Output path already exists: {output_path}. "
|
|
90
|
+
"Please choose a different name or remove the existing directory."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Check if parent directory is writable
|
|
94
|
+
parent = output_path.parent
|
|
95
|
+
if not parent.exists():
|
|
96
|
+
try:
|
|
97
|
+
ensure_directory(parent)
|
|
98
|
+
except (OSError, PermissionError) as e:
|
|
99
|
+
raise PermissionError(f"Cannot create parent directory {parent}: {e}") from e
|
|
100
|
+
|
|
101
|
+
if not os.access(parent, os.W_OK):
|
|
102
|
+
raise PermissionError(f"Parent directory is not writable: {parent}")
|
|
103
|
+
|
|
104
|
+
# Generate project in temporary directory first (atomic creation)
|
|
105
|
+
temp_dir = Path(tempfile.mkdtemp(prefix=f"quickscale_{project_name}_"))
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
# Generate project in temp directory
|
|
109
|
+
self._generate_project(project_name, temp_dir)
|
|
110
|
+
|
|
111
|
+
# Move to final location
|
|
112
|
+
shutil.move(str(temp_dir), str(output_path))
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
# Clean up temp directory on failure
|
|
116
|
+
if temp_dir.exists():
|
|
117
|
+
shutil.rmtree(temp_dir, ignore_errors=True)
|
|
118
|
+
raise RuntimeError(f"Failed to generate project: {e}") from e
|
|
119
|
+
|
|
120
|
+
def _generate_project(self, project_name: str, output_path: Path) -> None:
|
|
121
|
+
"""Generate project structure in specified directory"""
|
|
122
|
+
# Context for template rendering
|
|
123
|
+
context = {
|
|
124
|
+
"project_name": project_name,
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
# Map of template files to output files
|
|
128
|
+
# Format: (template_path, output_path, executable)
|
|
129
|
+
file_mappings = [
|
|
130
|
+
# Root level files
|
|
131
|
+
("README.md.j2", "README.md", False),
|
|
132
|
+
("manage.py.j2", "manage.py", True),
|
|
133
|
+
("pyproject.toml.j2", "pyproject.toml", False),
|
|
134
|
+
("poetry.lock.j2", "poetry.lock", False),
|
|
135
|
+
(".gitignore.j2", ".gitignore", False),
|
|
136
|
+
(".dockerignore.j2", ".dockerignore", False),
|
|
137
|
+
(".editorconfig.j2", ".editorconfig", False),
|
|
138
|
+
(".env.example.j2", ".env.example", False),
|
|
139
|
+
("Dockerfile.j2", "Dockerfile", False),
|
|
140
|
+
("docker-compose.yml.j2", "docker-compose.yml", False),
|
|
141
|
+
# Project package files
|
|
142
|
+
("project_name/__init__.py.j2", f"{project_name}/__init__.py", False),
|
|
143
|
+
("project_name/urls.py.j2", f"{project_name}/urls.py", False),
|
|
144
|
+
("project_name/wsgi.py.j2", f"{project_name}/wsgi.py", False),
|
|
145
|
+
("project_name/asgi.py.j2", f"{project_name}/asgi.py", False),
|
|
146
|
+
# Settings files
|
|
147
|
+
(
|
|
148
|
+
"project_name/settings/__init__.py.j2",
|
|
149
|
+
f"{project_name}/settings/__init__.py",
|
|
150
|
+
False,
|
|
151
|
+
),
|
|
152
|
+
("project_name/settings/base.py.j2", f"{project_name}/settings/base.py", False),
|
|
153
|
+
("project_name/settings/local.py.j2", f"{project_name}/settings/local.py", False),
|
|
154
|
+
(
|
|
155
|
+
"project_name/settings/production.py.j2",
|
|
156
|
+
f"{project_name}/settings/production.py",
|
|
157
|
+
False,
|
|
158
|
+
),
|
|
159
|
+
# Template files
|
|
160
|
+
("templates/base.html.j2", "templates/base.html", False),
|
|
161
|
+
("templates/index.html.j2", "templates/index.html", False),
|
|
162
|
+
# Static files
|
|
163
|
+
("static/css/style.css.j2", "static/css/style.css", False),
|
|
164
|
+
# CI/CD and quality tools
|
|
165
|
+
("github/workflows/ci.yml.j2", ".github/workflows/ci.yml", False),
|
|
166
|
+
(".pre-commit-config.yaml.j2", ".pre-commit-config.yaml", False),
|
|
167
|
+
# Tests
|
|
168
|
+
("tests/__init__.py.j2", "tests/__init__.py", False),
|
|
169
|
+
("tests/conftest.py.j2", "tests/conftest.py", False),
|
|
170
|
+
("tests/test_example.py.j2", "tests/test_example.py", False),
|
|
171
|
+
]
|
|
172
|
+
|
|
173
|
+
# Render and write all files
|
|
174
|
+
for template_path, output_file, executable in file_mappings:
|
|
175
|
+
# Render template
|
|
176
|
+
template = self.env.get_template(template_path)
|
|
177
|
+
content = template.render(**context)
|
|
178
|
+
|
|
179
|
+
# Write file
|
|
180
|
+
output_file_path = output_path / output_file
|
|
181
|
+
write_file(output_file_path, content, executable=executable)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Git
|
|
2
|
+
.git
|
|
3
|
+
.gitignore
|
|
4
|
+
.gitattributes
|
|
5
|
+
|
|
6
|
+
# Python
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*$py.class
|
|
10
|
+
*.so
|
|
11
|
+
.Python
|
|
12
|
+
env/
|
|
13
|
+
venv/
|
|
14
|
+
ENV/
|
|
15
|
+
env.bak/
|
|
16
|
+
venv.bak/
|
|
17
|
+
.venv/
|
|
18
|
+
|
|
19
|
+
# Django
|
|
20
|
+
*.log
|
|
21
|
+
db.sqlite3
|
|
22
|
+
db.sqlite3-journal
|
|
23
|
+
/media
|
|
24
|
+
/staticfiles
|
|
25
|
+
|
|
26
|
+
# Testing
|
|
27
|
+
.pytest_cache/
|
|
28
|
+
.coverage
|
|
29
|
+
htmlcov/
|
|
30
|
+
*.cover
|
|
31
|
+
.hypothesis/
|
|
32
|
+
|
|
33
|
+
# Development
|
|
34
|
+
.env
|
|
35
|
+
.env.local
|
|
36
|
+
.env.*.local
|
|
37
|
+
*.env
|
|
38
|
+
|
|
39
|
+
# IDE
|
|
40
|
+
.vscode/
|
|
41
|
+
.idea/
|
|
42
|
+
*.swp
|
|
43
|
+
*.swo
|
|
44
|
+
*~
|
|
45
|
+
.DS_Store
|
|
46
|
+
|
|
47
|
+
# Documentation
|
|
48
|
+
*.md
|
|
49
|
+
docs/
|
|
50
|
+
README.md
|
|
51
|
+
LICENSE
|
|
52
|
+
|
|
53
|
+
# CI/CD
|
|
54
|
+
.github/
|
|
55
|
+
.gitlab-ci.yml
|
|
56
|
+
|
|
57
|
+
# Other
|
|
58
|
+
*.bak
|
|
59
|
+
*.tmp
|
|
60
|
+
node_modules/
|
|
61
|
+
npm-debug.log*
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# EditorConfig helps maintain consistent coding styles across editors
|
|
2
|
+
# https://editorconfig.org
|
|
3
|
+
|
|
4
|
+
root = true
|
|
5
|
+
|
|
6
|
+
[*]
|
|
7
|
+
charset = utf-8
|
|
8
|
+
end_of_line = lf
|
|
9
|
+
insert_final_newline = true
|
|
10
|
+
trim_trailing_whitespace = true
|
|
11
|
+
|
|
12
|
+
[*.{py,pyi}]
|
|
13
|
+
indent_style = space
|
|
14
|
+
indent_size = 4
|
|
15
|
+
max_line_length = 100
|
|
16
|
+
|
|
17
|
+
[*.{js,jsx,ts,tsx,json,yml,yaml}]
|
|
18
|
+
indent_style = space
|
|
19
|
+
indent_size = 2
|
|
20
|
+
|
|
21
|
+
[*.{html,css,scss}]
|
|
22
|
+
indent_style = space
|
|
23
|
+
indent_size = 2
|
|
24
|
+
|
|
25
|
+
[*.md]
|
|
26
|
+
trim_trailing_whitespace = false
|
|
27
|
+
max_line_length = off
|
|
28
|
+
|
|
29
|
+
[Makefile]
|
|
30
|
+
indent_style = tab
|
|
31
|
+
|
|
32
|
+
[*.{bat,cmd}]
|
|
33
|
+
end_of_line = crlf
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Django Core Settings
|
|
2
|
+
# SECURITY WARNING: keep the secret key used in production secret!
|
|
3
|
+
# Generate a new secret key for production: python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'
|
|
4
|
+
SECRET_KEY=django-insecure-change-this-in-production-{{ project_name }}
|
|
5
|
+
|
|
6
|
+
# SECURITY WARNING: don't run with debug turned on in production!
|
|
7
|
+
DEBUG=True
|
|
8
|
+
|
|
9
|
+
# Comma-separated list of allowed hosts
|
|
10
|
+
ALLOWED_HOSTS=localhost,127.0.0.1
|
|
11
|
+
|
|
12
|
+
# Server Configuration
|
|
13
|
+
# Port for Django development server and Docker containers
|
|
14
|
+
PORT=8000
|
|
15
|
+
|
|
16
|
+
# Database Configuration
|
|
17
|
+
# Format: postgresql://user:password@host:port/dbname
|
|
18
|
+
# For local development with docker-compose, use:
|
|
19
|
+
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/{{ project_name }}
|
|
20
|
+
|
|
21
|
+
# Email Configuration (optional)
|
|
22
|
+
# EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
|
|
23
|
+
# EMAIL_HOST=smtp.gmail.com
|
|
24
|
+
# EMAIL_PORT=587
|
|
25
|
+
# EMAIL_USE_TLS=True
|
|
26
|
+
# EMAIL_HOST_USER=your-email@example.com
|
|
27
|
+
# EMAIL_HOST_PASSWORD=your-app-password
|
|
28
|
+
|
|
29
|
+
# Static and Media Files
|
|
30
|
+
# STATIC_URL=/static/
|
|
31
|
+
# STATIC_ROOT=staticfiles
|
|
32
|
+
# MEDIA_URL=/media/
|
|
33
|
+
# MEDIA_ROOT=media
|
|
34
|
+
|
|
35
|
+
# Application Settings
|
|
36
|
+
# Add your custom application settings here
|
|
37
|
+
# ENABLE_FEATURE_X=True
|
|
38
|
+
# API_KEY=your-api-key-here
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
|
|
8
|
+
# Virtual Environments (Poetry-managed and manual)
|
|
9
|
+
.venv/
|
|
10
|
+
venv/
|
|
11
|
+
env/
|
|
12
|
+
ENV/
|
|
13
|
+
env.bak/
|
|
14
|
+
venv.bak/
|
|
15
|
+
|
|
16
|
+
# Django
|
|
17
|
+
*.log
|
|
18
|
+
db.sqlite3
|
|
19
|
+
db.sqlite3-journal
|
|
20
|
+
/media
|
|
21
|
+
/staticfiles
|
|
22
|
+
/static_root
|
|
23
|
+
|
|
24
|
+
# Environment variables
|
|
25
|
+
.env
|
|
26
|
+
.env.local
|
|
27
|
+
.env.*.local
|
|
28
|
+
*.env
|
|
29
|
+
|
|
30
|
+
# Testing
|
|
31
|
+
.pytest_cache/
|
|
32
|
+
.coverage
|
|
33
|
+
htmlcov/
|
|
34
|
+
*.cover
|
|
35
|
+
.hypothesis/
|
|
36
|
+
.tox/
|
|
37
|
+
|
|
38
|
+
# IDEs
|
|
39
|
+
.vscode/
|
|
40
|
+
.idea/
|
|
41
|
+
*.swp
|
|
42
|
+
*.swo
|
|
43
|
+
*~
|
|
44
|
+
.DS_Store
|
|
45
|
+
*.sublime-project
|
|
46
|
+
*.sublime-workspace
|
|
47
|
+
|
|
48
|
+
# Build artifacts
|
|
49
|
+
build/
|
|
50
|
+
dist/
|
|
51
|
+
*.egg-info/
|
|
52
|
+
.eggs/
|
|
53
|
+
|
|
54
|
+
# Database
|
|
55
|
+
*.db
|
|
56
|
+
*.sqlite
|
|
57
|
+
*.sqlite3
|
|
58
|
+
|
|
59
|
+
# Logs
|
|
60
|
+
logs/
|
|
61
|
+
*.log
|
|
62
|
+
|
|
63
|
+
# Docker volumes
|
|
64
|
+
postgres_data/
|
|
65
|
+
redis_data/
|
|
66
|
+
|
|
67
|
+
# OS
|
|
68
|
+
.DS_Store
|
|
69
|
+
Thumbs.db
|
|
70
|
+
|
|
71
|
+
# Other
|
|
72
|
+
*.bak
|
|
73
|
+
*.tmp
|
|
74
|
+
.cache/
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# See https://pre-commit.com for more information
|
|
2
|
+
repos:
|
|
3
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
4
|
+
rev: v4.5.0
|
|
5
|
+
hooks:
|
|
6
|
+
- id: trailing-whitespace
|
|
7
|
+
- id: end-of-file-fixer
|
|
8
|
+
- id: check-yaml
|
|
9
|
+
- id: check-added-large-files
|
|
10
|
+
- id: check-json
|
|
11
|
+
- id: check-toml
|
|
12
|
+
- id: check-merge-conflict
|
|
13
|
+
- id: debug-statements
|
|
14
|
+
|
|
15
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
16
|
+
rev: v0.6.0
|
|
17
|
+
hooks:
|
|
18
|
+
# Run the linter
|
|
19
|
+
- id: ruff
|
|
20
|
+
args: [--fix]
|
|
21
|
+
# Run the formatter
|
|
22
|
+
- id: ruff-format
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
# Multi-stage build for production Django application
|
|
3
|
+
|
|
4
|
+
# Stage 1: Builder - Install dependencies and build artifacts
|
|
5
|
+
FROM python:3.11-slim as builder
|
|
6
|
+
|
|
7
|
+
# Set environment variables for Python
|
|
8
|
+
ENV PYTHONUNBUFFERED=1 \
|
|
9
|
+
PYTHONDONTWRITEBYTECODE=1 \
|
|
10
|
+
PIP_NO_CACHE_DIR=1 \
|
|
11
|
+
PIP_DISABLE_PIP_VERSION_CHECK=1
|
|
12
|
+
|
|
13
|
+
# Install system dependencies required for building Python packages
|
|
14
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
15
|
+
build-essential \
|
|
16
|
+
libpq-dev \
|
|
17
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
18
|
+
|
|
19
|
+
# Install Poetry
|
|
20
|
+
RUN pip install poetry==1.7.1
|
|
21
|
+
|
|
22
|
+
# Set working directory
|
|
23
|
+
WORKDIR /app
|
|
24
|
+
|
|
25
|
+
# Copy dependency files
|
|
26
|
+
COPY pyproject.toml poetry.lock ./
|
|
27
|
+
|
|
28
|
+
# Configure Poetry to not create virtual environments (we're in a container)
|
|
29
|
+
RUN poetry config virtualenvs.create false
|
|
30
|
+
|
|
31
|
+
# Install dependencies (--no-root: don't install project itself, only dependencies)
|
|
32
|
+
RUN poetry install --only main --no-root --no-interaction --no-ansi
|
|
33
|
+
|
|
34
|
+
# Stage 2: Runtime - Create minimal production image
|
|
35
|
+
FROM python:3.11-slim
|
|
36
|
+
|
|
37
|
+
# Set environment variables
|
|
38
|
+
ENV PYTHONUNBUFFERED=1 \
|
|
39
|
+
PYTHONDONTWRITEBYTECODE=1 \
|
|
40
|
+
DJANGO_SETTINGS_MODULE={{ project_name }}.settings.production
|
|
41
|
+
|
|
42
|
+
# Port configuration
|
|
43
|
+
ARG PORT=8000
|
|
44
|
+
ENV PORT=${PORT}
|
|
45
|
+
|
|
46
|
+
# Install runtime dependencies only
|
|
47
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
48
|
+
libpq5 \
|
|
49
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
50
|
+
|
|
51
|
+
# Create non-root user for security
|
|
52
|
+
RUN groupadd -r django && useradd -r -g django django
|
|
53
|
+
|
|
54
|
+
# Set working directory
|
|
55
|
+
WORKDIR /app
|
|
56
|
+
|
|
57
|
+
# Copy installed dependencies from builder stage
|
|
58
|
+
COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
|
59
|
+
COPY --from=builder /usr/local/bin /usr/local/bin
|
|
60
|
+
|
|
61
|
+
# Copy application code
|
|
62
|
+
COPY --chown=django:django . .
|
|
63
|
+
|
|
64
|
+
# Create necessary directories with correct permissions
|
|
65
|
+
RUN mkdir -p /app/staticfiles /app/media /app/logs && \
|
|
66
|
+
chown -R django:django /app
|
|
67
|
+
|
|
68
|
+
# Switch to non-root user
|
|
69
|
+
USER django
|
|
70
|
+
|
|
71
|
+
# Collect static files
|
|
72
|
+
RUN python manage.py collectstatic --noinput
|
|
73
|
+
|
|
74
|
+
# Expose port
|
|
75
|
+
EXPOSE ${PORT}
|
|
76
|
+
|
|
77
|
+
# Health check - verify gunicorn is responding (any response including 404/500 means it's alive)
|
|
78
|
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
|
|
79
|
+
CMD python -c "import os, urllib.request; urllib.request.urlopen(f'http://localhost:{os.environ.get(\"PORT\", \"8000\")}/', timeout=5)"
|
|
80
|
+
|
|
81
|
+
# Run gunicorn
|
|
82
|
+
CMD gunicorn {{ project_name }}.wsgi:application --bind 0.0.0.0:${PORT} --workers 4 --timeout 60 --access-logfile - --error-logfile -
|