Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
docker/Dockerfile
56 changes: 51 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ on:
- '**.cfg'
- '**.ini'
- 'requirements.d/*'
- 'docker/**'
- '**/Dockerfile'
- '!docs/**'
pull_request:
branches: [ master ]
Expand All @@ -22,6 +24,8 @@ on:
- '**.cfg'
- '**.ini'
- 'requirements.d/*'
- 'docker/**'
- '**/Dockerfile'
- '!docs/**'

jobs:
Expand All @@ -31,16 +35,58 @@ jobs:
timeout-minutes: 10

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v7
- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v7
with:
python-version: '3.12'
- name: Lint with flake8
run: |
pip install flake8-pyproject flake8
flake8 src scripts

hadolint:
runs-on: ubuntu-24.04
timeout-minutes: 5

steps:
- uses: actions/checkout@v7
- name: Lint Dockerfile with Hadolint
uses: hadolint/hadolint-action@v3.4.0
with:
dockerfile: docker/Dockerfile
failure-threshold: warning

docker-image-test:
runs-on: ubuntu-24.04
timeout-minutes: 15

steps:
- uses: actions/checkout@v7
- name: Build Docker image
run: |
docker build -f docker/Dockerfile -t bepasty-ci:${{ github.sha }} .
- name: Smoke test Python package in image
run: |
docker run --rm bepasty-ci:${{ github.sha }} /app/env/bin/python -c "import bepasty.app; print('bepasty import ok')"
- name: Container HTTP smoke test
run: |
set -euo pipefail
cid=$(docker run -d \
-e BEPASTY_SITENAME="CI Test Instance" \
-e BEPASTY_SECRET_KEY="ci-test-secret-key-change-me" \
-p 8000:8000 \
bepasty-ci:${{ github.sha }})
trap 'docker logs "$cid" || true; docker rm -f "$cid" || true' EXIT
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:8000/ >/dev/null; then
exit 0
fi
sleep 1
done
echo "Container did not become ready on :8000 in time"
exit 1

pytest:

needs: lint
Expand Down Expand Up @@ -71,12 +117,12 @@ jobs:
timeout-minutes: 10

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v7
with:
# Fetching only the latest commit is not enough for setuptools-scm; fetch full history
fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
Expand All @@ -88,7 +134,7 @@ jobs:
run: |
tox --skip-missing-interpreters
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v7
env:
OS: ${{ runner.os }}
python: ${{ matrix.python-version }}
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/dockerhub.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Publish Docker image

on:
release:
types: [published]
push:
branches:
- master

jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v7

- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v6
with:
images: duchenpaul/bepasty

- name: Build and push Docker image
uses: docker/build-push-action@v7
with:
context: .
file: ./docker/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Comment thread
duchenpaul marked this conversation as resolved.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ coverage.xml
docs/build/

.idea
.vscode/
32 changes: 32 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
FROM python:3.14-slim AS builder

# hadolint ignore=DL3008
RUN true \
&& mkdir /app \
&& apt-get update \
&& apt-get install --no-install-recommends -y git \
&& python -m venv /app/env \
&& /app/env/bin/pip install wheel gunicorn

COPY . /usr/local/src/bepasty
RUN /app/env/bin/pip install /usr/local/src/bepasty

# ---

FROM python:3.14-slim

RUN true \
&& mkdir /app \
&& adduser -u 17357 --system --home /app/data --disabled-login --disabled-password paste

COPY docker/init.sh /usr/local/bin/init.sh
COPY --from=builder /app/env /app/env

ENV WORKERS=4
ENV LISTEN=0.0.0.0:8000
ENV BEPASTY_CONFIG=/etc/bepasty.conf
COPY docker/autoconfig.py /etc/bepasty.conf

# hadolint ignore=DL3066
USER paste
CMD ["/usr/local/bin/init.sh"]
51 changes: 51 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# bepasty Docker image

A Docker image that provides very basic configuration via environment variables.

## Notes

* For a more advanced configuration mount a custom configuration to
`/etc/bepasty.conf`.
* All data will be owned by UID 17357, to change this you can use the
`--user <uid>` option from `docker run`.

## Quickstart

```sh
# build from the repository root
docker build -f docker/Dockerfile -t bepasty .

# create datadir
mkdir data
sudo chown 17357:0 data

# run
docker run -it \
-p 8000:8000 \
-v $PWD/data:/app/data \
-e BEPASTY_SECRET_KEY=$(openssl rand -hex 16) \
-e BEPASTY_SITENAME=localhost \
bepasty

# visit http://localhost:8000
```

## Docker image environment variables

* `WORKERS`: Number of Gunicorn workers (default: `4`)
* `LISTEN`: IP and address to listen on (default: `0.0.0.0:8000`)
* `BEPASTY_CONFIG`: Path to bepasty configuration (default: `/etc/bepasty.conf`)

## Bepasty environment variables

These match the bepasty options described in the [bepasty quickstart configuration docs][1].

* `BEPASTY_SITENAME` (**required**)
* `BEPASTY_APP_BASE_PATH`
* `BEPASTY_APP_STORAGE_FILESYSTEM_DIRECTORY`
* `BEPASTY_DEFAULT_PERMISSIONS`
* `BEPASTY_SECRET_KEY`
* `BEPASTY_MAX_ALLOWED_FILE_SIZE`
* `BEPASTY_MAX_BODY_SIZE`

[1]: https://bepasty-server.readthedocs.io/en/latest/quickstart.html#configuring-bepasty
42 changes: 42 additions & 0 deletions docker/autoconfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os
import sys


def error_exit(message):
print("\n\n%s" % message)
sys.exit(1)


SITENAME = os.environ.get("BEPASTY_SITENAME")
if SITENAME is None:
error_exit("Environment variable BEPASTY_SITENAME must be set.")

SECRET_KEY = os.environ.get("BEPASTY_SECRET_KEY")
if SECRET_KEY is None:
error_exit("Environment variable BEPASTY_SECRET_KEY must be set.")

APP_BASE_PATH = os.environ.get("BEPASTY_APP_BASE_PATH")

# Keep defaults in sync with bepasty by only setting values that are
# explicitly provided via environment variables.
storage_filesystem_directory = os.environ.get("BEPASTY_STORAGE_FILESYSTEM_DIRECTORY")
if storage_filesystem_directory is not None:
STORAGE_FILESYSTEM_DIRECTORY = storage_filesystem_directory

default_permissions = os.environ.get("BEPASTY_DEFAULT_PERMISSIONS")
if default_permissions is not None:
DEFAULT_PERMISSIONS = default_permissions

max_allowed_file_size = os.environ.get("BEPASTY_MAX_ALLOWED_FILE_SIZE")
if max_allowed_file_size is not None:
try:
MAX_ALLOWED_FILE_SIZE = int(max_allowed_file_size)
except ValueError as err:
error_exit("Invalid BEPASTY_MAX_ALLOWED_FILE_SIZE: %s" % str(err))

max_body_size = os.environ.get("BEPASTY_MAX_BODY_SIZE")
if max_body_size is not None:
try:
MAX_BODY_SIZE = int(max_body_size)
except ValueError as err:
error_exit("Invalid BEPASTY_MAX_BODY_SIZE: %s" % str(err))
11 changes: 11 additions & 0 deletions docker/goss.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
command:
app-import:
exec: /app/env/bin/python -c "import bepasty.app; print('bepasty import ok')"
exit-status: 0
stdout:
- /bepasty import ok/

http:
http://127.0.0.1:8000/:
status: 200
timeout: 10000
Comment thread
duchenpaul marked this conversation as resolved.
12 changes: 12 additions & 0 deletions docker/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

if [ ! -f "$BEPASTY_CONFIG" ]; then
echo "Please please mount a configuration file to '$BEPASTY_CONFIG'."
exit 1
fi

if ! python "$BEPASTY_CONFIG"; then
exit 1
fi

exec /app/env/bin/gunicorn -b "$LISTEN" --workers="$WORKERS" bepasty.wsgi:application