mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 03:16:10 -06:00 
			
		
		
		
	Compare commits
	
		
			8 Commits
		
	
	
		
			f3479d982c
			...
			feature-re
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					ec12e71487 | ||
| 
						 | 
					62b470f691 | ||
| 
						 | 
					a2e4977201 | ||
| 
						 | 
					0fcd69b739 | ||
| 
						 | 
					af1c64e969 | ||
| 
						 | 
					85c661dff2 | ||
| 
						 | 
					3a7eee2c2e | ||
| 
						 | 
					bc4d3925cc | 
							
								
								
									
										16
									
								
								.codecov.yml
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								.codecov.yml
									
									
									
									
									
								
							@@ -1,18 +1,18 @@
 | 
			
		||||
codecov:
 | 
			
		||||
  require_ci_to_pass: true
 | 
			
		||||
  # https://docs.codecov.com/docs/components
 | 
			
		||||
component_management:
 | 
			
		||||
  individual_components:
 | 
			
		||||
    - component_id: backend
 | 
			
		||||
# https://docs.codecov.com/docs/flags#recommended-automatic-flag-management
 | 
			
		||||
# Require each flag to have 1 upload before notification
 | 
			
		||||
flag_management:
 | 
			
		||||
  individual_flags:
 | 
			
		||||
    - name: backend
 | 
			
		||||
      paths:
 | 
			
		||||
        - src/**
 | 
			
		||||
    - component_id: frontend
 | 
			
		||||
        - src/
 | 
			
		||||
    - name: frontend
 | 
			
		||||
      paths:
 | 
			
		||||
        - src-ui/**
 | 
			
		||||
        - src-ui/
 | 
			
		||||
# https://docs.codecov.com/docs/pull-request-comments
 | 
			
		||||
# codecov will only comment if coverage changes
 | 
			
		||||
comment:
 | 
			
		||||
  layout: "header, diff, components, flags, files"
 | 
			
		||||
  require_changes: true
 | 
			
		||||
  # https://docs.codecov.com/docs/javascript-bundle-analysis
 | 
			
		||||
  require_bundle_changes: true
 | 
			
		||||
 
 | 
			
		||||
@@ -76,15 +76,18 @@ RUN set -eux \
 | 
			
		||||
    && apt-get update \
 | 
			
		||||
    && 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 \
 | 
			
		||||
  echo "Installing python packages" \
 | 
			
		||||
    && apt-get update \
 | 
			
		||||
    && apt-get install --yes --quiet ${PYTHON_PACKAGES}
 | 
			
		||||
 | 
			
		||||
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
 | 
			
		||||
 | 
			
		||||
RUN set -eux \
 | 
			
		||||
  && echo "Installing pre-built updates" \
 | 
			
		||||
    && echo "Installing qpdf ${QPDF_VERSION}" \
 | 
			
		||||
@@ -120,15 +123,13 @@ RUN set -eux \
 | 
			
		||||
WORKDIR /usr/src/paperless/src/docker/
 | 
			
		||||
 | 
			
		||||
COPY [ \
 | 
			
		||||
  "docker/rootfs/etc/ImageMagick-6/paperless-policy.xml", \
 | 
			
		||||
  "docker/imagemagick-policy.xml", \
 | 
			
		||||
  "./" \
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
RUN set -eux \
 | 
			
		||||
  && echo "Configuring ImageMagick" \
 | 
			
		||||
    && mv paperless-policy.xml /etc/ImageMagick-6/policy.xml
 | 
			
		||||
 | 
			
		||||
COPY --from=ghcr.io/astral-sh/uv:0.6 /uv /bin/uv
 | 
			
		||||
    && mv imagemagick-policy.xml /etc/ImageMagick-6/policy.xml
 | 
			
		||||
 | 
			
		||||
# Packages needed only for building a few quick Python
 | 
			
		||||
# dependencies
 | 
			
		||||
@@ -139,17 +140,18 @@ ARG BUILD_PACKAGES="\
 | 
			
		||||
  libpq-dev \
 | 
			
		||||
  # https://github.com/PyMySQL/mysqlclient#linux
 | 
			
		||||
  default-libmysqlclient-dev \
 | 
			
		||||
  pkg-config"
 | 
			
		||||
  pkg-config \
 | 
			
		||||
  pre-commit"
 | 
			
		||||
 | 
			
		||||
# 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 \
 | 
			
		||||
  && echo "Installing build system packages" \
 | 
			
		||||
    && apt-get update \
 | 
			
		||||
    && apt-get install --yes --quiet ${BUILD_PACKAGES}
 | 
			
		||||
 | 
			
		||||
RUN set -eux \
 | 
			
		||||
  && npm update -g pnpm
 | 
			
		||||
  && npm update npm -g
 | 
			
		||||
 | 
			
		||||
# add users, setup scripts
 | 
			
		||||
# Mount the compiled frontend to expected location
 | 
			
		||||
@@ -167,6 +169,9 @@ RUN set -eux \
 | 
			
		||||
    && mkdir --parents --verbose /usr/src/paperless/paperless-ngx/.venv \
 | 
			
		||||
  && echo "Adjusting all permissions" \
 | 
			
		||||
    && 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", \
 | 
			
		||||
        "/usr/src/paperless/paperless-ngx/media", \
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
# Paperless-ngx Development Environment
 | 
			
		||||
# 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.
 | 
			
		||||
Welcome to the Paperless NGX development environment! This setup uses VSCode DevContainers to provide a consistent and seamless development experience.
 | 
			
		||||
 | 
			
		||||
### What are DevContainers?
 | 
			
		||||
 | 
			
		||||
@@ -17,7 +17,7 @@ DevContainers are a feature in VSCode that allows you to develop within a Docker
 | 
			
		||||
 | 
			
		||||
## DevContainer Setup
 | 
			
		||||
 | 
			
		||||
The DevContainer configuration provides up all the necessary services for Paperless-ngx, including:
 | 
			
		||||
The DevContainer configuration provides up all the necessary services for Paperless NGX, including:
 | 
			
		||||
 | 
			
		||||
- Redis
 | 
			
		||||
- Gotenberg
 | 
			
		||||
@@ -36,7 +36,7 @@ The setup includes debugging configurations (`launch.json`) and tasks (`tasks.js
 | 
			
		||||
- **Maintenance Tasks:**
 | 
			
		||||
  - Create superuser
 | 
			
		||||
  - Run migrations
 | 
			
		||||
  - Recreate virtual environment (`.venv` with `uv`)
 | 
			
		||||
  - Recreate virtual environment (`.venv` with pipenv)
 | 
			
		||||
  - Compile frontend assets
 | 
			
		||||
 | 
			
		||||
## Getting Started
 | 
			
		||||
@@ -85,7 +85,7 @@ Once the DevContainer is up and running, perform the following steps:
 | 
			
		||||
 | 
			
		||||
You can start and debug backend services either as debugging sessions via `launch.json` or as tasks.
 | 
			
		||||
 | 
			
		||||
#### Using `launch.json`
 | 
			
		||||
#### Using `launch.json`:
 | 
			
		||||
 | 
			
		||||
1. Press `F5` or go to the **Run and Debug** view in VSCode.
 | 
			
		||||
2. Select the desired configuration:
 | 
			
		||||
@@ -93,7 +93,7 @@ You can start and debug backend services either as debugging sessions via `launc
 | 
			
		||||
   - `Document Consumer`
 | 
			
		||||
   - `Celery`
 | 
			
		||||
 | 
			
		||||
#### Using Tasks
 | 
			
		||||
#### Using Tasks:
 | 
			
		||||
 | 
			
		||||
1. Open the command palette:
 | 
			
		||||
   - **Windows/Linux**: `Ctrl+Shift+P`
 | 
			
		||||
@@ -108,7 +108,7 @@ You can start and debug backend services either as debugging sessions via `launc
 | 
			
		||||
 | 
			
		||||
Additional tasks are available for common maintenance operations:
 | 
			
		||||
 | 
			
		||||
- **Recreate .venv**: For setting up the virtual environment using `uv`.
 | 
			
		||||
- **Recreate .venv**: For setting up the virtual environment using pipenv.
 | 
			
		||||
- **Migrate Database**: To apply database migrations.
 | 
			
		||||
- **Create Superuser**: To create an admin user for the application.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,26 +3,14 @@
 | 
			
		||||
    "dockerComposeFile": "docker-compose.devcontainer.sqlite-tika.yml",
 | 
			
		||||
    "service": "paperless-development",
 | 
			
		||||
    "workspaceFolder": "/usr/src/paperless/paperless-ngx",
 | 
			
		||||
    "postCreateCommand": "/bin/bash -c 'uv sync --group dev && uv run pre-commit install'",
 | 
			
		||||
    "postCreateCommand": "/bin/bash -c pre-commit install && pipenv install --dev",
 | 
			
		||||
    "customizations": {
 | 
			
		||||
        "vscode": {
 | 
			
		||||
          "extensions": [
 | 
			
		||||
            "mhutchie.git-graph",
 | 
			
		||||
            "ms-python.python",
 | 
			
		||||
            "ms-vscode.js-debug-nightly",
 | 
			
		||||
            "eamodio.gitlens",
 | 
			
		||||
            "yzhang.markdown-all-in-one"
 | 
			
		||||
          ],
 | 
			
		||||
          "settings": {
 | 
			
		||||
            "python.defaultInterpreterPath": "/usr/src/paperless/paperless-ngx/.venv/bin/python",
 | 
			
		||||
            "python.pythonPath": "/usr/src/paperless/paperless-ngx/.venv/bin/python",
 | 
			
		||||
            "python.terminal.activateEnvInCurrentTerminal": true,
 | 
			
		||||
            "editor.formatOnPaste": false,
 | 
			
		||||
            "editor.formatOnSave": true,
 | 
			
		||||
            "editor.formatOnType": true,
 | 
			
		||||
            "files.trimTrailingWhitespace": true
 | 
			
		||||
          }
 | 
			
		||||
            "extensions": [
 | 
			
		||||
              "mhutchie.git-graph",
 | 
			
		||||
              "ms-python.python"
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "remoteUser": "paperless"
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
    "remoteUser": "paperless"
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@ services:
 | 
			
		||||
    image: docker.io/library/redis:7
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ./redisdata:/data
 | 
			
		||||
      - redisdata:/data
 | 
			
		||||
 | 
			
		||||
  # No ports need to be exposed; the VSCode DevContainer plugin manages them.
 | 
			
		||||
  paperless-development:
 | 
			
		||||
@@ -43,16 +43,14 @@ services:
 | 
			
		||||
    volumes:
 | 
			
		||||
      - ..:/usr/src/paperless/paperless-ngx:delegated
 | 
			
		||||
      - ../.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 # Pipenv environment persisted in volume
 | 
			
		||||
      - /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/.ruff_cache
 | 
			
		||||
      - /usr/src/paperless/paperless-ngx/htmlcov
 | 
			
		||||
      - /usr/src/paperless/paperless-ngx/.coverage
 | 
			
		||||
      - ./data:/usr/src/paperless/paperless-ngx/data
 | 
			
		||||
      - ./media:/usr/src/paperless/paperless-ngx/media
 | 
			
		||||
      - ./consume:/usr/src/paperless/paperless-ngx/consume
 | 
			
		||||
      - ~/.gitconfig:/usr/src/paperless/.gitconfig:ro
 | 
			
		||||
      - data:/usr/src/paperless/paperless-ngx/data
 | 
			
		||||
      - media:/usr/src/paperless/paperless-ngx/media
 | 
			
		||||
    environment:
 | 
			
		||||
      PAPERLESS_REDIS: redis://broker:6379
 | 
			
		||||
      PAPERLESS_TIKA_ENABLED: 1
 | 
			
		||||
@@ -65,7 +63,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"
 | 
			
		||||
 | 
			
		||||
  gotenberg:
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:8.17
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:7.10
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
 | 
			
		||||
    # The Gotenberg Chromium route is used to convert .eml files. We do not
 | 
			
		||||
@@ -83,4 +81,4 @@ volumes:
 | 
			
		||||
  data:
 | 
			
		||||
  media:
 | 
			
		||||
  redisdata:
 | 
			
		||||
  virtualenv:
 | 
			
		||||
  pipenv:
 | 
			
		||||
 
 | 
			
		||||
@@ -2,57 +2,42 @@
 | 
			
		||||
    "version": "0.2.0",
 | 
			
		||||
    "configurations": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Chrome: Debug Angular Frontend",
 | 
			
		||||
						"description": "Debug the Angular Dev Frontend in Chrome",
 | 
			
		||||
            "type": "chrome",
 | 
			
		||||
            "request": "launch",
 | 
			
		||||
            "url": "http://localhost:4200",
 | 
			
		||||
            "webRoot": "${workspaceFolder}/src-ui",
 | 
			
		||||
            "preLaunchTask": "Start: Frontend Angular"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Debug: Backend Server (manage.py runserver)",
 | 
			
		||||
						"description": "Debug the Django Backend Server",
 | 
			
		||||
            "name": "manage.py runserver",
 | 
			
		||||
            "type": "python",
 | 
			
		||||
            "request": "launch",
 | 
			
		||||
            "program": "${workspaceFolder}/src/manage.py",
 | 
			
		||||
            "args": [
 | 
			
		||||
                "runserver"
 | 
			
		||||
            ],
 | 
			
		||||
            "django": true,
 | 
			
		||||
            "console": "integratedTerminal",
 | 
			
		||||
            "env": {
 | 
			
		||||
                "PYTHONPATH": "${workspaceFolder}/src"
 | 
			
		||||
            },
 | 
			
		||||
            "python": "${workspaceFolder}/.venv/bin/python"
 | 
			
		||||
            "justMyCode": true,
 | 
			
		||||
            "args": ["runserver"],
 | 
			
		||||
            "django": true
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Debug: Consumer Service (manage.py document_consumer)",
 | 
			
		||||
						"description": "Debug the Consumer Service which processes files from a directory",
 | 
			
		||||
            "name": "manage.py document_consumer",
 | 
			
		||||
            "type": "python",
 | 
			
		||||
            "request": "launch",
 | 
			
		||||
            "program": "${workspaceFolder}/src/manage.py",
 | 
			
		||||
            "args": [
 | 
			
		||||
                "document_consumer"
 | 
			
		||||
            ],
 | 
			
		||||
            "django": true,
 | 
			
		||||
            "console": "integratedTerminal",
 | 
			
		||||
            "justMyCode": true,
 | 
			
		||||
            "args": ["document_consumer"],
 | 
			
		||||
            "django": true
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "name": "celery",
 | 
			
		||||
            "type": "python",
 | 
			
		||||
            "cwd": "${workspaceFolder}/src",
 | 
			
		||||
            "request": "launch",
 | 
			
		||||
            "module": "celery",
 | 
			
		||||
            "console": "integratedTerminal",
 | 
			
		||||
            "env": {
 | 
			
		||||
                "PYTHONPATH": "${workspaceFolder}/src"
 | 
			
		||||
            },
 | 
			
		||||
            "python": "${workspaceFolder}/.venv/bin/python"
 | 
			
		||||
        }
 | 
			
		||||
    ],
 | 
			
		||||
    "compounds": [
 | 
			
		||||
        {
 | 
			
		||||
            "name": "Debug: FullStack",
 | 
			
		||||
						"description": "Debug run the Angular dev frontend, Django backend, and consumer service",
 | 
			
		||||
            "configurations": [
 | 
			
		||||
                "Chrome: Debug Angular Frontend",
 | 
			
		||||
                "Debug: Backend Server (manage.py runserver)",
 | 
			
		||||
                "Debug: Consumer Service (manage.py document_consumer)"
 | 
			
		||||
            ],
 | 
			
		||||
            "preLaunchTask": "Start: Celery Worker"
 | 
			
		||||
              },
 | 
			
		||||
            "args": [
 | 
			
		||||
                "-A",
 | 
			
		||||
                "paperless",
 | 
			
		||||
                "worker",
 | 
			
		||||
                "-l",
 | 
			
		||||
                "DEBUG"
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,223 +1,136 @@
 | 
			
		||||
{
 | 
			
		||||
	"version": "2.0.0",
 | 
			
		||||
	"tasks": [
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Start: Celery Worker",
 | 
			
		||||
			"description": "Start the Celery Worker which processes background and consume tasks",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"command": "uv run celery --app paperless worker -l DEBUG",
 | 
			
		||||
			"isBackground": true,
 | 
			
		||||
			"options": {
 | 
			
		||||
				"cwd": "${workspaceFolder}/src"
 | 
			
		||||
			},
 | 
			
		||||
			"problemMatcher": [
 | 
			
		||||
				{
 | 
			
		||||
					"owner": "custom",
 | 
			
		||||
					"pattern": [
 | 
			
		||||
						{
 | 
			
		||||
							"regexp": ".",
 | 
			
		||||
							"file": 1,
 | 
			
		||||
							"location": 2,
 | 
			
		||||
							"message": 3
 | 
			
		||||
						}
 | 
			
		||||
					],
 | 
			
		||||
					"background": {
 | 
			
		||||
						"activeOnStart": true,
 | 
			
		||||
						"beginsPattern": "celery.*",
 | 
			
		||||
						"endsPattern": "ready"
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			]
 | 
			
		||||
	{
 | 
			
		||||
		"label": "manage.py document_consumer",
 | 
			
		||||
		"type": "shell",
 | 
			
		||||
		"command": "pipenv run python manage.py document_consumer",
 | 
			
		||||
		"group": "build",
 | 
			
		||||
		"presentation": {
 | 
			
		||||
			"echo": true,
 | 
			
		||||
			"reveal": "always",
 | 
			
		||||
			"focus": false,
 | 
			
		||||
			"panel": "shared",
 | 
			
		||||
			"showReuseMessage": false,
 | 
			
		||||
			"clear": true,
 | 
			
		||||
			"revealProblems": "onProblem"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Start: Frontend Angular",
 | 
			
		||||
			"description": "Start the Frontend Angular Dev Server",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"command": "pnpm start",
 | 
			
		||||
			"isBackground": true,
 | 
			
		||||
			"options": {
 | 
			
		||||
				"cwd": "${workspaceFolder}/src-ui"
 | 
			
		||||
			},
 | 
			
		||||
			"problemMatcher": [
 | 
			
		||||
				{
 | 
			
		||||
					"owner": "custom",
 | 
			
		||||
					"pattern": [
 | 
			
		||||
						{
 | 
			
		||||
							"regexp": ".",
 | 
			
		||||
							"file": 1,
 | 
			
		||||
							"location": 2,
 | 
			
		||||
							"message": 3
 | 
			
		||||
						}
 | 
			
		||||
					],
 | 
			
		||||
					"background": {
 | 
			
		||||
						"activeOnStart": true,
 | 
			
		||||
						"beginsPattern": ".*",
 | 
			
		||||
						"endsPattern": "Compiled successfully"
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			]
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Start: Consumer Service (manage.py document_consumer)",
 | 
			
		||||
			"description": "Start the Consumer Service which processes files from a directory",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"command": "uv run python manage.py document_consumer",
 | 
			
		||||
			"group": "build",
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"echo": true,
 | 
			
		||||
				"reveal": "always",
 | 
			
		||||
				"focus": false,
 | 
			
		||||
				"panel": "shared",
 | 
			
		||||
				"showReuseMessage": false,
 | 
			
		||||
				"clear": true,
 | 
			
		||||
				"revealProblems": "onProblem"
 | 
			
		||||
			},
 | 
			
		||||
			"options": {
 | 
			
		||||
				"cwd": "${workspaceFolder}/src"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Start: Backend Server (manage.py runserver)",
 | 
			
		||||
			"description": "Start the Backend Server which serves the Django API and the compiled Angular frontend",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"command": "uv run python manage.py runserver",
 | 
			
		||||
			"group": "build",
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"echo": true,
 | 
			
		||||
				"reveal": "always",
 | 
			
		||||
				"focus": false,
 | 
			
		||||
				"panel": "shared",
 | 
			
		||||
				"showReuseMessage": false,
 | 
			
		||||
				"clear": true,
 | 
			
		||||
				"revealProblems": "onProblem"
 | 
			
		||||
			},
 | 
			
		||||
			"options": {
 | 
			
		||||
				"cwd": "${workspaceFolder}/src"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Maintenance: manage.py migrate",
 | 
			
		||||
			"description": "Apply database migrations",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"command": "uv run python manage.py migrate",
 | 
			
		||||
			"group": "none",
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"echo": true,
 | 
			
		||||
				"reveal": "always",
 | 
			
		||||
				"focus": true,
 | 
			
		||||
				"panel": "shared",
 | 
			
		||||
				"showReuseMessage": false,
 | 
			
		||||
				"clear": true,
 | 
			
		||||
				"revealProblems": "onProblem"
 | 
			
		||||
			},
 | 
			
		||||
			"options": {
 | 
			
		||||
				"cwd": "${workspaceFolder}/src"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Maintenance: Build Documentation",
 | 
			
		||||
			"description": "Build the documentation with MkDocs",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"command": "uv run mkdocs build --config-file mkdocs.yml && uv run mkdocs serve",
 | 
			
		||||
			"group": "none",
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"echo": true,
 | 
			
		||||
				"reveal": "always",
 | 
			
		||||
				"focus": true,
 | 
			
		||||
				"panel": "shared",
 | 
			
		||||
				"showReuseMessage": false,
 | 
			
		||||
				"clear": true,
 | 
			
		||||
				"revealProblems": "onProblem"
 | 
			
		||||
			},
 | 
			
		||||
			"options": {
 | 
			
		||||
				"cwd": "${workspaceFolder}"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Maintenance: manage.py createsuperuser",
 | 
			
		||||
			"description": "Create a superuser",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"command": "uv run python manage.py createsuperuser",
 | 
			
		||||
			"group": "none",
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"echo": true,
 | 
			
		||||
				"reveal": "always",
 | 
			
		||||
				"focus": true,
 | 
			
		||||
				"panel": "shared",
 | 
			
		||||
				"showReuseMessage": false,
 | 
			
		||||
				"clear": true,
 | 
			
		||||
				"revealProblems": "onProblem"
 | 
			
		||||
			},
 | 
			
		||||
			"options": {
 | 
			
		||||
				"cwd": "${workspaceFolder}/src"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Maintenance: recreate .venv",
 | 
			
		||||
			"description": "Recreate the python virtual environment and install python dependencies",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"command": "rm -R -v .venv/* || uv install --dev",
 | 
			
		||||
			"group": "none",
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"echo": true,
 | 
			
		||||
				"reveal": "always",
 | 
			
		||||
				"focus": true,
 | 
			
		||||
				"panel": "shared",
 | 
			
		||||
				"showReuseMessage": false,
 | 
			
		||||
				"clear": true,
 | 
			
		||||
				"revealProblems": "onProblem"
 | 
			
		||||
			},
 | 
			
		||||
			"options": {
 | 
			
		||||
				"cwd": "${workspaceFolder}"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Maintenance: Install Frontend Dependencies",
 | 
			
		||||
			"description": "Install frontend (pnpm) dependencies",
 | 
			
		||||
			"type": "pnpm",
 | 
			
		||||
			"script": "install",
 | 
			
		||||
			"path": "src-ui",
 | 
			
		||||
			"group": "clean",
 | 
			
		||||
			"problemMatcher": [],
 | 
			
		||||
			"detail": "install dependencies from package"
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"description": "Clean install frontend dependencies and build the frontend for production",
 | 
			
		||||
			"label": "Maintenance: Compile frontend for production",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"command": "pnpm install && ./node_modules/.bin/ng build --configuration production",
 | 
			
		||||
			"group": "none",
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"echo": true,
 | 
			
		||||
				"reveal": "always",
 | 
			
		||||
				"focus": true,
 | 
			
		||||
				"panel": "shared",
 | 
			
		||||
				"showReuseMessage": false,
 | 
			
		||||
				"clear": true,
 | 
			
		||||
				"revealProblems": "onProblem"
 | 
			
		||||
			},
 | 
			
		||||
			"options": {
 | 
			
		||||
				"cwd": "${workspaceFolder}/src-ui"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Project Setup: Run all Init Tasks",
 | 
			
		||||
			"description": "Runs all init tasks to setup the project including migrate the database, create a superuser and compile the frontend for production",
 | 
			
		||||
			"dependsOrder": "sequence",
 | 
			
		||||
			"dependsOn": [
 | 
			
		||||
				"Maintenance: manage.py migrate",
 | 
			
		||||
				"Maintenance: manage.py createsuperuser",
 | 
			
		||||
				"Maintenance: Compile frontend for production"
 | 
			
		||||
			]
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "Project Start: Run all Services",
 | 
			
		||||
			"description": "Runs all services required to start the project including the Celery Worker, the Consumer Service and the Backend Server",
 | 
			
		||||
			"dependsOn": [
 | 
			
		||||
				"Start: Celery Worker",
 | 
			
		||||
				"Start: Consumer Service (manage.py document_consumer)",
 | 
			
		||||
				"Start: Backend Server (manage.py runserver)"
 | 
			
		||||
			]
 | 
			
		||||
		"options": {
 | 
			
		||||
			"cwd": "${workspaceFolder}/src"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"label": "manage.py runserver",
 | 
			
		||||
			"type": "shell",
 | 
			
		||||
			"command": "pipenv run python manage.py runserver",
 | 
			
		||||
			"group": "build",
 | 
			
		||||
			"presentation": {
 | 
			
		||||
				"echo": true,
 | 
			
		||||
				"reveal": "always",
 | 
			
		||||
				"focus": false,
 | 
			
		||||
				"panel": "shared",
 | 
			
		||||
				"showReuseMessage": false,
 | 
			
		||||
				"clear": true,
 | 
			
		||||
				"revealProblems": "onProblem"
 | 
			
		||||
			},
 | 
			
		||||
			"options": {
 | 
			
		||||
				"cwd": "${workspaceFolder}/src"
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			},
 | 
			
		||||
	  {
 | 
			
		||||
		"label": "Maintenance: manage.py migrate",
 | 
			
		||||
		"type": "shell",
 | 
			
		||||
		"command": "pipenv run python manage.py migrate",
 | 
			
		||||
		"group": "none",
 | 
			
		||||
		"presentation": {
 | 
			
		||||
			"echo": true,
 | 
			
		||||
			"reveal": "always",
 | 
			
		||||
			"focus": true,
 | 
			
		||||
			"panel": "shared",
 | 
			
		||||
			"showReuseMessage": false,
 | 
			
		||||
			"clear": true,
 | 
			
		||||
			"revealProblems": "onProblem"
 | 
			
		||||
		},
 | 
			
		||||
		"options": {
 | 
			
		||||
			"cwd": "${workspaceFolder}/src"
 | 
			
		||||
		}
 | 
			
		||||
	  },
 | 
			
		||||
	  {
 | 
			
		||||
		"label": "Maintenance: manage.py createsuperuser",
 | 
			
		||||
		"type": "shell",
 | 
			
		||||
		"command": "pipenv run python manage.py createsuperuser",
 | 
			
		||||
		"group": "none",
 | 
			
		||||
		"presentation": {
 | 
			
		||||
			"echo": true,
 | 
			
		||||
			"reveal": "always",
 | 
			
		||||
			"focus": true,
 | 
			
		||||
			"panel": "shared",
 | 
			
		||||
			"showReuseMessage": false,
 | 
			
		||||
			"clear": true,
 | 
			
		||||
			"revealProblems": "onProblem"
 | 
			
		||||
		},
 | 
			
		||||
		"options": {
 | 
			
		||||
			"cwd": "${workspaceFolder}/src"
 | 
			
		||||
		}
 | 
			
		||||
	  },
 | 
			
		||||
	  {
 | 
			
		||||
		"label": "compile frontend",
 | 
			
		||||
		"type": "shell",
 | 
			
		||||
		"command": "npm ci && ./node_modules/.bin/ng build --configuration production",
 | 
			
		||||
		"group": "none",
 | 
			
		||||
		"presentation": {
 | 
			
		||||
			"echo": true,
 | 
			
		||||
			"reveal": "always",
 | 
			
		||||
			"focus": true,
 | 
			
		||||
			"panel": "shared",
 | 
			
		||||
			"showReuseMessage": false,
 | 
			
		||||
			"clear": true,
 | 
			
		||||
			"revealProblems": "onProblem"
 | 
			
		||||
		},
 | 
			
		||||
		"options": {
 | 
			
		||||
			"cwd": "${workspaceFolder}/src-ui"
 | 
			
		||||
		}
 | 
			
		||||
	  },
 | 
			
		||||
	  {
 | 
			
		||||
		"label": "Maintenance: recreate .venv",
 | 
			
		||||
		"type": "shell",
 | 
			
		||||
		"command": "rm -R -v .venv/* || pipenv install --dev",
 | 
			
		||||
		"group": "none",
 | 
			
		||||
		"presentation": {
 | 
			
		||||
			"echo": true,
 | 
			
		||||
			"reveal": "always",
 | 
			
		||||
			"focus": true,
 | 
			
		||||
			"panel": "shared",
 | 
			
		||||
			"showReuseMessage": false,
 | 
			
		||||
			"clear": true,
 | 
			
		||||
			"revealProblems": "onProblem"
 | 
			
		||||
		},
 | 
			
		||||
		"options": {
 | 
			
		||||
			"cwd": "${workspaceFolder}"
 | 
			
		||||
		}
 | 
			
		||||
	  },
 | 
			
		||||
	  {
 | 
			
		||||
		"label": "Celery Worker",
 | 
			
		||||
		"type": "shell",
 | 
			
		||||
		"command": "pipenv run celery --app paperless worker -l DEBUG",
 | 
			
		||||
		"group": {
 | 
			
		||||
		  "kind": "build",
 | 
			
		||||
		  "isDefault": true
 | 
			
		||||
		},
 | 
			
		||||
		"presentation": {
 | 
			
		||||
			"echo": true,
 | 
			
		||||
			"reveal": "always",
 | 
			
		||||
			"focus": true,
 | 
			
		||||
			"panel": "shared",
 | 
			
		||||
			"showReuseMessage": false,
 | 
			
		||||
			"clear": true,
 | 
			
		||||
			"revealProblems": "onProblem"
 | 
			
		||||
		},
 | 
			
		||||
		"options": {
 | 
			
		||||
			"cwd": "${workspaceFolder}/src"
 | 
			
		||||
		}
 | 
			
		||||
	  }
 | 
			
		||||
	]
 | 
			
		||||
}
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -26,5 +26,3 @@
 | 
			
		||||
./dist
 | 
			
		||||
./scripts
 | 
			
		||||
./resources
 | 
			
		||||
# Other stuff
 | 
			
		||||
**/*.drawio.png
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,9 @@ indent_style = space
 | 
			
		||||
[*.md]
 | 
			
		||||
indent_style = space
 | 
			
		||||
 | 
			
		||||
[Pipfile.lock]
 | 
			
		||||
indent_style = space
 | 
			
		||||
 | 
			
		||||
# 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
 | 
			
		||||
# violate it.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
github: [shamoon, stumpylog]
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug-report.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug-report.yml
									
									
									
									
										vendored
									
									
								
							@@ -98,7 +98,7 @@ body:
 | 
			
		||||
      label: Browser
 | 
			
		||||
      description: Which browser you are using, if relevant.
 | 
			
		||||
      placeholder: e.g. Chrome, Safari
 | 
			
		||||
  - type: textarea
 | 
			
		||||
  - type: input
 | 
			
		||||
    id: config-changes
 | 
			
		||||
    attributes:
 | 
			
		||||
      label: Configuration changes
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/ISSUE_TEMPLATE/config.yml
									
									
									
									
										vendored
									
									
								
							@@ -2,10 +2,10 @@ blank_issues_enabled: false
 | 
			
		||||
contact_links:
 | 
			
		||||
  - name: 🤔 Questions and Help
 | 
			
		||||
    url: https://github.com/paperless-ngx/paperless-ngx/discussions
 | 
			
		||||
    about: General questions or support for using Paperless-ngx.
 | 
			
		||||
    about: This issue tracker is not for support questions. Please refer to our Discussions.
 | 
			
		||||
  - name: 💬 Chat
 | 
			
		||||
    url: https://matrix.to/#/#paperlessngx:matrix.org
 | 
			
		||||
    about: Want to discuss Paperless-ngx with others? Check out our chat.
 | 
			
		||||
  - name: 🚀 Feature Request
 | 
			
		||||
    url: https://github.com/paperless-ngx/paperless-ngx/discussions/new?category=feature-requests
 | 
			
		||||
    about: Remember to search for existing feature requests and "up-vote" those that you like.
 | 
			
		||||
    about: Remember to search for existing feature requests and "up-vote" any you like
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										66
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							@@ -1,15 +1,12 @@
 | 
			
		||||
# Please see the documentation for all configuration options:
 | 
			
		||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
 | 
			
		||||
# https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates#package-ecosystem
 | 
			
		||||
 | 
			
		||||
version: 2
 | 
			
		||||
# Required for uv support for now
 | 
			
		||||
enable-beta-ecosystems: true
 | 
			
		||||
updates:
 | 
			
		||||
 | 
			
		||||
  # Enable version updates for pnpm
 | 
			
		||||
  # Enable version updates for npm
 | 
			
		||||
  - package-ecosystem: "npm"
 | 
			
		||||
    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"
 | 
			
		||||
    open-pull-requests-limit: 10
 | 
			
		||||
    schedule:
 | 
			
		||||
@@ -37,8 +34,9 @@ updates:
 | 
			
		||||
          - "eslint"
 | 
			
		||||
 | 
			
		||||
  # Enable version updates for Python
 | 
			
		||||
  - package-ecosystem: "uv"
 | 
			
		||||
  - package-ecosystem: "pip"
 | 
			
		||||
    target-branch: "dev"
 | 
			
		||||
    # Look for a `Pipfile` in the `root` directory
 | 
			
		||||
    directory: "/"
 | 
			
		||||
    # Check for updates once a week
 | 
			
		||||
    schedule:
 | 
			
		||||
@@ -49,13 +47,14 @@ updates:
 | 
			
		||||
    # Add reviewers
 | 
			
		||||
    reviewers:
 | 
			
		||||
      - "paperless-ngx/backend"
 | 
			
		||||
    ignore:
 | 
			
		||||
      - dependency-name: "uvicorn"
 | 
			
		||||
    groups:
 | 
			
		||||
      development:
 | 
			
		||||
        patterns:
 | 
			
		||||
          - "*pytest*"
 | 
			
		||||
          - "ruff"
 | 
			
		||||
          - "mkdocs-material"
 | 
			
		||||
          - "pre-commit*"
 | 
			
		||||
      django:
 | 
			
		||||
        patterns:
 | 
			
		||||
          - "*django*"
 | 
			
		||||
@@ -66,10 +65,6 @@ updates:
 | 
			
		||||
        update-types:
 | 
			
		||||
          - "minor"
 | 
			
		||||
          - "patch"
 | 
			
		||||
      pre-built:
 | 
			
		||||
        patterns:
 | 
			
		||||
          - psycopg*
 | 
			
		||||
          - zxing-cpp
 | 
			
		||||
 | 
			
		||||
  # Enable updates for GitHub Actions
 | 
			
		||||
  - package-ecosystem: "github-actions"
 | 
			
		||||
@@ -90,50 +85,3 @@ updates:
 | 
			
		||||
          - "major"
 | 
			
		||||
          - "minor"
 | 
			
		||||
          - "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*"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										282
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										282
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							@@ -14,7 +14,9 @@ on:
 | 
			
		||||
      - 'translations**'
 | 
			
		||||
 | 
			
		||||
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.0.3"
 | 
			
		||||
  # This is the default version of Python to use in most steps which aren't specific
 | 
			
		||||
  DEFAULT_PYTHON_VERSION: "3.11"
 | 
			
		||||
 | 
			
		||||
@@ -28,7 +30,7 @@ jobs:
 | 
			
		||||
      github.repository
 | 
			
		||||
 | 
			
		||||
    name: Linting Checks
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    steps:
 | 
			
		||||
      -
 | 
			
		||||
        name: Checkout repository
 | 
			
		||||
@@ -44,7 +46,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
  documentation:
 | 
			
		||||
    name: "Build & Deploy Documentation"
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    needs:
 | 
			
		||||
      - pre-commit
 | 
			
		||||
    steps:
 | 
			
		||||
@@ -57,25 +59,24 @@ jobs:
 | 
			
		||||
        uses: actions/setup-python@v5
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
 | 
			
		||||
          cache: "pipenv"
 | 
			
		||||
          cache-dependency-path: 'Pipfile.lock'
 | 
			
		||||
      -
 | 
			
		||||
        name: Install uv
 | 
			
		||||
        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
 | 
			
		||||
        name: Install pipenv
 | 
			
		||||
        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
 | 
			
		||||
        run: |
 | 
			
		||||
          uv run \
 | 
			
		||||
            --python ${{ steps.setup-python.outputs.python-version }} \
 | 
			
		||||
            --dev \
 | 
			
		||||
            --frozen \
 | 
			
		||||
            mkdocs build --config-file ./mkdocs.yml
 | 
			
		||||
          pipenv --python ${{ steps.setup-python.outputs.python-version }} run mkdocs build --config-file ./mkdocs.yml
 | 
			
		||||
      -
 | 
			
		||||
        name: Deploy documentation
 | 
			
		||||
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
 | 
			
		||||
@@ -83,11 +84,7 @@ jobs:
 | 
			
		||||
          echo "docs.paperless-ngx.com" > "${{ github.workspace }}/docs/CNAME"
 | 
			
		||||
          git config --global user.name "${{ github.actor }}"
 | 
			
		||||
          git config --global user.email "${{ github.actor }}@users.noreply.github.com"
 | 
			
		||||
          uv run \
 | 
			
		||||
            --python ${{ steps.setup-python.outputs.python-version }} \
 | 
			
		||||
            --dev \
 | 
			
		||||
            --frozen \
 | 
			
		||||
            mkdocs gh-deploy --force --no-history
 | 
			
		||||
          pipenv --python ${{ steps.setup-python.outputs.python-version }} run mkdocs gh-deploy --force --no-history
 | 
			
		||||
      -
 | 
			
		||||
        name: Upload artifact
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
@@ -98,7 +95,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
  tests-backend:
 | 
			
		||||
    name: "Backend Tests (Python ${{ matrix.python-version }})"
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    needs:
 | 
			
		||||
      - pre-commit
 | 
			
		||||
    strategy:
 | 
			
		||||
@@ -120,13 +117,12 @@ jobs:
 | 
			
		||||
        uses: actions/setup-python@v5
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: "${{ matrix.python-version }}"
 | 
			
		||||
          cache: "pipenv"
 | 
			
		||||
          cache-dependency-path: 'Pipfile.lock'
 | 
			
		||||
      -
 | 
			
		||||
        name: Install uv
 | 
			
		||||
        uses: astral-sh/setup-uv@v5
 | 
			
		||||
        with:
 | 
			
		||||
          version: ${{ env.DEFAULT_UV_VERSION }}
 | 
			
		||||
          enable-cache: true
 | 
			
		||||
          python-version: ${{ steps.setup-python.outputs.python-version }}
 | 
			
		||||
        name: Install pipenv
 | 
			
		||||
        run: |
 | 
			
		||||
          pip install --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }}
 | 
			
		||||
      -
 | 
			
		||||
        name: Install system dependencies
 | 
			
		||||
        run: |
 | 
			
		||||
@@ -135,18 +131,16 @@ jobs:
 | 
			
		||||
      -
 | 
			
		||||
        name: Configure ImageMagick
 | 
			
		||||
        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
 | 
			
		||||
        run: |
 | 
			
		||||
          uv sync \
 | 
			
		||||
            --python ${{ steps.setup-python.outputs.python-version }} \
 | 
			
		||||
            --group testing \
 | 
			
		||||
            --frozen
 | 
			
		||||
          pipenv --python ${{ steps.setup-python.outputs.python-version }} run python --version
 | 
			
		||||
          pipenv --python ${{ steps.setup-python.outputs.python-version }} sync --dev
 | 
			
		||||
      -
 | 
			
		||||
        name: List installed Python dependencies
 | 
			
		||||
        run: |
 | 
			
		||||
          uv pip list
 | 
			
		||||
          pipenv --python ${{ steps.setup-python.outputs.python-version }} run pip list
 | 
			
		||||
      -
 | 
			
		||||
        name: Tests
 | 
			
		||||
        env:
 | 
			
		||||
@@ -156,26 +150,17 @@ jobs:
 | 
			
		||||
          PAPERLESS_MAIL_TEST_USER: ${{ secrets.TEST_MAIL_USER }}
 | 
			
		||||
          PAPERLESS_MAIL_TEST_PASSWD: ${{ secrets.TEST_MAIL_PASSWD }}
 | 
			
		||||
        run: |
 | 
			
		||||
          uv run \
 | 
			
		||||
            --python ${{ steps.setup-python.outputs.python-version }} \
 | 
			
		||||
            --dev \
 | 
			
		||||
            --frozen \
 | 
			
		||||
            pytest
 | 
			
		||||
          cd src/
 | 
			
		||||
          pipenv --python ${{ steps.setup-python.outputs.python-version }} run pytest -ra
 | 
			
		||||
      -
 | 
			
		||||
        name: Upload backend test results to Codecov
 | 
			
		||||
        if: always()
 | 
			
		||||
        uses: codecov/test-results-action@v1
 | 
			
		||||
        name: Upload coverage
 | 
			
		||||
        if: ${{ matrix.python-version == env.DEFAULT_PYTHON_VERSION }}
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
			
		||||
          flags: backend-python-${{ matrix.python-version }}
 | 
			
		||||
          files: junit.xml
 | 
			
		||||
      -
 | 
			
		||||
        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: backend-coverage-report
 | 
			
		||||
          path: src/coverage.xml
 | 
			
		||||
          retention-days: 7
 | 
			
		||||
          if-no-files-found: warn
 | 
			
		||||
      -
 | 
			
		||||
        name: Stop containers
 | 
			
		||||
        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 down
 | 
			
		||||
 | 
			
		||||
  install-frontend-dependencies:
 | 
			
		||||
  install-frontend-depedendencies:
 | 
			
		||||
    name: "Install Frontend Dependencies"
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    needs:
 | 
			
		||||
      - pre-commit
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Install pnpm
 | 
			
		||||
        uses: pnpm/action-setup@v4
 | 
			
		||||
        with:
 | 
			
		||||
          version: 10
 | 
			
		||||
      -
 | 
			
		||||
        name: Use Node.js 20
 | 
			
		||||
        uses: actions/setup-node@v4
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 20.x
 | 
			
		||||
          cache: 'pnpm'
 | 
			
		||||
          cache-dependency-path: 'src-ui/pnpm-lock.yaml'
 | 
			
		||||
          cache: 'npm'
 | 
			
		||||
          cache-dependency-path: 'src-ui/package-lock.json'
 | 
			
		||||
      - name: Cache frontend dependencies
 | 
			
		||||
        id: cache-frontend-deps
 | 
			
		||||
        uses: actions/cache@v4
 | 
			
		||||
        with:
 | 
			
		||||
          path: |
 | 
			
		||||
            ~/.pnpm-store
 | 
			
		||||
            ~/.npm
 | 
			
		||||
            ~/.cache
 | 
			
		||||
          key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/pnpm-lock.yaml') }}
 | 
			
		||||
          key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }}
 | 
			
		||||
      -
 | 
			
		||||
        name: Install dependencies
 | 
			
		||||
        if: steps.cache-frontend-deps.outputs.cache-hit != 'true'
 | 
			
		||||
        run: cd src-ui && pnpm install
 | 
			
		||||
        run: cd src-ui && npm ci
 | 
			
		||||
      -
 | 
			
		||||
        name: Install Playwright
 | 
			
		||||
        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:
 | 
			
		||||
    name: "Frontend Tests (Node ${{ matrix.node-version }} - ${{ matrix.shard-index }}/${{ matrix.shard-count }})"
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    needs:
 | 
			
		||||
      - install-frontend-dependencies
 | 
			
		||||
      - install-frontend-depedendencies
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
@@ -231,92 +212,128 @@ jobs:
 | 
			
		||||
        shard-count: [4]
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      - name: Install pnpm
 | 
			
		||||
        uses: pnpm/action-setup@v4
 | 
			
		||||
        with:
 | 
			
		||||
          version: 10
 | 
			
		||||
      -
 | 
			
		||||
        name: Use Node.js 20
 | 
			
		||||
        uses: actions/setup-node@v4
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 20.x
 | 
			
		||||
          cache: 'pnpm'
 | 
			
		||||
          cache-dependency-path: 'src-ui/pnpm-lock.yaml'
 | 
			
		||||
          cache: 'npm'
 | 
			
		||||
          cache-dependency-path: 'src-ui/package-lock.json'
 | 
			
		||||
      - name: Cache frontend dependencies
 | 
			
		||||
        id: cache-frontend-deps
 | 
			
		||||
        uses: actions/cache@v4
 | 
			
		||||
        with:
 | 
			
		||||
          path: |
 | 
			
		||||
            ~/.pnpm-store
 | 
			
		||||
            ~/.npm
 | 
			
		||||
            ~/.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
 | 
			
		||||
        run: cd src-ui && pnpm link @angular/cli
 | 
			
		||||
        run: cd src-ui && npm link @angular/cli
 | 
			
		||||
      -
 | 
			
		||||
        name: Linting checks
 | 
			
		||||
        run: cd src-ui && pnpm run lint
 | 
			
		||||
        run: cd src-ui && npm run lint
 | 
			
		||||
      -
 | 
			
		||||
        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
 | 
			
		||||
        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
 | 
			
		||||
        uses: codecov/test-results-action@v1
 | 
			
		||||
        name: Upload Playwright test results
 | 
			
		||||
        if: always()
 | 
			
		||||
        uses: actions/upload-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
			
		||||
          flags: frontend-node-${{ matrix.node-version }}
 | 
			
		||||
          directory: src-ui/
 | 
			
		||||
      -
 | 
			
		||||
        name: Upload frontend coverage to Codecov
 | 
			
		||||
        uses: codecov/codecov-action@v5
 | 
			
		||||
        with:
 | 
			
		||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
			
		||||
          flags: frontend-node-${{ matrix.node-version }}
 | 
			
		||||
          directory: src-ui/coverage/
 | 
			
		||||
          name: playwright-report-${{ matrix.shard-index }}
 | 
			
		||||
          path: src-ui/playwright-report
 | 
			
		||||
          retention-days: 7
 | 
			
		||||
 | 
			
		||||
  frontend-bundle-analysis:
 | 
			
		||||
    name: "Frontend Bundle Analysis"
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
  tests-coverage-upload:
 | 
			
		||||
    name: "Upload to Codecov"
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    needs:
 | 
			
		||||
      - tests-backend
 | 
			
		||||
      - tests-frontend
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/checkout@v4
 | 
			
		||||
      -
 | 
			
		||||
        name: Install pnpm
 | 
			
		||||
        uses: pnpm/action-setup@v4
 | 
			
		||||
        uses: actions/checkout@v4
 | 
			
		||||
      -
 | 
			
		||||
        name: Download frontend jest coverage
 | 
			
		||||
        uses: actions/download-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          version: 10
 | 
			
		||||
          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
 | 
			
		||||
        uses: codecov/codecov-action@v4
 | 
			
		||||
        with:
 | 
			
		||||
          # not required for public repos, but intermittently fails otherwise
 | 
			
		||||
          token: ${{ secrets.CODECOV_TOKEN }}
 | 
			
		||||
          flags: frontend
 | 
			
		||||
          directory: src-ui/coverage/
 | 
			
		||||
          # dont include backend coverage files here
 | 
			
		||||
          files: '!coverage.xml'
 | 
			
		||||
      -
 | 
			
		||||
        name: Download backend coverage
 | 
			
		||||
        uses: actions/download-artifact@v4
 | 
			
		||||
        with:
 | 
			
		||||
          name: backend-coverage-report
 | 
			
		||||
          path: src/
 | 
			
		||||
      -
 | 
			
		||||
        name: Upload coverage to Codecov
 | 
			
		||||
        uses: codecov/codecov-action@v4
 | 
			
		||||
        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
 | 
			
		||||
        uses: actions/setup-node@v4
 | 
			
		||||
        with:
 | 
			
		||||
          node-version: 20.x
 | 
			
		||||
          cache: 'pnpm'
 | 
			
		||||
          cache-dependency-path: 'src-ui/pnpm-lock.yaml'
 | 
			
		||||
          cache: 'npm'
 | 
			
		||||
          cache-dependency-path: 'src-ui/package-lock.json'
 | 
			
		||||
      -
 | 
			
		||||
        name: Cache frontend dependencies
 | 
			
		||||
        id: cache-frontend-deps
 | 
			
		||||
        uses: actions/cache@v4
 | 
			
		||||
        with:
 | 
			
		||||
          path: |
 | 
			
		||||
            ~/.pnpm-store
 | 
			
		||||
            ~/.npm
 | 
			
		||||
            ~/.cache
 | 
			
		||||
          key: ${{ runner.os }}-frontenddeps-${{ hashFiles('src-ui/package-lock.json') }}
 | 
			
		||||
      -
 | 
			
		||||
        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
 | 
			
		||||
        env:
 | 
			
		||||
          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:
 | 
			
		||||
    name: Build Docker image for ${{ github.ref_name }}
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    if: github.event_name == 'push' && (startsWith(github.ref, 'refs/heads/feature-') || startsWith(github.ref, 'refs/heads/fix-') || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/beta' || contains(github.ref, 'beta.rc') || startsWith(github.ref, 'refs/tags/v'))
 | 
			
		||||
    concurrency:
 | 
			
		||||
      group: ${{ github.workflow }}-build-docker-image-${{ github.ref_name }}
 | 
			
		||||
@@ -389,7 +406,7 @@ jobs:
 | 
			
		||||
      -
 | 
			
		||||
        name: Login to Docker Hub
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        # Don't attempt to login if not pushing to Docker Hub
 | 
			
		||||
        # Don't attempt to login is not pushing to Docker Hub
 | 
			
		||||
        if: steps.push-other-places.outputs.enable == 'true'
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKERHUB_USERNAME }}
 | 
			
		||||
@@ -397,7 +414,7 @@ jobs:
 | 
			
		||||
      -
 | 
			
		||||
        name: Login to Quay.io
 | 
			
		||||
        uses: docker/login-action@v3
 | 
			
		||||
        # Don't attempt to login if not pushing to Quay.io
 | 
			
		||||
        # Don't attempt to login is not pushing to Quay.io
 | 
			
		||||
        if: steps.push-other-places.outputs.enable == 'true'
 | 
			
		||||
        with:
 | 
			
		||||
          registry: quay.io
 | 
			
		||||
@@ -444,7 +461,7 @@ jobs:
 | 
			
		||||
    needs:
 | 
			
		||||
      - build-docker-image
 | 
			
		||||
      - documentation
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    steps:
 | 
			
		||||
      -
 | 
			
		||||
        name: Checkout
 | 
			
		||||
@@ -455,17 +472,16 @@ jobs:
 | 
			
		||||
        uses: actions/setup-python@v5
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
 | 
			
		||||
          cache: "pipenv"
 | 
			
		||||
          cache-dependency-path: 'Pipfile.lock'
 | 
			
		||||
      -
 | 
			
		||||
        name: Install uv
 | 
			
		||||
        uses: astral-sh/setup-uv@v5
 | 
			
		||||
        with:
 | 
			
		||||
          version: ${{ env.DEFAULT_UV_VERSION }}
 | 
			
		||||
          enable-cache: true
 | 
			
		||||
          python-version: ${{ steps.setup-python.outputs.python-version }}
 | 
			
		||||
        name: Install pipenv + tools
 | 
			
		||||
        run: |
 | 
			
		||||
          pip install --upgrade --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }} setuptools wheel
 | 
			
		||||
      -
 | 
			
		||||
        name: Install Python dependencies
 | 
			
		||||
        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
 | 
			
		||||
        run: |
 | 
			
		||||
@@ -486,21 +502,17 @@ jobs:
 | 
			
		||||
      -
 | 
			
		||||
        name: Generate requirements file
 | 
			
		||||
        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
 | 
			
		||||
        run: |
 | 
			
		||||
          cd src/
 | 
			
		||||
          uv run \
 | 
			
		||||
            --python ${{ steps.setup-python.outputs.python-version }} \
 | 
			
		||||
            manage.py compilemessages
 | 
			
		||||
          pipenv --python ${{ steps.setup-python.outputs.python-version }} run python3 manage.py compilemessages
 | 
			
		||||
      -
 | 
			
		||||
        name: Collect static files
 | 
			
		||||
        run: |
 | 
			
		||||
          cd src/
 | 
			
		||||
          uv run \
 | 
			
		||||
            --python ${{ steps.setup-python.outputs.python-version }} \
 | 
			
		||||
            manage.py collectstatic --no-input
 | 
			
		||||
          pipenv --python ${{ steps.setup-python.outputs.python-version }} run python3 manage.py collectstatic --no-input
 | 
			
		||||
      -
 | 
			
		||||
        name: Move files
 | 
			
		||||
        run: |
 | 
			
		||||
@@ -516,12 +528,13 @@ jobs:
 | 
			
		||||
          for file_name in .dockerignore \
 | 
			
		||||
                          .env \
 | 
			
		||||
                          Dockerfile \
 | 
			
		||||
                          pyproject.toml \
 | 
			
		||||
                          uv.lock \
 | 
			
		||||
                          Pipfile \
 | 
			
		||||
                          Pipfile.lock \
 | 
			
		||||
                          requirements.txt \
 | 
			
		||||
                          LICENSE \
 | 
			
		||||
                          README.md \
 | 
			
		||||
                          paperless.conf.example
 | 
			
		||||
                          paperless.conf.example \
 | 
			
		||||
                          gunicorn.conf.py
 | 
			
		||||
          do
 | 
			
		||||
            cp --verbose ${file_name} dist/paperless-ngx/
 | 
			
		||||
          done
 | 
			
		||||
@@ -556,7 +569,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
  publish-release:
 | 
			
		||||
    name: "Publish Release"
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    outputs:
 | 
			
		||||
      prerelease: ${{ steps.get_version.outputs.prerelease }}
 | 
			
		||||
      changelog: ${{ steps.create-release.outputs.body }}
 | 
			
		||||
@@ -606,7 +619,7 @@ jobs:
 | 
			
		||||
 | 
			
		||||
  append-changelog:
 | 
			
		||||
    name: "Append Changelog"
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    needs:
 | 
			
		||||
      - publish-release
 | 
			
		||||
    if: needs.publish-release.outputs.prerelease == 'false'
 | 
			
		||||
@@ -618,17 +631,15 @@ jobs:
 | 
			
		||||
          ref: main
 | 
			
		||||
      -
 | 
			
		||||
        name: Set up Python
 | 
			
		||||
        id: setup-python
 | 
			
		||||
        uses: actions/setup-python@v5
 | 
			
		||||
        with:
 | 
			
		||||
          python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
 | 
			
		||||
          cache: "pipenv"
 | 
			
		||||
          cache-dependency-path: 'Pipfile.lock'
 | 
			
		||||
      -
 | 
			
		||||
        name: Install uv
 | 
			
		||||
        uses: astral-sh/setup-uv@v5
 | 
			
		||||
        with:
 | 
			
		||||
          version: ${{ env.DEFAULT_UV_VERSION }}
 | 
			
		||||
          enable-cache: true
 | 
			
		||||
          python-version: ${{ env.DEFAULT_PYTHON_VERSION }}
 | 
			
		||||
        name: Install pipenv + tools
 | 
			
		||||
        run: |
 | 
			
		||||
          pip install --upgrade --user pipenv==${{ env.DEFAULT_PIP_ENV_VERSION }} setuptools wheel
 | 
			
		||||
      -
 | 
			
		||||
        name: Append Changelog to docs
 | 
			
		||||
        id: append-Changelog
 | 
			
		||||
@@ -644,10 +655,7 @@ jobs:
 | 
			
		||||
          CURRENT_CHANGELOG=`tail --lines +2 changelog.md`
 | 
			
		||||
          echo -e "$CURRENT_CHANGELOG" >> changelog-new.md
 | 
			
		||||
          mv changelog-new.md changelog.md
 | 
			
		||||
          uv run \
 | 
			
		||||
            --python ${{ steps.setup-python.outputs.python-version }} \
 | 
			
		||||
            --dev \
 | 
			
		||||
            pre-commit run --files changelog.md || true
 | 
			
		||||
          pipenv run pre-commit run --files changelog.md || true
 | 
			
		||||
          git config --global user.name "github-actions"
 | 
			
		||||
          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
 | 
			
		||||
          git commit -am "Changelog ${{ needs.publish-release.outputs.version }} - GHA"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								.github/workflows/cleanup-tags.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/cleanup-tags.yml
									
									
									
									
										vendored
									
									
								
							@@ -21,7 +21,7 @@ jobs:
 | 
			
		||||
  cleanup-images:
 | 
			
		||||
    name: Cleanup Image Tags for ${{ matrix.primary-name }}
 | 
			
		||||
    if: github.repository_owner == 'paperless-ngx'
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    strategy:
 | 
			
		||||
      fail-fast: false
 | 
			
		||||
      matrix:
 | 
			
		||||
@@ -33,7 +33,7 @@ jobs:
 | 
			
		||||
      -
 | 
			
		||||
        name: Clean temporary images
 | 
			
		||||
        if: "${{ env.TOKEN != '' }}"
 | 
			
		||||
        uses: stumpylog/image-cleaner-action/ephemeral@v0.10.0
 | 
			
		||||
        uses: stumpylog/image-cleaner-action/ephemeral@v0.8.0
 | 
			
		||||
        with:
 | 
			
		||||
          token: "${{ env.TOKEN }}"
 | 
			
		||||
          owner: "${{ github.repository_owner }}"
 | 
			
		||||
@@ -47,7 +47,7 @@ jobs:
 | 
			
		||||
  cleanup-untagged-images:
 | 
			
		||||
    name: Cleanup Untagged Images Tags for ${{ matrix.primary-name }}
 | 
			
		||||
    if: github.repository_owner == 'paperless-ngx'
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    needs:
 | 
			
		||||
      - cleanup-images
 | 
			
		||||
    strategy:
 | 
			
		||||
@@ -61,7 +61,7 @@ jobs:
 | 
			
		||||
      -
 | 
			
		||||
        name: Clean untagged images
 | 
			
		||||
        if: "${{ env.TOKEN != '' }}"
 | 
			
		||||
        uses: stumpylog/image-cleaner-action/untagged@v0.10.0
 | 
			
		||||
        uses: stumpylog/image-cleaner-action/untagged@v0.8.0
 | 
			
		||||
        with:
 | 
			
		||||
          token: "${{ env.TOKEN }}"
 | 
			
		||||
          owner: "${{ github.repository_owner }}"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/codeql-analysis.yml
									
									
									
									
										vendored
									
									
								
							@@ -23,7 +23,7 @@ on:
 | 
			
		||||
jobs:
 | 
			
		||||
  analyze:
 | 
			
		||||
    name: Analyze
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    permissions:
 | 
			
		||||
      actions: read
 | 
			
		||||
      contents: read
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/crowdin.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/crowdin.yml
									
									
									
									
										vendored
									
									
								
							@@ -16,7 +16,7 @@ jobs:
 | 
			
		||||
  synchronize-with-crowdin:
 | 
			
		||||
    name: Crowdin Sync
 | 
			
		||||
    if: github.repository_owner == 'paperless-ngx'
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
 | 
			
		||||
    steps:
 | 
			
		||||
    - name: Checkout
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								.github/workflows/project-actions.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/project-actions.yml
									
									
									
									
										vendored
									
									
								
							@@ -15,7 +15,7 @@ permissions:
 | 
			
		||||
jobs:
 | 
			
		||||
  pr_opened_or_reopened:
 | 
			
		||||
    name: pr_opened_or_reopened
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-22.04
 | 
			
		||||
    permissions:
 | 
			
		||||
      # write permission is required for autolabeler
 | 
			
		||||
      pull-requests: write
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								.github/workflows/repo-maintenance.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/repo-maintenance.yml
									
									
									
									
										vendored
									
									
								
							@@ -17,7 +17,7 @@ jobs:
 | 
			
		||||
  stale:
 | 
			
		||||
    name: 'Stale'
 | 
			
		||||
    if: github.repository_owner == 'paperless-ngx'
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/stale@v9
 | 
			
		||||
        with:
 | 
			
		||||
@@ -33,7 +33,7 @@ jobs:
 | 
			
		||||
  lock-threads:
 | 
			
		||||
    name: 'Lock Old Threads'
 | 
			
		||||
    if: github.repository_owner == 'paperless-ngx'
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: dessant/lock-threads@v5
 | 
			
		||||
        with:
 | 
			
		||||
@@ -59,7 +59,7 @@ jobs:
 | 
			
		||||
  close-answered-discussions:
 | 
			
		||||
    name: 'Close Answered Discussions'
 | 
			
		||||
    if: github.repository_owner == 'paperless-ngx'
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/github-script@v7
 | 
			
		||||
        with:
 | 
			
		||||
@@ -116,7 +116,7 @@ jobs:
 | 
			
		||||
  close-outdated-discussions:
 | 
			
		||||
    name: 'Close Outdated Discussions'
 | 
			
		||||
    if: github.repository_owner == 'paperless-ngx'
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/github-script@v7
 | 
			
		||||
        with:
 | 
			
		||||
@@ -208,7 +208,7 @@ jobs:
 | 
			
		||||
  close-unsupported-feature-requests:
 | 
			
		||||
    name: 'Close Unsupported Feature Requests'
 | 
			
		||||
    if: github.repository_owner == 'paperless-ngx'
 | 
			
		||||
    runs-on: ubuntu-24.04
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - uses: actions/github-script@v7
 | 
			
		||||
        with:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -44,7 +44,6 @@ nosetests.xml
 | 
			
		||||
coverage.xml
 | 
			
		||||
*,cover
 | 
			
		||||
.pytest_cache
 | 
			
		||||
junit.xml
 | 
			
		||||
 | 
			
		||||
# Translations
 | 
			
		||||
*.mo
 | 
			
		||||
@@ -101,9 +100,3 @@ scripts/nuke
 | 
			
		||||
 | 
			
		||||
# celery schedule file
 | 
			
		||||
celerybeat-schedule*
 | 
			
		||||
 | 
			
		||||
# ignore .devcontainer sub folders
 | 
			
		||||
/.devcontainer/consume/
 | 
			
		||||
/.devcontainer/data/
 | 
			
		||||
/.devcontainer/media/
 | 
			
		||||
/.devcontainer/redisdata/
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
repos:
 | 
			
		||||
  # General hooks
 | 
			
		||||
  - repo: https://github.com/pre-commit/pre-commit-hooks
 | 
			
		||||
    rev: v5.0.0
 | 
			
		||||
    rev: v4.6.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: check-docstring-first
 | 
			
		||||
      - id: check-json
 | 
			
		||||
@@ -29,10 +29,10 @@ repos:
 | 
			
		||||
      - id: check-case-conflict
 | 
			
		||||
      - id: detect-private-key
 | 
			
		||||
  - repo: https://github.com/codespell-project/codespell
 | 
			
		||||
    rev: v2.4.0
 | 
			
		||||
    rev: v2.3.0
 | 
			
		||||
    hooks:
 | 
			
		||||
      - 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:
 | 
			
		||||
          - pofile
 | 
			
		||||
          - json
 | 
			
		||||
@@ -45,19 +45,13 @@ repos:
 | 
			
		||||
          - javascript
 | 
			
		||||
          - ts
 | 
			
		||||
          - markdown
 | 
			
		||||
        additional_dependencies:
 | 
			
		||||
          - prettier@3.3.3
 | 
			
		||||
          - 'prettier-plugin-organize-imports@4.1.0'
 | 
			
		||||
        exclude: "(^Pipfile\\.lock$)"
 | 
			
		||||
  # Python hooks
 | 
			
		||||
  - repo: https://github.com/astral-sh/ruff-pre-commit
 | 
			
		||||
    rev: v0.9.9
 | 
			
		||||
    rev: 'v0.6.8'
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: ruff
 | 
			
		||||
      - id: ruff-format
 | 
			
		||||
  - repo: https://github.com/tox-dev/pyproject-fmt
 | 
			
		||||
    rev: "v2.5.1"
 | 
			
		||||
    hooks:
 | 
			
		||||
      - id: pyproject-fmt
 | 
			
		||||
  # Dockerfile hooks
 | 
			
		||||
  - repo: https://github.com/AleksaC/hadolint-py
 | 
			
		||||
    rev: v2.12.0.3
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
{
 | 
			
		||||
    # https://prettier.io/docs/en/options.html#semicolons
 | 
			
		||||
    "semi": false,
 | 
			
		||||
    # https://prettier.io/docs/en/options.html#quotes
 | 
			
		||||
    "singleQuote": true,
 | 
			
		||||
    # https://prettier.io/docs/en/options.html#trailing-commas
 | 
			
		||||
    "trailingComma": "es5",
 | 
			
		||||
    "overrides": [
 | 
			
		||||
        {
 | 
			
		||||
            "files": ["docs/*.md"],
 | 
			
		||||
            "options": {
 | 
			
		||||
                "tabWidth": 4,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,19 +0,0 @@
 | 
			
		||||
const config = {
 | 
			
		||||
	// https://prettier.io/docs/en/options.html#semicolons
 | 
			
		||||
	semi: false,
 | 
			
		||||
	// https://prettier.io/docs/en/options.html#quotes
 | 
			
		||||
	singleQuote: true,
 | 
			
		||||
	// https://prettier.io/docs/en/options.html#trailing-commas
 | 
			
		||||
	trailingComma: 'es5',
 | 
			
		||||
	overrides: [
 | 
			
		||||
		{
 | 
			
		||||
			files: ['docs/*.md'],
 | 
			
		||||
			options: {
 | 
			
		||||
				tabWidth: 4,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	],
 | 
			
		||||
	plugins: [require('prettier-plugin-organize-imports')],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = config
 | 
			
		||||
							
								
								
									
										1
									
								
								.python-version
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.python-version
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
3.10.15
 | 
			
		||||
							
								
								
									
										47
									
								
								.ruff.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								.ruff.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,47 @@
 | 
			
		||||
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
 | 
			
		||||
]
 | 
			
		||||
# TODO 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"]
 | 
			
		||||
"*/tests/*.py" = ["E501", "SIM117"]
 | 
			
		||||
"*/migrations/*.py" = ["E501", "SIM", "T201"]
 | 
			
		||||
"src/paperless_tesseract/tests/test_parser.py" = ["RUF001"]
 | 
			
		||||
"src/documents/models.py" = ["SIM115"]
 | 
			
		||||
 | 
			
		||||
[lint.isort]
 | 
			
		||||
force-single-line = true
 | 
			
		||||
@@ -5,6 +5,5 @@
 | 
			
		||||
/src-ui/ @paperless-ngx/frontend
 | 
			
		||||
 | 
			
		||||
/src/ @paperless-ngx/backend
 | 
			
		||||
pyproject.toml @paperless-ngx/backend
 | 
			
		||||
uv.lock @paperless-ngx/backend
 | 
			
		||||
Pipfile* @paperless-ngx/backend
 | 
			
		||||
*.py @paperless-ngx/backend
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										183
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										183
									
								
								Dockerfile
									
									
									
									
									
								
							@@ -4,17 +4,15 @@
 | 
			
		||||
# Stage: compile-frontend
 | 
			
		||||
# Purpose: Compiles the frontend
 | 
			
		||||
# 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
 | 
			
		||||
 | 
			
		||||
COPY ./src-ui /src/src-ui
 | 
			
		||||
 | 
			
		||||
WORKDIR /src/src-ui
 | 
			
		||||
RUN set -eux \
 | 
			
		||||
  && npm update -g pnpm \
 | 
			
		||||
  && npm install -g corepack@latest \
 | 
			
		||||
  && corepack enable \
 | 
			
		||||
  && pnpm install
 | 
			
		||||
  && npm update npm -g \
 | 
			
		||||
  && npm ci
 | 
			
		||||
 | 
			
		||||
ARG PNGX_TAG_VERSION=
 | 
			
		||||
# Add the tag to the environment file if its a tagged dev build
 | 
			
		||||
@@ -28,66 +26,28 @@ esac
 | 
			
		||||
RUN set -eux \
 | 
			
		||||
  && ./node_modules/.bin/ng build --configuration production
 | 
			
		||||
 | 
			
		||||
# Stage: s6-overlay-base
 | 
			
		||||
# Purpose: Installs s6-overlay and rootfs
 | 
			
		||||
# Stage: pipenv-base
 | 
			
		||||
# Purpose: Generates a requirements.txt file for building
 | 
			
		||||
# Comments:
 | 
			
		||||
#  - Don't leave anything extra in here either
 | 
			
		||||
FROM ghcr.io/astral-sh/uv:0.6.9-python3.12-bookworm-slim AS s6-overlay-base
 | 
			
		||||
#  - pipenv dependencies are not left in the final image
 | 
			
		||||
#  - 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
 | 
			
		||||
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"
 | 
			
		||||
COPY Pipfile* ./
 | 
			
		||||
 | 
			
		||||
RUN set -eux \
 | 
			
		||||
    && echo "Installing build time packages" \
 | 
			
		||||
      && apt-get update \
 | 
			
		||||
      && apt-get install --yes --quiet --no-install-recommends ${S6_BUILD_TIME_PKGS} \
 | 
			
		||||
    && echo "Determining arch" \
 | 
			
		||||
      && 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 /
 | 
			
		||||
  && echo "Installing pipenv" \
 | 
			
		||||
    && python3 -m pip install --no-cache-dir --upgrade pipenv==2024.0.3 \
 | 
			
		||||
  && echo "Generating requirement.txt" \
 | 
			
		||||
    && pipenv requirements > requirements.txt
 | 
			
		||||
 | 
			
		||||
# Stage: main-app
 | 
			
		||||
# Purpose: The final image
 | 
			
		||||
# Comments:
 | 
			
		||||
#  - 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.documentation="https://docs.paperless-ngx.com/"
 | 
			
		||||
@@ -101,19 +61,16 @@ ARG DEBIAN_FRONTEND=noninteractive
 | 
			
		||||
ARG TARGETARCH
 | 
			
		||||
 | 
			
		||||
# 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 GS_VERSION=10.03.1
 | 
			
		||||
 | 
			
		||||
# Set Python environment variables
 | 
			
		||||
ENV PYTHONDONTWRITEBYTECODE=1 \
 | 
			
		||||
    PYTHONUNBUFFERED=1 \
 | 
			
		||||
    # Ignore warning from Whitenoise about async iterators
 | 
			
		||||
    # Ignore warning from Whitenoise
 | 
			
		||||
    PYTHONWARNINGS="ignore:::django.http.response:517" \
 | 
			
		||||
    PNGX_CONTAINERIZED=1 \
 | 
			
		||||
    # https://docs.astral.sh/uv/reference/settings/#link-mode
 | 
			
		||||
    UV_LINK_MODE=copy \
 | 
			
		||||
    UV_CACHE_DIR=/cache/uv/
 | 
			
		||||
    PNGX_CONTAINERIZED=1
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Begin installation and configuration
 | 
			
		||||
@@ -170,51 +127,118 @@ RUN set -eux \
 | 
			
		||||
    && apt-get update \
 | 
			
		||||
    && apt-get install --yes --quiet --no-install-recommends ${RUNTIME_PACKAGES} \
 | 
			
		||||
    && 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}" \
 | 
			
		||||
        && 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 ./qpdf_${QPDF_VERSION}-1_${TARGETARCH}.deb \
 | 
			
		||||
      && 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_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
 | 
			
		||||
        && dpkg --install ./ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \
 | 
			
		||||
      && 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 \
 | 
			
		||||
      && echo "Configuring imagemagick" \
 | 
			
		||||
        && cp /etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml \
 | 
			
		||||
      && echo "Cleaning up image layer" \
 | 
			
		||||
        && 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/
 | 
			
		||||
 | 
			
		||||
# Python dependencies
 | 
			
		||||
# 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
 | 
			
		||||
# dependencies
 | 
			
		||||
ARG BUILD_PACKAGES="\
 | 
			
		||||
  build-essential \
 | 
			
		||||
  git \
 | 
			
		||||
  # https://www.psycopg.org/docs/install.html#prerequisites
 | 
			
		||||
  libpq-dev \
 | 
			
		||||
  # https://github.com/PyMySQL/mysqlclient#linux
 | 
			
		||||
  default-libmysqlclient-dev \
 | 
			
		||||
  pkg-config"
 | 
			
		||||
 | 
			
		||||
# 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 \
 | 
			
		||||
  && echo "Installing build system packages" \
 | 
			
		||||
    && apt-get update \
 | 
			
		||||
    && apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \
 | 
			
		||||
    && python3 -m pip install --no-cache-dir --upgrade wheel \
 | 
			
		||||
  && echo "Installing Python requirements" \
 | 
			
		||||
    && uv export --quiet --no-dev --all-extras --format requirements-txt --output-file requirements.txt \
 | 
			
		||||
    && uv pip install --system --no-python-downloads --python-preference system --requirements requirements.txt \
 | 
			
		||||
    && curl --fail --silent --show-error --location \
 | 
			
		||||
    --output psycopg_c-3.2.2-cp312-cp312-linux_x86_64.whl \
 | 
			
		||||
    https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.2/psycopg_c-3.2.2-cp312-cp312-linux_x86_64.whl \
 | 
			
		||||
    && curl --fail --silent --show-error --location \
 | 
			
		||||
    --output psycopg_c-3.2.2-cp312-cp312-linux_aarch64.whl  \
 | 
			
		||||
    https://github.com/paperless-ngx/builder/releases/download/psycopg-3.2.2/psycopg_c-3.2.2-cp312-cp312-linux_aarch64.whl \
 | 
			
		||||
    && python3 -m pip install --default-timeout=1000 --find-links . --requirement requirements.txt \
 | 
			
		||||
  && 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" stopwords \
 | 
			
		||||
@@ -239,7 +263,6 @@ COPY --from=compile-frontend --chown=1000:1000 /src/src/documents/static/fronten
 | 
			
		||||
# add users, setup scripts
 | 
			
		||||
# Mount the compiled frontend to expected location
 | 
			
		||||
RUN set -eux \
 | 
			
		||||
  && sed -i '1s|^#!/usr/bin/env python3|#!/command/with-contenv python3|' manage.py \
 | 
			
		||||
  && echo "Setting up user/group" \
 | 
			
		||||
    && addgroup --gid 1000 paperless \
 | 
			
		||||
    && useradd --uid 1000 --gid paperless --home-dir /usr/src/paperless paperless \
 | 
			
		||||
@@ -253,16 +276,18 @@ RUN set -eux \
 | 
			
		||||
  && echo "Adjusting all permissions" \
 | 
			
		||||
    && chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless \
 | 
			
		||||
  && echo "Collecting static files" \
 | 
			
		||||
    && s6-setuidgid paperless python3 manage.py collectstatic --clear --no-input --link \
 | 
			
		||||
    && s6-setuidgid paperless python3 manage.py compilemessages
 | 
			
		||||
    && gosu paperless python3 manage.py collectstatic --clear --no-input --link \
 | 
			
		||||
    && gosu paperless python3 manage.py compilemessages
 | 
			
		||||
 | 
			
		||||
VOLUME ["/usr/src/paperless/data", \
 | 
			
		||||
        "/usr/src/paperless/media", \
 | 
			
		||||
        "/usr/src/paperless/consume", \
 | 
			
		||||
        "/usr/src/paperless/export"]
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["/init"]
 | 
			
		||||
ENTRYPOINT ["/sbin/docker-entrypoint.sh"]
 | 
			
		||||
 | 
			
		||||
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" ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										100
									
								
								Pipfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								Pipfile
									
									
									
									
									
										Normal 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.1"
 | 
			
		||||
django-allauth = {extras = ["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.1"
 | 
			
		||||
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.5"
 | 
			
		||||
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.5"
 | 
			
		||||
setproctitle = "*"
 | 
			
		||||
tika-client = "*"
 | 
			
		||||
tqdm = "*"
 | 
			
		||||
# See https://github.com/paperless-ngx/paperless-ngx/issues/5494
 | 
			
		||||
uvicorn = {extras = ["standard"], version = "==0.25.0"}
 | 
			
		||||
watchdog = "~=4.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 = "*"
 | 
			
		||||
							
								
								
									
										4489
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										4489
									
								
								Pipfile.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -55,7 +55,7 @@ A full list of [features](https://docs.paperless-ngx.com/#features) and [screens
 | 
			
		||||
 | 
			
		||||
# Getting started
 | 
			
		||||
 | 
			
		||||
The easiest way to deploy paperless is `docker compose`. The files in the [`/docker/compose` directory](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose) are configured to pull the image from the GitHub container registry.
 | 
			
		||||
The easiest way to deploy paperless is `docker compose`. The files in the [`/docker/compose` directory](https://github.com/paperless-ngx/paperless-ngx/tree/main/docker/compose) are configured to pull the image from GitHub Packages.
 | 
			
		||||
 | 
			
		||||
If you'd like to jump right in, you can configure a `docker compose` environment with our install script:
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  gotenberg:
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:8.17
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:8.7
 | 
			
		||||
    hostname: gotenberg
 | 
			
		||||
    container_name: gotenberg
 | 
			
		||||
    network_mode: host
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,26 @@
 | 
			
		||||
###############################################################################
 | 
			
		||||
# Paperless-ngx settings                                                      #
 | 
			
		||||
###############################################################################
 | 
			
		||||
 | 
			
		||||
# See http://docs.paperless-ngx.com/configuration/ for all available options.
 | 
			
		||||
 | 
			
		||||
# The UID and GID of the user used to run paperless in the container. Set this
 | 
			
		||||
# to your UID and GID on the host so that you have write access to the
 | 
			
		||||
# consumption directory.
 | 
			
		||||
#USERMAP_UID=1000
 | 
			
		||||
#USERMAP_GID=1000
 | 
			
		||||
 | 
			
		||||
# See the documentation linked above for all options. A few commonly adjusted settings
 | 
			
		||||
# are provided below.
 | 
			
		||||
# Additional languages to install for text recognition, separated by a
 | 
			
		||||
# whitespace. Note that this is
 | 
			
		||||
# different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines the
 | 
			
		||||
# language used for OCR.
 | 
			
		||||
# The container installs English, German, Italian, Spanish and French by
 | 
			
		||||
# default.
 | 
			
		||||
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster
 | 
			
		||||
# for available languages.
 | 
			
		||||
#PAPERLESS_OCR_LANGUAGES=tur ces
 | 
			
		||||
 | 
			
		||||
###############################################################################
 | 
			
		||||
# Paperless-specific settings                                                 #
 | 
			
		||||
###############################################################################
 | 
			
		||||
 | 
			
		||||
# All settings defined in the paperless.conf.example can be used here. The
 | 
			
		||||
# Docker setup does not use the configuration file.
 | 
			
		||||
# A few commonly adjusted settings are provided below.
 | 
			
		||||
 | 
			
		||||
# This is required if you will be exposing Paperless-ngx on a public domain
 | 
			
		||||
# (if doing so please consider security measures such as reverse proxy)
 | 
			
		||||
@@ -21,17 +30,13 @@
 | 
			
		||||
# be a very long sequence of random characters. You don't need to remember it.
 | 
			
		||||
#PAPERLESS_SECRET_KEY=change-me
 | 
			
		||||
 | 
			
		||||
# Use this variable to set a timezone for the Paperless Docker containers. Defaults to UTC.
 | 
			
		||||
# Use this variable to set a timezone for the Paperless Docker containers. If not specified, defaults to UTC.
 | 
			
		||||
#PAPERLESS_TIME_ZONE=America/Los_Angeles
 | 
			
		||||
 | 
			
		||||
# The default language to use for OCR. Set this to the language most of your
 | 
			
		||||
# documents are written in.
 | 
			
		||||
#PAPERLESS_OCR_LANGUAGE=eng
 | 
			
		||||
 | 
			
		||||
# Additional languages to install for text recognition, separated by a whitespace.
 | 
			
		||||
# Note that this is different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines
 | 
			
		||||
# the language used for OCR.
 | 
			
		||||
# The container installs English, German, Italian, Spanish and French by default.
 | 
			
		||||
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster
 | 
			
		||||
# for available languages.
 | 
			
		||||
#PAPERLESS_OCR_LANGUAGES=tur ces
 | 
			
		||||
# Set if accessing paperless via a domain subpath e.g. https://domain.com/PATHPREFIX and using a reverse-proxy like traefik or nginx
 | 
			
		||||
#PAPERLESS_FORCE_SCRIPT_NAME=/PATHPREFIX
 | 
			
		||||
#PAPERLESS_STATIC_URL=/PATHPREFIX/static/ # trailing slash required
 | 
			
		||||
 
 | 
			
		||||
@@ -24,10 +24,8 @@
 | 
			
		||||
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
 | 
			
		||||
#   and '.env' into a folder.
 | 
			
		||||
# - Run 'docker compose pull'.
 | 
			
		||||
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
 | 
			
		||||
# - Run 'docker compose up -d'.
 | 
			
		||||
# - Wait until the webserver has completed startup
 | 
			
		||||
# - Run 'docker compose exec webserver createsuperuser' to create a user.
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
@@ -79,7 +77,7 @@ services:
 | 
			
		||||
      PAPERLESS_TIKA_ENDPOINT: http://tika:9998
 | 
			
		||||
 | 
			
		||||
  gotenberg:
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:8.17
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:8.7
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    # The gotenberg chromium route is used to convert .eml files. We do not
 | 
			
		||||
    # want to allow external content like tracking pixels or even javascript.
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,8 @@
 | 
			
		||||
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
 | 
			
		||||
#   and '.env' into a folder.
 | 
			
		||||
# - Run 'docker compose pull'.
 | 
			
		||||
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
 | 
			
		||||
# - Run 'docker compose up -d'.
 | 
			
		||||
# - Wait until the webserver has completed startup
 | 
			
		||||
# - Run 'docker compose exec webserver createsuperuser' to create a user.
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,10 @@
 | 
			
		||||
#
 | 
			
		||||
# - Open portainer Stacks list and click 'Add stack'
 | 
			
		||||
# - Paste the contents of this file and assign a name, e.g. 'paperless'
 | 
			
		||||
# - Upload 'docker-compose.env' by clicking on 'Load variables from .env file'
 | 
			
		||||
# - Modify the environment variables as needed
 | 
			
		||||
# - 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 'createsuperuser' to create a user
 | 
			
		||||
# - Run 'python3 manage.py createsuperuser' to create a user
 | 
			
		||||
# - Exit the console
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
@@ -38,7 +36,7 @@ services:
 | 
			
		||||
      - redisdata:/data
 | 
			
		||||
 | 
			
		||||
  db:
 | 
			
		||||
    image: docker.io/library/postgres:17
 | 
			
		||||
    image: docker.io/library/postgres:16
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    volumes:
 | 
			
		||||
      - pgdata:/var/lib/postgresql/data
 | 
			
		||||
@@ -63,8 +61,28 @@ services:
 | 
			
		||||
    environment:
 | 
			
		||||
      PAPERLESS_REDIS: redis://broker:6379
 | 
			
		||||
      PAPERLESS_DBHOST: db
 | 
			
		||||
    env_file:
 | 
			
		||||
      - stack.env
 | 
			
		||||
# The UID and GID of the user used to run paperless in the container. Set this
 | 
			
		||||
# to your UID and GID on the host so that you have write access to the
 | 
			
		||||
# consumption directory.
 | 
			
		||||
      USERMAP_UID: 1000
 | 
			
		||||
      USERMAP_GID: 100
 | 
			
		||||
# Additional languages to install for text recognition, separated by a
 | 
			
		||||
# whitespace. Note that this is
 | 
			
		||||
# different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines the
 | 
			
		||||
# language used for OCR.
 | 
			
		||||
# The container installs English, German, Italian, Spanish and French by
 | 
			
		||||
# default.
 | 
			
		||||
# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster
 | 
			
		||||
# for available languages.
 | 
			
		||||
      #PAPERLESS_OCR_LANGUAGES: tur ces
 | 
			
		||||
# Adjust this key if you plan to make paperless available publicly. It should
 | 
			
		||||
# be a very long sequence of random characters. You don't need to remember it.
 | 
			
		||||
      #PAPERLESS_SECRET_KEY: change-me
 | 
			
		||||
# Use this variable to set a timezone for the Paperless Docker containers. If not specified, defaults to UTC.
 | 
			
		||||
      #PAPERLESS_TIME_ZONE: America/Los_Angeles
 | 
			
		||||
# The default language to use for OCR. Set this to the language most of your
 | 
			
		||||
# documents are written in.
 | 
			
		||||
      #PAPERLESS_OCR_LANGUAGE: eng
 | 
			
		||||
 | 
			
		||||
volumes:
 | 
			
		||||
  data:
 | 
			
		||||
 
 | 
			
		||||
@@ -24,9 +24,8 @@
 | 
			
		||||
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
 | 
			
		||||
#   and '.env' into a folder.
 | 
			
		||||
# - Run 'docker compose pull'.
 | 
			
		||||
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
 | 
			
		||||
# - Run 'docker compose up -d'.
 | 
			
		||||
# - Wait until the webserver has completed startup
 | 
			
		||||
# - Run 'docker compose exec webserver createsuperuser' to create a user.
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
@@ -39,7 +38,7 @@ services:
 | 
			
		||||
      - redisdata:/data
 | 
			
		||||
 | 
			
		||||
  db:
 | 
			
		||||
    image: docker.io/library/postgres:17
 | 
			
		||||
    image: docker.io/library/postgres:16
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    volumes:
 | 
			
		||||
      - pgdata:/var/lib/postgresql/data
 | 
			
		||||
@@ -72,7 +71,7 @@ services:
 | 
			
		||||
      PAPERLESS_TIKA_ENDPOINT: http://tika:9998
 | 
			
		||||
 | 
			
		||||
  gotenberg:
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:8.17
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:8.7
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
 | 
			
		||||
    # The gotenberg chromium route is used to convert .eml files. We do not
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,8 @@
 | 
			
		||||
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
 | 
			
		||||
#   and '.env' into a folder.
 | 
			
		||||
# - Run 'docker compose pull'.
 | 
			
		||||
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
 | 
			
		||||
# - Run 'docker compose up -d'.
 | 
			
		||||
# - Wait until the webserver has completed startup
 | 
			
		||||
# - Run 'docker compose exec webserver createsuperuser' to create a user.
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
@@ -35,7 +34,7 @@ services:
 | 
			
		||||
      - redisdata:/data
 | 
			
		||||
 | 
			
		||||
  db:
 | 
			
		||||
    image: docker.io/library/postgres:17
 | 
			
		||||
    image: docker.io/library/postgres:16
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
    volumes:
 | 
			
		||||
      - pgdata:/var/lib/postgresql/data
 | 
			
		||||
 
 | 
			
		||||
@@ -24,9 +24,8 @@
 | 
			
		||||
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
 | 
			
		||||
#   and '.env' into a folder.
 | 
			
		||||
# - Run 'docker compose pull'.
 | 
			
		||||
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
 | 
			
		||||
# - Run 'docker compose up -d'.
 | 
			
		||||
# - Wait until the webserver has completed startup
 | 
			
		||||
# - Run 'docker compose exec webserver createsuperuser' to create a user.
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
@@ -60,7 +59,7 @@ services:
 | 
			
		||||
      PAPERLESS_TIKA_ENDPOINT: http://tika:9998
 | 
			
		||||
 | 
			
		||||
  gotenberg:
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:8.17
 | 
			
		||||
    image: docker.io/gotenberg/gotenberg:8.7
 | 
			
		||||
    restart: unless-stopped
 | 
			
		||||
 | 
			
		||||
    # The gotenberg chromium route is used to convert .eml files. We do not
 | 
			
		||||
 
 | 
			
		||||
@@ -17,9 +17,8 @@
 | 
			
		||||
# - Copy this file as 'docker-compose.yml' and the files 'docker-compose.env'
 | 
			
		||||
#   and '.env' into a folder.
 | 
			
		||||
# - Run 'docker compose pull'.
 | 
			
		||||
# - Run 'docker compose run --rm webserver createsuperuser' to create a user.
 | 
			
		||||
# - Run 'docker compose up -d'.
 | 
			
		||||
# - Wait until the webserver has completed startup
 | 
			
		||||
# - Run 'docker compose exec webserver createsuperuser' to create a user.
 | 
			
		||||
#
 | 
			
		||||
# For more extensive installation and update instructions, refer to the
 | 
			
		||||
# documentation.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										120
									
								
								docker/docker-prepare.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										120
									
								
								docker/docker-prepare.sh
									
									
									
									
									
										Executable 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
									
								
							
							
						
						
									
										42
									
								
								docker/env-from-file.sh
									
									
									
									
									
										Normal 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
 | 
			
		||||
							
								
								
									
										12
									
								
								docker/flower-conditional.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								docker/flower-conditional.sh
									
									
									
									
									
										Normal 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  | 
@@ -1,7 +1,5 @@
 | 
			
		||||
#!/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
 | 
			
		||||
 | 
			
		||||
for command in decrypt_documents \
 | 
			
		||||
@@ -17,11 +15,9 @@ for command in decrypt_documents \
 | 
			
		||||
	document_sanity_checker \
 | 
			
		||||
	document_fuzzy_match \
 | 
			
		||||
	manage_superuser \
 | 
			
		||||
	convert_mariadb_uuid \
 | 
			
		||||
	prune_audit_logs \
 | 
			
		||||
	createsuperuser;
 | 
			
		||||
	convert_mariadb_uuid;
 | 
			
		||||
do
 | 
			
		||||
	echo "installing $command..."
 | 
			
		||||
	sed "s/management_command/$command/g" management_script.sh >"$PWD/rootfs/usr/local/bin/$command"
 | 
			
		||||
	chmod u=rwx,g=rwx,o=rx "$PWD/rootfs/usr/local/bin/$command"
 | 
			
		||||
	sed "s/management_command/$command/g" management_script.sh > /usr/local/bin/$command
 | 
			
		||||
	chmod +x /usr/local/bin/$command
 | 
			
		||||
done
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,17 @@
 | 
			
		||||
#!/command/with-contenv /usr/bin/bash
 | 
			
		||||
# shellcheck shell=bash
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
	s6-setuidgid paperless python3 manage.py management_command "$@"
 | 
			
		||||
elif [[ $(id -un) == "paperless" ]]; then
 | 
			
		||||
if [[ $(id -u) == 0 ]] ;
 | 
			
		||||
then
 | 
			
		||||
	gosu paperless python3 manage.py management_command "$@"
 | 
			
		||||
elif [[ $(id -un) == "paperless" ]] ;
 | 
			
		||||
then
 | 
			
		||||
	python3 manage.py management_command "$@"
 | 
			
		||||
else
 | 
			
		||||
	echo "Unknown user."
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								docker/paperless_cmd.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								docker/paperless_cmd.sh
									
									
									
									
									
										Executable 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[@]}"
 | 
			
		||||
@@ -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"
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/init-complete/run
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/init-custom-init/run
 | 
			
		||||
@@ -1,30 +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/}
 | 
			
		||||
				# 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
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/init-env-file/run
 | 
			
		||||
@@ -1,33 +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}"
 | 
			
		||||
 | 
			
		||||
echo "${log_prefix} Checking for folder existence"
 | 
			
		||||
 | 
			
		||||
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}" \
 | 
			
		||||
	"${tmp_dir}"; do
 | 
			
		||||
	if [[ ! -d "${dir}" ]]; then
 | 
			
		||||
		mkdir --parents --verbose "${dir}"
 | 
			
		||||
	fi
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
echo "${log_prefix} Adjusting file and folder permissions"
 | 
			
		||||
for dir in \
 | 
			
		||||
	"${export_dir}" \
 | 
			
		||||
	"${data_dir}" \
 | 
			
		||||
	"${media_root_dir}" \
 | 
			
		||||
	"${consume_dir}" \
 | 
			
		||||
	"${tmp_dir}"; do
 | 
			
		||||
	find "${dir}" -not \( -user paperless -and -group paperless \) -exec chown --changes paperless:paperless {} +
 | 
			
		||||
done
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/init-folders/run
 | 
			
		||||
@@ -1,20 +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}"
 | 
			
		||||
 | 
			
		||||
(
 | 
			
		||||
	# 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 "${log_prefix} Apply database migrations..."
 | 
			
		||||
	cd "${PAPERLESS_SRC_DIR}"
 | 
			
		||||
 | 
			
		||||
	if [[ -n "${USER_IS_NON_ROOT}" ]]; then
 | 
			
		||||
		exec python3 manage.py migrate --skip-checks --no-input
 | 
			
		||||
	else
 | 
			
		||||
		exec s6-setuidgid paperless python3 manage.py migrate --skip-checks --no-input
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
) 200>"${data_dir}/migration_lock"
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/init-migrations/run
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/init-modify-user/run
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/init-search-index/run
 | 
			
		||||
@@ -1,19 +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 -u) == $(id -u paperless) ]; then
 | 
			
		||||
	printf "true" > /var/run/s6/container_environment/USER_IS_NON_ROOT
 | 
			
		||||
	echo "${log_prefix}  paperless-ngx docker container running under a user"
 | 
			
		||||
else
 | 
			
		||||
	echo "${log_prefix}  paperless-ngx docker container starting init as root"
 | 
			
		||||
fi
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/init-start/run
 | 
			
		||||
@@ -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
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
oneshot
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
/etc/s6-overlay/s6-rc.d/init-superuser/run
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user