Docker + docker compose support

This commit is contained in:
Thomas Sileo 2022-07-18 20:44:55 +02:00
parent 3e7ad917e2
commit fe88481431
10 changed files with 141 additions and 29 deletions

2
.gitignore vendored
View file

@ -4,4 +4,4 @@ __pycache__/
.pytest_cache/ .pytest_cache/
docs/dist/ docs/dist/
requirements.txt requirements.txt
app/scss/_vars.scss app/_version.py

28
Dockerfile Normal file
View file

@ -0,0 +1,28 @@
FROM python:3.10-slim as python-base
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
POETRY_HOME="/opt/poetry" \
POETRY_VIRTUALENVS_IN_PROJECT=true \
POETRY_NO_INTERACTION=1 \
PYSETUP_PATH="/opt/venv" \
VENV_PATH="/opt/venv/.venv"
ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH"
FROM python-base as builder-base
RUN apt-get update
RUN apt-get install -y --no-install-recommends curl build-essential gcc
RUN curl -sSL https://install.python-poetry.org | python3 -
WORKDIR $PYSETUP_PATH
COPY poetry.lock pyproject.toml ./
RUN poetry install --no-dev
FROM python-base as production
RUN groupadd --gid 1000 microblogpub \
&& useradd --uid 1000 --gid microblogpub --shell /bin/bash microblogpub
COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
COPY . /app/
RUN chown -R 1000:1000 /app
USER microblogpub
WORKDIR /app
EXPOSE 8000
CMD ["supervisord", "-n", "-c", "misc/docker-supervisord.conf"]

7
Makefile Normal file
View file

@ -0,0 +1,7 @@
SHELL := /bin/bash
PWD=$(shell pwd)
.PHONY: config
config:
# Run and remove instantly
-docker run --rm -it --volume `pwd`/data:/app/data microblogpub/microblogpub inv configuration-wizard

View file

@ -145,7 +145,7 @@ async def save_actor(db_session: AsyncSession, ap_actor: ap.RawObject) -> "Actor
handle=_handle(ap_actor), handle=_handle(ap_actor),
) )
db_session.add(actor) db_session.add(actor)
await db_session.flush() await db_session.commit()
return actor return actor

View file

@ -1,6 +1,5 @@
import os import os
import secrets import secrets
import subprocess
from pathlib import Path from pathlib import Path
import bcrypt import bcrypt
@ -19,11 +18,13 @@ ROOT_DIR = Path().parent.resolve()
_CONFIG_FILE = os.getenv("MICROBLOGPUB_CONFIG_FILE", "profile.toml") _CONFIG_FILE = os.getenv("MICROBLOGPUB_CONFIG_FILE", "profile.toml")
VERSION_COMMIT = ( VERSION_COMMIT = "dev"
subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"])
.split()[0] try:
.decode() from app._version import VERSION_COMMIT # type: ignore
) except ImportError:
pass
VERSION = f"2.0.0+{VERSION_COMMIT}" VERSION = f"2.0.0+{VERSION_COMMIT}"
USER_AGENT = f"microblogpub/{VERSION}" USER_AGENT = f"microblogpub/{VERSION}"

12
docker-compose.yml Normal file
View file

@ -0,0 +1,12 @@
version: "3"
services:
server:
image: microblogpub/microblogpub:latest
container_name: microblogpub
user: 1000:1000
restart: always
volumes:
- ./data:/app/data
ports:
- "8000:8000"

View file

@ -0,0 +1,28 @@
[supervisord]
nodaemon=true
logfile=/dev/null
logfile_maxbytes=0
pidfile=data/supervisord.pid
[fcgi-program:uvicorn]
socket=tcp://0.0.0.0:8000
command=uvicorn app.main:app --no-server-header --fd 0
numprocs=2
process_name=uvicorn-%(process_num)d
redirect_stderr=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
[program:incoming_worker]
command=inv process-incoming-activities
numproc=1
redirect_stderr=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0
[program:outgoing_worker]
command=inv process-outgoing-activities
numproc=1
redirect_stderr=true
stdout_logfile=/dev/fd/1
stdout_logfile_maxbytes=0

28
poetry.lock generated
View file

@ -161,7 +161,7 @@ testing = ["pytest"]
name = "boussole" name = "boussole"
version = "2.0.0" version = "2.0.0"
description = "Commandline interface to build Sass projects using libsass-python" description = "Commandline interface to build Sass projects using libsass-python"
category = "dev" category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
@ -248,7 +248,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
name = "colorlog" name = "colorlog"
version = "6.6.0" version = "6.6.0"
description = "Add colours to the output of Python's logging module." description = "Add colours to the output of Python's logging module."
category = "dev" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
@ -489,7 +489,7 @@ python-versions = "*"
name = "invoke" name = "invoke"
version = "1.7.1" version = "1.7.1"
description = "Pythonic task execution" description = "Pythonic task execution"
category = "dev" category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
@ -533,7 +533,7 @@ i18n = ["Babel (>=2.7)"]
name = "libsass" name = "libsass"
version = "0.21.0" version = "0.21.0"
description = "Sass for Python: A straightforward binding of libsass for Python." description = "Sass for Python: A straightforward binding of libsass for Python."
category = "dev" category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
@ -741,7 +741,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
name = "pyaml" name = "pyaml"
version = "21.10.1" version = "21.10.1"
description = "PyYAML-based module to produce pretty and readable YAML-serialized data" description = "PyYAML-based module to produce pretty and readable YAML-serialized data"
category = "dev" category = "main"
optional = false optional = false
python-versions = "*" python-versions = "*"
@ -894,7 +894,7 @@ six = ">=1.4.0"
name = "pyyaml" name = "pyyaml"
version = "6.0" version = "6.0"
description = "YAML parser and emitter for Python" description = "YAML parser and emitter for Python"
category = "dev" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
@ -1022,6 +1022,17 @@ anyio = ">=3.4.0,<5"
[package.extras] [package.extras]
full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"] full = ["itsdangerous", "jinja2", "python-multipart", "pyyaml", "requests"]
[[package]]
name = "supervisor"
version = "4.2.4"
description = "A system for controlling process state under UNIX"
category = "main"
optional = false
python-versions = "*"
[package.extras]
testing = ["pytest", "pytest-cov"]
[[package]] [[package]]
name = "tabulate" name = "tabulate"
version = "0.8.10" version = "0.8.10"
@ -1165,7 +1176,7 @@ standard = ["websockets (>=10.0)", "httptools (>=0.4.0)", "watchgod (>=0.6)", "p
name = "watchdog" name = "watchdog"
version = "2.1.9" version = "2.1.9"
description = "Filesystem events monitoring" description = "Filesystem events monitoring"
category = "dev" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
@ -1202,7 +1213,7 @@ dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.10" python-versions = "^3.10"
content-hash = "d0c330a9153ced7f4ba61e5354bc7bb3136a3d11f4f700bb76498c20e200c509" content-hash = "5ee42d968baa21950366d1ca0597fb1e0e45e6e26005f93acbcf8b43cd1fb370"
[metadata.files] [metadata.files]
aiosqlite = [ aiosqlite = [
@ -2002,6 +2013,7 @@ starlette = [
{file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"}, {file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
{file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"}, {file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
] ]
supervisor = []
tabulate = [] tabulate = []
tomli = [ tomli = [
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},

View file

@ -40,6 +40,9 @@ cachetools = "^5.2.0"
humanize = "^4.2.3" humanize = "^4.2.3"
tabulate = "^0.8.10" tabulate = "^0.8.10"
asgiref = "^3.5.2" asgiref = "^3.5.2"
supervisor = "^4.2.4"
invoke = "^1.7.1"
boussole = "^2.0.0"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
black = "^22.3.0" black = "^22.3.0"

View file

@ -1,7 +1,10 @@
import asyncio import asyncio
import io import io
import subprocess
import tarfile import tarfile
from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from typing import Generator
from typing import Optional from typing import Optional
import httpx import httpx
@ -13,13 +16,13 @@ from invoke import task # type: ignore
@task @task
def generate_db_migration(ctx, message): def generate_db_migration(ctx, message):
# type: (Context, str) -> None # type: (Context, str) -> None
run(f'poetry run alembic revision --autogenerate -m "{message}"', echo=True) run(f'alembic revision --autogenerate -m "{message}"', echo=True)
@task @task
def migrate_db(ctx): def migrate_db(ctx):
# type: (Context) -> None # type: (Context) -> None
run("poetry run alembic upgrade head", echo=True) run("alembic upgrade head", echo=True)
@task @task
@ -46,15 +49,15 @@ def compile_scss(ctx, watch=False):
theme_file.write_text("// override vars for theming here") theme_file.write_text("// override vars for theming here")
if watch: if watch:
run("poetry run boussole watch", echo=True) run("boussole watch", echo=True)
else: else:
run("poetry run boussole compile", echo=True) run("boussole compile", echo=True)
@task @task
def uvicorn(ctx): def uvicorn(ctx):
# type: (Context) -> None # type: (Context) -> None
run("poetry run uvicorn app.main:app --no-server-header", pty=True, echo=True) run("uvicorn app.main:app --no-server-header", pty=True, echo=True)
@task @task
@ -96,16 +99,11 @@ def generate_requirements_txt(ctx, where="requirements.txt"):
) )
@task(generate_requirements_txt)
def build_configuration_wizard_image(ctx):
# type: (Context) -> None
run("docker build -t testmpw -f configuration_wizard.dockerfile .")
@task @task
def build_docs(ctx): def build_docs(ctx):
# type: (Context) -> None # type: (Context) -> None
run("PYTHONPATH=. poetry run python scripts/build_docs.py", pty=True, echo=True) with embed_version():
run("PYTHONPATH=. python scripts/build_docs.py", pty=True, echo=True)
@task @task
@ -131,7 +129,7 @@ def download_twemoji(ctx):
@task(download_twemoji, compile_scss, migrate_db) @task(download_twemoji, compile_scss, migrate_db)
def configuration_wizard(ctx): def configuration_wizard(ctx):
# type: (Context) -> None # type: (Context) -> None
run("PYTHONPATH=. poetry run python scripts/config_wizard.py", pty=True, echo=True) run("PYTHONPATH=. python scripts/config_wizard.py", pty=True, echo=True)
@task @task
@ -152,3 +150,26 @@ def stats(ctx):
from app.utils.stats import print_stats from app.utils.stats import print_stats
print_stats() print_stats()
@contextmanager
def embed_version() -> Generator[None, None, None]:
version_file = Path("app/_version.py")
version_file.unlink(missing_ok=True)
version = (
subprocess.check_output(["git", "rev-parse", "--short=8", "v2"])
.split()[0]
.decode()
)
version_file.write_text(f'VERSION_COMMIT = "{version}"')
try:
yield
finally:
version_file.unlink()
@task
def build_docker_image(ctx):
# type: (Context) -> None
with embed_version():
run("docker build -t microblogpub/microblogpub .")