From 2d3879a58bc0f5151221e849bc17974c8cc89d84 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Mon, 23 Nov 2020 00:35:18 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20docker=20support=20to=20start?= =?UTF-8?q?project?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/test.yml | 2 +- ROADMAP.md | 6 +++++- manage_fastapi/constants.py | 1 - .../{{ cookiecutter.folder_name }}/Dockerfile | 17 +++++++++++++++++ .../app/config.py | 13 ++++++++++++- .../app/main.py | 18 +++++++++++++++++- .../docker-compose.yaml | 8 ++++++++ pyproject.toml | 1 - setup.cfg | 5 ++++- tests/test_startproject.py | 19 +++++++++++++------ 10 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/Dockerfile create mode 100644 manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/docker-compose.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index faa9181..a36b379 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: [3.6, 3.7, 3.8] fail-fast: true steps: diff --git a/ROADMAP.md b/ROADMAP.md index 44ef599..0a9d139 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -8,7 +8,7 @@ The package plans are here. If you want to contribute with new ideas, or develop ## Checklist - [X] License support on `startproject`. -- [ ] Docker/Docker-compose support on `startproject`. +- [X] Docker/Docker-compose support on `startproject`. - [ ] VSCode debugger support on `startproject` (available via docker). - [X] Add basic linter tools on `startproject` (flake8, mypy and isort). - [X] Add `.pre-commit-config.yaml` on `startproject`. @@ -18,3 +18,7 @@ The package plans are here. If you want to contribute with new ideas, or develop - [ ] Integrate databases on `startproject`. - [ ] Create `migrations`/`migrate` command. - [ ] Different Authentication support on `startproject`. + +## Questions + +- Should we support .git by default? diff --git a/manage_fastapi/constants.py b/manage_fastapi/constants.py index 7787a1c..0fb1877 100644 --- a/manage_fastapi/constants.py +++ b/manage_fastapi/constants.py @@ -27,7 +27,6 @@ class PythonVersion(BaseEnum): THREE_DOT_SIX = "3.6" THREE_DOT_SEV = "3.7" THREE_DOT_EIG = "3.8" - THREE_DOT_NIN = "3.9" class License(BaseEnum): diff --git a/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/Dockerfile b/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/Dockerfile new file mode 100644 index 0000000..99462b0 --- /dev/null +++ b/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/Dockerfile @@ -0,0 +1,17 @@ +FROM tiangolo/uvicorn-gunicorn-fastapi:python{{ cookiecutter.python }} + +ENV PYTHONPATH "${PYTHONPATH}:/" +ENV PORT=8000 + +# Install Poetry +RUN curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | POETRY_HOME=/opt/poetry python && \ + cd /usr/local/bin && \ + ln -s /opt/poetry/bin/poetry && \ + poetry config virtualenvs.create false + +# Copy using poetry.lock* in case it doesn't exist yet +COPY ./pyproject.toml ./poetry.lock* /app/ + +RUN poetry install --no-root --no-dev + +COPY ./app /app diff --git a/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/config.py b/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/config.py index ea4ba23..1baa449 100644 --- a/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/config.py +++ b/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/config.py @@ -1,8 +1,19 @@ -from pydantic import BaseSettings +from typing import List, Union + +from pydantic import AnyHttpUrl, BaseSettings, validator class Settings(BaseSettings): PROJECT_NAME: str + BACKEND_CORS_ORIGINS: List[AnyHttpUrl] = [] + + @validator("BACKEND_CORS_ORIGINS", pre=True) + def assemble_cors_origins(cls, v: Union[str, List[str]]) -> Union[List[str], str]: + if isinstance(v, str) and not v.startswith("["): + return [i.strip() for i in v.split(",")] + elif isinstance(v, (list, str)): + return v + raise ValueError(v) class Config: env_file = ".env" diff --git a/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/main.py b/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/main.py index c777bb9..001d1ee 100644 --- a/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/main.py +++ b/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/app/main.py @@ -1,5 +1,21 @@ from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware from app.config import settings -app = FastAPI(title=settings.PROJECT_NAME) + +def get_application(): + _app = FastAPI(title=settings.PROJECT_NAME) + + _app.add_middleware( + CORSMiddleware, + allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], + ) + + return _app + + +app = get_application() diff --git a/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/docker-compose.yaml b/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/docker-compose.yaml new file mode 100644 index 0000000..cb32fdc --- /dev/null +++ b/manage_fastapi/templates/project/{{ cookiecutter.folder_name }}/docker-compose.yaml @@ -0,0 +1,8 @@ +version: "3.8" + +services: + app: + build: . + env_file: ".env" + ports: + - "8000:8000" diff --git a/pyproject.toml b/pyproject.toml index 4fd5b1a..5ea6606 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,6 @@ classifiers = [ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", ] diff --git a/setup.cfg b/setup.cfg index bd7d343..0f20a21 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [isort] profile = black known_first_party = manage_fastapi -skip = '{{ cookiecutter.folder_name }}/*' +skip = */templates/* [flake8] max-complexity = 7 @@ -16,3 +16,6 @@ plugins = pydantic.mypy ignore_missing_imports = True follow_imports = skip strict_optional = True + +[coverage:run] +omit = */templates/* diff --git a/tests/test_startproject.py b/tests/test_startproject.py index c2d00ef..578492e 100644 --- a/tests/test_startproject.py +++ b/tests/test_startproject.py @@ -1,9 +1,8 @@ from unittest.mock import patch import pytest -from typer.testing import CliRunner - from manage_fastapi.main import app +from typer.testing import CliRunner runner = CliRunner() @@ -11,11 +10,19 @@ CREATED_SUCCESSFULLY = "FastAPI project created successfully! 🎉\n" ALREADY_EXISTS = "Folder 'potato' already exists. 😞\n" -@pytest.mark.parametrize("pkg", ["pip", "poetry"]) -@pytest.mark.parametrize("py", ["3.6", "3.7", "3.8", "3.9"]) -def test_startproject(project_name: str, pkg: str, py: str): +@pytest.mark.parametrize("package_", ["pip", "poetry"]) +@pytest.mark.parametrize("python", ["3.6", "3.7", "3.8"]) +@pytest.mark.parametrize( + "license_", ["MIT", "BSD-3", "GNU GPL v3.0", "Apache Software License 2.0"] +) +@pytest.mark.parametrize("pre_commit", [True, False]) +def test_startproject( + project_name: str, package_: str, python: str, license_: str, pre_commit: bool +): package = "manage_fastapi.main.launch_cli" - with patch(package, return_value=[pkg, py]) as mock_obj: + with patch( + package, return_value=[package_, python, license_, pre_commit] + ) as mock_obj: result = runner.invoke(app, ["startproject", project_name]) assert mock_obj.assert_called_once assert result.output == CREATED_SUCCESSFULLY