Docker + docker compose support
This commit is contained in:
parent
3e7ad917e2
commit
fe88481431
10 changed files with 141 additions and 29 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -4,4 +4,4 @@ __pycache__/
|
|||
.pytest_cache/
|
||||
docs/dist/
|
||||
requirements.txt
|
||||
app/scss/_vars.scss
|
||||
app/_version.py
|
||||
|
|
28
Dockerfile
Normal file
28
Dockerfile
Normal 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
7
Makefile
Normal 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
|
|
@ -145,7 +145,7 @@ async def save_actor(db_session: AsyncSession, ap_actor: ap.RawObject) -> "Actor
|
|||
handle=_handle(ap_actor),
|
||||
)
|
||||
db_session.add(actor)
|
||||
await db_session.flush()
|
||||
await db_session.commit()
|
||||
return actor
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import os
|
||||
import secrets
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import bcrypt
|
||||
|
@ -19,11 +18,13 @@ ROOT_DIR = Path().parent.resolve()
|
|||
|
||||
_CONFIG_FILE = os.getenv("MICROBLOGPUB_CONFIG_FILE", "profile.toml")
|
||||
|
||||
VERSION_COMMIT = (
|
||||
subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"])
|
||||
.split()[0]
|
||||
.decode()
|
||||
)
|
||||
VERSION_COMMIT = "dev"
|
||||
|
||||
try:
|
||||
from app._version import VERSION_COMMIT # type: ignore
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
VERSION = f"2.0.0+{VERSION_COMMIT}"
|
||||
USER_AGENT = f"microblogpub/{VERSION}"
|
||||
|
|
12
docker-compose.yml
Normal file
12
docker-compose.yml
Normal 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"
|
28
misc/docker-supervisord.conf
Normal file
28
misc/docker-supervisord.conf
Normal 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
28
poetry.lock
generated
|
@ -161,7 +161,7 @@ testing = ["pytest"]
|
|||
name = "boussole"
|
||||
version = "2.0.0"
|
||||
description = "Commandline interface to build Sass projects using libsass-python"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
|
@ -248,7 +248,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
|||
name = "colorlog"
|
||||
version = "6.6.0"
|
||||
description = "Add colours to the output of Python's logging module."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
|
@ -489,7 +489,7 @@ python-versions = "*"
|
|||
name = "invoke"
|
||||
version = "1.7.1"
|
||||
description = "Pythonic task execution"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
|
@ -533,7 +533,7 @@ i18n = ["Babel (>=2.7)"]
|
|||
name = "libsass"
|
||||
version = "0.21.0"
|
||||
description = "Sass for Python: A straightforward binding of libsass for Python."
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
|
@ -741,7 +741,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
|||
name = "pyaml"
|
||||
version = "21.10.1"
|
||||
description = "PyYAML-based module to produce pretty and readable YAML-serialized data"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
|
@ -894,7 +894,7 @@ six = ">=1.4.0"
|
|||
name = "pyyaml"
|
||||
version = "6.0"
|
||||
description = "YAML parser and emitter for Python"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
|
@ -1022,6 +1022,17 @@ anyio = ">=3.4.0,<5"
|
|||
[package.extras]
|
||||
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]]
|
||||
name = "tabulate"
|
||||
version = "0.8.10"
|
||||
|
@ -1165,7 +1176,7 @@ standard = ["websockets (>=10.0)", "httptools (>=0.4.0)", "watchgod (>=0.6)", "p
|
|||
name = "watchdog"
|
||||
version = "2.1.9"
|
||||
description = "Filesystem events monitoring"
|
||||
category = "dev"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
|
@ -1202,7 +1213,7 @@ dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"]
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "d0c330a9153ced7f4ba61e5354bc7bb3136a3d11f4f700bb76498c20e200c509"
|
||||
content-hash = "5ee42d968baa21950366d1ca0597fb1e0e45e6e26005f93acbcf8b43cd1fb370"
|
||||
|
||||
[metadata.files]
|
||||
aiosqlite = [
|
||||
|
@ -2002,6 +2013,7 @@ starlette = [
|
|||
{file = "starlette-0.19.1-py3-none-any.whl", hash = "sha256:5a60c5c2d051f3a8eb546136aa0c9399773a689595e099e0877704d5888279bf"},
|
||||
{file = "starlette-0.19.1.tar.gz", hash = "sha256:c6d21096774ecb9639acad41b86b7706e52ba3bf1dc13ea4ed9ad593d47e24c7"},
|
||||
]
|
||||
supervisor = []
|
||||
tabulate = []
|
||||
tomli = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
|
|
|
@ -40,6 +40,9 @@ cachetools = "^5.2.0"
|
|||
humanize = "^4.2.3"
|
||||
tabulate = "^0.8.10"
|
||||
asgiref = "^3.5.2"
|
||||
supervisor = "^4.2.4"
|
||||
invoke = "^1.7.1"
|
||||
boussole = "^2.0.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^22.3.0"
|
||||
|
|
47
tasks.py
47
tasks.py
|
@ -1,7 +1,10 @@
|
|||
import asyncio
|
||||
import io
|
||||
import subprocess
|
||||
import tarfile
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import Generator
|
||||
from typing import Optional
|
||||
|
||||
import httpx
|
||||
|
@ -13,13 +16,13 @@ from invoke import task # type: ignore
|
|||
@task
|
||||
def generate_db_migration(ctx, message):
|
||||
# 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
|
||||
def migrate_db(ctx):
|
||||
# type: (Context) -> None
|
||||
run("poetry run alembic upgrade head", echo=True)
|
||||
run("alembic upgrade head", echo=True)
|
||||
|
||||
|
||||
@task
|
||||
|
@ -46,15 +49,15 @@ def compile_scss(ctx, watch=False):
|
|||
theme_file.write_text("// override vars for theming here")
|
||||
|
||||
if watch:
|
||||
run("poetry run boussole watch", echo=True)
|
||||
run("boussole watch", echo=True)
|
||||
else:
|
||||
run("poetry run boussole compile", echo=True)
|
||||
run("boussole compile", echo=True)
|
||||
|
||||
|
||||
@task
|
||||
def uvicorn(ctx):
|
||||
# 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
|
||||
|
@ -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
|
||||
def build_docs(ctx):
|
||||
# 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
|
||||
|
@ -131,7 +129,7 @@ def download_twemoji(ctx):
|
|||
@task(download_twemoji, compile_scss, migrate_db)
|
||||
def configuration_wizard(ctx):
|
||||
# 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
|
||||
|
@ -152,3 +150,26 @@ def stats(ctx):
|
|||
from app.utils.stats import 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 .")
|
||||
|
|
Loading…
Reference in a new issue