Compare commits

..

No commits in common. "dev" and "v2.14.4" have entirely different histories.
dev ... v2.14.4

553 changed files with 95459 additions and 127218 deletions

View File

@ -1,18 +1,18 @@
codecov: codecov:
require_ci_to_pass: true require_ci_to_pass: true
# https://docs.codecov.com/docs/components # https://docs.codecov.com/docs/flags#recommended-automatic-flag-management
component_management: # Require each flag to have 1 upload before notification
individual_components: flag_management:
- component_id: backend individual_flags:
- name: backend
paths: paths:
- src/** - src/
- component_id: frontend - name: frontend
paths: paths:
- src-ui/** - src-ui/
# https://docs.codecov.com/docs/pull-request-comments # https://docs.codecov.com/docs/pull-request-comments
# codecov will only comment if coverage changes # codecov will only comment if coverage changes
comment: comment:
layout: "header, diff, components, flags, files"
require_changes: true require_changes: true
# https://docs.codecov.com/docs/javascript-bundle-analysis # https://docs.codecov.com/docs/javascript-bundle-analysis
require_bundle_changes: true require_bundle_changes: true

View File

@ -76,15 +76,18 @@ RUN set -eux \
&& apt-get update \ && apt-get update \
&& apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES} && apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES}
ARG PYTHON_PACKAGES="ca-certificates" ARG PYTHON_PACKAGES="\
python3 \
python3-pip \
python3-wheel \
pipenv \
ca-certificates"
RUN set -eux \ RUN set -eux \
echo "Installing python packages" \ echo "Installing python packages" \
&& apt-get update \ && apt-get update \
&& apt-get install --yes --quiet ${PYTHON_PACKAGES} && apt-get install --yes --quiet ${PYTHON_PACKAGES}
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
RUN set -eux \ RUN set -eux \
&& echo "Installing pre-built updates" \ && echo "Installing pre-built updates" \
&& echo "Installing qpdf ${QPDF_VERSION}" \ && echo "Installing qpdf ${QPDF_VERSION}" \
@ -120,15 +123,13 @@ RUN set -eux \
WORKDIR /usr/src/paperless/src/docker/ WORKDIR /usr/src/paperless/src/docker/
COPY [ \ COPY [ \
"docker/rootfs/etc/ImageMagick-6/paperless-policy.xml", \ "docker/imagemagick-policy.xml", \
"./" \ "./" \
] ]
RUN set -eux \ RUN set -eux \
&& echo "Configuring ImageMagick" \ && echo "Configuring ImageMagick" \
&& mv paperless-policy.xml /etc/ImageMagick-6/policy.xml && mv imagemagick-policy.xml /etc/ImageMagick-6/policy.xml
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
# Packages needed only for building a few quick Python # Packages needed only for building a few quick Python
# dependencies # dependencies
@ -139,17 +140,18 @@ ARG BUILD_PACKAGES="\
libpq-dev \ libpq-dev \
# https://github.com/PyMySQL/mysqlclient#linux # https://github.com/PyMySQL/mysqlclient#linux
default-libmysqlclient-dev \ default-libmysqlclient-dev \
pkg-config" pkg-config \
pre-commit"
# hadolint ignore=DL3042 # hadolint ignore=DL3042
RUN --mount=type=cache,target=/root/.cache/uv,id=pip-cache \ RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
set -eux \ set -eux \
&& echo "Installing build system packages" \ && echo "Installing build system packages" \
&& apt-get update \ && apt-get update \
&& apt-get install --yes --quiet ${BUILD_PACKAGES} && apt-get install --yes --quiet ${BUILD_PACKAGES}
RUN set -eux \ RUN set -eux \
&& npm update -g pnpm && npm update npm -g
# add users, setup scripts # add users, setup scripts
# Mount the compiled frontend to expected location # Mount the compiled frontend to expected location
@ -167,6 +169,9 @@ RUN set -eux \
&& mkdir --parents --verbose /usr/src/paperless/paperless-ngx/.venv \ && mkdir --parents --verbose /usr/src/paperless/paperless-ngx/.venv \
&& echo "Adjusting all permissions" \ && echo "Adjusting all permissions" \
&& chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless && chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless
# && echo "Collecting static files" \
# && gosu paperless python3 manage.py collectstatic --clear --no-input --link \
# && gosu paperless python3 manage.py compilemessages
VOLUME ["/usr/src/paperless/paperless-ngx/data", \ VOLUME ["/usr/src/paperless/paperless-ngx/data", \
"/usr/src/paperless/paperless-ngx/media", \ "/usr/src/paperless/paperless-ngx/media", \

View File

@ -1,117 +0,0 @@
# Paperless-ngx Development Environment
## Overview
Welcome to the Paperless-ngx development environment! This setup uses VSCode DevContainers to provide a consistent and seamless development experience.
### What are DevContainers?
DevContainers are a feature in VSCode that allows you to develop within a Docker container. This ensures that your development environment is consistent across different machines and setups. By defining a containerized environment, you can eliminate the "works on my machine" problem.
### Advantages of DevContainers
- **Consistency**: Same environment for all developers.
- **Isolation**: Separate development environment from your local machine.
- **Reproducibility**: Easily recreate the environment on any machine.
- **Pre-configured Tools**: Include all necessary tools and dependencies in the container.
## DevContainer Setup
The DevContainer configuration provides up all the necessary services for Paperless-ngx, including:
- Redis
- Gotenberg
- Tika
Data is stored using Docker volumes to ensure persistence across container restarts.
## Configuration Files
The setup includes debugging configurations (`launch.json`) and tasks (`tasks.json`) to help you manage and debug various parts of the project:
- **Backend Debugging:**
- `manage.py runserver`
- `manage.py document-consumer`
- `celery`
- **Maintenance Tasks:**
- Create superuser
- Run migrations
- Recreate virtual environment (`.venv` with `uv`)
- Compile frontend assets
## Getting Started
### Step 1: Running the DevContainer
To start the DevContainer:
1. Open VSCode.
2. Open the project folder.
3. Open the command palette:
- **Windows/Linux**: `Ctrl+Shift+P`
- **Mac**: `Cmd+Shift+P`
4. Type and select `Dev Containers: Rebuild and Reopen in Container`.
VSCode will build and start the DevContainer environment.
### Step 2: Initial Setup
Once the DevContainer is up and running, perform the following steps:
1. **Compile Frontend Assets**:
- Open the command palette:
- **Windows/Linux**: `Ctrl+Shift+P`
- **Mac**: `Cmd+Shift+P`
- Select `Tasks: Run Task`.
- Choose `Frontend Compile`.
2. **Run Database Migrations**:
- Open the command palette:
- **Windows/Linux**: `Ctrl+Shift+P`
- **Mac**: `Cmd+Shift+P`
- Select `Tasks: Run Task`.
- Choose `Migrate Database`.
3. **Create Superuser**:
- Open the command palette:
- **Windows/Linux**: `Ctrl+Shift+P`
- **Mac**: `Cmd+Shift+P`
- Select `Tasks: Run Task`.
- Choose `Create Superuser`.
### Debugging and Running Services
You can start and debug backend services either as debugging sessions via `launch.json` or as tasks.
#### Using `launch.json`
1. Press `F5` or go to the **Run and Debug** view in VSCode.
2. Select the desired configuration:
- `Runserver`
- `Document Consumer`
- `Celery`
#### Using Tasks
1. Open the command palette:
- **Windows/Linux**: `Ctrl+Shift+P`
- **Mac**: `Cmd+Shift+P`
2. Select `Tasks: Run Task`.
3. Choose the desired task:
- `Runserver`
- `Document Consumer`
- `Celery`
### Additional Maintenance Tasks
Additional tasks are available for common maintenance operations:
- **Recreate .venv**: For setting up the virtual environment using `uv`.
- **Migrate Database**: To apply database migrations.
- **Create Superuser**: To create an admin user for the application.
## Let's Get Started!
Follow the steps above to get your development environment up and running. Happy coding!

View File

@ -3,7 +3,7 @@
"dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml", "dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml",
"service": "paperless-development", "service": "paperless-development",
"workspaceFolder": "/usr/src/paperless/paperless-ngx", "workspaceFolder": "/usr/src/paperless/paperless-ngx",
"postCreateCommand": "/bin/bash -c 'uv sync --group dev && uv run pre-commit install'", "postCreateCommand": "pipenv install --dev && pipenv run pre-commit install",
"customizations": { "customizations": {
"vscode": { "vscode": {
"extensions": [ "extensions": [

View File

@ -43,7 +43,7 @@ services:
volumes: volumes:
- ..:/usr/src/paperless/paperless-ngx:delegated - ..:/usr/src/paperless/paperless-ngx:delegated
- ../.devcontainer/vscode:/usr/src/paperless/paperless-ngx/.vscode:delegated # VSCode config files - ../.devcontainer/vscode:/usr/src/paperless/paperless-ngx/.vscode:delegated # VSCode config files
- virtualenv:/usr/src/paperless/paperless-ngx/.venv # Virtual environment persisted in volume - pipenv:/usr/src/paperless/paperless-ngx/.venv
- /usr/src/paperless/paperless-ngx/src/documents/static/frontend # Static frontend files exist only in container - /usr/src/paperless/paperless-ngx/src/documents/static/frontend # Static frontend files exist only in container
- /usr/src/paperless/paperless-ngx/src/.pytest_cache - /usr/src/paperless/paperless-ngx/src/.pytest_cache
- /usr/src/paperless/paperless-ngx/.ruff_cache - /usr/src/paperless/paperless-ngx/.ruff_cache
@ -65,7 +65,7 @@ services:
command: /bin/sh -c "chown -R paperless:paperless /usr/src/paperless/paperless-ngx/src/documents/static/frontend && chown -R paperless:paperless /usr/src/paperless/paperless-ngx/.ruff_cache && while sleep 1000; do :; done" command: /bin/sh -c "chown -R paperless:paperless /usr/src/paperless/paperless-ngx/src/documents/static/frontend && chown -R paperless:paperless /usr/src/paperless/paperless-ngx/.ruff_cache && while sleep 1000; do :; done"
gotenberg: gotenberg:
image: docker.io/gotenberg/gotenberg:8.17 image: docker.io/gotenberg/gotenberg:7.10
restart: unless-stopped restart: unless-stopped
# The Gotenberg Chromium route is used to convert .eml files. We do not # The Gotenberg Chromium route is used to convert .eml files. We do not
@ -80,7 +80,4 @@ services:
restart: unless-stopped restart: unless-stopped
volumes: volumes:
data: pipenv:
media:
redisdata:
virtualenv:

View File

@ -5,7 +5,7 @@
"label": "Start: Celery Worker", "label": "Start: Celery Worker",
"description": "Start the Celery Worker which processes background and consume tasks", "description": "Start the Celery Worker which processes background and consume tasks",
"type": "shell", "type": "shell",
"command": "uv run celery --app paperless worker -l DEBUG", "command": "pipenv run celery --app paperless worker -l DEBUG",
"isBackground": true, "isBackground": true,
"options": { "options": {
"cwd": "${workspaceFolder}/src" "cwd": "${workspaceFolder}/src"
@ -33,7 +33,7 @@
"label": "Start: Frontend Angular", "label": "Start: Frontend Angular",
"description": "Start the Frontend Angular Dev Server", "description": "Start the Frontend Angular Dev Server",
"type": "shell", "type": "shell",
"command": "pnpm start", "command": "npm start",
"isBackground": true, "isBackground": true,
"options": { "options": {
"cwd": "${workspaceFolder}/src-ui" "cwd": "${workspaceFolder}/src-ui"
@ -61,7 +61,7 @@
"label": "Start: Consumer Service (manage.py document_consumer)", "label": "Start: Consumer Service (manage.py document_consumer)",
"description": "Start the Consumer Service which processes files from a directory", "description": "Start the Consumer Service which processes files from a directory",
"type": "shell", "type": "shell",
"command": "uv run python manage.py document_consumer", "command": "pipenv run python manage.py document_consumer",
"group": "build", "group": "build",
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -80,7 +80,7 @@
"label": "Start: Backend Server (manage.py runserver)", "label": "Start: Backend Server (manage.py runserver)",
"description": "Start the Backend Server which serves the Django API and the compiled Angular frontend", "description": "Start the Backend Server which serves the Django API and the compiled Angular frontend",
"type": "shell", "type": "shell",
"command": "uv run python manage.py runserver", "command": "pipenv run python manage.py runserver",
"group": "build", "group": "build",
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -99,7 +99,7 @@
"label": "Maintenance: manage.py migrate", "label": "Maintenance: manage.py migrate",
"description": "Apply database migrations", "description": "Apply database migrations",
"type": "shell", "type": "shell",
"command": "uv run python manage.py migrate", "command": "pipenv run python manage.py migrate",
"group": "none", "group": "none",
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -118,7 +118,7 @@
"label": "Maintenance: Build Documentation", "label": "Maintenance: Build Documentation",
"description": "Build the documentation with MkDocs", "description": "Build the documentation with MkDocs",
"type": "shell", "type": "shell",
"command": "uv run mkdocs build --config-file mkdocs.yml && uv run mkdocs serve", "command": "pipenv run mkdocs build --config-file mkdocs.yml && pipenv run mkdocs serve",
"group": "none", "group": "none",
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -137,7 +137,7 @@
"label": "Maintenance: manage.py createsuperuser", "label": "Maintenance: manage.py createsuperuser",
"description": "Create a superuser", "description": "Create a superuser",
"type": "shell", "type": "shell",
"command": "uv run python manage.py createsuperuser", "command": "pipenv run python manage.py createsuperuser",
"group": "none", "group": "none",
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -156,7 +156,7 @@
"label": "Maintenance: recreate .venv", "label": "Maintenance: recreate .venv",
"description": "Recreate the python virtual environment and install python dependencies", "description": "Recreate the python virtual environment and install python dependencies",
"type": "shell", "type": "shell",
"command": "rm -R -v .venv/* || uv install --dev", "command": "rm -R -v .venv/* || pipenv install --dev",
"group": "none", "group": "none",
"presentation": { "presentation": {
"echo": true, "echo": true,
@ -173,8 +173,8 @@
}, },
{ {
"label": "Maintenance: Install Frontend Dependencies", "label": "Maintenance: Install Frontend Dependencies",
"description": "Install frontend (pnpm) dependencies", "description": "Install frontend (npm) dependencies",
"type": "pnpm", "type": "npm",
"script": "install", "script": "install",
"path": "src-ui", "path": "src-ui",
"group": "clean", "group": "clean",
@ -185,7 +185,7 @@
"description": "Clean install frontend dependencies and build the frontend for production", "description": "Clean install frontend dependencies and build the frontend for production",
"label": "Maintenance: Compile frontend for production", "label": "Maintenance: Compile frontend for production",
"type": "shell", "type": "shell",
"command": "pnpm install && ./node_modules/.bin/ng build --configuration production", "command": "npm ci && ./node_modules/.bin/ng build --configuration production",
"group": "none", "group": "none",
"presentation": { "presentation": {
"echo": true, "echo": true,

View File

@ -26,5 +26,3 @@
./dist ./dist
./scripts ./scripts
./resources ./resources
# Other stuff
**/*.drawio.png

View File

@ -27,6 +27,9 @@ indent_style = space
[*.md] [*.md]
indent_style = space indent_style = space
[Pipfile.lock]
indent_style = space
# Tests don't get a line width restriction. It's still a good idea to follow # Tests don't get a line width restriction. It's still a good idea to follow
# the 79 character rule, but in the interests of clarity, tests often need to # the 79 character rule, but in the interests of clarity, tests often need to
# violate it. # violate it.

1
.github/FUNDING.yml vendored
View File

@ -1 +0,0 @@
github: [shamoon, stumpylog]

View File

@ -1,15 +1,12 @@
# Please see the documentation for all configuration options: # https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates#package-ecosystem
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2 version: 2
# Required for uv support for now
enable-beta-ecosystems: true
updates: updates:
# Enable version updates for pnpm # Enable version updates for npm
- package-ecosystem: "npm" - package-ecosystem: "npm"
target-branch: "dev" target-branch: "dev"
# Look for `pnpm-lock.yaml` file in the `/src-ui` directory # Look for `package.json` and `lock` files in the `/src-ui` directory
directory: "/src-ui" directory: "/src-ui"
open-pull-requests-limit: 10 open-pull-requests-limit: 10
schedule: schedule:
@ -37,8 +34,9 @@ updates:
- "eslint" - "eslint"
# Enable version updates for Python # Enable version updates for Python
- package-ecosystem: "uv" - package-ecosystem: "pip"
target-branch: "dev" target-branch: "dev"
# Look for a `Pipfile` in the `root` directory
directory: "/" directory: "/"
# Check for updates once a week # Check for updates once a week
schedule: schedule:
@ -49,13 +47,14 @@ updates:
# Add reviewers # Add reviewers
reviewers: reviewers:
- "paperless-ngx/backend" - "paperless-ngx/backend"
ignore:
- dependency-name: "uvicorn"
groups: groups:
development: development:
patterns: patterns:
- "*pytest*" - "*pytest*"
- "ruff" - "ruff"
- "mkdocs-material" - "mkdocs-material"
- "pre-commit*"
django: django:
patterns: patterns:
- "*django*" - "*django*"
@ -66,10 +65,6 @@ updates:
update-types: update-types:
- "minor" - "minor"
- "patch" - "patch"
pre-built:
patterns:
- psycopg*
- zxing-cpp
# Enable updates for GitHub Actions # Enable updates for GitHub Actions
- package-ecosystem: "github-actions" - package-ecosystem: "github-actions"
@ -90,50 +85,3 @@ updates:
- "major" - "major"
- "minor" - "minor"
- "patch" - "patch"
# Update Dockerfile in root directory
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
reviewers:
- "paperless-ngx/ci-cd"
labels:
- "ci-cd"
- "dependencies"
commit-message:
prefix: "docker"
include: "scope"
# Update Docker Compose files in docker/compose directory
- package-ecosystem: "docker-compose"
directory: "/docker/compose/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
reviewers:
- "paperless-ngx/ci-cd"
labels:
- "ci-cd"
- "dependencies"
commit-message:
prefix: "docker-compose"
include: "scope"
groups:
# Individual groups for each image
gotenberg:
patterns:
- "docker.io/gotenberg/gotenberg*"
tika:
patterns:
- "docker.io/apache/tika*"
redis:
patterns:
- "docker.io/library/redis*"
mariadb:
patterns:
- "docker.io/library/mariadb*"
postgres:
patterns:
- "docker.io/library/postgres*"

View File

@ -14,7 +14,9 @@ on:
- 'translations**' - 'translations**'
env: env:
DEFAULT_UV_VERSION: "0.6.x" # This is the version of pipenv all the steps will use
# If changing this, change Dockerfile
DEFAULT_PIP_ENV_VERSION: "2024.4.0"
# This is the default version of Python to use in most steps which aren't specific # This is the default version of Python to use in most steps which aren't specific
DEFAULT_PYTHON_VERSION: "3.11" DEFAULT_PYTHON_VERSION: "3.11"
@ -57,25 +59,24 @@ jobs:
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }} python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
cache: "pipenv"
cache-dependency-path: 'Pipfile.lock'
- -
name: Install uv name: Install pipenv
uses: astral-sh/setup-uv@v5
with:
version: ${{ env.DEFAULT_UV_VERSION }}
enable-cache: true
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
-
name: Install Python dependencies
run: | run: |
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen pip install --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }}
-
name: Install dependencies
run: |
pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
-
name: List installed Python dependencies
run: |
pipenv --python ${{ steps.setup-python.outputs.python-version }} run pip list
- -
name: Make documentation name: Make documentation
run: | run: |
uv run \ pipenv --python ${{ steps.setup-python.outputs.python-version }} run mkdocs build --config-file ./mkdocs.yml
--python ${{ steps.setup-python.outputs.python-version }} \
--dev \
--frozen \
mkdocs build --config-file ./mkdocs.yml
- -
name: Deploy documentation name: Deploy documentation
if: github.event_name == 'push' && github.ref == 'refs/heads/main' if: github.event_name == 'push' && github.ref == 'refs/heads/main'
@ -83,11 +84,7 @@ jobs:
echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME" echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
git config --global user.name "${{ github.actor }}" git config --global user.name "${{ github.actor }}"
git config --global user.email "${{ github.actor }}@users.noreply.github.com" git config --global user.email "${{ github.actor }}@users.noreply.github.com"
uv run \ pipenv --python ${{ steps.setup-python.outputs.python-version }} run mkdocs gh-deploy --force --no-history
--python ${{ steps.setup-python.outputs.python-version }} \
--dev \
--frozen \
mkdocs gh-deploy --force --no-history
- -
name: Upload artifact name: Upload artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
@ -120,13 +117,12 @@ jobs:
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: "${{ matrix.python-version }}" python-version: "${{ matrix.python-version }}"
cache: "pipenv"
cache-dependency-path: 'Pipfile.lock'
- -
name: Install uv name: Install pipenv
uses: astral-sh/setup-uv@v5 run: |
with: pip install --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }}
version: ${{ env.DEFAULT_UV_VERSION }}
enable-cache: true
python-version: ${{ steps.setup-python.outputs.python-version }}
- -
name: Install system dependencies name: Install system dependencies
run: | run: |
@ -135,18 +131,16 @@ jobs:
- -
name: Configure ImageMagick name: Configure ImageMagick
run: | run: |
sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml sudo cp docker/imagemagick-policy.xml /etc/ImageMagick-6/policy.xml
- -
name: Install Python dependencies name: Install Python dependencies
run: | run: |
uv sync \ pipenv --python ${{ steps.setup-python.outputs.python-version }} run python --version
--python ${{ steps.setup-python.outputs.python-version }} \ pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
--group testing \
--frozen
- -
name: List installed Python dependencies name: List installed Python dependencies
run: | run: |
uv pip list pipenv --python ${{ steps.setup-python.outputs.python-version }} run pip list
- -
name: Tests name: Tests
env: env:
@ -156,26 +150,17 @@ jobs:
PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }} PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }} PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
run: | run: |
uv run \ cd src/
--python ${{ steps.setup-python.outputs.python-version }} \ pipenv --python ${{ steps.setup-python.outputs.python-version }} run pytest -ra
--dev \
--frozen \
pytest
- -
name: Upload backend test results to Codecov name: Upload coverage
if: always() if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
uses: codecov/test-results-action@v1 uses: actions/upload-artifact@v4
with: with:
token: ${{ secrets.CODECOV_TOKEN }} name: backend-coverage-report
flags: backend-python-${{ matrix.python-version }} path: src/coverage.xml
files: junit.xml retention-days: 7
- if-no-files-found: warn
name: Upload backend coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: backend-python-${{ matrix.python-version }}
files: coverage.xml
- -
name: Stop containers name: Stop containers
if: always() if: always()
@ -183,46 +168,42 @@ jobs:
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml logs
docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml down
install-frontend-dependencies: install-frontend-depedendencies:
name: "Install Frontend Dependencies" name: "Install Frontend Dependencies"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- pre-commit - pre-commit
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- -
name: Use Node.js 20 name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 20.x node-version: 20.x
cache: 'pnpm' cache: 'npm'
cache-dependency-path: 'src-ui/pnpm-lock.yaml' cache-dependency-path: 'src-ui/package-lock.json'
- name: Cache frontend dependencies - name: Cache frontend dependencies
id: cache-frontend-deps id: cache-frontend-deps
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: | path: |
~/.pnpm-store ~/.npm
~/.cache ~/.cache
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }} key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }}
- -
name: Install dependencies name: Install dependencies
if: steps.cache-frontend-deps.outputs.cache-hit != 'true' if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
run: cd src-ui && pnpm install run: cd src-ui && npm ci
- -
name: Install Playwright name: Install Playwright
if: steps.cache-frontend-deps.outputs.cache-hit != 'true' if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
run: cd src-ui && pnpm playwright install --with-deps run: cd src-ui && npx playwright install --with-deps
tests-frontend: tests-frontend:
name: "Frontend Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})" name: "Frontend Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})"
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
needs: needs:
- install-frontend-dependencies - install-frontend-depedendencies
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -231,88 +212,124 @@ jobs:
shard-count: [4] shard-count: [4]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- -
name: Use Node.js 20 name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 20.x node-version: 20.x
cache: 'pnpm' cache: 'npm'
cache-dependency-path: 'src-ui/pnpm-lock.yaml' cache-dependency-path: 'src-ui/package-lock.json'
- name: Cache frontend dependencies - name: Cache frontend dependencies
id: cache-frontend-deps id: cache-frontend-deps
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: | path: |
~/.pnpm-store ~/.npm
~/.cache ~/.cache
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }} key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }}
- name: Re-link Angular cli - name: Re-link Angular cli
run: cd src-ui && pnpm link @angular/cli run: cd src-ui && npm link @angular/cli
- -
name: Linting checks name: Linting checks
run: cd src-ui && pnpm run lint run: cd src-ui && npm run lint
- -
name: Run Jest unit tests name: Run Jest unit tests
run: cd src-ui && pnpm run test --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }} run: cd src-ui && npm run test -- --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }}
-
name: Upload Jest coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: jest-coverage-report-${{ matrix.shard-index }}
path: |
src-ui/coverage/coverage-final.json
src-ui/coverage/lcov.info
src-ui/coverage/clover.xml
retention-days: 7
if-no-files-found: warn
- -
name: Run Playwright e2e tests name: Run Playwright e2e tests
run: cd src-ui && pnpm exec playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }} run: cd src-ui && npx playwright test --shard ${{ matrix.shard-index }}/${{ matrix.shard-count }}
- -
name: Upload frontend test results to Codecov name: Upload Playwright test results
uses: codecov/test-results-action@v1
if: always() if: always()
uses: actions/upload-artifact@v4
with: with:
token: ${{ secrets.CODECOV_TOKEN }} name: playwright-report-${{ matrix.shard-index }}
flags: frontend-node-${{ matrix.node-version }} path: src-ui/playwright-report
directory: src-ui/ retention-days: 7
tests-coverage-upload:
name: "Upload to Codecov"
runs-on: ubuntu-24.04
needs:
- tests-backend
- tests-frontend
steps:
-
uses: actions/checkout@v4
-
name: Download frontend jest coverage
uses: actions/download-artifact@v4
with:
path: src-ui/coverage/
pattern: jest-coverage-report-*
-
name: Download frontend playwright coverage
uses: actions/download-artifact@v4
with:
path: src-ui/coverage/
pattern: playwright-report-*
merge-multiple: true
- -
name: Upload frontend coverage to Codecov name: Upload frontend coverage to Codecov
uses: codecov/codecov-action@v5 uses: codecov/codecov-action@v5
with: with:
# not required for public repos, but intermittently fails otherwise
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
flags: frontend-node-${{ matrix.node-version }} flags: frontend
directory: src-ui/coverage/ directory: src-ui/coverage/
# dont include backend coverage files here
frontend-bundle-analysis: files: '!coverage.xml'
name: "Frontend Bundle Analysis"
runs-on: ubuntu-24.04
needs:
- tests-frontend
steps:
- uses: actions/checkout@v4
- -
name: Install pnpm name: Download backend coverage
uses: pnpm/action-setup@v4 uses: actions/download-artifact@v4
with: with:
version: 10 name: backend-coverage-report
path: src/
-
name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
# not required for public repos, but intermittently fails otherwise
token: ${{ secrets.CODECOV_TOKEN }}
# future expansion
flags: backend
directory: src/
- -
name: Use Node.js 20 name: Use Node.js 20
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: 20.x node-version: 20.x
cache: 'pnpm' cache: 'npm'
cache-dependency-path: 'src-ui/pnpm-lock.yaml' cache-dependency-path: 'src-ui/package-lock.json'
- -
name: Cache frontend dependencies name: Cache frontend dependencies
id: cache-frontend-deps id: cache-frontend-deps
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: | path: |
~/.pnpm-store ~/.npm
~/.cache ~/.cache
key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }} key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }}
- -
name: Re-link Angular cli name: Re-link Angular cli
run: cd src-ui && pnpm link @angular/cli run: cd src-ui && npm link @angular/cli
- -
name: Build frontend and upload analysis name: Build frontend and upload analysis
env: env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
run: cd src-ui && pnpm run build --configuration=production run: cd src-ui && ng build --configuration=production
build-docker-image: build-docker-image:
name: Build Docker image for ${{ github.ref_name }} name: Build Docker image for ${{ github.ref_name }}
@ -455,17 +472,16 @@ jobs:
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }} python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
cache: "pipenv"
cache-dependency-path: 'Pipfile.lock'
- -
name: Install uv name: Install pipenv + tools
uses: astral-sh/setup-uv@v5 run: |
with: pip install --upgrade --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }} setuptools wheel
version: ${{ env.DEFAULT_UV_VERSION }}
enable-cache: true
python-version: ${{ steps.setup-python.outputs.python-version }}
- -
name: Install Python dependencies name: Install Python dependencies
run: | run: |
uv sync --python ${{ steps.setup-python.outputs.python-version }} --dev --frozen pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
- -
name: Install system dependencies name: Install system dependencies
run: | run: |
@ -486,21 +502,17 @@ jobs:
- -
name: Generate requirements file name: Generate requirements file
run: | run: |
uv export --quiet --no-dev --all-extras --format requirements-txt --output-file requirements.txt pipenv --python ${{ steps.setup-python.outputs.python-version }} requirements > requirements.txt
- -
name: Compile messages name: Compile messages
run: | run: |
cd src/ cd src/
uv run \ pipenv --python ${{ steps.setup-python.outputs.python-version }} run python3 manage.py compilemessages
--python ${{ steps.setup-python.outputs.python-version }} \
manage.py compilemessages
- -
name: Collect static files name: Collect static files
run: | run: |
cd src/ cd src/
uv run \ pipenv --python ${{ steps.setup-python.outputs.python-version }} run python3 manage.py collectstatic --no-input
--python ${{ steps.setup-python.outputs.python-version }} \
manage.py collectstatic --no-input
- -
name: Move files name: Move files
run: | run: |
@ -516,12 +528,13 @@ jobs:
for file_name in .dockerignore \ for file_name in .dockerignore \
.env \ .env \
Dockerfile \ Dockerfile \
pyproject.toml \ Pipfile \
uv.lock \ Pipfile.lock \
requirements.txt \ requirements.txt \
LICENSE \ LICENSE \
README.md \ README.md \
paperless.conf.example paperless.conf.example \
gunicorn.conf.py
do do
cp --verbose ${file_name} dist/paperless-ngx/ cp --verbose ${file_name} dist/paperless-ngx/
done done
@ -618,17 +631,15 @@ jobs:
ref: main ref: main
- -
name: Set up Python name: Set up Python
id: setup-python
uses: actions/setup-python@v5 uses: actions/setup-python@v5
with: with:
python-version: ${{ env.DEFAULT_PYTHON_VERSION }} python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
cache: "pipenv"
cache-dependency-path: 'Pipfile.lock'
- -
name: Install uv name: Install pipenv + tools
uses: astral-sh/setup-uv@v5 run: |
with: pip install --upgrade --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }} setuptools wheel
version: ${{ env.DEFAULT_UV_VERSION }}
enable-cache: true
python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
- -
name: Append Changelog to docs name: Append Changelog to docs
id: append-Changelog id: append-Changelog
@ -644,10 +655,7 @@ jobs:
CURRENT_CHANGELOG=`tail --lines +2 changelog.md` CURRENT_CHANGELOG=`tail --lines +2 changelog.md`
echo -e "$CURRENT_CHANGELOG" >> changelog-new.md echo -e "$CURRENT_CHANGELOG" >> changelog-new.md
mv changelog-new.md changelog.md mv changelog-new.md changelog.md
uv run \ pipenv run pre-commit run --files changelog.md || true
--python ${{ steps.setup-python.outputs.python-version }} \
--dev \
pre-commit run --files changelog.md || true
git config --global user.name "github-actions" git config --global user.name "github-actions"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git commit -am "Changelog ${{ needs.publish-release.outputs.version }} - GHA" git commit -am "Changelog ${{ needs.publish-release.outputs.version }} - GHA"

View File

@ -33,7 +33,7 @@ jobs:
- -
name: Clean temporary images name: Clean temporary images
if: "${{ env.TOKEN != '' }}" if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/ephemeral@v0.10.0 uses: stumpylog/image-cleaner-action/ephemeral@v0.9.0
with: with:
token: "${{ env.TOKEN }}" token: "${{ env.TOKEN }}"
owner: "${{ github.repository_owner }}" owner: "${{ github.repository_owner }}"
@ -61,7 +61,7 @@ jobs:
- -
name: Clean untagged images name: Clean untagged images
if: "${{ env.TOKEN != '' }}" if: "${{ env.TOKEN != '' }}"
uses: stumpylog/image-cleaner-action/untagged@v0.10.0 uses: stumpylog/image-cleaner-action/untagged@v0.9.0
with: with:
token: "${{ env.TOKEN }}" token: "${{ env.TOKEN }}"
owner: "${{ github.repository_owner }}" owner: "${{ github.repository_owner }}"

1
.gitignore vendored
View File

@ -44,7 +44,6 @@ nosetests.xml
coverage.xml coverage.xml
*,cover *,cover
.pytest_cache .pytest_cache
junit.xml
# Translations # Translations
*.mo *.mo

View File

@ -29,10 +29,10 @@ repos:
- id: check-case-conflict - id: check-case-conflict
- id: detect-private-key - id: detect-private-key
- repo: https://github.com/codespell-project/codespell - repo: https://github.com/codespell-project/codespell
rev: v2.4.0 rev: v2.3.0
hooks: hooks:
- id: codespell - id: codespell
exclude: "(^src-ui/src/locale/)|(^src-ui/pnpm-lock.yaml)|(^src-ui/e2e/)|(^src/paperless_mail/tests/samples/)" exclude: "(^src-ui/src/locale/)|(^src-ui/e2e/)|(^src/paperless_mail/tests/samples/)"
exclude_types: exclude_types:
- pofile - pofile
- json - json
@ -45,19 +45,16 @@ repos:
- javascript - javascript
- ts - ts
- markdown - markdown
exclude: "(^Pipfile\\.lock$)"
additional_dependencies: additional_dependencies:
- prettier@3.3.3 - prettier@3.3.3
- 'prettier-plugin-organize-imports@4.1.0' - 'prettier-plugin-organize-imports@4.1.0'
# Python hooks # Python hooks
- repo: https://github.com/astral-sh/ruff-pre-commit - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.9 rev: v0.8.6
hooks: hooks:
- id: ruff - id: ruff
- id: ruff-format - id: ruff-format
- repo: https://github.com/tox-dev/pyproject-fmt
rev: "v2.5.1"
hooks:
- id: pyproject-fmt
# Dockerfile hooks # Dockerfile hooks
- repo: https://github.com/AleksaC/hadolint-py - repo: https://github.com/AleksaC/hadolint-py
rev: v2.12.0.3 rev: v2.12.0.3

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.10.15

85
.ruff.toml Normal file
View File

@ -0,0 +1,85 @@
fix = true
line-length = 88
respect-gitignore = true
src = ["src"]
target-version = "py310"
output-format = "grouped"
show-fixes = true
# https://docs.astral.sh/ruff/settings/
# https://docs.astral.sh/ruff/rules/
[lint]
extend-select = [
"W", # https://docs.astral.sh/ruff/rules/#pycodestyle-e-w
"I", # https://docs.astral.sh/ruff/rules/#isort-i
"UP", # https://docs.astral.sh/ruff/rules/#pyupgrade-up
"COM", # https://docs.astral.sh/ruff/rules/#flake8-commas-com
"DJ", # https://docs.astral.sh/ruff/rules/#flake8-django-dj
"EXE", # https://docs.astral.sh/ruff/rules/#flake8-executable-exe
"ISC", # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc
"ICN", # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn
"G201", # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g
"INP", # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp
"PIE", # https://docs.astral.sh/ruff/rules/#flake8-pie-pie
"Q", # https://docs.astral.sh/ruff/rules/#flake8-quotes-q
"RSE", # https://docs.astral.sh/ruff/rules/#flake8-raise-rse
"T20", # https://docs.astral.sh/ruff/rules/#flake8-print-t20
"SIM", # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim
"TID", # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid
"TCH", # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tch
"PLC", # https://docs.astral.sh/ruff/rules/#pylint-pl
"PLE", # https://docs.astral.sh/ruff/rules/#pylint-pl
"RUF", # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf
"FLY", # https://docs.astral.sh/ruff/rules/#flynt-fly
"PTH", # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth
]
ignore = ["DJ001", "SIM105", "RUF012"]
[lint.per-file-ignores]
".github/scripts/*.py" = ["E501", "INP001", "SIM117"]
"docker/wait-for-redis.py" = ["INP001", "T201"]
"src/documents/consumer.py" = ["PTH"] # TODO Enable & remove
"src/documents/file_handling.py" = ["PTH"] # TODO Enable & remove
"src/documents/management/commands/document_consumer.py" = ["PTH"] # TODO Enable & remove
"src/documents/management/commands/document_exporter.py" = ["PTH"] # TODO Enable & remove
"src/documents/migrations/0012_auto_20160305_0040.py" = ["PTH"] # TODO Enable & remove
"src/documents/migrations/0014_document_checksum.py" = ["PTH"] # TODO Enable & remove
"src/documents/migrations/1003_mime_types.py" = ["PTH"] # TODO Enable & remove
"src/documents/migrations/1012_fix_archive_files.py" = ["PTH"] # TODO Enable & remove
"src/documents/models.py" = ["SIM115", "PTH"] # TODO PTH Enable & remove
"src/documents/parsers.py" = ["PTH"] # TODO Enable & remove
"src/documents/signals/handlers.py" = ["PTH"] # TODO Enable & remove
"src/documents/tasks.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_api_app_config.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_api_bulk_download.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_api_documents.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_classifier.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_consumer.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_file_handling.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_management.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_management_consumer.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_management_exporter.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_management_thumbnails.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_migration_archive_files.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_migration_document_pages_count.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_migration_mime_type.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_sanity_check.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_tasks.py" = ["PTH"] # TODO Enable & remove
"src/documents/tests/test_views.py" = ["PTH"] # TODO Enable & remove
"src/documents/views.py" = ["PTH"] # TODO Enable & remove
"src/paperless/checks.py" = ["PTH"] # TODO Enable & remove
"src/paperless/settings.py" = ["PTH"] # TODO Enable & remove
"src/paperless/tests/test_checks.py" = ["PTH"] # TODO Enable & remove
"src/paperless/urls.py" = ["PTH"] # TODO Enable & remove
"src/paperless/views.py" = ["PTH"] # TODO Enable & remove
"src/paperless_mail/mail.py" = ["PTH"] # TODO Enable & remove
"src/paperless_mail/preprocessor.py" = ["PTH"] # TODO Enable & remove
"src/paperless_tesseract/parsers.py" = ["PTH"] # TODO Enable & remove
"src/paperless_tesseract/tests/test_parser.py" = ["RUF001", "PTH"] # TODO PTH Enable & remove
"src/paperless_tika/tests/test_live_tika.py" = ["PTH"] # TODO Enable & remove
"src/paperless_tika/tests/test_tika_parser.py" = ["PTH"] # TODO Enable & remove
"*/tests/*.py" = ["E501", "SIM117"]
"*/migrations/*.py" = ["E501", "SIM", "T201"]
[lint.isort]
force-single-line = true

View File

@ -5,6 +5,5 @@
/src-ui/ @paperless-ngx/frontend /src-ui/ @paperless-ngx/frontend
/src/ @paperless-ngx/backend /src/ @paperless-ngx/backend
pyproject.toml @paperless-ngx/backend Pipfile* @paperless-ngx/backend
uv.lock @paperless-ngx/backend
*.py @paperless-ngx/backend *.py @paperless-ngx/backend

View File

@ -81,7 +81,7 @@ Some notes about translation:
If a language has already been added, and you would like to contribute new translations or change existing translations, please read the "Translation" section in the README.md file for further details on that. If a language has already been added, and you would like to contribute new translations or change existing translations, please read the "Translation" section in the README.md file for further details on that.
If you would like the project to be translated to another language, first head over to https://crowdin.com/project/paperless-ngx to check if that language has already been enabled for translation. If you would like the project to be translated to another language, first head over to https://crwd.in/paperless-ngx to check if that language has already been enabled for translation.
If not, please request the language to be added by creating an issue on GitHub. The issue should contain: If not, please request the language to be added by creating an issue on GitHub. The issue should contain:
- English name of the language (the localized name can be added on Crowdin). - English name of the language (the localized name can be added on Crowdin).

View File

@ -4,17 +4,15 @@
# Stage: compile-frontend # Stage: compile-frontend
# Purpose: Compiles the frontend # Purpose: Compiles the frontend
# Notes: # Notes:
# - Does PNPM stuff with Typescript and such # - Does NPM stuff with Typescript and such
FROM --platform=$BUILDPLATFORM docker.io/node:20-bookworm-slim AS compile-frontend FROM --platform=$BUILDPLATFORM docker.io/node:20-bookworm-slim AS compile-frontend
COPY ./src-ui /src/src-ui COPY ./src-ui /src/src-ui
WORKDIR /src/src-ui WORKDIR /src/src-ui
RUN set -eux \ RUN set -eux \
&& npm update -g pnpm \ && npm update npm -g \
&& npm install -g corepack@latest \ && npm ci
&& corepack enable \
&& pnpm install
ARG PNGX_TAG_VERSION= ARG PNGX_TAG_VERSION=
# Add the tag to the environment file if its a tagged dev build # Add the tag to the environment file if its a tagged dev build
@ -28,66 +26,28 @@ esac
RUN set -eux \ RUN set -eux \
&& ./node_modules/.bin/ng build --configuration production && ./node_modules/.bin/ng build --configuration production
# Stage: s6-overlay-base # Stage: pipenv-base
# Purpose: Installs s6-overlay and rootfs # Purpose: Generates a requirements.txt file for building
# Comments: # Comments:
# - Don't leave anything extra in here either # - pipenv dependencies are not left in the final image
FROM ghcr.io/astral-sh/uv:0.6.13-python3.12-bookworm-slim AS s6-overlay-base # - pipenv can't touch the final image somehow
FROM --platform=$BUILDPLATFORM docker.io/python:3.12-alpine AS pipenv-base
WORKDIR /usr/src/s6 WORKDIR /usr/src/pipenv
# https://github.com/just-containers/s6-overlay#customizing-s6-overlay-behaviour COPY Pipfile* ./
ENV \
S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \
S6_VERBOSITY=1 \
PATH=/command:$PATH
# Buildx provided, must be defined to use though
ARG TARGETARCH
ARG TARGETVARIANT
# Lock this version
ARG S6_OVERLAY_VERSION=3.2.0.2
ARG S6_BUILD_TIME_PKGS="curl \
xz-utils"
RUN set -eux \ RUN set -eux \
&& echo "Installing build time packages" \ && echo "Installing pipenv" \
&& apt-get update \ && python3 -m pip install --no-cache-dir --upgrade pipenv==2024.4.0 \
&& apt-get install --yes --quiet --no-install-recommends ${S6_BUILD_TIME_PKGS} \ && echo "Generating requirement.txt" \
&& echo "Determining arch" \ && pipenv requirements > requirements.txt
&& S6_ARCH="" \
&& if [ "${TARGETARCH}${TARGETVARIANT}" = "amd64" ]; then S6_ARCH="x86_64"; \
elif [ "${TARGETARCH}${TARGETVARIANT}" = "arm64" ]; then S6_ARCH="aarch64"; fi\
&& if [ -z "${S6_ARCH}" ]; then { echo "Error: Not able to determine arch"; exit 1; }; fi \
&& echo "Installing s6-overlay for ${S6_ARCH}" \
&& curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \
"https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz" \
"https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz.sha256" \
"https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz" \
"https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz.sha256" \
&& echo "Validating s6-archive checksums" \
&& sha256sum --check ./*.sha256 \
&& echo "Unpacking archives" \
&& tar --directory / -Jxpf s6-overlay-noarch.tar.xz \
&& tar --directory / -Jxpf s6-overlay-${S6_ARCH}.tar.xz \
&& echo "Removing downloaded archives" \
&& rm ./*.tar.xz \
&& rm ./*.sha256 \
&& echo "Cleaning up image" \
&& apt-get --yes purge ${S6_BUILD_TIME_PKGS} \
&& apt-get --yes autoremove --purge \
&& rm -rf /var/lib/apt/lists/*
# Copy our service defs and filesystem
COPY ./docker/rootfs /
# Stage: main-app # Stage: main-app
# Purpose: The final image # Purpose: The final image
# Comments: # Comments:
# - Don't leave anything extra in here # - Don't leave anything extra in here
FROM s6-overlay-base AS main-app FROM docker.io/python:3.12-slim-bookworm AS main-app
LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>" LABEL org.opencontainers.image.authors="paperless-ngx team <hello@paperless-ngx.com>"
LABEL org.opencontainers.image.documentation="https://docs.paperless-ngx.com/" LABEL org.opencontainers.image.documentation="https://docs.paperless-ngx.com/"
@ -101,19 +61,16 @@ ARG DEBIAN_FRONTEND=noninteractive
ARG TARGETARCH ARG TARGETARCH
# Can be workflow provided, defaults set for manual building # Can be workflow provided, defaults set for manual building
ARG JBIG2ENC_VERSION=0.30 ARG JBIG2ENC_VERSION=0.29
ARG QPDF_VERSION=11.9.0 ARG QPDF_VERSION=11.9.0
ARG GS_VERSION=10.03.1 ARG GS_VERSION=10.03.1
# Set Python environment variables # Set Python environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \ ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \ PYTHONUNBUFFERED=1 \
# Ignore warning from Whitenoise about async iterators # Ignore warning from Whitenoise
PYTHONWARNINGS="ignore:::django.http.response:517" \ PYTHONWARNINGS="ignore:::django.http.response:517" \
PNGX_CONTAINERIZED=1 \ PNGX_CONTAINERIZED=1
# https://docs.astral.sh/uv/reference/settings/#link-mode
UV_LINK_MODE=copy \
UV_CACHE_DIR=/cache/uv/
# #
# Begin installation and configuration # Begin installation and configuration
@ -170,51 +127,118 @@ RUN set -eux \
&& apt-get update \ && apt-get update \
&& apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES} \ && apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES} \
&& echo "Installing pre-built updates" \ && echo "Installing pre-built updates" \
&& curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \
https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
https://github.com/paperless-ngx/builder/releases/download/jbig2enc-${JBIG2ENC_VERSION}/jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
&& echo "Installing qpdf ${QPDF_VERSION}" \ && echo "Installing qpdf ${QPDF_VERSION}" \
&& curl --fail --silent --show-error --location \
--output libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
&& curl --fail --silent --show-error --location \
--output qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/qpdf-${QPDF_VERSION}/qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
&& dpkg --install ./libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \ && dpkg --install ./libqpdf29_${QPDF_VERSION}-1_${TARGETARCH}.deb \
&& dpkg --install ./qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \ && dpkg --install ./qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
&& echo "Installing Ghostscript ${GS_VERSION}" \ && echo "Installing Ghostscript ${GS_VERSION}" \
&& curl --fail --silent --show-error --location \
--output libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& curl --fail --silent --show-error --location \
--output ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& curl --fail --silent --show-error --location \
--output libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
https://github.com/paperless-ngx/builder/releases/download/ghostscript-${GS_VERSION}/libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
&& dpkg --install ./libgs10-common_${GS_VERSION}.dfsg-1_all.deb \ && dpkg --install ./libgs10-common_${GS_VERSION}.dfsg-1_all.deb \
&& dpkg --install ./libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ && dpkg --install ./libgs10_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& dpkg --install ./ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ && dpkg --install ./ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
&& echo "Installing jbig2enc" \ && echo "Installing jbig2enc" \
&& curl --fail --silent --show-error --location \
--output jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
https://github.com/paperless-ngx/builder/releases/download/jbig2enc-${JBIG2ENC_VERSION}/jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
&& dpkg --install ./jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \ && dpkg --install ./jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \
&& echo "Configuring imagemagick" \
&& cp /etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml \
&& echo "Cleaning up image layer" \ && echo "Cleaning up image layer" \
&& rm --force --verbose *.deb \ && rm --force --verbose *.deb \
&& rm --recursive --force --verbose /var/lib/apt/lists/* && rm --recursive --force --verbose /var/lib/apt/lists/* \
&& echo "Installing supervisor" \
&& python3 -m pip install --default-timeout=1000 --upgrade --no-cache-dir supervisor==4.2.5
# Copy gunicorn config
# Changes very infrequently
WORKDIR /usr/src/paperless/
COPY gunicorn.conf.py .
# setup docker-specific things
# These change sometimes, but rarely
WORKDIR /usr/src/paperless/src/docker/
COPY [ \
"docker/imagemagick-policy.xml", \
"docker/supervisord.conf", \
"docker/docker-entrypoint.sh", \
"docker/docker-prepare.sh", \
"docker/paperless_cmd.sh", \
"docker/wait-for-redis.py", \
"docker/env-from-file.sh", \
"docker/management_script.sh", \
"docker/flower-conditional.sh", \
"docker/install_management_commands.sh", \
"/usr/src/paperless/src/docker/" \
]
RUN set -eux \
&& echo "Configuring ImageMagick" \
&& mv imagemagick-policy.xml /etc/ImageMagick-6/policy.xml \
&& echo "Configuring supervisord" \
&& mkdir /var/log/supervisord /var/run/supervisord \
&& mv supervisord.conf /etc/supervisord.conf \
&& echo "Setting up Docker scripts" \
&& mv docker-entrypoint.sh /sbin/docker-entrypoint.sh \
&& chmod 755 /sbin/docker-entrypoint.sh \
&& mv docker-prepare.sh /sbin/docker-prepare.sh \
&& chmod 755 /sbin/docker-prepare.sh \
&& mv wait-for-redis.py /sbin/wait-for-redis.py \
&& chmod 755 /sbin/wait-for-redis.py \
&& mv env-from-file.sh /sbin/env-from-file.sh \
&& chmod 755 /sbin/env-from-file.sh \
&& mv paperless_cmd.sh /usr/local/bin/paperless_cmd.sh \
&& chmod 755 /usr/local/bin/paperless_cmd.sh \
&& mv flower-conditional.sh /usr/local/bin/flower-conditional.sh \
&& chmod 755 /usr/local/bin/flower-conditional.sh \
&& echo "Installing management commands" \
&& chmod +x install_management_commands.sh \
&& ./install_management_commands.sh
WORKDIR /usr/src/paperless/src/ WORKDIR /usr/src/paperless/src/
# Python dependencies # Python dependencies
# Change pretty frequently # Change pretty frequently
COPY --chown=1000:1000 ["pyproject.toml", "uv.lock", "/usr/src/paperless/src/"] COPY --from=pipenv-base /usr/src/pipenv/requirements.txt ./
# Packages needed only for building a few quick Python # Packages needed only for building a few quick Python
# dependencies # dependencies
ARG BUILD_PACKAGES="\ ARG BUILD_PACKAGES="\
build-essential \ build-essential \
git \
# https://www.psycopg.org/docs/install.html#prerequisites
libpq-dev \
# https://github.com/PyMySQL/mysqlclient#linux # https://github.com/PyMySQL/mysqlclient#linux
default-libmysqlclient-dev \ default-libmysqlclient-dev \
pkg-config" pkg-config"
# hadolint ignore=DL3042 # hadolint ignore=DL3042
RUN --mount=type=cache,target=${UV_CACHE_DIR},id=python-cache \ RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \
set -eux \ set -eux \
&& echo "Installing build system packages" \ && echo "Installing build system packages" \
&& apt-get update \ && apt-get update \
&& apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \ && apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
&& python3 -m pip install --no-cache-dir --upgrade wheel \
&& echo "Installing Python requirements" \ && echo "Installing Python requirements" \
&& uv export --quiet --no-dev --all-extras --format requirements-txt --output-file requirements.txt \ && curl --fail --silent --show-error --location \
&& uv pip install --system --no-python-downloads --python-preference system --requirements requirements.txt \ --output psycopg_c-3.2.3-cp312-cp312-linux_x86_64.whl \
https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.3/psycopg_c-3.2.3-cp312-cp312-linux_x86_64.whl \
&& curl --fail --silent --show-error --location \
--output psycopg_c-3.2.3-cp312-cp312-linux_aarch64.whl \
https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.3/psycopg_c-3.2.3-cp312-cp312-linux_aarch64.whl \
&& python3 -m pip install --default-timeout=1000 --find-links . --requirement requirements.txt \
&& echo "Installing NLTK data" \ && echo "Installing NLTK data" \
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \ && python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" snowball_data \
&& python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \ && python3 -W ignore::RuntimeWarning -m nltk.downloader -d "/usr/share/nltk_data" stopwords \
@ -239,7 +263,6 @@ COPY --from=compile-frontend --chown=1000:1000 /src/src/documents/static/fronten
# add users, setup scripts # add users, setup scripts
# Mount the compiled frontend to expected location # Mount the compiled frontend to expected location
RUN set -eux \ RUN set -eux \
&& sed -i '1s|^#!/usr/bin/env python3|#!/command/with-contenv python3|' manage.py \
&& echo "Setting up user/group" \ && echo "Setting up user/group" \
&& addgroup --gid 1000 paperless \ && addgroup --gid 1000 paperless \
&& useradd --uid 1000 --gid paperless --home-dir /usr/src/paperless paperless \ && useradd --uid 1000 --gid paperless --home-dir /usr/src/paperless paperless \
@ -253,16 +276,18 @@ RUN set -eux \
&& echo "Adjusting all permissions" \ && echo "Adjusting all permissions" \
&& chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless \ && chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless \
&& echo "Collecting static files" \ && echo "Collecting static files" \
&& s6-setuidgid paperless python3 manage.py collectstatic --clear --no-input --link \ && gosu paperless python3 manage.py collectstatic --clear --no-input --link \
&& s6-setuidgid paperless python3 manage.py compilemessages && gosu paperless python3 manage.py compilemessages
VOLUME ["/usr/src/paperless/data", \ VOLUME ["/usr/src/paperless/data", \
"/usr/src/paperless/media", \ "/usr/src/paperless/media", \
"/usr/src/paperless/consume", \ "/usr/src/paperless/consume", \
"/usr/src/paperless/export"] "/usr/src/paperless/export"]
ENTRYPOINT ["/init"] ENTRYPOINT ["/sbin/docker-entrypoint.sh"]
EXPOSE 8000 EXPOSE 8000
CMD ["/usr/local/bin/paperless_cmd.sh"]
HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000" ] HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000" ]

100
Pipfile Normal file
View File

@ -0,0 +1,100 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[packages]
dateparser = "~=1.2"
# WARNING: django does not use semver.
# Only patch versions are guaranteed to not introduce breaking changes.
django = "~=5.1.5"
django-allauth = {extras = ["mfa", "socialaccount"], version = "*"}
django-auditlog = "*"
django-celery-results = "*"
django-compression-middleware = "*"
django-cors-headers = "*"
django-extensions = "*"
django-filter = "~=24.3"
django-guardian = "*"
django-multiselectfield = "*"
django-soft-delete = "*"
djangorestframework = "~=3.15.2"
djangorestframework-guardian = "*"
drf-writable-nested = "*"
bleach = "*"
celery = {extras = ["redis"], version = "*"}
channels = "~=4.2"
channels-redis = "*"
concurrent-log-handler = "*"
filelock = "*"
flower = "*"
gotenberg-client = "*"
gunicorn = "*"
httpx-oauth = "*"
imap-tools = "*"
inotifyrecursive = "~=0.3"
jinja2 = "~=3.1"
langdetect = "*"
mysqlclient = "*"
nltk = "*"
ocrmypdf = "~=16.8"
pathvalidate = "*"
pdf2image = "*"
psycopg = {version = "*", extras = ["c"]}
python-dateutil = "*"
python-dotenv = "*"
python-gnupg = "*"
python-ipware = "*"
python-magic = "*"
pyzbar = "*"
rapidfuzz = "*"
redis = {extras = ["hiredis"], version = "*"}
scikit-learn = "~=1.6"
setproctitle = "*"
tika-client = "*"
tqdm = "*"
# See https://github.com/paperless-ngx/paperless-ngx/issues/5494
uvicorn = {extras = ["standard"], version = "==0.25.0"}
watchdog = "~=6.0"
whitenoise = "~=6.8"
whoosh = "~=2.7"
zxing-cpp = {version = "*", platform_machine = "== 'x86_64'"}
[dev-packages]
# Linting
pre-commit = "*"
ruff = "*"
factory-boy = "*"
# Testing
pytest = "*"
pytest-cov = "*"
pytest-django = "*"
pytest-httpx = "*"
pytest-env = "*"
pytest-sugar = "*"
pytest-xdist = "*"
pytest-mock = "*"
pytest-rerunfailures = "*"
imagehash = "*"
daphne = "*"
# Documentation
mkdocs-material = "*"
mkdocs-glightbox = "*"
[typing-dev]
mypy = "*"
types-Pillow = "*"
django-filter-stubs = "*"
types-python-dateutil = "*"
djangorestframework-stubs = {extras= ["compatible-mypy"], version="*"}
celery-types = "*"
django-stubs = {extras= ["compatible-mypy"], version="*"}
types-dateparser = "*"
types-bleach = "*"
types-redis = "*"
types-tqdm = "*"
types-Markdown = "*"
types-Pygments = "*"
types-colorama = "*"
types-setuptools = "*"

4839
Pipfile.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -83,7 +83,7 @@ People interested in continuing the work on paperless-ngx are encouraged to reac
## Translation ## Translation
Paperless-ngx is available in many languages that are coordinated on Crowdin. If you want to help out by translating paperless-ngx into your language, please head over to https://crowdin.com/project/paperless-ngx, and thank you! More details can be found in [CONTRIBUTING.md](https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md#translating-paperless-ngx). Paperless-ngx is available in many languages that are coordinated on Crowdin. If you want to help out by translating paperless-ngx into your language, please head over to https://crwd.in/paperless-ngx, and thank you! More details can be found in [CONTRIBUTING.md](https://github.com/paperless-ngx/paperless-ngx/blob/main/CONTRIBUTING.md#translating-paperless-ngx).
## Feature Requests ## Feature Requests

View File

@ -5,7 +5,7 @@
services: services:
gotenberg: gotenberg:
image: docker.io/gotenberg/gotenberg:8.19 image: docker.io/gotenberg/gotenberg:8.7
hostname: gotenberg hostname: gotenberg
container_name: gotenberg container_name: gotenberg
network_mode: host network_mode: host

View File

@ -24,8 +24,8 @@
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env' # - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder. # and '.env' into a folder.
# - Run 'docker compose pull'. # - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'. # - Run 'docker compose up -d'.
# #
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
# documentation. # documentation.
@ -77,7 +77,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998 PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg: gotenberg:
image: docker.io/gotenberg/gotenberg:8.19 image: docker.io/gotenberg/gotenberg:8.7
restart: unless-stopped restart: unless-stopped
# The gotenberg chromium route is used to convert .eml files. We do not # The gotenberg chromium route is used to convert .eml files. We do not
# want to allow external content like tracking pixels or even javascript. # want to allow external content like tracking pixels or even javascript.

View File

@ -20,6 +20,7 @@
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env' # - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder. # and '.env' into a folder.
# - Run 'docker compose pull'. # - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'. # - Run 'docker compose up -d'.
# #
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the

View File

@ -22,6 +22,10 @@
# - Upload 'docker-compose.env' by clicking on 'Load variables from .env file' # - Upload 'docker-compose.env' by clicking on 'Load variables from .env file'
# - Modify the environment variables as needed # - Modify the environment variables as needed
# - Click 'Deploy the stack' and wait for it to be deployed # - Click 'Deploy the stack' and wait for it to be deployed
# - Open the list of containers, select paperless_webserver_1
# - Click 'Console' and then 'Connect' to open the command line inside the container
# - Run 'python3 manage.py createsuperuser' to create a user
# - Exit the console
# #
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
# documentation. # documentation.
@ -34,7 +38,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/postgres:17 image: docker.io/library/postgres:16
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- pgdata:/var/lib/postgresql/data - pgdata:/var/lib/postgresql/data

View File

@ -24,6 +24,7 @@
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env' # - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder. # and '.env' into a folder.
# - Run 'docker compose pull'. # - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'. # - Run 'docker compose up -d'.
# #
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
@ -37,7 +38,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/postgres:17 image: docker.io/library/postgres:16
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- pgdata:/var/lib/postgresql/data - pgdata:/var/lib/postgresql/data
@ -70,7 +71,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998 PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg: gotenberg:
image: docker.io/gotenberg/gotenberg:8.19 image: docker.io/gotenberg/gotenberg:8.7
restart: unless-stopped restart: unless-stopped
# The gotenberg chromium route is used to convert .eml files. We do not # The gotenberg chromium route is used to convert .eml files. We do not

View File

@ -20,6 +20,7 @@
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env' # - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder. # and '.env' into a folder.
# - Run 'docker compose pull'. # - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'. # - Run 'docker compose up -d'.
# #
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
@ -33,7 +34,7 @@ services:
- redisdata:/data - redisdata:/data
db: db:
image: docker.io/library/postgres:17 image: docker.io/library/postgres:16
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- pgdata:/var/lib/postgresql/data - pgdata:/var/lib/postgresql/data

View File

@ -24,6 +24,7 @@
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env' # - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder. # and '.env' into a folder.
# - Run 'docker compose pull'. # - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'. # - Run 'docker compose up -d'.
# #
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the
@ -58,7 +59,7 @@ services:
PAPERLESS_TIKA_ENDPOINT: http://tika:9998 PAPERLESS_TIKA_ENDPOINT: http://tika:9998
gotenberg: gotenberg:
image: docker.io/gotenberg/gotenberg:8.19 image: docker.io/gotenberg/gotenberg:8.7
restart: unless-stopped restart: unless-stopped
# The gotenberg chromium route is used to convert .eml files. We do not # The gotenberg chromium route is used to convert .eml files. We do not

View File

@ -17,6 +17,7 @@
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env' # - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
# and '.env' into a folder. # and '.env' into a folder.
# - Run 'docker compose pull'. # - Run 'docker compose pull'.
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
# - Run 'docker compose up -d'. # - Run 'docker compose up -d'.
# #
# For more extensive installation and update instructions, refer to the # For more extensive installation and update instructions, refer to the

179
docker/docker-entrypoint.sh Executable file
View File

@ -0,0 +1,179 @@
#!/usr/bin/env bash
set -e
# Source: https://github.com/sameersbn/docker-gitlab/
map_uidgid() {
local -r usermap_original_uid=$(id -u paperless)
local -r usermap_original_gid=$(id -g paperless)
local -r usermap_new_uid=${USERMAP_UID:-$usermap_original_uid}
local -r usermap_new_gid=${USERMAP_GID:-${usermap_original_gid:-$usermap_new_uid}}
if [[ ${usermap_new_uid} != "${usermap_original_uid}" || ${usermap_new_gid} != "${usermap_original_gid}" ]]; then
echo "Mapping UID and GID for paperless:paperless to $usermap_new_uid:$usermap_new_gid"
usermod --non-unique --uid "${usermap_new_uid}" paperless
groupmod --non-unique --gid "${usermap_new_gid}" paperless
fi
}
map_folders() {
# Export these so they can be used in docker-prepare.sh
export DATA_DIR="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}"
export MEDIA_ROOT_DIR="${PAPERLESS_MEDIA_ROOT:-/usr/src/paperless/media}"
export CONSUME_DIR="${PAPERLESS_CONSUMPTION_DIR:-/usr/src/paperless/consume}"
}
custom_container_init() {
# Mostly borrowed from the LinuxServer.io base image
# https://github.com/linuxserver/docker-baseimage-ubuntu/tree/bionic/root/etc/cont-init.d
local -r custom_script_dir="/custom-cont-init.d"
# Tamper checking.
# Don't run files which are owned by anyone except root
# Don't run files which are writeable by others
if [ -d "${custom_script_dir}" ]; then
if [ -n "$(/usr/bin/find "${custom_script_dir}" -maxdepth 1 ! -user root)" ]; then
echo "**** Potential tampering with custom scripts detected ****"
echo "**** The folder '${custom_script_dir}' must be owned by root ****"
return 0
fi
if [ -n "$(/usr/bin/find "${custom_script_dir}" -maxdepth 1 -perm -o+w)" ]; then
echo "**** The folder '${custom_script_dir}' or some of contents have write permissions for others, which is a security risk. ****"
echo "**** Please review the permissions and their contents to make sure they are owned by root, and can only be modified by root. ****"
return 0
fi
# Make sure custom init directory has files in it
if [ -n "$(/bin/ls --almost-all "${custom_script_dir}" 2>/dev/null)" ]; then
echo "[custom-init] files found in ${custom_script_dir} executing"
# Loop over files in the directory
for SCRIPT in "${custom_script_dir}"/*; do
NAME="$(basename "${SCRIPT}")"
if [ -f "${SCRIPT}" ]; then
echo "[custom-init] ${NAME}: executing..."
/bin/bash "${SCRIPT}"
echo "[custom-init] ${NAME}: exited $?"
elif [ ! -f "${SCRIPT}" ]; then
echo "[custom-init] ${NAME}: is not a file"
fi
done
else
echo "[custom-init] no custom files found exiting..."
fi
fi
}
initialize() {
# Setup environment from secrets before anything else
# Check for a version of this var with _FILE appended
# and convert the contents to the env var value
# Source it so export is persistent
# shellcheck disable=SC1091
source /sbin/env-from-file.sh
# Change the user and group IDs if needed
map_uidgid
# Check for overrides of certain folders
map_folders
local -r export_dir="/usr/src/paperless/export"
for dir in \
"${export_dir}" \
"${DATA_DIR}" "${DATA_DIR}/index" \
"${MEDIA_ROOT_DIR}" "${MEDIA_ROOT_DIR}/documents" "${MEDIA_ROOT_DIR}/documents/originals" "${MEDIA_ROOT_DIR}/documents/thumbnails" \
"${CONSUME_DIR}"; do
if [[ ! -d "${dir}" ]]; then
echo "Creating directory ${dir}"
mkdir --parents --verbose "${dir}"
fi
done
local -r tmp_dir="${PAPERLESS_SCRATCH_DIR:=/tmp/paperless}"
echo "Creating directory scratch directory ${tmp_dir}"
mkdir --parents --verbose "${tmp_dir}"
set +e
echo "Adjusting permissions of paperless files. This may take a while."
chown -R paperless:paperless "${tmp_dir}"
for dir in \
"${export_dir}" \
"${DATA_DIR}" \
"${MEDIA_ROOT_DIR}" \
"${CONSUME_DIR}"; do
find "${dir}" -not \( -user paperless -and -group paperless \) -exec chown --changes paperless:paperless {} +
done
set -e
"${gosu_cmd[@]}" /sbin/docker-prepare.sh
# Leave this last thing
custom_container_init
}
install_languages() {
echo "Installing languages..."
read -ra langs <<<"$1"
# Check that it is not empty
if [ ${#langs[@]} -eq 0 ]; then
return
fi
# Build list of packages to install
to_install=()
for lang in "${langs[@]}"; do
pkg="tesseract-ocr-$lang"
if dpkg --status "$pkg" &>/dev/null; then
echo "Package $pkg already installed!"
continue
else
to_install+=("$pkg")
fi
done
# Use apt only when we install packages
if [ ${#to_install[@]} -gt 0 ]; then
apt-get update
for pkg in "${to_install[@]}"; do
if ! apt-cache show "$pkg" &>/dev/null; then
echo "Skipped $pkg: Package not found! :("
continue
fi
echo "Installing package $pkg..."
if ! apt-get --assume-yes install "$pkg" &>/dev/null; then
echo "Could not install $pkg"
exit 1
fi
done
fi
}
echo "Paperless-ngx docker container starting..."
gosu_cmd=(gosu paperless)
if [ "$(id --user)" == "$(id --user paperless)" ]; then
gosu_cmd=()
fi
# Install additional languages if specified
if [[ -n "$PAPERLESS_OCR_LANGUAGES" ]]; then
install_languages "$PAPERLESS_OCR_LANGUAGES"
fi
initialize
if [[ "$1" != "/"* ]]; then
echo Executing management command "$@"
exec "${gosu_cmd[@]}" python3 manage.py "$@"
else
echo Executing "$@"
exec "$@"
fi

120
docker/docker-prepare.sh Executable file
View File

@ -0,0 +1,120 @@
#!/usr/bin/env bash
set -e
wait_for_postgres() {
local attempt_num=1
local -r max_attempts=5
echo "Waiting for PostgreSQL to start..."
local -r host="${PAPERLESS_DBHOST:-localhost}"
local -r port="${PAPERLESS_DBPORT:-5432}"
# Disable warning, host and port can't have spaces
# shellcheck disable=SC2086
while [ ! "$(pg_isready --host ${host} --port ${port})" ]; do
if [ $attempt_num -eq $max_attempts ]; then
echo "Unable to connect to database."
exit 1
else
echo "Attempt $attempt_num failed! Trying again in 5 seconds..."
fi
attempt_num=$(("$attempt_num" + 1))
sleep 5
done
echo "Connected to PostgreSQL"
}
wait_for_mariadb() {
echo "Waiting for MariaDB to start..."
local -r host="${PAPERLESS_DBHOST:=localhost}"
local -r port="${PAPERLESS_DBPORT:=3306}"
local attempt_num=1
local -r max_attempts=5
# Disable warning, host and port can't have spaces
# shellcheck disable=SC2086
while ! true > /dev/tcp/$host/$port; do
if [ $attempt_num -eq $max_attempts ]; then
echo "Unable to connect to database."
exit 1
else
echo "Attempt $attempt_num failed! Trying again in 5 seconds..."
fi
attempt_num=$(("$attempt_num" + 1))
sleep 5
done
echo "Connected to MariaDB"
}
wait_for_redis() {
# We use a Python script to send the Redis ping
# instead of installing redis-tools just for 1 thing
if ! python3 /sbin/wait-for-redis.py; then
exit 1
fi
}
migrations() {
(
# flock is in place to prevent multiple containers from doing migrations
# simultaneously. This also ensures that the db is ready when the command
# of the current container starts.
flock 200
echo "Apply database migrations..."
python3 manage.py migrate --skip-checks --no-input
) 200>"${DATA_DIR}/migration_lock"
}
django_checks() {
# Explicitly run the Django system checks
echo "Running Django checks"
python3 manage.py check
}
search_index() {
local -r index_version=9
local -r index_version_file=${DATA_DIR}/.index_version
if [[ (! -f "${index_version_file}") || $(<"${index_version_file}") != "$index_version" ]]; then
echo "Search index out of date. Updating..."
python3 manage.py document_index reindex --no-progress-bar
echo ${index_version} | tee "${index_version_file}" >/dev/null
fi
}
superuser() {
if [[ -n "${PAPERLESS_ADMIN_USER}" ]]; then
python3 manage.py manage_superuser
fi
}
do_work() {
if [[ "${PAPERLESS_DBENGINE}" == "mariadb" ]]; then
wait_for_mariadb
elif [[ -n "${PAPERLESS_DBHOST}" ]]; then
wait_for_postgres
fi
wait_for_redis
migrations
django_checks
search_index
superuser
}
do_work

42
docker/env-from-file.sh Normal file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env bash
# Scans the environment variables for those with the suffix _FILE
# When located, checks the file exists, and exports the contents
# of the file as the same name, minus the suffix
# This allows the use of Docker secrets or mounted files
# to fill in any of the settings configurable via environment
# variables
set -eu
for line in $(printenv)
do
# Extract the name of the environment variable
env_name=${line%%=*}
# Check if it starts with "PAPERLESS_" and ends in "_FILE"
if [[ ${env_name} == PAPERLESS_*_FILE ]]; then
# This should have been named different..
if [[ ${env_name} == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" || ${env_name} == "PAPERLESS_MODEL_FILE" ]]; then
continue
fi
# Extract the value of the environment
env_value=${line#*=}
# Check the file exists
if [[ -f ${env_value} ]]; then
# Trim off the _FILE suffix
non_file_env_name=${env_name%"_FILE"}
echo "Setting ${non_file_env_name} from file"
# Reads the value from th file
val="$(< "${!env_name}")"
# Sets the normal name to the read file contents
export "${non_file_env_name}"="${val}"
else
echo "File ${env_value} referenced by ${env_name} doesn't exist"
fi
fi
done

View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
echo "Checking if we should start flower..."
if [[ -n "${PAPERLESS_ENABLE_FLOWER}" ]]; then
# Small delay to allow celery to be up first
echo "Starting flower in 5s"
sleep 5
celery --app paperless flower --conf=/usr/src/paperless/src/paperless/flowerconfig.py
else
echo "Not starting flower"
fi

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -1,7 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Run this script to generate the management commands again (for example if a new command is create or the template is updated)
set -eu set -eu
for command in decrypt_documents \ for command in decrypt_documents \
@ -18,10 +16,9 @@ for command in decrypt_documents \
document_fuzzy_match \ document_fuzzy_match \
manage_superuser \ manage_superuser \
convert_mariadb_uuid \ convert_mariadb_uuid \
prune_audit_logs \ prune_audit_logs;
createsuperuser;
do do
echo "installing $command..." echo "installing $command..."
sed "s/management_command/$command/g" management_script.sh >"$PWD/rootfs/usr/local/bin/$command" sed "s/management_command/$command/g" management_script.sh > /usr/local/bin/$command
chmod u=rwx,g=rwx,o=rx "$PWD/rootfs/usr/local/bin/$command" chmod +x /usr/local/bin/$command
done done

View File

@ -1,13 +1,17 @@
#!/command/with-contenv /usr/bin/bash #!/usr/bin/env bash
# shellcheck shell=bash
set -e set -e
cd "${PAPERLESS_SRC_DIR}" cd /usr/src/paperless/src/
# This ensures environment is setup
# shellcheck disable=SC1091
source /sbin/env-from-file.sh
if [[ $(id -u) == 0 ]]; then if [[ $(id -u) == 0 ]] ;
s6-setuidgid paperless python3 manage.py management_command "$@" then
elif [[ $(id -un) == "paperless" ]]; then gosu paperless python3 manage.py management_command "$@"
elif [[ $(id -un) == "paperless" ]] ;
then
python3 manage.py management_command "$@" python3 manage.py management_command "$@"
else else
echo "Unknown user." echo "Unknown user."

16
docker/paperless_cmd.sh Executable file
View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
SUPERVISORD_WORKING_DIR="${PAPERLESS_SUPERVISORD_WORKING_DIR:-$PWD}"
rootless_args=()
if [ "$(id -u)" == "$(id -u paperless)" ]; then
rootless_args=(
--user
paperless
--logfile
"${SUPERVISORD_WORKING_DIR}/supervisord.log"
--pidfile
"${SUPERVISORD_WORKING_DIR}/supervisord.pid"
)
fi
exec /usr/local/bin/supervisord -c /etc/supervisord.conf "${rootless_args[@]}"

View File

@ -1,8 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[init-complete]"
declare -r end_time=$(date +%s)
declare -r start_time=${PAPERLESS_START_TIME_S}
echo "${log_prefix} paperless-ngx docker container init completed in $(($end_time-$start_time)) seconds"
echo "${log_prefix} Starting services"

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-complete/run

View File

@ -1,44 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[custom-init]"
# Mostly borrowed from the LinuxServer.io base image
# https://github.com/linuxserver/docker-baseimage-ubuntu/tree/bionic/root/etc/cont-init.d
declare -r custom_script_dir="/custom-cont-init.d"
# Tamper checking.
# Don't run files which are owned by anyone except root
# Don't run files which are writeable by others
if [ -d "${custom_script_dir}" ]; then
if [ -n "$(/usr/bin/find "${custom_script_dir}" -maxdepth 1 ! -user root)" ]; then
echo "${log_prefix} **** Potential tampering with custom scripts detected ****"
echo "${log_prefix} **** The folder '${custom_script_dir}' must be owned by root ****"
exit 0
fi
if [ -n "$(/usr/bin/find "${custom_script_dir}" -maxdepth 1 -perm -o+w)" ]; then
echo "${log_prefix} **** The folder '${custom_script_dir}' or some of contents have write permissions for others, which is a security risk. ****"
echo "${log_prefix} **** Please review the permissions and their contents to make sure they are owned by root, and can only be modified by root. ****"
exit 0
fi
# Make sure custom init directory has files in it
if [ -n "$(/bin/ls --almost-all "${custom_script_dir}" 2>/dev/null)" ]; then
echo "${log_prefix} files found in ${custom_script_dir} executing"
# Loop over files in the directory
for SCRIPT in "${custom_script_dir}"/*; do
NAME="$(basename "${SCRIPT}")"
if [ -f "${SCRIPT}" ]; then
echo "${log_prefix} ${NAME}: executing..."
/command/with-contenv /bin/bash "${SCRIPT}"
echo "${log_prefix} ${NAME}: exited $?"
elif [ ! -f "${SCRIPT}" ]; then
echo "${log_prefix} ${NAME}: is not a file"
fi
done
else
echo "${log_prefix} no custom files found exiting..."
fi
else
echo "${log_prefix} ${custom_script_dir} doesn't exist, nothing to do"
fi

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-custom-init/run

View File

@ -1,33 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[env-init]"
echo "${log_prefix} Checking for environment from files"
if find /run/s6/container_environment/*"_FILE" -maxdepth 1 > /dev/null 2>&1; then
for FILENAME in /run/s6/container_environment/*; do
if [[ "${FILENAME##*/}" == PAPERLESS_*_FILE ]]; then
# This should have been named different..
if [[ ${FILENAME} == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" || ${FILENAME} == "PAPERLESS_MODEL_FILE" ]]; then
continue
fi
SECRETFILE=$(cat "${FILENAME}")
# Check the file exists
if [[ -f ${SECRETFILE} ]]; then
# Trim off trailing _FILE
FILESTRIP=${FILENAME//_FILE/}
if [[ $(tail -n1 "${SECRETFILE}" | wc -l) != 0 ]]; then
echo "${log_prefix} Your secret: ${FILENAME##*/} contains a trailing newline and may not work as expected"
fi
# Set environment variable
cat "${SECRETFILE}" > "${FILESTRIP}"
echo "${log_prefix} ${FILESTRIP##*/} set from ${FILENAME##*/}"
else
echo "${log_prefix} cannot find secret in ${FILENAME##*/}"
fi
fi
done
else
echo "${log_prefix} No *_FILE environment found"
fi

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-env-file/run

View File

@ -1,65 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[init-folders]"
declare -r export_dir="/usr/src/paperless/export"
declare -r data_dir="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}"
declare -r media_root_dir="${PAPERLESS_MEDIA_ROOT:-/usr/src/paperless/media}"
declare -r consume_dir="${PAPERLESS_CONSUMPTION_DIR:-/usr/src/paperless/consume}"
declare -r tmp_dir="${PAPERLESS_SCRATCH_DIR:=/tmp/paperless}"
declare -r main_dirs=(
"${export_dir}"
"${data_dir}"
"${media_root_dir}"
"${consume_dir}"
"${tmp_dir}"
)
declare -r extra_dirs=(
"${main_dirs[@]}"
"${data_dir}/index"
"${media_root_dir}/documents"
"${media_root_dir}/documents/originals"
"${media_root_dir}/documents/thumbnails"
)
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
# Non-root mode: Create directories as current user, warn about permission issues
echo "${log_prefix} Running in non-root mode, checking directories"
current_uid=$(id --user)
current_gid=$(id --group)
for dir in "${extra_dirs[@]}"; do
if [[ ! -d "${dir}" ]]; then
mkdir --parents --verbose "${dir}" || echo "${log_prefix} WARNING: Could not create ${dir} - permission denied"
fi
# Check permissions on existing directories too
if [[ -d "${dir}" && ! -w "${dir}" ]]; then
echo "${log_prefix} WARNING: No write permission to ${dir}"
fi
done
# Warn about ownership issues
for dir in "${main_dirs[@]}"; do
if [[ -d "${dir}" ]]; then
find "${dir}" -not \( -user ${current_uid} -and -group ${current_gid} \) -exec echo "${log_prefix} WARNING: Permission issue on {}: not owned by current user (${current_uid}:${current_gid})" \; 2>/dev/null || echo "${log_prefix} WARNING: Cannot check permissions on ${dir}"
fi
done
else
# Root mode: Create and fix permissions as needed
echo "${log_prefix} Running with root privileges, adjusting directories and permissions"
# First create directories
for dir in "${extra_dirs[@]}"; do
if [[ ! -d "${dir}" ]]; then
mkdir --parents --verbose "${dir}"
fi
done
# Then fix permissions on all directories
for dir in "${main_dirs[@]}"; do
find "${dir}" -not \( -user paperless -and -group paperless \) -exec chown --changes paperless:paperless {} +
done
fi

View File

@ -1 +0,0 @@
oneshot

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-folders/run

View File

@ -1,18 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[init-migrations]"
declare -r data_dir="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}"
echo "${log_prefix} Apply database migrations..."
cd "${PAPERLESS_SRC_DIR}"
# The whole migrate, with flock, needs to run as the right user
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
exec s6-setlock -n "${data_dir}/migration_lock" python3 manage.py migrate --skip-checks --no-input
else
exec s6-setuidgid paperless \
s6-setlock -n "${data_dir}/migration_lock" \
python3 manage.py migrate --skip-checks --no-input
fi

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-migrations/run

View File

@ -1,22 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[init-user]"
declare -r usermap_original_uid=$(id -u paperless)
declare -r usermap_original_gid=$(id -g paperless)
declare -r usermap_new_uid=${USERMAP_UID:-$usermap_original_uid}
declare -r usermap_new_gid=${USERMAP_GID:-${usermap_original_gid:-$usermap_new_uid}}
if [[ ${usermap_new_uid} != "${usermap_original_uid}" ]]; then
echo "${log_prefix} Mapping UID for paperless to $usermap_new_uid"
usermod --non-unique --uid "${usermap_new_uid}" paperless
else
echo "${log_prefix} No UID changes for paperless"
fi
if [[ ${usermap_new_gid} != "${usermap_original_gid}" ]]; then
echo "${log_prefix} Mapping GID for paperless to $usermap_new_gid"
groupmod --non-unique --gid "${usermap_new_gid}" paperless
else
echo "${log_prefix} No GID changes for paperless"
fi

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-modify-user/run

View File

@ -1,28 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[init-index]"
declare -r index_version=9
declare -r data_dir="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}"
declare -r index_version_file="${data_dir}/.index_version"
update_index () {
echo "${log_prefix} Search index out of date. Updating..."
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py document_index reindex --no-progress-bar
echo ${index_version} | tee "${index_version_file}" > /dev/null
else
s6-setuidgid paperless python3 manage.py document_index reindex --no-progress-bar
echo ${index_version} | s6-setuidgid paperless tee "${index_version_file}" > /dev/null
fi
}
if [[ (! -f "${index_version_file}") ]]; then
echo "${log_prefix} No index version file found"
update_index
elif [[ $(<"${index_version_file}") != "$index_version" ]]; then
echo "${log_prefix} index version updated"
update_index
fi

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-search-index/run

View File

@ -1,20 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[init-start]"
echo "${log_prefix} paperless-ngx docker container starting..."
# Set some directories into environment for other steps to access via environment
# Sort of like variables for later
printf "/usr/src/paperless/src" > /var/run/s6/container_environment/PAPERLESS_SRC_DIR
echo $(date +%s) > /var/run/s6/container_environment/PAPERLESS_START_TIME_S
# Check if we're starting as a non-root user
if [ "$(id --user)" != "0" ]; then
printf "true" > /var/run/s6/container_environment/USER_IS_NON_ROOT
echo "${log_prefix} paperless-ngx docker container running under a user ($(id --user):$(id --group))"
else
printf "/usr/src/paperless" > /var/run/s6/container_environment/HOME
echo "${log_prefix} paperless-ngx docker container starting init as root"
fi

View File

@ -1 +0,0 @@
oneshot

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-start/run

View File

@ -1,20 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[init-superuser]"
if [[ -n "${PAPERLESS_ADMIN_USER}" ]]; then
echo "${log_prefix} Creating superuser..."
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py manage_superuser
else
s6-setuidgid paperless python3 manage.py manage_superuser
fi
echo "${log_prefix} Superuser creation done"
else
echo "${log_prefix} Not creating superuser"
fi

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-superuser/run

View File

@ -1,15 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[init-checks]"
# Explicitly run the Django system checks
echo "${log_prefix} Running Django checks"
cd "${PAPERLESS_SRC_DIR}"
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
python3 manage.py check
else
s6-setuidgid paperless python3 manage.py check
fi

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-system-checks/run

View File

@ -1,65 +0,0 @@
#!/command/with-contenv /usr/bin/bash
# shellcheck shell=bash
declare -r log_prefix="[init-tesseract-langs]"
install_languages() {
echo "Installing languages..."
read -ra langs <<<"$1"
# Check that it is not empty
if [ ${#langs[@]} -eq 0 ]; then
return
fi
# Build list of packages to install
to_install=()
for lang in "${langs[@]}"; do
pkg="tesseract-ocr-$lang"
if dpkg --status "$pkg" &>/dev/null; then
echo "${log_prefix} Package $pkg already installed!"
continue
else
to_install+=("$pkg")
fi
done
# Use apt only when we install packages
if [ ${#to_install[@]} -gt 0 ]; then
# Warn the user if they're not root, but try anyway
if [[ -n "${USER_IS_NON_ROOT}" ]]; then
echo "${log_prefix} ERROR: Unable to install language ${pkg} as non-root, startup may fail"
fi
apt-get --quiet update &>/dev/null
for pkg in "${to_install[@]}"; do
if ! apt-cache --quiet show "$pkg" &>/dev/null; then
echo "${log_prefix} Skipped $pkg: Package not found! :("
continue
fi
echo "${log_prefix} Installing package $pkg..."
if ! apt-get --quiet --assume-yes install "$pkg" &>/dev/null; then
echo "${log_prefix} Could not install $pkg"
exit 1
else
echo "${log_prefix} Installed $pkg"
fi
done
fi
}
echo "${log_prefix} Checking if additional teseract languages needed"
# Install additional languages if specified
if [[ -n "$PAPERLESS_OCR_LANGUAGES" ]]; then
install_languages "$PAPERLESS_OCR_LANGUAGES"
echo "${log_prefix} Additional packages installed"
else
echo "${log_prefix} No additional installs requested"
fi

View File

@ -1 +0,0 @@
/etc/s6-overlay/s6-rc.d/init-tesseract-langs/run

Some files were not shown because too many files have changed in this diff Show More