fastapi-spawn 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. fastapi_spawn/__init__.py +6 -0
  2. fastapi_spawn/cli.py +387 -0
  3. fastapi_spawn/config.py +162 -0
  4. fastapi_spawn/constants.py +133 -0
  5. fastapi_spawn/generator.py +294 -0
  6. fastapi_spawn/interactive.py +192 -0
  7. fastapi_spawn/templates/alembic/alembic.ini.j2 +39 -0
  8. fastapi_spawn/templates/alembic/env.py.j2 +64 -0
  9. fastapi_spawn/templates/app/__init__.py.j2 +1 -0
  10. fastapi_spawn/templates/app/api/deps.py.j2 +39 -0
  11. fastapi_spawn/templates/app/api/v1/auth.py.j2 +59 -0
  12. fastapi_spawn/templates/app/api/v1/health.py.j2 +40 -0
  13. fastapi_spawn/templates/app/core/ai.py.j2 +76 -0
  14. fastapi_spawn/templates/app/core/config.py.j2 +177 -0
  15. fastapi_spawn/templates/app/core/exceptions.py.j2 +43 -0
  16. fastapi_spawn/templates/app/core/logging.py.j2 +70 -0
  17. fastapi_spawn/templates/app/core/security.py.j2 +42 -0
  18. fastapi_spawn/templates/app/core/storage.py.j2 +73 -0
  19. fastapi_spawn/templates/app/db/session.py.j2 +84 -0
  20. fastapi_spawn/templates/app/main.py.j2 +71 -0
  21. fastapi_spawn/templates/base/Makefile.j2 +45 -0
  22. fastapi_spawn/templates/base/README.md.j2 +74 -0
  23. fastapi_spawn/templates/base/env.j2 +82 -0
  24. fastapi_spawn/templates/base/env_example.j2 +85 -0
  25. fastapi_spawn/templates/base/gitignore.j2 +38 -0
  26. fastapi_spawn/templates/base/pre_commit.j2 +17 -0
  27. fastapi_spawn/templates/base/pyproject.toml.j2 +129 -0
  28. fastapi_spawn/templates/ci/github/publish.yml.j2 +32 -0
  29. fastapi_spawn/templates/ci/github/tests.yml.j2 +39 -0
  30. fastapi_spawn/templates/ci/gitlab/gitlab-ci.yml.j2 +29 -0
  31. fastapi_spawn/templates/docker/Dockerfile.j2 +17 -0
  32. fastapi_spawn/templates/docker/docker-compose.yml.j2 +97 -0
  33. fastapi_spawn/templates/docker/dockerignore.j2 +13 -0
  34. fastapi_spawn/templates/infra/docker/docker-compose.prod.yml.j2 +43 -0
  35. fastapi_spawn/templates/infra/helm/Chart.yaml.j2 +6 -0
  36. fastapi_spawn/templates/infra/helm/values.yaml.j2 +26 -0
  37. fastapi_spawn/templates/infra/terraform/main.tf.j2 +26 -0
  38. fastapi_spawn/templates/infra/terraform/variables.tf.j2 +17 -0
  39. fastapi_spawn/templates/root/main.py.j2 +16 -0
  40. fastapi_spawn/templates/tasks/celery_app.py.j2 +37 -0
  41. fastapi_spawn/templates/tasks/sample_tasks.py.j2 +27 -0
  42. fastapi_spawn/templates/tests/conftest.py.j2 +22 -0
  43. fastapi_spawn/templates/tests/test_health.py.j2 +30 -0
  44. fastapi_spawn/utils.py +58 -0
  45. fastapi_spawn/validators.py +67 -0
  46. fastapi_spawn-0.1.0.dist-info/METADATA +262 -0
  47. fastapi_spawn-0.1.0.dist-info/RECORD +50 -0
  48. fastapi_spawn-0.1.0.dist-info/WHEEL +4 -0
  49. fastapi_spawn-0.1.0.dist-info/entry_points.txt +2 -0
  50. fastapi_spawn-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,45 @@
1
+ .PHONY: help install dev run test lint format clean
2
+
3
+ help: ## Show this help
4
+ @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
5
+
6
+ install: ## Install dependencies
7
+ pip install -e .
8
+
9
+ dev: ## Install dev dependencies
10
+ pip install -e ".[dev]"
11
+
12
+ run: ## Run development server
13
+ uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
14
+
15
+ {% if include_tests %}
16
+ test: ## Run tests
17
+ pytest --cov=app --cov-report=term-missing
18
+
19
+ test-ci: ## Run tests with XML report
20
+ pytest --cov=app --cov-report=xml
21
+ {% endif %}
22
+
23
+ lint: ## Run linters
24
+ ruff check .
25
+ mypy app/
26
+
27
+ format: ## Auto-format code
28
+ ruff format .
29
+ ruff check --fix .
30
+
31
+ clean: ## Remove build artifacts
32
+ find . -type d -name __pycache__ -exec rm -rf {} +
33
+ find . -type f -name "*.pyc" -delete
34
+ rm -rf dist/ build/ *.egg-info .pytest_cache .coverage htmlcov/
35
+
36
+ {% if has_docker %}
37
+ docker-build: ## Build Docker image
38
+ docker build -t {{ slug }} .
39
+
40
+ docker-up: ## Start all services
41
+ docker compose up --build
42
+
43
+ docker-down: ## Stop all services
44
+ docker compose down
45
+ {% endif %}
@@ -0,0 +1,74 @@
1
+ # {{ project_name }}
2
+
3
+ > Auto-generated by [fastapi-spawn](https://github.com/Bishwajitgarai/fastapi-spawn) 🚀
4
+
5
+ ## Stack
6
+
7
+ | Component | Choice |
8
+ |-----------|--------|
9
+ | Database | `{{ db }}` |
10
+ | ORM / ODM | `{{ orm }}` |
11
+ | Auth | `{{ auth }}` |
12
+ | Broker | `{{ broker }}` |
13
+ | Cache | `{{ cache }}` |
14
+ | Stack | `{{ stack }}` |
15
+ | Logging | `{{ log_lib }}` |
16
+
17
+ ## Getting Started
18
+
19
+ ```bash
20
+ # 1. Create virtual environment
21
+ python -m venv .venv
22
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
23
+
24
+ # 2. Install dependencies
25
+ pip install -e ".[dev]"
26
+
27
+ # 3. Copy env file and fill in values
28
+ cp .env.example .env
29
+
30
+ {% if has_docker %}
31
+ # 4. Start with Docker
32
+ docker compose up --build
33
+ {% else %}
34
+ # 4. Run locally
35
+ uvicorn app.main:app --reload
36
+ {% endif %}
37
+ ```
38
+
39
+ ## API Endpoints
40
+
41
+ | Method | Path | Description |
42
+ |--------|------|-------------|
43
+ | GET | `/health` | Health check |
44
+ | GET | `/api/v1/health/readiness` | Readiness probe |
45
+ | GET | `/api/v1/health/liveness` | Liveness probe |
46
+ {% if has_auth %}
47
+ | POST | `/api/v1/auth/login` | Obtain token |
48
+ | POST | `/api/v1/auth/refresh` | Refresh token |
49
+ {% endif %}
50
+
51
+ ## Project Structure
52
+
53
+ ```
54
+ {{ project_name }}/
55
+ ├── app/
56
+ │ ├── main.py # FastAPI application entrypoint
57
+ │ ├── core/ # Config, logging, security
58
+ │ ├── api/v1/ # Versioned route handlers
59
+ │ ├── models/ # ORM models
60
+ │ ├── schemas/ # Pydantic schemas
61
+ │ ├── services/ # Business logic
62
+ │ └── repositories/ # Data access layer
63
+ {% if include_tests %}
64
+ ├── tests/ # pytest test suite
65
+ {% endif %}
66
+ {% if has_docker %}
67
+ ├── Dockerfile
68
+ └── docker-compose.yml
69
+ {% endif %}
70
+ ```
71
+
72
+ ## License
73
+
74
+ MIT
@@ -0,0 +1,82 @@
1
+ # Application
2
+ APP_NAME="{{ project_name }}"
3
+ ENVIRONMENT=dev
4
+ DEBUG=true
5
+ SECRET_KEY=super-secret-change-in-production
6
+ API_V1_PREFIX=/api/v1
7
+ CORS_ORIGINS=http://localhost:3000,http://localhost:8000
8
+
9
+ {% if db == "postgresql" %}
10
+ # PostgreSQL
11
+ POSTGRES_USER=postgres
12
+ POSTGRES_PASSWORD=postgres
13
+ POSTGRES_HOST=localhost
14
+ POSTGRES_PORT=5432
15
+ POSTGRES_DB={{ slug }}_db
16
+ {% endif %}
17
+ {% if db == "mysql" %}
18
+ # MySQL
19
+ MYSQL_USER=root
20
+ MYSQL_PASSWORD=mysql
21
+ MYSQL_HOST=localhost
22
+ MYSQL_PORT=3306
23
+ MYSQL_DB={{ slug }}_db
24
+ {% endif %}
25
+ {% if db == "sqlite" %}
26
+ # SQLite
27
+ SQLITE_FILE={{ slug }}.db
28
+ {% endif %}
29
+ {% if has_mongo %}
30
+ # MongoDB
31
+ MONGODB_USER=mongo
32
+ MONGODB_PASSWORD=mongo
33
+ MONGODB_HOST=localhost
34
+ MONGODB_PORT=27017
35
+ MONGODB_DB={{ slug }}_db
36
+ {% endif %}
37
+ {% if broker == "redis" or cache == "redis" %}
38
+ # Redis
39
+ REDIS_HOST=localhost
40
+ REDIS_PORT=6379
41
+ REDIS_PASSWORD=
42
+ REDIS_DB=0
43
+ {% endif %}
44
+ {% if broker == "rabbitmq" %}
45
+ # RabbitMQ
46
+ RABBITMQ_USER=guest
47
+ RABBITMQ_PASSWORD=guest
48
+ RABBITMQ_HOST=localhost
49
+ RABBITMQ_PORT=5672
50
+ RABBITMQ_VHOST=/
51
+ {% endif %}
52
+ {% if broker == "kafka" %}
53
+ # Kafka
54
+ KAFKA_HOST=localhost
55
+ KAFKA_PORT=9092
56
+ {% endif %}
57
+ {% if has_s3 %}
58
+ # AWS S3
59
+ AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
60
+ AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
61
+ AWS_REGION=us-east-1
62
+ AWS_S3_BUCKET={{ slug }}-bucket
63
+ AWS_S3_ENDPOINT_URL=
64
+ {% endif %}
65
+ {% if has_auth %}
66
+ # Auth / JWT
67
+ ACCESS_TOKEN_EXPIRE_MINUTES=30
68
+ REFRESH_TOKEN_EXPIRE_DAYS=7
69
+ ALGORITHM=HS256
70
+ {% endif %}
71
+ {% if ai == "openai" %}
72
+ # OpenAI
73
+ OPENAI_API_KEY=sk-placeholder
74
+ OPENAI_MODEL=gpt-4o
75
+ OPENAI_EMBEDDING_MODEL=text-embedding-3-small
76
+ OPENAI_BASE_URL= # leave blank for api.openai.com; set for Azure / LM Studio / local
77
+ {% endif %}
78
+ {% if ai == "anthropic" %}
79
+ # Anthropic
80
+ ANTHROPIC_API_KEY=sk-ant-placeholder
81
+ ANTHROPIC_MODEL=claude-3-5-sonnet-20241022
82
+ {% endif %}
@@ -0,0 +1,85 @@
1
+ # Copy this file to .env and fill in real values.
2
+ # NEVER commit .env to version control.
3
+
4
+ # Application
5
+ APP_NAME="{{ project_name }}"
6
+ ENVIRONMENT=dev
7
+ DEBUG=true
8
+ SECRET_KEY=CHANGE_ME
9
+ API_V1_PREFIX=/api/v1
10
+ CORS_ORIGINS=http://localhost:3000,http://localhost:8000
11
+
12
+ {% if db == "postgresql" %}
13
+ # PostgreSQL
14
+ POSTGRES_USER=postgres
15
+ POSTGRES_PASSWORD=CHANGE_ME
16
+ POSTGRES_HOST=localhost
17
+ POSTGRES_PORT=5432
18
+ POSTGRES_DB={{ slug }}_db
19
+ {% endif %}
20
+ {% if db == "mysql" %}
21
+ # MySQL
22
+ MYSQL_USER=root
23
+ MYSQL_PASSWORD=CHANGE_ME
24
+ MYSQL_HOST=localhost
25
+ MYSQL_PORT=3306
26
+ MYSQL_DB={{ slug }}_db
27
+ {% endif %}
28
+ {% if db == "sqlite" %}
29
+ # SQLite
30
+ SQLITE_FILE={{ slug }}.db
31
+ {% endif %}
32
+ {% if has_mongo %}
33
+ # MongoDB
34
+ MONGODB_USER=mongo
35
+ MONGODB_PASSWORD=CHANGE_ME
36
+ MONGODB_HOST=localhost
37
+ MONGODB_PORT=27017
38
+ MONGODB_DB={{ slug }}_db
39
+ {% endif %}
40
+ {% if broker == "redis" or cache == "redis" %}
41
+ # Redis
42
+ REDIS_HOST=localhost
43
+ REDIS_PORT=6379
44
+ REDIS_PASSWORD=
45
+ REDIS_DB=0
46
+ {% endif %}
47
+ {% if broker == "rabbitmq" %}
48
+ # RabbitMQ
49
+ RABBITMQ_USER=guest
50
+ RABBITMQ_PASSWORD=CHANGE_ME
51
+ RABBITMQ_HOST=localhost
52
+ RABBITMQ_PORT=5672
53
+ RABBITMQ_VHOST=/
54
+ {% endif %}
55
+ {% if broker == "kafka" %}
56
+ # Kafka
57
+ KAFKA_HOST=localhost
58
+ KAFKA_PORT=9092
59
+ {% endif %}
60
+ {% if has_s3 %}
61
+ # AWS S3
62
+ AWS_ACCESS_KEY_ID=CHANGE_ME
63
+ AWS_SECRET_ACCESS_KEY=CHANGE_ME
64
+ AWS_REGION=us-east-1
65
+ AWS_S3_BUCKET={{ slug }}-bucket
66
+ AWS_S3_ENDPOINT_URL=
67
+ {% endif %}
68
+ {% if has_auth %}
69
+ # Auth / JWT
70
+ ACCESS_TOKEN_EXPIRE_MINUTES=30
71
+ REFRESH_TOKEN_EXPIRE_DAYS=7
72
+ ALGORITHM=HS256
73
+ {% endif %}
74
+ {% if ai == "openai" %}
75
+ # OpenAI
76
+ OPENAI_API_KEY=sk-placeholder
77
+ OPENAI_MODEL=gpt-4o
78
+ OPENAI_EMBEDDING_MODEL=text-embedding-3-small
79
+ OPENAI_BASE_URL= # leave blank for api.openai.com; set for Azure / LM Studio / local
80
+ {% endif %}
81
+ {% if ai == "anthropic" %}
82
+ # Anthropic
83
+ ANTHROPIC_API_KEY=sk-ant-placeholder
84
+ ANTHROPIC_MODEL=claude-3-5-sonnet-20241022
85
+ {% endif %}
@@ -0,0 +1,38 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+
8
+ # Envs
9
+ .venv/
10
+ venv/
11
+ env/
12
+
13
+ # Distribution
14
+ dist/
15
+ build/
16
+ *.egg-info/
17
+
18
+ # Secrets
19
+ .env
20
+ .env.*
21
+ !.env.example
22
+
23
+ # Testing
24
+ .pytest_cache/
25
+ .coverage
26
+ htmlcov/
27
+ coverage.xml
28
+
29
+ # Type checking
30
+ .mypy_cache/
31
+
32
+ # IDE
33
+ .vscode/
34
+ .idea/
35
+
36
+ # OS
37
+ .DS_Store
38
+ Thumbs.db
@@ -0,0 +1,17 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.4.4
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
8
+
9
+ - repo: https://github.com/pre-commit/pre-commit-hooks
10
+ rev: v4.6.0
11
+ hooks:
12
+ - id: trailing-whitespace
13
+ - id: end-of-file-fixer
14
+ - id: check-yaml
15
+ - id: check-toml
16
+ - id: check-merge-conflict
17
+ - id: detect-private-key
@@ -0,0 +1,129 @@
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 = "A production-ready FastAPI application"
9
+ requires-python = ">=3.10"
10
+ authors = [{ name = "Your Name", email = "you@example.com" }]
11
+ dependencies = [
12
+ "fastapi>=0.111.0",
13
+ "uvicorn[standard]>=0.29.0",
14
+ "pydantic>=2.7.0",
15
+ "pydantic-settings>=2.2.0",
16
+ {% if log_lib == "loguru" %}
17
+ "loguru>=0.7.2",
18
+ {% elif log_lib == "structlog" %}
19
+ "structlog>=24.1.0",
20
+ {% endif %}
21
+ {% if has_relational_db and orm == "sqlalchemy" %}
22
+ "sqlalchemy[asyncio]>=2.0.0",
23
+ {% if db == "postgresql" %}
24
+ "asyncpg>=0.29.0",
25
+ {% elif db == "mysql" %}
26
+ "aiomysql>=0.2.0",
27
+ {% elif db == "sqlite" %}
28
+ "aiosqlite>=0.20.0",
29
+ {% endif %}
30
+ {% endif %}
31
+ {% if has_relational_db and orm == "tortoise" %}
32
+ "tortoise-orm>=0.21.0",
33
+ {% endif %}
34
+ {% if has_migration and migration == "alembic" %}
35
+ "alembic>=1.13.0",
36
+ {% elif has_migration and migration == "aerich" %}
37
+ "aerich>=0.7.2",
38
+ {% endif %}
39
+ {% if has_mongo and orm == "beanie" %}
40
+ "beanie>=1.25.0",
41
+ "motor>=3.4.0",
42
+ {% endif %}
43
+ {% if auth == "jwt" or auth == "oauth2" %}
44
+ "python-jose[cryptography]>=3.3.0",
45
+ "passlib[bcrypt]>=1.7.4",
46
+ "python-multipart>=0.0.9",
47
+ {% elif auth == "api-key" %}
48
+ "python-multipart>=0.0.9",
49
+ {% endif %}
50
+ {% if broker == "redis" or broker == "rabbitmq" %}
51
+ "celery[redis]>=5.3.6",
52
+ {% elif broker == "kafka" %}
53
+ "aiokafka>=0.10.0",
54
+ {% endif %}
55
+ {% if cache == "redis" or broker == "redis" %}
56
+ "redis[hiredis]>=5.0.0",
57
+ {% elif cache == "memcached" %}
58
+ "aiomcache>=0.8.0",
59
+ {% endif %}
60
+ {% if has_s3 %}
61
+ "boto3>=1.34.0",
62
+ {% endif %}
63
+ {% if ai == "openai" %}
64
+ "openai>=1.30.0",
65
+ {% elif ai == "anthropic" %}
66
+ "anthropic>=0.28.0",
67
+ {% endif %}
68
+ ]
69
+
70
+ [project.optional-dependencies]
71
+ dev = [
72
+ "pytest>=8.0.0",
73
+ "pytest-asyncio>=0.23.0",
74
+ "pytest-cov>=5.0.0",
75
+ "httpx>=0.27.0",
76
+ "anyio>=4.3.0",
77
+ "ruff>=0.4.0",
78
+ "mypy>=1.10.0",
79
+ "pre-commit>=3.7.0",
80
+ ]
81
+
82
+ [tool.uv]
83
+ dev-dependencies = [
84
+ "pytest>=8.0.0",
85
+ "pytest-asyncio>=0.23.0",
86
+ "pytest-cov>=5.0.0",
87
+ "httpx>=0.27.0",
88
+ "anyio>=4.3.0",
89
+ "ruff>=0.4.0",
90
+ "mypy>=1.10.0",
91
+ "pre-commit>=3.7.0",
92
+ ]
93
+
94
+ [tool.pytest.ini_options]
95
+ asyncio_mode = "auto"
96
+ testpaths = ["tests"]
97
+ addopts = "--cov=app --cov-report=term-missing"
98
+
99
+ [tool.ruff]
100
+ line-length = 100
101
+ target-version = "py310"
102
+
103
+ [tool.ruff.lint]
104
+ select = ["E", "F", "I", "UP", "B", "SIM"]
105
+ ignore = ["E501"]
106
+
107
+ [tool.mypy]
108
+ python_version = "3.10"
109
+ strict = true
110
+ ignore_missing_imports = true
111
+
112
+ # ── uv run scripts ─────────────────────────────────────────────────────────
113
+ # Usage: uv run <script-name>
114
+ [tool.uv.scripts]
115
+ dev = "uvicorn app.main:app --reload --host 0.0.0.0 --port 8000"
116
+ start = "python main.py"
117
+ test = "pytest --cov=app --cov-report=term-missing"
118
+ lint = "ruff check ."
119
+ format = "ruff format ."
120
+ typecheck = "mypy app/"
121
+ {% if has_alembic %}
122
+ migrate = "alembic upgrade head"
123
+ rollback = "alembic downgrade -1"
124
+ makemig = "alembic revision --autogenerate -m"
125
+ {% endif %}
126
+ {% if has_broker %}
127
+ worker = "celery -A tasks.celery_app worker --loglevel=info"
128
+ beat = "celery -A tasks.celery_app beat --loglevel=info"
129
+ {% endif %}
@@ -0,0 +1,32 @@
1
+ name: Publish Docker Image
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ push:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: read
13
+ packages: write
14
+
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+
18
+ - name: Log in to GHCR
19
+ uses: docker/login-action@v3
20
+ with:
21
+ registry: ghcr.io
22
+ username: ${{ "{{" }} github.actor {{ "}}" }}
23
+ password: ${{ "{{" }} secrets.GITHUB_TOKEN {{ "}}" }}
24
+
25
+ - name: Build and push
26
+ uses: docker/build-push-action@v5
27
+ with:
28
+ context: .
29
+ push: true
30
+ tags: |
31
+ ghcr.io/${{ "{{" }} github.repository {{ "}}" }}:latest
32
+ ghcr.io/${{ "{{" }} github.repository {{ "}}" }}:${{ "{{" }} github.ref_name {{ "}}" }}
@@ -0,0 +1,39 @@
1
+ name: Tests
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
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Python ${{ "{{" }} matrix.python-version {{ "}}" }}
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ "{{" }} matrix.python-version {{ "}}" }}
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v4
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --all-extras
29
+
30
+ - name: Lint with ruff
31
+ run: uv run ruff check .
32
+
33
+ - name: Run tests
34
+ run: uv run pytest --cov=app --cov-report=xml
35
+
36
+ - name: Upload coverage
37
+ uses: codecov/codecov-action@v4
38
+ with:
39
+ file: coverage.xml
@@ -0,0 +1,29 @@
1
+ stages:
2
+ - test
3
+ - build
4
+
5
+ test:
6
+ stage: test
7
+ image: python:3.12-slim
8
+ before_script:
9
+ - pip install uv
10
+ - uv sync --all-extras
11
+ script:
12
+ - uv run ruff check .
13
+ - uv run pytest --cov=app --cov-report=xml
14
+ coverage: '/TOTAL.*\s+(\d+%)$/'
15
+ artifacts:
16
+ reports:
17
+ coverage_report:
18
+ coverage_format: cobertura
19
+ path: coverage.xml
20
+
21
+ build:
22
+ stage: build
23
+ image: docker:latest
24
+ services:
25
+ - docker:dind
26
+ script:
27
+ - docker build -t {{ slug }}:latest .
28
+ only:
29
+ - tags
@@ -0,0 +1,17 @@
1
+ FROM python:3.12-slim
2
+
3
+ # Install uv
4
+ COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
5
+
6
+ WORKDIR /app
7
+
8
+ # Install deps first for layer caching
9
+ COPY pyproject.toml ./
10
+ RUN uv sync --no-dev --frozen
11
+
12
+ # Copy source
13
+ COPY . .
14
+
15
+ EXPOSE 8000
16
+
17
+ CMD ["uv", "run", "main.py"]
@@ -0,0 +1,97 @@
1
+ services:
2
+ app:
3
+ build: .
4
+ ports:
5
+ - "8000:8000"
6
+ env_file:
7
+ - .env
8
+ depends_on:
9
+ {% if has_relational_db and db == "postgresql" %}
10
+ - postgres
11
+ {% endif %}
12
+ {% if has_mongo %}
13
+ - mongodb
14
+ {% endif %}
15
+ {% if broker == "redis" or cache == "redis" %}
16
+ - redis
17
+ {% endif %}
18
+ {% if broker == "rabbitmq" %}
19
+ - rabbitmq
20
+ {% endif %}
21
+ volumes:
22
+ - .:/app
23
+ restart: unless-stopped
24
+
25
+ {% if has_broker and broker != "kafka" %}
26
+ worker:
27
+ build: .
28
+ command: celery -A tasks.celery_app worker --loglevel=info --concurrency=2
29
+ env_file:
30
+ - .env
31
+ depends_on:
32
+ - app
33
+ restart: unless-stopped
34
+ {% endif %}
35
+
36
+ {% if has_relational_db and db == "postgresql" %}
37
+ postgres:
38
+ image: postgres:16-alpine
39
+ environment:
40
+ POSTGRES_DB: {{ slug }}_db
41
+ POSTGRES_USER: user
42
+ POSTGRES_PASSWORD: password
43
+ ports:
44
+ - "5432:5432"
45
+ volumes:
46
+ - postgres_data:/var/lib/postgresql/data
47
+ healthcheck:
48
+ test: ["CMD-SHELL", "pg_isready -U user -d {{ slug }}_db"]
49
+ interval: 10s
50
+ timeout: 5s
51
+ retries: 5
52
+ {% endif %}
53
+
54
+ {% if has_mongo %}
55
+ mongodb:
56
+ image: mongo:7
57
+ ports:
58
+ - "27017:27017"
59
+ volumes:
60
+ - mongo_data:/data/db
61
+ {% endif %}
62
+
63
+ {% if broker == "redis" or cache == "redis" %}
64
+ redis:
65
+ image: redis:7-alpine
66
+ ports:
67
+ - "6379:6379"
68
+ volumes:
69
+ - redis_data:/data
70
+ healthcheck:
71
+ test: ["CMD", "redis-cli", "ping"]
72
+ interval: 10s
73
+ timeout: 3s
74
+ retries: 3
75
+ {% endif %}
76
+
77
+ {% if broker == "rabbitmq" %}
78
+ rabbitmq:
79
+ image: rabbitmq:3-management-alpine
80
+ ports:
81
+ - "5672:5672"
82
+ - "15672:15672"
83
+ environment:
84
+ RABBITMQ_DEFAULT_USER: guest
85
+ RABBITMQ_DEFAULT_PASS: guest
86
+ {% endif %}
87
+
88
+ volumes:
89
+ {% if has_relational_db and db == "postgresql" %}
90
+ postgres_data:
91
+ {% endif %}
92
+ {% if has_mongo %}
93
+ mongo_data:
94
+ {% endif %}
95
+ {% if broker == "redis" or cache == "redis" %}
96
+ redis_data:
97
+ {% endif %}
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ .env
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .coverage
10
+ htmlcov/
11
+ .mypy_cache/
12
+ .ruff_cache/
13
+ .DS_Store