snapstack 1.0.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.
- pysnap/__init__.py +1 -0
- pysnap/_shared/Dockerfile.j2 +34 -0
- pysnap/_shared/ci.yml.j2 +53 -0
- pysnap/_shared/docker-compose.yml.j2 +46 -0
- pysnap/_shared/dockerignore.j2 +13 -0
- pysnap/_shared/env_example.j2 +24 -0
- pysnap/_shared/gitignore.j2 +15 -0
- pysnap/commands/__init__.py +1 -0
- pysnap/commands/add.py +171 -0
- pysnap/commands/create.py +136 -0
- pysnap/commands/templates_cmd.py +134 -0
- pysnap/commands/update.py +133 -0
- pysnap/community.py +113 -0
- pysnap/config.py +76 -0
- pysnap/generator.py +262 -0
- pysnap/main.py +65 -0
- pysnap/manifest.py +101 -0
- pysnap/plugins.py +123 -0
- pysnap/preview.py +131 -0
- pysnap/prompts.py +217 -0
- pysnap/registry.py +123 -0
- pysnap/templates/django/.dockerignore.j2 +15 -0
- pysnap/templates/django/.github/workflows/ci.yml.j2 +34 -0
- pysnap/templates/django/.gitignore.j2 +14 -0
- pysnap/templates/django/Dockerfile.j2 +14 -0
- pysnap/templates/django/README.md.j2 +36 -0
- pysnap/templates/django/apps/__init__.py.j2 +0 -0
- pysnap/templates/django/apps/core/__init__.py.j2 +0 -0
- pysnap/templates/django/apps/core/apps.py.j2 +6 -0
- pysnap/templates/django/apps/core/urls.py.j2 +7 -0
- pysnap/templates/django/apps/core/views.py.j2 +6 -0
- pysnap/templates/django/apps/users/__init__.py.j2 +0 -0
- pysnap/templates/django/apps/users/apps.py.j2 +6 -0
- pysnap/templates/django/apps/users/models.py.j2 +14 -0
- pysnap/templates/django/apps/users/serializers.py.j2 +13 -0
- pysnap/templates/django/apps/users/urls.py.j2 +10 -0
- pysnap/templates/django/apps/users/views.py.j2 +22 -0
- pysnap/templates/django/config/__init__.py.j2 +0 -0
- pysnap/templates/django/config/asgi.py.j2 +9 -0
- pysnap/templates/django/config/settings.py.j2 +110 -0
- pysnap/templates/django/config/urls.py.j2 +12 -0
- pysnap/templates/django/config/wsgi.py.j2 +9 -0
- pysnap/templates/django/docker-compose.yml.j2 +29 -0
- pysnap/templates/django/manage.py.j2 +22 -0
- pysnap/templates/django/pyproject.toml.j2 +40 -0
- pysnap/templates/django/template.json +50 -0
- pysnap/templates/django/tests/__init__.py.j2 +1 -0
- pysnap/templates/django/tests/conftest.py.j2 +6 -0
- pysnap/templates/django/tests/test_health.py.j2 +9 -0
- pysnap/templates/fastapi/.dockerignore.j2 +8 -0
- pysnap/templates/fastapi/.github/workflows/ci.yml.j2 +46 -0
- pysnap/templates/fastapi/.gitignore.j2 +13 -0
- pysnap/templates/fastapi/Dockerfile.j2 +14 -0
- pysnap/templates/fastapi/README.md.j2 +57 -0
- pysnap/templates/fastapi/api/__init__.py.j2 +0 -0
- pysnap/templates/fastapi/api/routes/__init__.py.j2 +0 -0
- pysnap/templates/fastapi/api/routes/auth.py.j2 +18 -0
- pysnap/templates/fastapi/api/routes/health.py.j2 +8 -0
- pysnap/templates/fastapi/app/__init__.py.j2 +1 -0
- pysnap/templates/fastapi/core/__init__.py.j2 +0 -0
- pysnap/templates/fastapi/core/config.py.j2 +26 -0
- pysnap/templates/fastapi/core/security.py.j2 +22 -0
- pysnap/templates/fastapi/db/__init__.py.j2 +0 -0
- pysnap/templates/fastapi/db/base.py.j2 +5 -0
- pysnap/templates/fastapi/db/session.py.j2 +15 -0
- pysnap/templates/fastapi/docker-compose.yml.j2 +30 -0
- pysnap/templates/fastapi/main.py.j2 +27 -0
- pysnap/templates/fastapi/models/__init__.py.j2 +0 -0
- pysnap/templates/fastapi/models/user.py.j2 +13 -0
- pysnap/templates/fastapi/pyproject.toml.j2 +48 -0
- pysnap/templates/fastapi/schemas/__init__.py.j2 +0 -0
- pysnap/templates/fastapi/schemas/user.py.j2 +18 -0
- pysnap/templates/fastapi/template.json +53 -0
- pysnap/templates/fastapi/tests/__init__.py.j2 +0 -0
- pysnap/templates/fastapi/tests/conftest.py.j2 +9 -0
- pysnap/templates/fastapi/tests/test_health.py.j2 +9 -0
- pysnap/templates/flask/.dockerignore.j2 +14 -0
- pysnap/templates/flask/.github/workflows/ci.yml.j2 +34 -0
- pysnap/templates/flask/.gitignore.j2 +13 -0
- pysnap/templates/flask/Dockerfile.j2 +14 -0
- pysnap/templates/flask/README.md.j2 +34 -0
- pysnap/templates/flask/app/__init__.py.j2 +30 -0
- pysnap/templates/flask/app/config.py.j2 +23 -0
- pysnap/templates/flask/app/extensions.py.j2 +9 -0
- pysnap/templates/flask/app/models/__init__.py.j2 +1 -0
- pysnap/templates/flask/app/models/user.py.j2 +16 -0
- pysnap/templates/flask/app/routes/__init__.py.j2 +1 -0
- pysnap/templates/flask/app/routes/auth.py.j2 +31 -0
- pysnap/templates/flask/app/routes/health.py.j2 +11 -0
- pysnap/templates/flask/docker-compose.yml.j2 +29 -0
- pysnap/templates/flask/pyproject.toml.j2 +39 -0
- pysnap/templates/flask/template.json +44 -0
- pysnap/templates/flask/tests/__init__.py.j2 +1 -0
- pysnap/templates/flask/tests/conftest.py.j2 +16 -0
- pysnap/templates/flask/tests/test_health.py.j2 +8 -0
- pysnap/templates/flask/wsgi.py.j2 +8 -0
- pysnap/validator.py +89 -0
- snapstack-1.0.0.dist-info/METADATA +267 -0
- snapstack-1.0.0.dist-info/RECORD +102 -0
- snapstack-1.0.0.dist-info/WHEEL +4 -0
- snapstack-1.0.0.dist-info/entry_points.txt +2 -0
- snapstack-1.0.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
lint-and-test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- name: Set up Python
|
|
15
|
+
uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.11"
|
|
18
|
+
{% if package_manager == "uv" %}
|
|
19
|
+
- name: Install uv
|
|
20
|
+
run: pip install uv
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: uv sync
|
|
23
|
+
- name: Lint
|
|
24
|
+
run: uv run ruff check .
|
|
25
|
+
- name: Test
|
|
26
|
+
run: uv run pytest
|
|
27
|
+
{% else %}
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
run: pip install -e ".[dev]"
|
|
30
|
+
- name: Lint
|
|
31
|
+
run: ruff check .
|
|
32
|
+
- name: Test
|
|
33
|
+
run: pytest
|
|
34
|
+
{% endif %}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM python:3.11-slim
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
{% if package_manager == "uv" -%}
|
|
4
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
|
5
|
+
COPY pyproject.toml uv.lock* ./
|
|
6
|
+
RUN uv sync --frozen --no-dev
|
|
7
|
+
COPY . .
|
|
8
|
+
CMD ["uv", "run", "python", "manage.py", "runserver", "0.0.0.0:8000"]
|
|
9
|
+
{%- else -%}
|
|
10
|
+
COPY pyproject.toml ./
|
|
11
|
+
RUN pip install --no-cache-dir -e .
|
|
12
|
+
COPY . .
|
|
13
|
+
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
|
|
14
|
+
{%- endif %}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# {{ project_name }}
|
|
2
|
+
|
|
3
|
+
A Django project generated by [pysnap](https://github.com/pysnap-dev/pysnap).
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
{% if package_manager == "uv" %}
|
|
9
|
+
uv sync
|
|
10
|
+
uv run python manage.py migrate
|
|
11
|
+
uv run python manage.py runserver
|
|
12
|
+
{% else %}
|
|
13
|
+
pip install -e ".[dev]"
|
|
14
|
+
python manage.py migrate
|
|
15
|
+
python manage.py runserver
|
|
16
|
+
{% endif %}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
{% if include_docker %}
|
|
20
|
+
## Docker
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
docker compose up
|
|
24
|
+
```
|
|
25
|
+
{% endif %}
|
|
26
|
+
{% if include_tests %}
|
|
27
|
+
## Tests
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
{% if package_manager == "uv" %}
|
|
31
|
+
uv run pytest
|
|
32
|
+
{% else %}
|
|
33
|
+
pytest
|
|
34
|
+
{% endif %}
|
|
35
|
+
```
|
|
36
|
+
{% endif %}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from django.contrib.auth.models import AbstractUser
|
|
2
|
+
from django.db import models
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class User(AbstractUser):
|
|
6
|
+
"""Custom user model."""
|
|
7
|
+
|
|
8
|
+
email = models.EmailField(unique=True)
|
|
9
|
+
|
|
10
|
+
USERNAME_FIELD = "email"
|
|
11
|
+
REQUIRED_FIELDS = ["username"]
|
|
12
|
+
|
|
13
|
+
class Meta:
|
|
14
|
+
ordering = ["-date_joined"]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from rest_framework import serializers
|
|
2
|
+
from apps.users.models import User
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RegisterSerializer(serializers.ModelSerializer):
|
|
6
|
+
password = serializers.CharField(write_only=True, min_length=8)
|
|
7
|
+
|
|
8
|
+
class Meta:
|
|
9
|
+
model = User
|
|
10
|
+
fields = ["email", "username", "password"]
|
|
11
|
+
|
|
12
|
+
def create(self, validated_data):
|
|
13
|
+
return User.objects.create_user(**validated_data)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from django.urls import path
|
|
2
|
+
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView
|
|
3
|
+
|
|
4
|
+
from apps.users.views import RegisterView
|
|
5
|
+
|
|
6
|
+
urlpatterns = [
|
|
7
|
+
path("register/", RegisterView.as_view(), name="register"),
|
|
8
|
+
path("token/", TokenObtainPairView.as_view(), name="token-obtain"),
|
|
9
|
+
path("token/refresh/", TokenRefreshView.as_view(), name="token-refresh"),
|
|
10
|
+
]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from rest_framework import status
|
|
2
|
+
from rest_framework.response import Response
|
|
3
|
+
from rest_framework.views import APIView
|
|
4
|
+
from rest_framework.permissions import AllowAny
|
|
5
|
+
from rest_framework_simplejwt.tokens import RefreshToken
|
|
6
|
+
|
|
7
|
+
from apps.users.serializers import RegisterSerializer
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RegisterView(APIView):
|
|
11
|
+
permission_classes = [AllowAny]
|
|
12
|
+
|
|
13
|
+
def post(self, request):
|
|
14
|
+
serializer = RegisterSerializer(data=request.data)
|
|
15
|
+
if serializer.is_valid():
|
|
16
|
+
user = serializer.save()
|
|
17
|
+
refresh = RefreshToken.for_user(user)
|
|
18
|
+
return Response(
|
|
19
|
+
{"access": str(refresh.access_token), "refresh": str(refresh)},
|
|
20
|
+
status=status.HTTP_201_CREATED,
|
|
21
|
+
)
|
|
22
|
+
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
|
File without changes
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django settings for {{ project_name }}.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from decouple import Csv, config
|
|
8
|
+
|
|
9
|
+
BASE_DIR = Path(__file__).resolve().parent.parent
|
|
10
|
+
|
|
11
|
+
SECRET_KEY = config("SECRET_KEY", default="insecure-change-me-in-production")
|
|
12
|
+
|
|
13
|
+
DEBUG = config("DEBUG", default=False, cast=bool)
|
|
14
|
+
|
|
15
|
+
ALLOWED_HOSTS: list[str] = config("ALLOWED_HOSTS", default="localhost,127.0.0.1", cast=Csv())
|
|
16
|
+
|
|
17
|
+
INSTALLED_APPS = [
|
|
18
|
+
"django.contrib.admin",
|
|
19
|
+
"django.contrib.auth",
|
|
20
|
+
"django.contrib.contenttypes",
|
|
21
|
+
"django.contrib.sessions",
|
|
22
|
+
"django.contrib.messages",
|
|
23
|
+
"django.contrib.staticfiles",
|
|
24
|
+
"apps.core",
|
|
25
|
+
{% if include_auth %}
|
|
26
|
+
"rest_framework",
|
|
27
|
+
"rest_framework_simplejwt",
|
|
28
|
+
"apps.users",
|
|
29
|
+
{% endif %}
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
MIDDLEWARE = [
|
|
33
|
+
"django.middleware.security.SecurityMiddleware",
|
|
34
|
+
"django.contrib.sessions.middleware.SessionMiddleware",
|
|
35
|
+
"django.middleware.common.CommonMiddleware",
|
|
36
|
+
"django.middleware.csrf.CsrfViewMiddleware",
|
|
37
|
+
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
|
38
|
+
"django.contrib.messages.middleware.MessageMiddleware",
|
|
39
|
+
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
ROOT_URLCONF = "config.urls"
|
|
43
|
+
|
|
44
|
+
TEMPLATES = [
|
|
45
|
+
{
|
|
46
|
+
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
|
47
|
+
"DIRS": [],
|
|
48
|
+
"APP_DIRS": True,
|
|
49
|
+
"OPTIONS": {
|
|
50
|
+
"context_processors": [
|
|
51
|
+
"django.template.context_processors.debug",
|
|
52
|
+
"django.template.context_processors.request",
|
|
53
|
+
"django.contrib.auth.context_processors.auth",
|
|
54
|
+
"django.contrib.messages.context_processors.messages",
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
WSGI_APPLICATION = "config.wsgi.application"
|
|
61
|
+
|
|
62
|
+
{% if database == "postgresql" %}
|
|
63
|
+
DATABASES = {
|
|
64
|
+
"default": {
|
|
65
|
+
"ENGINE": "django.db.backends.postgresql",
|
|
66
|
+
"NAME": config("POSTGRES_DB", default="{{ project_name_slug }}"),
|
|
67
|
+
"USER": config("POSTGRES_USER", default="user"),
|
|
68
|
+
"PASSWORD": config("POSTGRES_PASSWORD", default="password"),
|
|
69
|
+
"HOST": config("POSTGRES_HOST", default="localhost"),
|
|
70
|
+
"PORT": config("POSTGRES_PORT", default="5432"),
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
{% else %}
|
|
74
|
+
DATABASES = {
|
|
75
|
+
"default": {
|
|
76
|
+
"ENGINE": "django.db.backends.sqlite3",
|
|
77
|
+
"NAME": BASE_DIR / "db.sqlite3",
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
{% endif %}
|
|
81
|
+
|
|
82
|
+
AUTH_PASSWORD_VALIDATORS = [
|
|
83
|
+
{"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"},
|
|
84
|
+
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
|
|
85
|
+
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
|
|
86
|
+
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
LANGUAGE_CODE = "en-us"
|
|
90
|
+
TIME_ZONE = "UTC"
|
|
91
|
+
USE_I18N = True
|
|
92
|
+
USE_TZ = True
|
|
93
|
+
|
|
94
|
+
STATIC_URL = "static/"
|
|
95
|
+
STATIC_ROOT = BASE_DIR / "staticfiles"
|
|
96
|
+
|
|
97
|
+
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
98
|
+
|
|
99
|
+
{% if include_auth %}
|
|
100
|
+
AUTH_USER_MODEL = "users.User"
|
|
101
|
+
|
|
102
|
+
REST_FRAMEWORK = {
|
|
103
|
+
"DEFAULT_AUTHENTICATION_CLASSES": [
|
|
104
|
+
"rest_framework_simplejwt.authentication.JWTAuthentication",
|
|
105
|
+
],
|
|
106
|
+
"DEFAULT_PERMISSION_CLASSES": [
|
|
107
|
+
"rest_framework.permissions.IsAuthenticated",
|
|
108
|
+
],
|
|
109
|
+
}
|
|
110
|
+
{% endif %}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""config URL configuration."""
|
|
2
|
+
|
|
3
|
+
from django.contrib import admin
|
|
4
|
+
from django.urls import include, path
|
|
5
|
+
|
|
6
|
+
urlpatterns = [
|
|
7
|
+
path("admin/", admin.site.urls),
|
|
8
|
+
path("api/", include("apps.core.urls")),
|
|
9
|
+
{% if include_auth %}
|
|
10
|
+
path("api/auth/", include("apps.users.urls")),
|
|
11
|
+
{% endif %}
|
|
12
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
services:
|
|
2
|
+
app:
|
|
3
|
+
build: .
|
|
4
|
+
ports:
|
|
5
|
+
- "8000:8000"
|
|
6
|
+
env_file:
|
|
7
|
+
- .env
|
|
8
|
+
{% if database == "postgresql" %}
|
|
9
|
+
depends_on:
|
|
10
|
+
db:
|
|
11
|
+
condition: service_healthy
|
|
12
|
+
db:
|
|
13
|
+
image: postgres:16-alpine
|
|
14
|
+
environment:
|
|
15
|
+
POSTGRES_USER: ${POSTGRES_USER:-user}
|
|
16
|
+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-password}
|
|
17
|
+
POSTGRES_DB: ${POSTGRES_DB:-{{ project_name_slug }}}
|
|
18
|
+
ports:
|
|
19
|
+
- "5432:5432"
|
|
20
|
+
volumes:
|
|
21
|
+
- pgdata:/var/lib/postgresql/data
|
|
22
|
+
healthcheck:
|
|
23
|
+
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER:-user}"]
|
|
24
|
+
interval: 5s
|
|
25
|
+
timeout: 5s
|
|
26
|
+
retries: 5
|
|
27
|
+
volumes:
|
|
28
|
+
pgdata:
|
|
29
|
+
{% endif %}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""Django's command-line utility for administrative tasks."""
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main():
|
|
8
|
+
"""Run administrative tasks."""
|
|
9
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
|
10
|
+
try:
|
|
11
|
+
from django.core.management import execute_from_command_line
|
|
12
|
+
except ImportError as exc:
|
|
13
|
+
raise ImportError(
|
|
14
|
+
"Couldn't import Django. Are you sure it's installed and "
|
|
15
|
+
"available on your PYTHONPATH environment variable? Did you "
|
|
16
|
+
"forget to activate a virtual environment?"
|
|
17
|
+
) from exc
|
|
18
|
+
execute_from_command_line(sys.argv)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
main()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "{{ project_name }}"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Generated by pysnap"
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"django>=4.2.0",
|
|
12
|
+
"python-decouple>=3.8",
|
|
13
|
+
{%- if database == "postgresql" %}
|
|
14
|
+
"psycopg[binary]>=3.1.0",
|
|
15
|
+
{%- endif %}
|
|
16
|
+
{%- if include_auth %}
|
|
17
|
+
"djangorestframework>=3.15.0",
|
|
18
|
+
"djangorestframework-simplejwt>=5.3.0",
|
|
19
|
+
{%- endif %}
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
dev = [
|
|
24
|
+
{%- if include_tests %}
|
|
25
|
+
"pytest>=8.0.0",
|
|
26
|
+
"pytest-django>=4.8.0",
|
|
27
|
+
{%- endif %}
|
|
28
|
+
"ruff>=0.8.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.hatch.build.targets.wheel]
|
|
32
|
+
packages = ["apps", "config"]
|
|
33
|
+
|
|
34
|
+
[tool.pytest.ini_options]
|
|
35
|
+
DJANGO_SETTINGS_MODULE = "config.settings"
|
|
36
|
+
pythonpath = ["."]
|
|
37
|
+
|
|
38
|
+
[tool.ruff]
|
|
39
|
+
line-length = 100
|
|
40
|
+
target-version = "py311"
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "django",
|
|
3
|
+
"display_name": "Django",
|
|
4
|
+
"description": "Classic Django project with best-practice structure, health check, and optional PostgreSQL",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"files": {
|
|
7
|
+
"always": {
|
|
8
|
+
"manage.py.j2": "manage.py",
|
|
9
|
+
"pyproject.toml.j2": "pyproject.toml",
|
|
10
|
+
".gitignore.j2": ".gitignore",
|
|
11
|
+
".env.example.j2": ".env.example",
|
|
12
|
+
"README.md.j2": "README.md",
|
|
13
|
+
"config/__init__.py.j2": "config/__init__.py",
|
|
14
|
+
"config/settings.py.j2": "config/settings.py",
|
|
15
|
+
"config/urls.py.j2": "config/urls.py",
|
|
16
|
+
"config/wsgi.py.j2": "config/wsgi.py",
|
|
17
|
+
"config/asgi.py.j2": "config/asgi.py",
|
|
18
|
+
"apps/__init__.py.j2": "apps/__init__.py",
|
|
19
|
+
"apps/core/__init__.py.j2": "apps/core/__init__.py",
|
|
20
|
+
"apps/core/apps.py.j2": "apps/core/apps.py",
|
|
21
|
+
"apps/core/views.py.j2": "apps/core/views.py",
|
|
22
|
+
"apps/core/urls.py.j2": "apps/core/urls.py"
|
|
23
|
+
},
|
|
24
|
+
"when_auth": {
|
|
25
|
+
"apps/users/__init__.py.j2": "apps/users/__init__.py",
|
|
26
|
+
"apps/users/apps.py.j2": "apps/users/apps.py",
|
|
27
|
+
"apps/users/models.py.j2": "apps/users/models.py",
|
|
28
|
+
"apps/users/views.py.j2": "apps/users/views.py",
|
|
29
|
+
"apps/users/urls.py.j2": "apps/users/urls.py",
|
|
30
|
+
"apps/users/serializers.py.j2": "apps/users/serializers.py"
|
|
31
|
+
},
|
|
32
|
+
"when_docker": {
|
|
33
|
+
"Dockerfile.j2": "Dockerfile",
|
|
34
|
+
"docker-compose.yml.j2": "docker-compose.yml",
|
|
35
|
+
".dockerignore.j2": ".dockerignore"
|
|
36
|
+
},
|
|
37
|
+
"when_tests": {
|
|
38
|
+
"tests/__init__.py.j2": "tests/__init__.py",
|
|
39
|
+
"tests/conftest.py.j2": "tests/conftest.py",
|
|
40
|
+
"tests/test_health.py.j2": "tests/test_health.py"
|
|
41
|
+
},
|
|
42
|
+
"when_ci": {
|
|
43
|
+
".github/workflows/ci.yml.j2": ".github/workflows/ci.yml"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"prompts": [],
|
|
47
|
+
"hooks": {
|
|
48
|
+
"post_generate": []
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Django tests package."""
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Set up Python
|
|
16
|
+
uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.11"
|
|
19
|
+
{%- if package_manager == "uv" %}
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v4
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: uv sync --frozen
|
|
26
|
+
|
|
27
|
+
- name: Lint
|
|
28
|
+
run: uv run ruff check .
|
|
29
|
+
{%- if include_tests %}
|
|
30
|
+
|
|
31
|
+
- name: Test
|
|
32
|
+
run: uv run pytest tests/ -v
|
|
33
|
+
{%- endif %}
|
|
34
|
+
{%- else %}
|
|
35
|
+
|
|
36
|
+
- name: Install dependencies
|
|
37
|
+
run: pip install -e ".[dev]"
|
|
38
|
+
|
|
39
|
+
- name: Lint
|
|
40
|
+
run: ruff check .
|
|
41
|
+
{%- if include_tests %}
|
|
42
|
+
|
|
43
|
+
- name: Test
|
|
44
|
+
run: pytest tests/ -v
|
|
45
|
+
{%- endif %}
|
|
46
|
+
{%- endif %}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM python:3.11-slim
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
{% if package_manager == "uv" -%}
|
|
4
|
+
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
|
|
5
|
+
COPY pyproject.toml uv.lock* ./
|
|
6
|
+
RUN uv sync --frozen --no-dev
|
|
7
|
+
COPY . .
|
|
8
|
+
CMD ["uv", "run", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
9
|
+
{%- else -%}
|
|
10
|
+
COPY pyproject.toml app/__init__.py ./app/
|
|
11
|
+
RUN pip install --no-cache-dir .
|
|
12
|
+
COPY . .
|
|
13
|
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
|
14
|
+
{%- endif %}
|