From 366494c020c1e0e447d67156137722442f104daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Szamszur?= Date: Mon, 3 Oct 2022 17:32:11 +0200 Subject: [PATCH 1/9] Extend and imrpove documentation --- .../docs/deployment.rst | 7 + {{cookiecutter.folder_name}}/docs/index.rst | 124 ++++++++- {{cookiecutter.folder_name}}/docs/install.rst | 64 +++++ {{cookiecutter.folder_name}}/docs/nix.rst | 38 +++ {{cookiecutter.folder_name}}/docs/usage.rst | 254 ++++++++++++++++++ 5 files changed, 482 insertions(+), 5 deletions(-) create mode 100644 {{cookiecutter.folder_name}}/docs/deployment.rst create mode 100644 {{cookiecutter.folder_name}}/docs/install.rst create mode 100644 {{cookiecutter.folder_name}}/docs/nix.rst create mode 100644 {{cookiecutter.folder_name}}/docs/usage.rst diff --git a/{{cookiecutter.folder_name}}/docs/deployment.rst b/{{cookiecutter.folder_name}}/docs/deployment.rst new file mode 100644 index 0000000..2bce234 --- /dev/null +++ b/{{cookiecutter.folder_name}}/docs/deployment.rst @@ -0,0 +1,7 @@ +Deployment +========== + +Will be added soon: `issue `__ + +In the meantime, `FastAPI deployment documentation `__ might be helpful. +And, if you shall have any questions feel free to issue them `here `__. diff --git a/{{cookiecutter.folder_name}}/docs/index.rst b/{{cookiecutter.folder_name}}/docs/index.rst index 7d95de0..63efdcc 100644 --- a/{{cookiecutter.folder_name}}/docs/index.rst +++ b/{{cookiecutter.folder_name}}/docs/index.rst @@ -1,15 +1,130 @@ Documentation ============= -.. _fastapi-mvc: - https://github.com/fastapi-mvc/fastapi-mvc - -------------- -**This project was generated with fastapi-mvc** +**This project was generated with:** `fastapi-mvc `__ -------------- +Quickstart +~~~~~~~~~~ + +If You want to go easy way and use provided virtualized environment You'll need to have installed: + +* rsync +* Vagrant `(How to install vagrant) `__ +* (Optional) Enabled virtualization in BIOS + +First run ``vagrant up`` in project root directory and enter virtualized environment using ``vagrant ssh`` +Then run following commands to bootstrap local development cluster exposing ``fastapi-mvc`` application. + +.. code-block:: bash + + cd /syncd + make dev-env + +.. note:: + This process may take a while on first run. + +Once development cluster is up and running you should see summary listing application address: + +.. code-block:: bash + + Kubernetes cluster ready + + fastapi-mvc available under: http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/ + + You can delete dev-env by issuing: minikube delete + +.. note:: + Above address may be different for your installation. + +.. note:: + Provided virtualized env doesn't have port forwarding configured which means, that bootstrapped application stack in k8s won't be accessible on Host OS.* + +Deployed application stack in Kubernetes: +{%- if cookiecutter.redis == "yes" %} + +.. code-block:: bash + + vagrant@ubuntu-focal:/syncd$ make dev-env + ... + ... + ... + Kubernetes cluster ready + FastAPI available under: http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/ + You can delete dev-env by issuing: make clean + vagrant@ubuntu-focal:/syncd$ kubectl get all -n {{cookiecutter.folder_name}} + NAME READY STATUS RESTARTS AGE + pod/{{cookiecutter.folder_name}}-7f4dd8dc7f-p2kr7 1/1 Running 0 55s + pod/rfr-redisfailover-persistent-keep-0 1/1 Running 0 3m39s + pod/rfr-redisfailover-persistent-keep-1 1/1 Running 0 3m39s + pod/rfr-redisfailover-persistent-keep-2 1/1 Running 0 3m39s + pod/rfs-redisfailover-persistent-keep-5d46b5bcf8-2r7th 1/1 Running 0 3m39s + pod/rfs-redisfailover-persistent-keep-5d46b5bcf8-6kqv5 1/1 Running 0 3m39s + pod/rfs-redisfailover-persistent-keep-5d46b5bcf8-sgtvv 1/1 Running 0 3m39s + + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE + service/{{cookiecutter.folder_name}} ClusterIP 10.110.42.252 8000/TCP 56s + service/rfs-redisfailover-persistent-keep ClusterIP 10.110.4.24 26379/TCP 3m39s + + NAME READY UP-TO-DATE AVAILABLE AGE + deployment.apps/{{cookiecutter.folder_name}} 1/1 1 1 55s + deployment.apps/rfs-redisfailover-persistent-keep 3/3 3 3 3m39s + + NAME DESIRED CURRENT READY AGE + replicaset.apps/{{cookiecutter.folder_name}}-7f4dd8dc7f 1 1 1 55s + replicaset.apps/rfs-redisfailover-persistent-keep-5d46b5bcf8 3 3 3 3m39s + + NAME READY AGE + statefulset.apps/rfr-redisfailover-persistent-keep 3/3 3m39s + + NAME AGE + redisfailover.databases.spotahome.com/redisfailover-persistent-keep 3m39s + vagrant@ubuntu-focal:/syncd$ curl http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/api/ready + {"status":"ok"} +{%- else %} + +.. code-block:: bash + + vagrant@ubuntu-focal:/syncd$ make dev-env + ... + ... + ... + Kubernetes cluster ready + FastAPI available under: http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/ + You can delete dev-env by issuing: make clean + vagrant@ubuntu-focal:/syncd$ kubectl get all -n {{cookiecutter.folder_name}} + NAME READY STATUS RESTARTS AGE + pod/{{cookiecutter.folder_name}}-649966bb7f-r694l 1/1 Running 0 114s + + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE + service/{{cookiecutter.folder_name}} ClusterIP 10.97.16.46 8000/TCP 114s + + NAME READY UP-TO-DATE AVAILABLE AGE + deployment.apps/{{cookiecutter.folder_name}} 1/1 1 1 114s + + NAME DESIRED CURRENT READY AGE + replicaset.apps/{{cookiecutter.folder_name}}-649966bb7f 1 1 1 114s + vagrant@ubuntu-focal:/syncd$ curl http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/api/ready + {"status":"ok"} + +{% endif %} +Documentation +------------- + +This part of the documentation guides you through all of the features and usage. + +.. toctree:: + :maxdepth: 2 + + install + {% if cookiecutter.redis == "yes" %}nix{% endif %} + usage + deployment + + API Reference ------------- @@ -29,4 +144,3 @@ Miscellaneous Pages license CHANGELOG.md - diff --git a/{{cookiecutter.folder_name}}/docs/install.rst b/{{cookiecutter.folder_name}}/docs/install.rst new file mode 100644 index 0000000..70de46c --- /dev/null +++ b/{{cookiecutter.folder_name}}/docs/install.rst @@ -0,0 +1,64 @@ +Installation +============ + +Application +----------- + +Prerequisites: + +* Python 3.7 or later `(How to install python) `__ +* make +* (optional) curl +* (optional) Poetry `(How to install poetry) `__ + +To install fastapi-mvc from source first clone the repository and use ``make install`` target: + +.. code-block:: bash + + make install + +By default ``make install`` target will search first for ``python3`` then ``python`` executable in your ``PATH``. +If needed this can be overridden by ``PYTHON`` environment variable. + +.. code-block:: bash + + export PYTHON=/path/to/my/python + make install + +Lastly if Poetry is not found in its default installation directory (${HOME}/.local/share/pypoetry) this target will install it for you. +However, one can always point to existing/customize Poetry installation with `environment variables `__: + +.. code-block:: bash + + export POETRY_HOME=/custom/poetry/path + export POETRY_CACHE_DIR=/custom/poetry/path/cache + export POETRY_VIRTUALENVS_IN_PROJECT=true + make install + +Or using Poetry directly, should you choose: + +.. code-block:: bash + + poetry install + +Infrastructure +-------------- + +Prerequisites: + +* make +* gcc +* golang +* minikube version 1.22.0 `(How to install minikube) `__ +* helm version 3.0.0 or higher `(How to install helm) `__ +* kubectl version 1.16 up to 1.20.8 `(How to install kubectl) `__ +* Container runtime interface. + +.. note:: + Makefile dev-env target uses docker for minikube, for other CRI you'll need to modify this line in ``build/dev-env.sh`` ``MINIKUBE_IN_STYLE=0 minikube start --driver=docker 2>/dev/null`` + +To bootstrap local minikube Kubernetes cluster exposing ``{{cookiecutter.folder_name}}`` application run: + +.. code-block:: bash + + make dev-env diff --git a/{{cookiecutter.folder_name}}/docs/nix.rst b/{{cookiecutter.folder_name}}/docs/nix.rst new file mode 100644 index 0000000..156e3f9 --- /dev/null +++ b/{{cookiecutter.folder_name}}/docs/nix.rst @@ -0,0 +1,38 @@ +Using Nix +========= + +Installation +------------ + +Prerequisites: + +* Nix 2.8.x or later installed `(How to install Nix) `__ + +First configure Nix channel if needed: + +.. code-block:: bash + + nix-channel --add https://nixos.org/channels/nixos-22.05 + nix-channel --update + +Next install make via Nix: + +.. code-block:: bash + + nix-env --install gnumake + # If you do not want to install make to your profile, one can always use it ad-hoc via nix-shell + nix-shell -p gnumake + +Lastly, use ``make install`` target: + +.. code-block:: bash + + make install + # Or + nix-shell -p gnumake --run "make install" + +Or using Nix directly, should you choose: + +.. code-block:: bash + + nix-build -E 'with import { overlays = [ (import ./overlay.nix) ]; }; callPackage ./editable.nix {python = pkgs.python310; poetry2nix = pkgs.poetry2nix;}' diff --git a/{{cookiecutter.folder_name}}/docs/usage.rst b/{{cookiecutter.folder_name}}/docs/usage.rst new file mode 100644 index 0000000..c3fd13f --- /dev/null +++ b/{{cookiecutter.folder_name}}/docs/usage.rst @@ -0,0 +1,254 @@ +Usage +===== + +CLI +--- + +This package exposes simple CLI for easier interaction: + +.. code-block:: bash + + $ {{cookiecutter.script_name}} --help + Usage: {{cookiecutter.script_name}} [OPTIONS] COMMAND [ARGS]... + + {{cookiecutter.project_name.capitalize()}} CLI root. + + Options: + -v, --verbose Enable verbose logging. + --help Show this message and exit. + + Commands: + serve {{cookiecutter.project_name}} CLI serve command. + $ {{cookiecutter.script_name}} serve --help + Usage: {{cookiecutter.script_name}} serve [OPTIONS] + + Run production gunicorn (WSGI) server with uvicorn (ASGI) workers. + + Options: + --bind TEXT Host to bind. + -w, --workers INTEGER RANGE The number of worker processes for handling + requests. + -D, --daemon Daemonize the Gunicorn process. + -e, --env TEXT Set environment variables in the execution + environment. + --pid PATH Specifies the PID file. + --help Show this message and exit. + +.. note:: + Maximum number of workers may be different in your case, it's limited to ``multiprocessing.cpu_count()`` + +WSGI + ASGI production server +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To run production unicorn + uvicorn (WSGI + ASGI) server you can use project CLI serve command: + +.. code-block:: bash + {{cookiecutter.script_name}} serve + [2022-04-23 20:21:49 +0000] [4769] [INFO] Start gunicorn WSGI with ASGI workers. + [2022-04-23 20:21:49 +0000] [4769] [INFO] Starting gunicorn 20.1.0 + [2022-04-23 20:21:49 +0000] [4769] [INFO] Listening at: http://127.0.0.1:8000 (4769) + [2022-04-23 20:21:49 +0000] [4769] [INFO] Using worker: uvicorn.workers.UvicornWorker + [2022-04-23 20:21:49 +0000] [4769] [INFO] Server is ready. Spawning workers + [2022-04-23 20:21:49 +0000] [4771] [INFO] Booting worker with pid: 4771 + [2022-04-23 20:21:49 +0000] [4771] [INFO] Worker spawned (pid: 4771) + [2022-04-23 20:21:49 +0000] [4771] [INFO] Started server process [4771] + [2022-04-23 20:21:49 +0000] [4771] [INFO] Waiting for application startup. + [2022-04-23 20:21:49 +0000] [4771] [INFO] Application startup complete. + [2022-04-23 20:21:49 +0000] [4772] [INFO] Booting worker with pid: 4772 + [2022-04-23 20:21:49 +0000] [4772] [INFO] Worker spawned (pid: 4772) + [2022-04-23 20:21:49 +0000] [4772] [INFO] Started server process [4772] + [2022-04-23 20:21:49 +0000] [4772] [INFO] Waiting for application startup. + [2022-04-23 20:21:49 +0000] [4772] [INFO] Application startup complete. + +To confirm it's working: + +.. code-block:: bash + $ curl localhost:8000/api/ready + {"status":"ok"} + +Dockerfile +---------- + +This project provides Dockerfile for containerized environment. + +.. code-block:: bash + $ make image + $ podman run -dit --name {{cookiecutter.docker_image_name}} -p 8000:8000 {{cookiecutter.docker_image_name}}:$(cat TAG) + f41e5fa7ffd512aea8f1aad1c12157bf1e66f961aeb707f51993e9ac343f7a4b + $ podman ps + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + f41e5fa7ffd5 localhost/{{cookiecutter.docker_image_name}}:0.1.0 /usr/bin/fastapi ... 2 seconds ago Up 3 seconds ago 0.0.0.0:8000->8000/tcp {{cookiecutter.docker_image_name}} + $ curl localhost:8000/api/ready + {"status":"ok"} + +.. note:: + Replace podman with docker if it's yours containerization engine. + +Development +----------- + +You can implement your own web routes logic straight away in ``{{cookiecutter.package_name}}.controllers`` submodule. For more information please see `FastAPI documentation `__. + +Makefile +~~~~~~~~ + +Provided Makefile is a starting point for application and infrastructure development: + +.. code-block:: bash + Usage: + make + help Display this help + image Build {{cookiecutter.project_name}} image + clean-image Clean {{cookiecutter.project_name}} image + install Install {{cookiecutter.project_name}} with poetry + metrics Run {{cookiecutter.project_name}} metrics checks + unit-test Run {{cookiecutter.project_name}} unit tests + integration-test Run {{cookiecutter.project_name}} integration tests + docs Build {{cookiecutter.project_name}} documentation + dev-env Start a local Kubernetes cluster using minikube and deploy application + clean Remove .cache directory and cached minikube + +Utilities +~~~~~~~~~ + +Available utilities: +* RedisClient ``{{cookiecutter.package_name}}.app.utils.redis`` +* AiohttpClient ``{{cookiecutter.package_name}}.app.utils.aiohttp_client`` + +They're initialized in ``asgi.py`` on FastAPI startup event handler: + +.. code-block:: python + :emphasize-lines: 11, 13, 25, 27 + + async def on_startup(): + """Fastapi startup event handler. + + Creates RedisClient and AiohttpClient session. + + """ + log.debug("Execute FastAPI startup event handler.") + # Initialize utilities for whole FastAPI application without passing object + # instances within the logic. Feel free to disable it if you don't need it. + if settings.USE_REDIS: + await RedisClient.open_redis_client() + + AiohttpClient.get_aiohttp_client() + + + async def on_shutdown(): + """Fastapi shutdown event handler. + + Destroys RedisClient and AiohttpClient session. + + """ + log.debug("Execute FastAPI shutdown event handler.") + # Gracefully close utilities. + if settings.USE_REDIS: + await RedisClient.close_redis_client() + + await AiohttpClient.close_aiohttp_client() + +and are available for whole application scope without passing object instances. In order to utilize it just execute classmethods directly. + +Example: + +.. code-block:: python + + from {{cookiecutter.package_name}}.app.utils import RedisClient + + response = RedisClient.get("Key") + +Exceptions +~~~~~~~~~~ + +**HTTPException and handler** + +.. literalinclude:: ../{{cookiecutter.package_name}}/app/exceptions/http.py + :language: python + +This exception combined with ``http_exception_handler`` method allows you to use it the same manner as you'd use ``FastAPI.HTTPException`` with one difference. +You have freedom to define returned response body, whereas in ``FastAPI.HTTPException`` content is returned under "detail" JSON key. +In this application custom handler is added in ``asgi.py`` while initializing FastAPI application. This is needed in order to handle it globally. + +Web Routes +~~~~~~~~~~ + +All routes documentation is available on: + +* ``/`` with Swagger +* ``/redoc`` or ReDoc. + + +Configuration +------------- + +This application provides flexibility of configuration. All significant settings are defined by the environment variables, each with the default value. +Moreover, package CLI allows overriding core ones: host, port, workers. You can modify all other available configuration settings in the gunicorn.conf.py file. + +Priority of overriding configuration: + +1. cli +2. environment variables +3. ``gunicorn.py`` + +All application configuration is available in ``{{cookiecutter.package_name}}.config`` submodule. + +Environment variables +~~~~~~~~~~~~~~~~~~~~~ + +**Application configuration** + ++-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ +| Key | Default | Description | ++=============================+====================================================================+====================================================================================================+ +| FASTAPI_BIND | ``"127.0.0.1:8000"`` | The socket to bind. A string of the form: 'HOST', 'HOST:PORT', 'unix:PATH'. An IP is a valid HOST. | ++-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ +| FASTAPI_WORKERS | ``"2"`` | Number of gunicorn workers (uvicorn.workers.UvicornWorker. | ++-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ +| FASTAPI_DEBUG | ``"True"`` | FastAPI logging level. You should disable this for production. | ++-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ +| FASTAPI_PROJECT_NAME | ``"{{cookiecutter.package_name}}"`` | FastAPI project name. | ++-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ +| FASTAPI_VERSION | ``"0.4.0"`` | Application version. | ++-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ +| FASTAPI_DOCS_URL | ``"/"`` | Path where swagger ui will be served at. | ++-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ +| FASTAPI_USE_REDIS | ``"False"`` | Whether or not to use Redis. | ++-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ +| FASTAPI_GUNICORN_LOG_LEVEL | ``"info"`` | The granularity of gunicorn log output | ++-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ +| FASTAPI_GUNICORN_LOG_FORMAT | ``'%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'`` | Gunicorn log format | ++-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ + + +**Redis configuration** + ++-----------------------------+------------------+--------------------------------------------+ +| Key | Default | Description | ++=============================+==================+============================================+ +| FASTAPI_REDIS_HOTS | ``"127.0.0.1"`` | Redis host. | ++-----------------------------+------------------+--------------------------------------------+ +| FASTAPI_REDIS_PORT | ``"6379"`` | Redis port. | ++-----------------------------+------------------+--------------------------------------------+ +| FASTAPI_REDIS_USERNAME | ``""`` | Redis username. | ++-----------------------------+------------------+--------------------------------------------+ +| FASTAPI_REDIS_PASSWORD | ``""`` | Redis password. | ++-----------------------------+------------------+--------------------------------------------+ +| FASTAPI_REDIS_USE_SENTINEL | ``"False"`` | If provided Redis config is for Sentinel. | ++-----------------------------+------------------+--------------------------------------------+ + +Gunicorn +~~~~~~~~ + +`Gunicorn configuration file documentation `__ + +.. literalinclude:: ../{{cookiecutter.package_name}}/config/gunicorn.py + :language: python + +Routes +~~~~~~ + +Endpoints are defined in ``{{cookiecutter.package_name}}.app.router`` submodule. Just simply import your controller and include it to FastAPI router: + +.. literalinclude:: ../{{cookiecutter.package_name}}/app/router.py + :language: python From 4477ffbf3c275ba82fe5548f31e093fda7198fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Szamszur?= Date: Mon, 3 Oct 2022 17:33:14 +0200 Subject: [PATCH 2/9] Update readme --- {{cookiecutter.folder_name}}/README.md | 371 +------------------------ 1 file changed, 9 insertions(+), 362 deletions(-) diff --git a/{{cookiecutter.folder_name}}/README.md b/{{cookiecutter.folder_name}}/README.md index ec1ca6f..d54cb2c 100644 --- a/{{cookiecutter.folder_name}}/README.md +++ b/{{cookiecutter.folder_name}}/README.md @@ -11,376 +11,23 @@ ![GitHub](https://img.shields.io/badge/python-3.7%20%7C%203.8%20%7C%203.9%20%7C%203.10-blue) ![GitHub](https://img.shields.io/badge/license-{{cookiecutter.license}}-blue) -## This project was generated with [fastapi-mvc](https://github.com/fastapi-mvc/fastapi-mvc) - --- -## Prerequisites - -If You want to go easy way and use provided virtualized environment You'll need to have installed: -* rsync -* Vagrant [How to install vagrant](https://www.vagrantup.com/downloads) -* (Optional) Enabled virtualization in BIOS - -Otherwise, for local complete project environment with k8s infrastructure bootstrapping You'll need: - -For application: -* Python 3.7 or later installed [How to install python](https://docs.python-guide.org/starting/installation/) -* Poetry [How to install poetry](https://python-poetry.org/docs/#installation) - -For infrastructure: -* make, gcc, golang -* minikube version 1.22.0 [How_to_install_minikube](https://minikube.sigs.k8s.io/docs/start/) -* helm version 3.0.0 or higher [How_to_install_helm](https://helm.sh/docs/intro/install/) -* kubectl version 1.16 up to 1.20.8 [How_to_install_kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/) -* Container runtime interface. NOTE! dev-env script uses docker for minikube, for other CRI you'll need to modify this line in dev-env.sh `MINIKUBE_IN_STYLE=0 minikube start --driver=docker 2>/dev/null` - -### Environment with [Nix](https://nixos.org/) - -To start a shell with development environment run: -```shell -nix-shell shell.nix -``` - -## Quickstart -First run `vagrant up` in project root directory and enter virtualized environment using `vagrant ssh` -Then run following commands to bootstrap local development cluster exposing `fastapi-mvc` application. -```sh -$ cd /syncd -$ make dev-env -``` -*Note: this process may take a while on first run.* - -Once development cluster is up and running you should see summary listing application address: -``` -Kubernetes cluster ready - -fastapi-mvc available under: http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/ - -You can delete dev-env by issuing: minikube delete -``` -*Note: above address may be different for your installation.* - -*Note: provided virtualized env doesn't have port forwarding configured which means, that bootstrapped application stack in k8s won't be accessible on Host OS.* - -Deployed application stack in Kubernetes: -{%- if cookiecutter.redis == "yes" %} -```shell -vagrant@ubuntu-focal:/syncd$ make dev-env -... -... -... -Kubernetes cluster ready -FastAPI available under: http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/ -You can delete dev-env by issuing: make clean -vagrant@ubuntu-focal:/syncd$ kubectl get all -n {{cookiecutter.folder_name}} -NAME READY STATUS RESTARTS AGE -pod/{{cookiecutter.folder_name}}-7f4dd8dc7f-p2kr7 1/1 Running 0 55s -pod/rfr-redisfailover-persistent-keep-0 1/1 Running 0 3m39s -pod/rfr-redisfailover-persistent-keep-1 1/1 Running 0 3m39s -pod/rfr-redisfailover-persistent-keep-2 1/1 Running 0 3m39s -pod/rfs-redisfailover-persistent-keep-5d46b5bcf8-2r7th 1/1 Running 0 3m39s -pod/rfs-redisfailover-persistent-keep-5d46b5bcf8-6kqv5 1/1 Running 0 3m39s -pod/rfs-redisfailover-persistent-keep-5d46b5bcf8-sgtvv 1/1 Running 0 3m39s - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/{{cookiecutter.folder_name}} ClusterIP 10.110.42.252 8000/TCP 56s -service/rfs-redisfailover-persistent-keep ClusterIP 10.110.4.24 26379/TCP 3m39s - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/{{cookiecutter.folder_name}} 1/1 1 1 55s -deployment.apps/rfs-redisfailover-persistent-keep 3/3 3 3 3m39s - -NAME DESIRED CURRENT READY AGE -replicaset.apps/{{cookiecutter.folder_name}}-7f4dd8dc7f 1 1 1 55s -replicaset.apps/rfs-redisfailover-persistent-keep-5d46b5bcf8 3 3 3 3m39s - -NAME READY AGE -statefulset.apps/rfr-redisfailover-persistent-keep 3/3 3m39s - -NAME AGE -redisfailover.databases.spotahome.com/redisfailover-persistent-keep 3m39s -vagrant@ubuntu-focal:/syncd$ curl http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/api/ready -{"status":"ok"} -``` - -{%- else %} -```shell -vagrant@ubuntu-focal:/syncd$ make dev-env -... -... -... -Kubernetes cluster ready -FastAPI available under: http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/ -You can delete dev-env by issuing: make clean -vagrant@ubuntu-focal:/syncd$ kubectl get all -n {{cookiecutter.folder_name}} -NAME READY STATUS RESTARTS AGE -pod/{{cookiecutter.folder_name}}-649966bb7f-r694l 1/1 Running 0 114s - -NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE -service/{{cookiecutter.folder_name}} ClusterIP 10.97.16.46 8000/TCP 114s - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/{{cookiecutter.folder_name}} 1/1 1 1 114s - -NAME DESIRED CURRENT READY AGE -replicaset.apps/{{cookiecutter.folder_name}}-649966bb7f 1 1 1 114s -vagrant@ubuntu-focal:/syncd$ curl http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/api/ready -{"status":"ok"} -``` - -{%- endif %} - -## Installation - -With make: -```shell -make install -``` - -You can customize poetry installation with [environment variables](https://python-poetry.org/docs/configuration/#using-environment-variables) -```shell -export POETRY_HOME=/custom/poetry/path -export POETRY_CACHE_DIR=/custom/poetry/path/cache -export POETRY_VIRTUALENVS_IN_PROJECT=true -make install -``` - -Or using poetry directly: -```shell -poetry install -``` - -To bootstrap local minikube Kubernetes cluster exposing `{{cookiecutter.folder_name}}` application: -```shell -make dev-env -``` - -## CLI - -This package exposes simple CLI for easier interaction: - -```shell -$ {{cookiecutter.script_name}} --help -Usage: {{cookiecutter.script_name}} [OPTIONS] COMMAND [ARGS]... - - {{cookiecutter.project_name.capitalize()}} CLI root. - -Options: - -v, --verbose Enable verbose logging. - --help Show this message and exit. - -Commands: - serve {{cookiecutter.project_name}} CLI serve command. -$ {{cookiecutter.script_name}} serve --help -Usage: {{cookiecutter.script_name}} serve [OPTIONS] - - Run production gunicorn (WSGI) server with uvicorn (ASGI) workers. - -Options: - --bind TEXT Host to bind. - -w, --workers INTEGER RANGE The number of worker processes for handling - requests. - -D, --daemon Daemonize the Gunicorn process. - -e, --env TEXT Set environment variables in the execution - environment. - --pid PATH Specifies the PID file. - --help Show this message and exit. -``` - -*NOTE: Maximum number of workers may be different in your case, it's limited to `multiprocessing.cpu_count()`* - -To serve application simply run: - -```shell -$ {{cookiecutter.script_name}} serve -``` - -To confirm it's working: - -```shell -$ curl localhost:8000/api/ready -{"status":"ok"} -``` +## This project was generated with [fastapi-mvc](https://github.com/fastapi-mvc/fastapi-mvc) -## Dockerfile +### Documentation -This repository provides Dockerfile for virtualized environment. +Example generated project documentation: [https://fastapi-mvc.github.io/example](https://fastapi-mvc.github.io/example) +{% if cookiecutter.github_actions == "yes" %} +You should have documentation deployed to your project GitHub pages via [Build Docs workflow]({{cookiecutter.repo_url}}/actions/workflows/docs.yml) -*NOTE: Replace podman with docker if it's yours containerization engine.* +**NOTE!** You might need to enable GitHub pages for this project first. +{% endif %} +Build documentation: ```shell -$ make image -$ podman run -dit --name {{cookiecutter.docker_image_name}} -p 8000:8000 {{cookiecutter.docker_image_name}}:$(cat TAG) -f41e5fa7ffd512aea8f1aad1c12157bf1e66f961aeb707f51993e9ac343f7a4b -$ podman ps -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -f41e5fa7ffd5 localhost/{{cookiecutter.docker_image_name}}:0.1.0 /usr/bin/fastapi ... 2 seconds ago Up 3 seconds ago 0.0.0.0:8000->8000/tcp {{cookiecutter.docker_image_name}} -$ curl localhost:8000/api/ready -{"status":"ok"} -``` - -## Application configuration - -This application provides flexibility of configuration. -All significant settings are defined by the environment variables, each with the default value. -Moreover, package CLI allows overriding core ones: host, port, workers. -You can modify all other available configuration settings in the gunicorn.conf.py file. - -Priority of overriding configuration: -1. cli -2. environment variables -3. gunicorn.py - -All application configuration is available in `{{cookiecutter.package_name}}.config` submodule. - -### Environment variables - -#### Application configuration - -| Key | Default | Description | -|----------------------|-----------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| -| FASTAPI_BIND | `"127.0.0.1:8000"` | The socket to bind. A string of the form: 'HOST', 'HOST:PORT', 'unix:PATH'. An IP is a valid HOST. | -| FASTAPI_WORKERS | `"2"` | Number of gunicorn workers (uvicorn.workers.UvicornWorker) | -| FASTAPI_DEBUG | `"True"` | FastAPI logging level. You should disable this for production. | -| FASTAPI_PROJECT_NAME | `"{{cookiecutter.project_name}}"` | FastAPI project name. | -| FASTAPI_VERSION | `"0.4.0"` | Application version. | -| FASTAPI_DOCS_URL | `"/"` | Path where swagger ui will be served at. | -| FASTAPI_USE_REDIS | `"False"` | Whether or not to use Redis. | -| FASTAPI_GUNICORN_LOG_LEVEL | `"info"` | The granularity of gunicorn log output | -| FASTAPI_GUNICORN_LOG_FORMAT | `'%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'` | Gunicorn log format | -{%- if cookiecutter.redis == "yes" %} - -#### Redis configuration - -| Key | Default | Description | -|----------------------------|---------------|-------------------------------------------| -| FASTAPI_REDIS_HOTS | `"127.0.0.1"` | Redis host. | -| FASTAPI_REDIS_PORT | `"6379"` | Redis port. | -| FASTAPI_REDIS_USERNAME | `""` | Redis username. | -| FASTAPI_REDIS_PASSWORD | `""` | Redis password. | -| FASTAPI_REDIS_USE_SENTINEL | `"False"` | If provided Redis config is for Sentinel. | -{%- endif %} - -### gunicorn.conf.py - -1. Source: `{{cookiecutter.package_name}}.config/gunicorn.conf.py` -2. [Gunicorn configuration file documentation](https://docs.gunicorn.org/en/latest/settings.html) - -### Routes definition - -Endpoints are defined in `{{cookiecutter.package_name}}.app.router`. Just simply import your controller and include it to FastAPI router: - -```python -from fastapi import APIRouter -from {{cookiecutter.package_name}}.app.controllers.api.v1 import ready - -root_api_router = APIRouter( - prefix="/api" -) - -root_api_router.include_router(ready.router, tags=["ready"]) -``` - -## Development - -You can implement your own web routes logic straight away in `{{cookiecutter.package_name}}.app.controllers.api.v1` submodule. For more information please see [FastAPI documentation](https://fastapi.tiangolo.com/tutorial/). -{%- if cookiecutter.redis == "yes" or cookiecutter.aiohttp == "yes" %} - -### Utilities - -For your discretion, I've provided some basic utilities: -{%- if cookiecutter.redis == "yes" %} -* RedisClient `{{cookiecutter.package_name}}.app.utils.redis` -{%- endif %} -{%- if cookiecutter.aiohttp == "yes" %} -* AiohttpClient `{{cookiecutter.package_name}}.app.utils.aiohttp_client` -{%- endif %} - -They're initialized in `asgi.py` on FastAPI startup event handler: - -```python -async def on_startup(): - """Fastapi startup event handler. - - Creates RedisClient and AiohttpClient session. - - """ - log.debug("Execute FastAPI startup event handler.") - # Initialize utilities for whole FastAPI application without passing object - # instances within the logic. Feel free to disable it if you don't need it. - {%- if cookiecutter.redis == "yes" and cookiecutter.aiohttp == "yes" %} - if settings.USE_REDIS: - await RedisClient.open_redis_client() - - AiohttpClient.get_aiohttp_client() - {%- elif cookiecutter.redis == "yes" %} - if settings.USE_REDIS: - await RedisClient.open_redis_client() - {%- elif cookiecutter.aiohttp == "yes" %} - AiohttpClient.get_aiohttp_client() - {%- else %} - pass - {%- endif %} - - -async def on_shutdown(): - """Fastapi shutdown event handler. - - Destroys RedisClient and AiohttpClient session. - - """ - log.debug("Execute FastAPI shutdown event handler.") - # Gracefully close utilities. - {%- if cookiecutter.redis == "yes" and cookiecutter.aiohttp == "yes" %} - if settings.USE_REDIS: - await RedisClient.close_redis_client() - - await AiohttpClient.close_aiohttp_client() - {%- elif cookiecutter.redis == "yes" %} - if settings.USE_REDIS: - await RedisClient.close_redis_client() - {%- elif cookiecutter.aiohttp == "yes" %} - await AiohttpClient.close_aiohttp_client() - {%- else %} - pass - {%- endif %} +make docs ``` -and are available for whole application scope without passing object instances. In order to utilize it just execute classmethods directly. - -Example: -{%- if cookiecutter.redis == "yes" %} -```python -from {{cookiecutter.package_name}}.app.utils import RedisClient - -response = RedisClient.get("Key") -``` -{%- endif %} -{%- if cookiecutter.aiohttp == "yes" %} -```python -from {{cookiecutter.package_name}}.app.utils import AiohttpClient - -response = AiohttpClient.get("http://foo.bar") -``` -{%- endif %} -{%- endif %} - -### Exceptions - -#### HTTPException and handler - -Source: `{{cookiecutter.package_name}}.app.exceptions.http.py` - -This exception combined with `http_exception_handler` method allows you to use it the same manner as you'd use `FastAPI.HTTPException` with one difference. -You have freedom to define returned response body, whereas in `FastAPI.HTTPException` content is returned under "detail" JSON key. -In this application custom handler is added in `asgi.py` while initializing FastAPI application. -This is needed in order to handle it globally. - -### Web Routes -All routes documentation is available on: -* `/` with Swagger -* `/redoc` or ReDoc. - ## License This project is licensed under the terms of the {{cookiecutter.license}} license. From dca6ae77471ac8f1e4cdbbde5300725ca9bdff4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Szamszur?= Date: Mon, 3 Oct 2022 17:33:26 +0200 Subject: [PATCH 3/9] Update set_nix post hook --- hooks/post_gen_project.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 6d4e192..3bc9386 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -76,6 +76,7 @@ def set_nix(): "overlay.nix", "Nix.mk", ".github/workflows/nix.yml", + "docs/nix.rst", ] ) From e714303ca527df897e912f216a579f09c93ab47f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Szamszur?= Date: Mon, 3 Oct 2022 17:48:09 +0200 Subject: [PATCH 4/9] Fix rst formatting issues --- {{cookiecutter.folder_name}}/docs/index.rst | 3 +-- {{cookiecutter.folder_name}}/docs/usage.rst | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/{{cookiecutter.folder_name}}/docs/index.rst b/{{cookiecutter.folder_name}}/docs/index.rst index 63efdcc..b197171 100644 --- a/{{cookiecutter.folder_name}}/docs/index.rst +++ b/{{cookiecutter.folder_name}}/docs/index.rst @@ -40,8 +40,7 @@ Once development cluster is up and running you should see summary listing applic .. note:: Above address may be different for your installation. -.. note:: - Provided virtualized env doesn't have port forwarding configured which means, that bootstrapped application stack in k8s won't be accessible on Host OS.* + Provided virtualized env doesn't have port forwarding configured which means, that bootstrapped application stack in k8s won't be accessible on Host OS. Deployed application stack in Kubernetes: {%- if cookiecutter.redis == "yes" %} diff --git a/{{cookiecutter.folder_name}}/docs/usage.rst b/{{cookiecutter.folder_name}}/docs/usage.rst index c3fd13f..4092b22 100644 --- a/{{cookiecutter.folder_name}}/docs/usage.rst +++ b/{{cookiecutter.folder_name}}/docs/usage.rst @@ -43,6 +43,7 @@ WSGI + ASGI production server To run production unicorn + uvicorn (WSGI + ASGI) server you can use project CLI serve command: .. code-block:: bash + {{cookiecutter.script_name}} serve [2022-04-23 20:21:49 +0000] [4769] [INFO] Start gunicorn WSGI with ASGI workers. [2022-04-23 20:21:49 +0000] [4769] [INFO] Starting gunicorn 20.1.0 @@ -63,6 +64,7 @@ To run production unicorn + uvicorn (WSGI + ASGI) server you can use project CLI To confirm it's working: .. code-block:: bash + $ curl localhost:8000/api/ready {"status":"ok"} @@ -72,6 +74,7 @@ Dockerfile This project provides Dockerfile for containerized environment. .. code-block:: bash + $ make image $ podman run -dit --name {{cookiecutter.docker_image_name}} -p 8000:8000 {{cookiecutter.docker_image_name}}:$(cat TAG) f41e5fa7ffd512aea8f1aad1c12157bf1e66f961aeb707f51993e9ac343f7a4b @@ -95,6 +98,7 @@ Makefile Provided Makefile is a starting point for application and infrastructure development: .. code-block:: bash + Usage: make help Display this help From 398c2fcb356c0c83126294472a63ba4579852a37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Szamszur?= Date: Mon, 3 Oct 2022 18:02:56 +0200 Subject: [PATCH 5/9] Fix toctree --- {{cookiecutter.folder_name}}/docs/index.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/{{cookiecutter.folder_name}}/docs/index.rst b/{{cookiecutter.folder_name}}/docs/index.rst index b197171..5e2f95e 100644 --- a/{{cookiecutter.folder_name}}/docs/index.rst +++ b/{{cookiecutter.folder_name}}/docs/index.rst @@ -83,6 +83,7 @@ Deployed application stack in Kubernetes: redisfailover.databases.spotahome.com/redisfailover-persistent-keep 3m39s vagrant@ubuntu-focal:/syncd$ curl http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/api/ready {"status":"ok"} + {%- else %} .. code-block:: bash @@ -109,7 +110,7 @@ Deployed application stack in Kubernetes: vagrant@ubuntu-focal:/syncd$ curl http://{{cookiecutter.folder_name}}.192.168.49.2.nip.io/api/ready {"status":"ok"} -{% endif %} +{%- endif %} Documentation ------------- @@ -118,11 +119,10 @@ This part of the documentation guides you through all of the features and usage. .. toctree:: :maxdepth: 2 - install - {% if cookiecutter.redis == "yes" %}nix{% endif %} - usage - deployment - + install + {% if cookiecutter.redis == "yes" %}nix{% endif %} + usage + deployment API Reference ------------- From 263fecf810a167a05537050bc504fea507dced1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Szamszur?= Date: Mon, 3 Oct 2022 18:26:14 +0200 Subject: [PATCH 6/9] Fix rst table --- {{cookiecutter.folder_name}}/docs/usage.rst | 92 +++++++++++++-------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/{{cookiecutter.folder_name}}/docs/usage.rst b/{{cookiecutter.folder_name}}/docs/usage.rst index 4092b22..9fc33a6 100644 --- a/{{cookiecutter.folder_name}}/docs/usage.rst +++ b/{{cookiecutter.folder_name}}/docs/usage.rst @@ -116,6 +116,7 @@ Utilities ~~~~~~~~~ Available utilities: + * RedisClient ``{{cookiecutter.package_name}}.app.utils.redis`` * AiohttpClient ``{{cookiecutter.package_name}}.app.utils.aiohttp_client`` @@ -202,44 +203,65 @@ Environment variables **Application configuration** -+-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| Key | Default | Description | -+=============================+====================================================================+====================================================================================================+ -| FASTAPI_BIND | ``"127.0.0.1:8000"`` | The socket to bind. A string of the form: 'HOST', 'HOST:PORT', 'unix:PATH'. An IP is a valid HOST. | -+-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| FASTAPI_WORKERS | ``"2"`` | Number of gunicorn workers (uvicorn.workers.UvicornWorker. | -+-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| FASTAPI_DEBUG | ``"True"`` | FastAPI logging level. You should disable this for production. | -+-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| FASTAPI_PROJECT_NAME | ``"{{cookiecutter.package_name}}"`` | FastAPI project name. | -+-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| FASTAPI_VERSION | ``"0.4.0"`` | Application version. | -+-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| FASTAPI_DOCS_URL | ``"/"`` | Path where swagger ui will be served at. | -+-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| FASTAPI_USE_REDIS | ``"False"`` | Whether or not to use Redis. | -+-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| FASTAPI_GUNICORN_LOG_LEVEL | ``"info"`` | The granularity of gunicorn log output | -+-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ -| FASTAPI_GUNICORN_LOG_FORMAT | ``'%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'`` | Gunicorn log format | -+-----------------------------+--------------------------------------------------------------------+----------------------------------------------------------------------------------------------------+ - +.. list-table:: + :widths: 25 25 50 + :header-rows: 1 + + * - Key + - Default + - Description + * - FASTAPI_BIND + - ``"127.0.0.1:8000"`` + - The socket to bind. A string of the form: 'HOST', 'HOST:PORT', 'unix:PATH'. An IP is a valid HOST. + * - FASTAPI_WORKERS + - ``"2"`` + - Number of gunicorn workers (uvicorn.workers.UvicornWorker). + * - FASTAPI_DEBUG + - ``"True"`` + - FastAPI logging level. You should disable this for production. + * - FASTAPI_PROJECT_NAME + - ``"{{cookiecutter.package_name}}"`` + - FastAPI project name. + * - FASTAPI_VERSION + - ``"0.1.0"`` + - Application version. + * - FASTAPI_DOCS_URL + - ``"/"`` + - Path where swagger ui will be served at. + * - FASTAPI_USE_REDIS + - ``"False"`` + - Whether or not to use Redis. + * - FASTAPI_GUNICORN_LOG_LEVEL + - ``"info"`` + - The granularity of gunicorn log output. + * - FASTAPI_GUNICORN_LOG_FORMAT + - ``'%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"'`` + - Gunicorn log format. **Redis configuration** -+-----------------------------+------------------+--------------------------------------------+ -| Key | Default | Description | -+=============================+==================+============================================+ -| FASTAPI_REDIS_HOTS | ``"127.0.0.1"`` | Redis host. | -+-----------------------------+------------------+--------------------------------------------+ -| FASTAPI_REDIS_PORT | ``"6379"`` | Redis port. | -+-----------------------------+------------------+--------------------------------------------+ -| FASTAPI_REDIS_USERNAME | ``""`` | Redis username. | -+-----------------------------+------------------+--------------------------------------------+ -| FASTAPI_REDIS_PASSWORD | ``""`` | Redis password. | -+-----------------------------+------------------+--------------------------------------------+ -| FASTAPI_REDIS_USE_SENTINEL | ``"False"`` | If provided Redis config is for Sentinel. | -+-----------------------------+------------------+--------------------------------------------+ +.. list-table:: + :widths: 25 25 50 + :header-rows: 1 + + * - Key + - Default + - Description + * - FASTAPI_REDIS_HOTS + - ``"127.0.0.1"`` + - Redis host. + * - FASTAPI_REDIS_PORT + - ``"6379"`` + - Redis port. + * - FASTAPI_REDIS_USERNAME + - ``""`` + - Redis username. + * - FASTAPI_REDIS_PASSWORD + - ``""`` + - Redis password. + * - FASTAPI_REDIS_USE_SENTINEL + - ``"False"`` + - If provided Redis config is for Sentinel. Gunicorn ~~~~~~~~ From 7ff87439e0364f2ae0a56a846947f4f1af4318ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Szamszur?= Date: Mon, 3 Oct 2022 18:29:31 +0200 Subject: [PATCH 7/9] Update readme --- {{cookiecutter.folder_name}}/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/{{cookiecutter.folder_name}}/README.md b/{{cookiecutter.folder_name}}/README.md index d54cb2c..e461071 100644 --- a/{{cookiecutter.folder_name}}/README.md +++ b/{{cookiecutter.folder_name}}/README.md @@ -17,13 +17,12 @@ ### Documentation -Example generated project documentation: [https://fastapi-mvc.github.io/example](https://fastapi-mvc.github.io/example) {% if cookiecutter.github_actions == "yes" %} You should have documentation deployed to your project GitHub pages via [Build Docs workflow]({{cookiecutter.repo_url}}/actions/workflows/docs.yml) **NOTE!** You might need to enable GitHub pages for this project first. {% endif %} -Build documentation: +To build manually: ```shell make docs ``` From 646b5c0760c4bb19b16b7ac5c5466bb29c1c3983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Szamszur?= Date: Tue, 4 Oct 2022 16:18:00 +0200 Subject: [PATCH 8/9] Update pyproject.toml --- {{cookiecutter.folder_name}}/pyproject.toml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/{{cookiecutter.folder_name}}/pyproject.toml b/{{cookiecutter.folder_name}}/pyproject.toml index c2d7a81..d0d38cc 100644 --- a/{{cookiecutter.folder_name}}/pyproject.toml +++ b/{{cookiecutter.folder_name}}/pyproject.toml @@ -7,13 +7,13 @@ license = "{{cookiecutter.license}}" readme = "README.md" repository = "{{cookiecutter.repo_url}}" classifiers = [ - 'Intended Audience :: Developers', - 'Natural Language :: English', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', + "Intended Audience :: Developers", + "Natural Language :: English", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", ] [tool.poetry.dependencies] From 18461df40e2928e3bca10f274d3c64a41d999e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rados=C5=82aw=20Szamszur?= Date: Tue, 4 Oct 2022 16:18:39 +0200 Subject: [PATCH 9/9] Update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23a2dea..dee95a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ This file documents changes to [fastapi-mvc/cookiecutter](https://github.com/fastapi-mvc/cookiecutter). The release numbering uses [semantic versioning](http://semver.org). +## Unreleased + +### Internal + +* Extend project documentation [#21](https://github.com/fastapi-mvc/cookiecutter/issues/21). PR [#20](https://github.com/fastapi-mvc/cookiecutter/pull/20) + ## 0.2.0 (29.09.2022) ### Features