Change to gunicorn #4

Merged
lukas merged 7 commits from change-to-gunicorn into main 2023-01-07 21:17:49 +00:00
13 changed files with 139 additions and 20 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
src/web/static/*

3
.gitignore vendored
View File

@ -4,4 +4,5 @@ __pycache__
.venv .venv
node_modules node_modules
package-lock.json package-lock.json
db.sqlite3 db.sqlite3
src/web/static

View File

@ -1,3 +1,7 @@
## 0.1.2 / 2023-01-07 22:05+01:00
* Switch to Uvicorn/Gunicorn + Caddy (https://git.kucharczyk.xyz/lukas/timetracker/pulls/4)
## 0.1.1 / 2023-01-05 23:26+01:00 ## 0.1.1 / 2023-01-05 23:26+01:00
* Order by timestamp_start by default * Order by timestamp_start by default
* Add pre-commit hook to update version * Add pre-commit hook to update version

14
Caddyfile Normal file
View File

@ -0,0 +1,14 @@
{
auto_https off
admin off
}
:8000 {
handle_path /static/* {
root * src/web/static/
file_server
}
handle {
reverse_proxy :8001
}
}

View File

@ -4,12 +4,17 @@ COPY . /app
RUN npm install && \ RUN npm install && \
npx tailwindcss -i ./src/input.css -o ./src/web/tracker/static/base.css --minify npx tailwindcss -i ./src/input.css -o ./src/web/tracker/static/base.css --minify
FROM python:3.10-slim-bullseye FROM python:3.10.9-alpine
ENV VERSION_NUMBER 0.1.0-51-g9dead36 ENV VERSION_NUMBER 0.1.2
ENV PROD 1 ENV PROD 1
RUN useradd --create-home --uid 1000 timetracker RUN apk add \
bash \
vim \
curl \
caddy
RUN adduser -D -u 1000 timetracker
WORKDIR /home/timetracker/app WORKDIR /home/timetracker/app
COPY . /home/timetracker/app/ COPY . /home/timetracker/app/
RUN chown -R timetracker:timetracker /home/timetracker/app RUN chown -R timetracker:timetracker /home/timetracker/app

View File

@ -1,5 +1,3 @@
.PHONY: createsuperuser shell
all: css migrate all: css migrate
initialize: npm css migrate sethookdir loadplatforms initialize: npm css migrate sethookdir loadplatforms
@ -24,6 +22,12 @@ migrate: makemigrations
dev: migrate sethookdir dev: migrate sethookdir
poetry run python src/web/manage.py runserver_plus poetry run python src/web/manage.py runserver_plus
caddy:
caddy run --watch
dev-prod: migrate collectstatic sethookdir
cd src/web/; PROD=1 poetry run python -m gunicorn --bind 0.0.0.0:8001 web.asgi:application -k uvicorn.workers.UvicornWorker
dumptracker: dumptracker:
poetry run python src/web/manage.py dumpdata --format yaml tracker --output tracker_fixture.yaml poetry run python src/web/manage.py dumpdata --format yaml tracker --output tracker_fixture.yaml
@ -39,6 +43,9 @@ createsuperuser:
shell: shell:
poetry run python src/web/manage.py shell poetry run python src/web/manage.py shell
collectstatic:
poetry run python src/web/manage.py collectstatic
poetry.lock: pyproject.toml poetry.lock: pyproject.toml
poetry install poetry install
@ -48,5 +55,10 @@ test: poetry.lock
sethookdir: sethookdir:
git config core.hooksPath .githooks git config core.hooksPath .githooks
make date: date:
python3 -c 'import datetime; from zoneinfo import ZoneInfo; print(datetime.datetime.isoformat(datetime.datetime.now(ZoneInfo("Europe/Prague")), timespec="minutes", sep=" "))' python3 -c 'import datetime; from zoneinfo import ZoneInfo; print(datetime.datetime.isoformat(datetime.datetime.now(ZoneInfo("Europe/Prague")), timespec="minutes", sep=" "))'
cleanstatic:
rm -r src/web/static/*
clean: cleanstatic

View File

@ -1,9 +1,10 @@
--- ---
version: "2.1"
services: services:
timetracker: timetracker:
image: registry.kucharczyk.xyz/timetracker image: registry.kucharczyk.xyz/timetracker
build: Dockerfile build:
context: .
dockerfile: Dockerfile
container_name: timetracker container_name: timetracker
environment: environment:
- TZ=Europe/Prague - TZ=Europe/Prague

View File

@ -1,11 +1,13 @@
#!/bin/bash #!/bin/bash
# Apply database migrations # Apply database migrations
set -euo pipefail
echo "Apply database migrations" echo "Apply database migrations"
poetry run python src/web/manage.py migrate poetry run python src/web/manage.py migrate
echo "Collect static files" echo "Collect static files"
poetry run python src/web/manage.py collectstatic poetry run python src/web/manage.py collectstatic
# Start server
echo "Starting server" echo "Starting server"
poetry run python src/web/manage.py runserver 0.0.0.0:8000 caddy start
cd src/web || exit
poetry run python -m gunicorn --bind 0.0.0.0:8001 web.asgi:application -k uvicorn.workers.UvicornWorker

75
poetry.lock generated
View File

@ -73,7 +73,7 @@ uvloop = ["uvloop (>=0.15.2)"]
name = "click" name = "click"
version = "8.1.3" version = "8.1.3"
description = "Composable command line interface toolkit" description = "Composable command line interface toolkit"
category = "dev" category = "main"
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
@ -88,7 +88,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
name = "colorama" name = "colorama"
version = "0.4.6" version = "0.4.6"
description = "Cross-platform colored terminal text." description = "Cross-platform colored terminal text."
category = "dev" category = "main"
optional = false optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [ files = [
@ -215,6 +215,39 @@ files = [
[package.extras] [package.extras]
test = ["pytest (>=6)"] test = ["pytest (>=6)"]
[[package]]
name = "gunicorn"
version = "20.1.0"
description = "WSGI HTTP Server for UNIX"
category = "main"
optional = false
python-versions = ">=3.5"
files = [
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"},
]
[package.dependencies]
setuptools = ">=3.0"
[package.extras]
eventlet = ["eventlet (>=0.24.1)"]
gevent = ["gevent (>=1.4.0)"]
setproctitle = ["setproctitle"]
tornado = ["tornado (>=0.2)"]
[[package]]
name = "h11"
version = "0.14.0"
description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"},
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
]
[[package]] [[package]]
name = "html-tag-names" name = "html-tag-names"
version = "0.1.2" version = "0.1.2"
@ -627,6 +660,23 @@ files = [
{file = "regex-2022.10.31.tar.gz", hash = "sha256:a3a98921da9a1bf8457aeee6a551948a83601689e5ecdd736894ea9bbec77e83"}, {file = "regex-2022.10.31.tar.gz", hash = "sha256:a3a98921da9a1bf8457aeee6a551948a83601689e5ecdd736894ea9bbec77e83"},
] ]
[[package]]
name = "setuptools"
version = "65.6.3"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "setuptools-65.6.3-py3-none-any.whl", hash = "sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54"},
{file = "setuptools-65.6.3.tar.gz", hash = "sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75"},
]
[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]] [[package]]
name = "six" name = "six"
version = "1.16.0" version = "1.16.0"
@ -708,6 +758,25 @@ files = [
{file = "tzdata-2022.7.tar.gz", hash = "sha256:fe5f866eddd8b96e9fcba978f8e503c909b19ea7efda11e52e39494bad3a7bfa"}, {file = "tzdata-2022.7.tar.gz", hash = "sha256:fe5f866eddd8b96e9fcba978f8e503c909b19ea7efda11e52e39494bad3a7bfa"},
] ]
[[package]]
name = "uvicorn"
version = "0.20.0"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "uvicorn-0.20.0-py3-none-any.whl", hash = "sha256:c3ed1598a5668208723f2bb49336f4509424ad198d6ab2615b7783db58d919fd"},
{file = "uvicorn-0.20.0.tar.gz", hash = "sha256:a4e12017b940247f836bc90b72e725d7dfd0c8ed1c51eb365f5ba30d9f5127d8"},
]
[package.dependencies]
click = ">=7.0"
h11 = ">=0.8"
[package.extras]
standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"]
[[package]] [[package]]
name = "werkzeug" name = "werkzeug"
version = "2.2.2" version = "2.2.2"
@ -745,4 +814,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "4e2d507e5549e97f894ebcaf0781c1fa54acf3444583510c36d3807e6c4761d9" content-hash = "fd85e51c8fb99824a433b451c9712b7418c13688b9eb0e8ca6c51768f544e48f"

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "timetracker" name = "timetracker"
version = "0.1.0" version = "0.1.2"
description = "A simple time tracker." description = "A simple time tracker."
authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"] authors = ["Lukáš Kucharczyk <lukas@kucharczyk.xyz>"]
license = "GPL" license = "GPL"
@ -9,6 +9,8 @@ readme = "README.md"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.10" python = "^3.10"
django = "^4.1.4" django = "^4.1.4"
gunicorn = "^20.1.0"
uvicorn = "^0.20.0"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
black = "^22.12.0" black = "^22.12.0"

View File

@ -1,4 +1,5 @@
from django import template from django import template
from django.conf import settings
import time import time
import os import os
@ -9,7 +10,11 @@ register = template.Library()
def version_date(): def version_date():
return time.strftime( return time.strftime(
"%d-%b-%Y %H:%m", "%d-%b-%Y %H:%m",
time.gmtime(os.path.getmtime(os.path.abspath(os.path.join(".git")))), time.gmtime(
os.path.getmtime(
os.path.abspath(os.path.join(settings.BASE_DIR, "..", "..", ".git"))
)
),
) )

View File

@ -34,7 +34,6 @@ ALLOWED_HOSTS = ["*"]
INSTALLED_APPS = [ INSTALLED_APPS = [
"tracker.apps.TrackerConfig", "tracker.apps.TrackerConfig",
"django.contrib.admin",
"django.contrib.auth", "django.contrib.auth",
"django.contrib.contenttypes", "django.contrib.contenttypes",
"django.contrib.sessions", "django.contrib.sessions",
@ -44,6 +43,7 @@ INSTALLED_APPS = [
if DEBUG: if DEBUG:
INSTALLED_APPS.append("django_extensions") INSTALLED_APPS.append("django_extensions")
INSTALLED_APPS.append("django.contrib.admin")
MIDDLEWARE = [ MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware", "django.middleware.security.SecurityMiddleware",
@ -123,7 +123,7 @@ USE_TZ = True
# https://docs.djangoproject.com/en/4.1/howto/static-files/ # https://docs.djangoproject.com/en/4.1/howto/static-files/
STATIC_URL = "static/" STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "/static" STATIC_ROOT = BASE_DIR / "static"
# Default primary key field type # Default primary key field type
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field

View File

@ -16,10 +16,13 @@ Including another URLconf
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
from django.views.generic import RedirectView from django.views.generic import RedirectView
from django.conf import settings
urlpatterns = [ urlpatterns = [
path("admin/", admin.site.urls),
path("", RedirectView.as_view(url="/tracker/list-sessions")), path("", RedirectView.as_view(url="/tracker/list-sessions")),
path("tracker/", include("tracker.urls")), path("tracker/", include("tracker.urls")),
] ]
if settings.DEBUG:
urlpatterns.append(path("admin/", admin.site.urls))