forked from forks/microblog.pub
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/
|
.pytest_cache/
|
||||||
docs/dist/
|
docs/dist/
|
||||||
requirements.txt
|
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),
|
handle=_handle(ap_actor),
|
||||||
)
|
)
|
||||||
db_session.add(actor)
|
db_session.add(actor)
|
||||||
await db_session.flush()
|
await db_session.commit()
|
||||||
return actor
|
return actor
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
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"
|
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"},
|
||||||
|
|
|
@ -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"
|
||||||
|
|
47
tasks.py
47
tasks.py
|
@ -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 .")
|
||||||
|
|
Loading…
Reference in a new issue