commit fb25d95f0cf88bf016ca2eb84c2a1de10d354c5a Author: Yehuda Deutsch Date: Tue Apr 29 18:32:48 2025 -0400 Initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..80a39ea --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Yehuda Deutsch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..1e9dec1 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Ajal Template - FastAPI + +An opinionated FastAPI template, managed using [copier](https://copier.readthedocs.io/) diff --git a/copier.yml b/copier.yml new file mode 100644 index 0000000..220e5b8 --- /dev/null +++ b/copier.yml @@ -0,0 +1,68 @@ +_subdirectory: template + +_tasks: + - command: "git init" + - command: "git add ." + - command: "git commit -m 'Initial commit'" + - command: "uv venv .venv" + when: "{{ dependency_manager == 'uv' }}" + - command: "poetry env activate" + when: "{{ dependency_manager == 'poetry' }}" + +# Questions +project_name: + type: str + help: What is your project name? + +project_description: + type: str + help: Describe your project + default: A project based on ajal-template-fastapi + +minimum_python_version: + type: str + help: Minimum python version + choices: + - "3.10" + - "3.11" + - "3.12" + - "3.13" + +license: + type: str + help: A license for the project + default: MIT + choices: + - "None" + - "MIT" + +dependency_manager: + type: str + help: Python dependency manager + choices: + - uv + - poetry + - hatch + - setuptools + - flit + - pdm + default: uv + +user_name: + type: str + help: Author name + default: "" + +user_email: + type: str + help: Author email + default: "" + +container_template: + type: str + help: Choose a container template. Cached - using cache with this template can speed up subsequent builds, Uncached - will always install latest dependencies, using cache with this template will lead to outdated images + default: cached + choices: + - none + - cached + - uncached diff --git a/project-name.jinja b/project-name.jinja new file mode 100644 index 0000000..36209e2 --- /dev/null +++ b/project-name.jinja @@ -0,0 +1 @@ +{{ project_name | lower | replace(' ', '_') | replace('-', '_') }} \ No newline at end of file diff --git a/template/.gitignore b/template/.gitignore new file mode 100644 index 0000000..505a3b1 --- /dev/null +++ b/template/.gitignore @@ -0,0 +1,10 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv diff --git a/template/.python-version.jinja b/template/.python-version.jinja new file mode 100644 index 0000000..739b645 --- /dev/null +++ b/template/.python-version.jinja @@ -0,0 +1 @@ +{{minimum_python_version}} \ No newline at end of file diff --git a/template/README.md.jinja b/template/README.md.jinja new file mode 100644 index 0000000..2526d25 --- /dev/null +++ b/template/README.md.jinja @@ -0,0 +1,3 @@ +# {{project_name}} + +{{project_description}} diff --git a/template/__init__.py b/template/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/template/pyproject.toml.jinja b/template/pyproject.toml.jinja new file mode 100644 index 0000000..070d7ec --- /dev/null +++ b/template/pyproject.toml.jinja @@ -0,0 +1,36 @@ +[project] +name = "{{ project_name | lower | replace(' ', '-') | replace('_', '-') }}" +version = "0.1.0" +description = "{{project_description}}" +authors = [ + {name = "{{user_name}}", email = "{{user_email}}"} +] +readme = "README.md" +requires-python = ">={{minimum_python_version}}" +dependencies = [ + "aiofiles>=24.1.0", + "fastapi-jinja>=0.2.0", + "fastapi[standard]>=0.115.12", + "orjson>=3.10.16", + "pydantic-settings>=2.9.1", +] + +[build-system] +{%- if dependency_manager == "poetry" %} +requires = ["poetry-core>=2.0.0,<3.0.0"] +build-backend = "poetry.core.masonry.api" +{%- elif dependency_manager == "setuptools" %} +{%- elif dependency_manager == "hatch" %} +requires = ["hatchling"] +build-backend = "hatchling.build" +{%- elif dependency_manager == "flit" %} +requires = ["flit_core >= 3.4"] +build-backend = "flit_core.buildapi" +{%- elif dependency_manager == "pdm" %} +requires = ["pdm-backend"] +build-backend = "pdm.backend" +{%- else %} +{#- This is the default #} +requires = ["setuptools >= 61.0"] +build-backend = "setuptools.build_meta" +{%- endif %} diff --git a/template/{% if container_template == 'cached' %}Dockerfile{% endif %}.jinja b/template/{% if container_template == 'cached' %}Dockerfile{% endif %}.jinja new file mode 100644 index 0000000..a52197f --- /dev/null +++ b/template/{% if container_template == 'cached' %}Dockerfile{% endif %}.jinja @@ -0,0 +1,16 @@ +FROM docker.io/library/alpine:latest + +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +ENV UV_LINK_MODE=copy \ + UV_COMPILE_BYTECODE=1 \ + PATH=/app/.venv/bin:$PATH + +WORKDIR /app +RUN --mount=type=cache,target=/root/.cache/uv \ + --mount=type=bind,source=uv.lock,target=uv.lock \ + --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ + --mount=type=bind,source={{project_name}},target={{project_name}} \ + uv sync --locked --no-editable + +CMD ["fastapi", "run"] diff --git a/template/{% if container_template == 'uncached' %}Dockerfile{% endif %}.jinja b/template/{% if container_template == 'uncached' %}Dockerfile{% endif %}.jinja new file mode 100644 index 0000000..de2ef83 --- /dev/null +++ b/template/{% if container_template == 'uncached' %}Dockerfile{% endif %}.jinja @@ -0,0 +1,18 @@ +FROM docker.io/library/alpine:latest + +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +ENV UV_LINK_MODE=copy \ + UV_COMPILE_BYTECODE=1 \ + PATH=/app/.venv/bin:$PATH + +WORKDIR /app +COPY pyproject.toml uv.lock /app/ +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --locked --no-install-project --no-editable + +COPY {{project_name}}/ /app/{{project_name}}/ +RUN --mount=type=cache,target=/root/.cache/uv \ + uv sync --locked --no-editable + +CMD ["fastapi", "run"] diff --git a/template/{% if license == 'MIT' %}LICENSE{% endif %}.jinja b/template/{% if license == 'MIT' %}LICENSE{% endif %}.jinja new file mode 100644 index 0000000..d5ef7b3 --- /dev/null +++ b/template/{% if license == 'MIT' %}LICENSE{% endif %}.jinja @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 {{user_name}} + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/template/{% include 'project-name.jinja' %}/__init__.py b/template/{% include 'project-name.jinja' %}/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/template/{% include 'project-name.jinja' %}/app.py b/template/{% include 'project-name.jinja' %}/app.py new file mode 100644 index 0000000..eb3929e --- /dev/null +++ b/template/{% include 'project-name.jinja' %}/app.py @@ -0,0 +1,9 @@ +from fastapi import FastAPI + +from .settings import MainSettings + +settings = MainSettings() +app = FastAPI( + title=settings.app_name, + debug=settings.debug, +) diff --git a/template/{% include 'project-name.jinja' %}/settings.py b/template/{% include 'project-name.jinja' %}/settings.py new file mode 100644 index 0000000..a58ecd9 --- /dev/null +++ b/template/{% include 'project-name.jinja' %}/settings.py @@ -0,0 +1,22 @@ +from pathlib import Path + +from pydantic_settings import BaseSettings + +APP_PATH = Path(__file__).parent.absolute() + + +class MainSettings(BaseSettings): + debug: bool = False + verbosity: int = 1 + + app_name = "Ajal Template - FastAPI" + + base_path: Path = APP_PATH.parent.absolute() + app_path: Path = APP_PATH + + templates_dir: str = 'templates' + static_dir: str = 'static' + + class Config: + env_prefix = 'fastapi_' + env_file = '.env' diff --git a/template/{% include 'project-name.jinja' %}/web.py b/template/{% include 'project-name.jinja' %}/web.py new file mode 100644 index 0000000..0dd7810 --- /dev/null +++ b/template/{% include 'project-name.jinja' %}/web.py @@ -0,0 +1,13 @@ +import fastapi_jinja +from fastapi.staticfiles import StaticFiles + +from .app import app, settings +from .views import index + +fastapi_jinja.global_init( + template_folder=str(settings.app_path / settings.templates_dir), + auto_reload=settings.debug, +) +app.mount("/static", StaticFiles(directory=settings.app_path / settings.static_dir), name="static") + +app.mount('/', index.router, name='index') diff --git a/template/{{ _copier_conf.answers_file }}.jinja b/template/{{ _copier_conf.answers_file }}.jinja new file mode 100644 index 0000000..88acac8 --- /dev/null +++ b/template/{{ _copier_conf.answers_file }}.jinja @@ -0,0 +1,2 @@ +# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY +{{ _copier_answers|to_nice_yaml -}} \ No newline at end of file