Skip to content

Commit

Permalink
Drop s6, tidy up and modernify dockerfile, drop user creation (just u…
Browse files Browse the repository at this point in the history
…se --user)
  • Loading branch information
TheReverend403 committed Feb 23, 2024
1 parent 6002bc3 commit 79157d4
Show file tree
Hide file tree
Showing 18 changed files with 138 additions and 223 deletions.
6 changes: 3 additions & 3 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ root = true
[*]
end_of_line = lf
insert_final_newline = true

[*.py]
indent_style = space
indent_size = 4
max_line_length = 120

[*.py]
max_line_length = 88

[*.{yml,yaml}]
indent_style = space
indent_size = 2
127 changes: 41 additions & 86 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,50 +6,25 @@ ARG NODE_VERSION=20

## Base
FROM python:${PYTHON_VERSION}-slim-${DEBIAN_VERSION} as python-base
ARG ARG_APP_USER=app

ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=off \
PIP_DISABLE_PIP_VERSION_CHECK=on \
PIP_DEFAULT_TIMEOUT=100 \
POETRY_VERSION="" \
POETRY_HOME="/opt/poetry" \
POETRY_VIRTUALENVS_IN_PROJECT=true \
POETRY_VIRTUALENVS_CREATE=false \
POETRY_NO_INTERACTION=1 \
PYSETUP_PATH="/opt/pysetup" \
VENV_PATH="/opt/pysetup/.venv" \
NODE_MODULES="/opt/node" \
S6_DOWNLOAD_PATH="/opt/s6" \
SETTINGS_FILE_FOR_DYNACONF="/config/settings.yml" \
GUNICORN_HOST="0.0.0.0" \
GUNICORN_PORT=5000 \
FLASK_APP="pste" \
PATHS_STATIC="/static" \
PATHS_DATA="/data" \
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \
APP_USER=$ARG_APP_USER

ENV PATHS_NODE_MODULES=$NODE_MODULES \
PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH"


FROM python-base as s6-base
# Latest
POETRY_VERSION="" \
VIRTUAL_ENV="/venv"

RUN --mount=type=cache,target=/var/cache/apt,sharing=private \
apt-get update && \
apt-get install --no-install-recommends -y \
xz-utils \
&& apt-get autoclean && rm -rf /var/lib/apt/lists/*
ENV PATH="${POETRY_HOME}/bin:${VIRTUAL_ENV}/bin:${PATH}" \
PYTHONPATH="/app:${PYTHONPATH}"

ARG S6_OVERLAY_VERSION="3.0.0.2"
RUN python -m venv "${VIRTUAL_ENV}"

ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch-${S6_OVERLAY_VERSION}.tar.xz /tmp
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64-${S6_OVERLAY_VERSION}.tar.xz /tmp
RUN mkdir -p "$S6_DOWNLOAD_PATH" && \
tar -C "$S6_DOWNLOAD_PATH/" -Jxpf /tmp/s6-overlay-x86_64-${S6_OVERLAY_VERSION}.tar.xz && \
tar -C "$S6_DOWNLOAD_PATH/" -Jxpf /tmp/s6-overlay-noarch-${S6_OVERLAY_VERSION}.tar.xz
WORKDIR /app


## Python builder
Expand All @@ -64,94 +39,74 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=private \
libpq-dev \
&& apt-get autoclean && rm -rf /var/lib/apt/lists/*

# Install poetry - respects $POETRY_VERSION & $POETRY_HOME
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN --mount=type=cache,target=/root/.cache \
curl -sSL https://install.python-poetry.org | python -
curl -sSL https://install.python-poetry.org | python3 -

# Copy project requirement files here to ensure they will be cached.
WORKDIR $PYSETUP_PATH
COPY poetry.lock pyproject.toml ./

# Install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally
RUN --mount=type=cache,target=/root/.cache \
poetry install --no-root --only main


## JS builder
FROM node:${NODE_VERSION}-bullseye-slim as node-builder-base
FROM node:${NODE_VERSION}-${DEBIAN_VERSION}-slim as node-builder-base

ENV NODE_MODULES="/opt/node"
WORKDIR $NODE_MODULES
WORKDIR /opt/node

COPY yarn.lock package.json ./
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn \
yarn install


## Dev image
FROM python-base as development
## Base image
FROM python-base as flask-base

RUN --mount=type=cache,target=/var/cache/apt,sharing=private \
apt-get update && \
apt-get install --no-install-recommends -y \
libpq5 \
curl \
libmagic1 \
&& apt-get autoclean && rm -rf /var/lib/apt/lists/*

WORKDIR $PYSETUP_PATH

COPY --from=python-builder-base $PYSETUP_PATH $PYSETUP_PATH
COPY --from=python-builder-base $POETRY_HOME $POETRY_HOME
COPY --from=node-builder-base $NODE_MODULES/node_modules $NODE_MODULES/

RUN --mount=type=cache,target=/root/.cache \
poetry install --no-root

COPY --from=s6-base $S6_DOWNLOAD_PATH /
COPY --from=node-builder-base /opt/node /opt/node
COPY --from=python-builder-base ${VIRTUAL_ENV} ${VIRTUAL_ENV}
COPY docker/rootfs /
COPY pste ./pste

RUN addgroup --gid 1000 --system $APP_USER && \
adduser --uid 1000 --system --gid 1000 --no-create-home $APP_USER

WORKDIR /app

ENV FLASK_DEBUG=1 \
FLASK_ENV="development"
ENV ROOT_PATH_FOR_DYNACONF="/config" \
GUNICORN_HOST="0.0.0.0" \
GUNICORN_PORT=5000 \
FLASK_APP="pste" \
CFG_PATHS__STATIC="/data/static" \
CFG_PATHS__DATA="/data" \
CFG_PATHS__NODE_MODULES="/opt/node/node_modules"

VOLUME ["/static", "/config", "/data"]
VOLUME ["/config", "/data"]
EXPOSE 5000

ENTRYPOINT ["/init"]

ENTRYPOINT ["/docker-entrypoint.sh"]

## Production image
FROM python-base as production

RUN --mount=type=cache,target=/var/cache/apt,sharing=private \
apt-get update && \
apt-get install --no-install-recommends -y \
curl \
libpq5 \
libmagic1 \
&& apt-get autoclean && rm -rf /var/lib/apt/lists/*

COPY --from=python-builder-base $PYSETUP_PATH $PYSETUP_PATH
COPY --from=node-builder-base $NODE_MODULES/node_modules $NODE_MODULES/
## Dev image
FROM flask-base as development

COPY --from=s6-base $S6_DOWNLOAD_PATH /
COPY docker/rootfs /
COPY --from=python-builder-base ${POETRY_HOME} ${POETRY_HOME}
COPY poetry.lock pyproject.toml ./

RUN addgroup --gid 1000 --system $APP_USER && \
adduser --uid 1000 --system --gid 1000 --no-create-home $APP_USER
RUN --mount=type=cache,target=/root/.cache \
poetry install --no-root

WORKDIR /app
ENV FLASK_DEBUG=1 \
ENV_FOR_DYNACONF=development \
GUNICORN_OPTS="--reload --reload-extra-file /config" \
# Don't compile assets on startup as built-in autobuild is used in dev/testing environments.
SKIP_ASSETS=true

COPY --chown=$APP_USER:$APP_USER ./pste ./pste

ENV FLASK_ENV="production"
## Production image
FROM flask-base as production

VOLUME ["/static", "/config", "/data"]
EXPOSE 5000
ENV ENV_FOR_DYNACONF=production

HEALTHCHECK --start-interval=1s --start-period=10s --interval=10s --timeout=5s CMD ["/docker-healthcheck.sh"]
ENTRYPOINT ["/init"]
25 changes: 25 additions & 0 deletions docker/rootfs/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/sh
set -eu

if [ -z "$(find "${ROOT_PATH_FOR_DYNACONF}" -type f -mindepth 1 -maxdepth 1)" ]; then
echo "Copying default settings.yml to ${ROOT_PATH_FOR_DYNACONF}"
cp -au "${FLASK_APP}/resources/config/settings.yml" "${ROOT_PATH_FOR_DYNACONF}"
fi

if [ "${SKIP_MIGRATIONS:-false}" = "false" ]; then
flask db upgrade
else
echo "Skipping migrations due to SKIP_MIGRATIONS=${SKIP_MIGRATIONS}"
fi

if [ "${SKIP_ASSETS:-false}" = "false" ]; then
if [ -d "${CFG_PATHS__STATIC}/.webassets-cache" ]; then
flask assets clean
fi
flask assets build
else
echo "Skipping assets due to SKIP_ASSETS=${SKIP_ASSETS}"
fi

# shellcheck disable=SC2086
exec gunicorn "${FLASK_APP}:create_app()" --worker-class gevent --bind "${GUNICORN_HOST}:${GUNICORN_PORT}" ${GUNICORN_OPTS:-}
3 changes: 2 additions & 1 deletion docker/rootfs/docker-healthcheck.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#!/bin/sh
set -eu
curl -sSL "http://$GUNICORN_HOST:$GUNICORN_PORT" > /dev/null && echo "OK"

curl -sSL "http://${GUNICORN_HOST}:${GUNICORN_PORT}" > /dev/null && echo "OK"
7 changes: 0 additions & 7 deletions docker/rootfs/etc/cont-init.d/10-config

This file was deleted.

17 changes: 0 additions & 17 deletions docker/rootfs/etc/cont-init.d/20-fix-perms

This file was deleted.

7 changes: 0 additions & 7 deletions docker/rootfs/etc/cont-init.d/30-migrate-db

This file was deleted.

11 changes: 0 additions & 11 deletions docker/rootfs/etc/cont-init.d/40-assets

This file was deleted.

8 changes: 0 additions & 8 deletions docker/rootfs/etc/services.d/app/run

This file was deleted.

10 changes: 5 additions & 5 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 16 additions & 7 deletions pste/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#
# You should have received a copy of the GNU General Public License
# along with pste. If not, see <https://www.gnu.org/licenses/>.

import shutil
import subprocess

import sentry_sdk
Expand Down Expand Up @@ -42,11 +42,16 @@ def create_app():
template_folder=str(paths.TEMPLATES),
)

for path in [paths.STATIC, paths.DATA]:
path.mkdir(exist_ok=True, parents=True)

load_configuration(app)
register_extensions(app)
register_commands(app)
register_blueprints(app)
register_assets(app)

with app.app_context():
register_assets(app)

if not app.debug and not app.testing:
app.wsgi_app = ProxyFix(app.wsgi_app)
Expand Down Expand Up @@ -104,8 +109,6 @@ def register_extensions(app):
assets.init_app(app)
login.init_app(app)

login.login_view = "auth.login"

from pste.extensions import debugbar

if app.debug and debugbar is not None:
Expand Down Expand Up @@ -159,11 +162,17 @@ def register_assets(app):
),
}

with app.app_context():
assets.directory = app.static_folder
assets.append_path(paths.ASSETS)
assets.directory = app.static_folder
assets.auto_build = app.debug or app.testing
assets.append_path(paths.ASSETS)

for name, bundle in bundles.items():
assets.register(name, bundle)

precompiled_assets = ["img", "fonts"]
for asset_type in precompiled_assets:
shutil.copytree(
paths.ASSETS / asset_type, paths.STATIC / asset_type, dirs_exist_ok=True
)

app.logger.debug("Assets registered.")
7 changes: 4 additions & 3 deletions pste/assets/scss/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
* You should have received a copy of the GNU General Public License
* along with pste. If not, see <https://www.gnu.org/licenses/>.
*/
@import "/opt/node/bootstrap/scss/functions";
@import "/opt/node/bootstrap/scss/variables";

@import "/opt/node/node_modules/bootstrap/scss/functions";
@import "/opt/node/node_modules/bootstrap/scss/variables";

$enable-rounded: false;

Expand All @@ -26,7 +27,7 @@ $theme-colors: (
$input-focus-border-color: theme-color("primary");
$input-focus-box-shadow: 0;

@import "/opt/node/bootstrap/scss/bootstrap";
@import "/opt/node/node_modules/bootstrap/scss/bootstrap";

.navbar {
margin-bottom: 20px;
Expand Down
Loading

0 comments on commit 79157d4

Please sign in to comment.