--- - name: verify operating system fail: msg: Sorry, only Debian and Ubuntu supported at the moment. when: not(ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu') - name: install base dependencies apt: update_cache: yes pkg: # paperless-ng - python3-pip - python3-dev - fonts-liberation - imagemagick - optipng - gnupg - libpoppler-cpp-dev - libpq-dev - libmagic-dev - mime-support # OCRmyPDF - unpaper - ghostscript - icc-profiles-free - qpdf - liblept5 - libxml2 - pngquant - zlib1g - tesseract-ocr # dev - sudo - build-essential - python3-setuptools - python3-wheel - python3-virtualenv - name: install ocr languages apt: pkg: "{{ paperlessng_ocr_languages | map('regex_replace', '^(.*)$', 'tesseract-ocr-\\1') | list }}" - name: set up notesalexp repository key (for jbig2enc) apt_key: url: https://notesalexp.org/debian/alexp_key.asc state: present when: paperlessng_use_jbig2enc - name: set up notesalexp repository (for jbig2enc) apt_repository: repo: "deb https://notesalexp.org/debian/{{ ansible_distribution_release }}/ {{ ansible_distribution_release }} main" state: present when: paperlessng_use_jbig2enc - name: set up notesalexp repository pinning copy: content: | Package: * Pin: release o=notesalexp.org Pin-Priority: 1 Package: jbig2enc Pin: release o=notesalexp.org Pin-Priority: 500 dest: /etc/apt/preferences.d/notesalexp when: paperlessng_use_jbig2enc - name: install jbig2enc apt: pkg: jbig2enc update_cache: yes when: paperlessng_use_jbig2enc - name: install redis apt: pkg: redis-server when: paperlessng_redis_host == 'localhost' or paperlessng_redis_host == '127.0.0.1' - name: enable redis systemd: name: redis-server enabled: yes masked: no state: started when: paperlessng_redis_host == 'localhost' or paperlessng_redis_host == '127.0.0.1' - name: create paperless system group group: name: "{{ paperlessng_system_group }}" - name: create paperless system user user: name: "{{ paperlessng_system_user }}" groups: - "{{ paperlessng_system_group }}" shell: /usr/sbin/nologin # GNUPG_HOME required due to paperless db.py create_home: yes - name: check for paperless-ng installation command: cmd: 'grep -Po "(?<=Paperless-ng )\d+\.\d+\.\d+" {{ paperlessng_directory }}/docs/changelog.html' changed_when: '"No such file or directory" in paperlessng_current_version.stderr or paperlessng_current_version.stdout != paperlessng_version | string' failed_when: false ignore_errors: yes register: paperlessng_current_version - name: register current state set_fact: fresh_installation: '{{ "No such file or directory" in paperlessng_current_version.stderr }}' update_installation: '{{ "No such file or directory" not in paperlessng_current_version.stderr and paperlessng_current_version.stdout != paperlessng_version | string }}' reconfigure_only: '{{ paperlessng_current_version.stdout == paperlessng_version | string }}' - name: backup current paperless-ng installation copy: src: "{{ paperlessng_directory }}" remote_src: yes dest: "{{ paperlessng_directory }}-{{ ansible_date_time.iso8601 }}/" when: update_installation - name: remove current paperless sources file: path: "{{ paperlessng_directory }}/{{ item }}" state: absent with_items: - docker - docs - scripts - src - static when: update_installation - name: create temporary directory tempfile: state: directory register: tempdir when: not reconfigure_only - name: extract paperless-ng unarchive: src: "https://github.com/jonaswinkler/paperless-ng/releases/download/ng-{{ paperlessng_version }}/paperless-ng-{{ paperlessng_version }}.tar.xz" remote_src: yes dest: "{{ tempdir.path }}" when: not reconfigure_only - name: change owner and permissions of paperless-ng command: cmd: "{{ item }}" warn: false with_items: - "chown -R {{ paperlessng_system_user }}:{{ paperlessng_system_group }} {{ tempdir.path }}" - "find {{ tempdir.path }} -type d -exec chmod 0750 {} ;" - "find {{ tempdir.path }} -type f -exec chmod 0640 {} ;" when: not reconfigure_only - name: move paperless-ng command: cmd: "cp -a {{ tempdir.path }}/paperless-ng/. {{ paperlessng_directory }}" when: not reconfigure_only - name: remove temporary directory file: path: "{{ tempdir.path }}" state: absent when: not reconfigure_only - name: create paperless-ng directories and set permissions file: path: "{{ item }}" state: directory owner: "{{ paperlessng_system_user }}" group: "{{ paperlessng_system_group }}" mode: "750" with_items: - "{{ paperlessng_directory }}" - "{{ paperlessng_consumption_dir }}" - "{{ paperlessng_data_dir }}" - "{{ paperlessng_media_root }}" - "{{ paperlessng_static_dir }}" - name: rename initial config command: cmd: "mv {{ paperlessng_directory }}/paperless.conf {{ paperlessng_directory }}/paperless.conf.template" removes: "{{ paperlessng_directory }}/paperless.conf" - name: configure paperless-ng lineinfile: path: "{{ paperlessng_directory }}/paperless.conf.template" regexp: "^#?{{ item.regexp }}=" line: "{{ item.line }}" with_items: # Required services - regexp: PAPERLESS_REDIS line: "PAPERLESS_REDIS=redis://{{ paperlessng_redis_host }}:{{ paperlessng_redis_port }}" # Paths and folders - regexp: PAPERLESS_CONSUMPTION_DIR line: "PAPERLESS_CONSUMPTION_DIR={{ paperlessng_consumption_dir }}" - regexp: PAPERLESS_DATA_DIR line: "PAPERLESS_DATA_DIR={{ paperlessng_data_dir }}" - regexp: PAPERLESS_MEDIA_ROOT line: "PAPERLESS_MEDIA_ROOT={{ paperlessng_media_root }}" - regexp: PAPERLESS_STATICDIR line: "PAPERLESS_STATICDIR={{ paperlessng_static_dir }}" - regexp: PAPERLESS_FILENAME_FORMAT line: "PAPERLESS_FILENAME_FORMAT={{ paperlessng_filename_format }}" # Hosting & Security - regexp: PAPERLESS_SECRET_KEY line: "PAPERLESS_SECRET_KEY={{ paperless_secret_key }}" - regexp: PAPERLESS_ALLOWED_HOSTS line: "PAPERLESS_ALLOWED_HOSTS={{ paperless_allowed_hosts }}" - regexp: PAPERLESS_CORS_ALLOWED_HOSTS line: "PAPERLESS_CORS_ALLOWED_HOSTS={{ paperless_cors_allowed_hosts }}" - regexp: PAPERLESS_FORCE_SCRIPT_NAME line: "PAPERLESS_FORCE_SCRIPT_NAME={{ paperless_force_script_name }}" - regexp: PAPERLESS_STATIC_URL line: "PAPERLESS_STATIC_URL={{ paperless_static_url }}" - regexp: PAPERLESS_AUTO_LOGIN_USERNAME line: "PAPERLESS_AUTO_LOGIN_USERNAME={{ paperless_auto_login_username }}" - regexp: PAPERLESS_COOKIE_PREFIX line: "PAPERLESS_COOKIE_PREFIX={{ paperless_cookie_prefix }}" - regexp: PAPERLESS_ENABLE_HTTP_REMOTE_USER line: "PAPERLESS_ENABLE_HTTP_REMOTE_USER={{ paperless_enable_http_remote_user }}" # OCR settings - regexp: PAPERLESS_OCR_LANGUAGE line: "PAPERLESS_OCR_LANGUAGE={{ paperlessng_ocr_languages | join('+') }}" - regexp: PAPERLESS_OCR_MODE line: "PAPERLESS_OCR_MODE={{ paperlessng_ocr_mode }}" - regexp: PAPERLESS_OCR_OUTPUT_TYPE line: "PAPERLESS_OCR_OUTPUT_TYPE={{ paperlessng_ocr_output_type }}" - regexp: PAPERLESS_OCR_PAGES line: "PAPERLESS_OCR_PAGES={{ paperlessng_ocr_pages }}" - regexp: PAPERLESS_OCR_IMAGE_DPI line: "PAPERLESS_OCR_IMAGE_DPI={{ paperlessng_ocr_image_dpi }}" - regexp: PAPERLESS_OCR_USER_ARGS line: "PAPERLESS_OCR_USER_ARGS={{ paperlessng_ocr_user_args | combine({'jbig2_lossy': true} if paperlessng_big2enc_lossy else {}) | to_json }}" # Tika settings - regexp: PAPERLESS_TIKA_ENABLED line: "PAPERLESS_TIKA_ENABLED={{ paperlessng_tika_enabled }}" - regexp: PAPERLESS_TIKA_ENDPOINT line: "PAPERLESS_TIKA_ENDPOINT={{ paperlessng_tika_endpoint }}" - regexp: PAPERLESS_TIKA_GOTENBERG_ENDPOINT line: "PAPERLESS_TIKA_GOTENBERG_ENDPOINT={{ paperlessng_tika_endpoint }}" # Software tweaks - regexp: PAPERLESS_TIME_ZONE line: "PAPERLESS_TIME_ZONE={{ paperlessng_time_zone }}" - regexp: PAPERLESS_CONSUMER_POLLING line: "PAPERLESS_CONSUMER_POLLING={{ paperlessng_consumer_polling }}" - regexp: PAPERLESS_CONSUMER_DELETE_DUPLICATES line: "PAPERLESS_CONSUMER_DELETE_DUPLICATES={{ paperlessng_consumer_delete_duplicates }}" - regexp: PAPERLESS_CONSUMER_RECURSIVE line: "PAPERLESS_CONSUMER_RECURSIVE={{ paperlessng_consumer_recursive }}" - regexp: PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS line: "PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS={{ paperlessng_consumer_subdirs_as_tags }}" - regexp: PAPERLESS_OPTIMIZE_THUMBNAILS line: "PAPERLESS_OPTIMIZE_THUMBNAILS={{ paperlessng_optimize_thumbnails }}" - regexp: PAPERLESS_POST_CONSUME_SCRIPT line: "PAPERLESS_POST_CONSUME_SCRIPT={{ paperlessng_post_consume_script }}" - regexp: PAPERLESS_FILENAME_DATE_ORDER line: "PAPERLESS_FILENAME_DATE_ORDER={{ paperlessng_filename_date_order }}" - regexp: PAPERLESS_THUMBNAIL_FONT_NAME line: "PAPERLESS_THUMBNAIL_FONT_NAME={{ paperlessng_thumbnail_font_name }}" - regexp: PAPERLESS_IGNORE_DATES line: "PAPERLESS_IGNORE_DATES={{ paperlessng_ignore_dates }}" no_log: yes - name: configure paperless-ng database [sqlite] lineinfile: path: "{{ paperlessng_directory }}/paperless.conf.template" regexp: "^#?PAPERLESS_DBHOST=(.*)$" line: '#PAPERLESS_DBHOST=\1' backrefs: yes when: paperlessng_db_type == 'sqlite' - name: configure paperless-ng database [postgresql] lineinfile: path: "{{ paperlessng_directory }}/paperless.conf.template" regexp: "^#?{{ item.regexp }}=" line: "{{ item.line }}" with_items: - regexp: PAPERLESS_DBHOST line: "PAPERLESS_DBHOST={{ paperlessng_db_host }}" - regexp: PAPERLESS_DBPORT line: "PAPERLESS_DBPORT={{ paperlessng_db_port }}" - regexp: PAPERLESS_DBNAME line: "PAPERLESS_DBNAME={{ paperlessng_db_name }}" - regexp: PAPERLESS_DBUSER line: "PAPERLESS_DBUSER={{ paperlessng_db_user }}" - regexp: PAPERLESS_DBPASS line: "PAPERLESS_DBPASS={{ paperlessng_db_pass }}" - regexp: PAPERLESS_DBSSLMODE line: "PAPERLESS_DBSSLMODE={{ paperlessng_db_sslmode }}" when: paperlessng_db_type == 'postgresql' no_log: yes - name: deploy paperless-ng configuration copy: src: "{{ paperlessng_directory }}/paperless.conf.template" remote_src: yes dest: /etc/paperless.conf owner: root group: root mode: "0644" register: configuration - name: create paperlessng venv become: yes become_user: "{{ paperlessng_system_user }}" command: cmd: "python3 -m virtualenv {{ paperlessng_virtualenv }} -p /usr/bin/python3" creates: "{{ paperlessng_virtualenv }}" register: venv - name: install paperlessng requirements become: yes become_user: "{{ paperlessng_system_user }}" pip: requirements: "{{ paperlessng_directory }}/requirements.txt" executable: "{{ paperlessng_virtualenv }}/bin/pip3" extra_args: --upgrade when: not reconfigure_only - name: migrate database schema become: yes become_user: "{{ paperlessng_system_user }}" command: "{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py migrate" register: database_schema changed_when: '"No migrations to apply." not in database_schema.stdout' when: not reconfigure_only - name: configure paperless superuser become: yes become_user: "{{ paperlessng_system_user }}" # "manage.py createsuperuser" only works on interactive TTYs vars: creation_script: | from django.contrib.auth.models import User from django.contrib.auth.hashers import get_hasher if User.objects.filter(username='{{ paperlessng_superuser_name }}').exists(): user = User.objects.get(username='{{ paperlessng_superuser_name }}') old = user.__dict__.copy() user.is_superuser = True user.email = '{{ paperlessng_superuser_email }}' user.set_password('{{ paperlessng_superuser_password }}') user.save() new = user.__dict__ algorithm, iterations, old_salt, old_hash = old['password'].split('$') new_password_old_salt = get_hasher(algorithm).encode(password='{{ paperlessng_superuser_password }}', salt=old_salt, iterations=int(iterations)) _, _, _, new_hash = new_password_old_salt.split('$') if not (old_hash == new_hash and old['is_superuser'] == new['is_superuser'] and old['email'] == new['email']): print('changed') else: User.objects.create_superuser('{{ paperlessng_superuser_name }}', '{{ paperlessng_superuser_email }}', '{{ paperlessng_superuser_password }}') print('changed') command: '{{ paperlessng_virtualenv }}/bin/python3 {{ paperlessng_directory }}/src/manage.py shell -c "{{ creation_script }}"' register: superuser changed_when: superuser.stdout == 'changed' no_log: yes - name: set ownership and permissions on paperlessng venv file: path: "{{ paperlessng_virtualenv }}" state: directory recurse: yes owner: "{{ paperlessng_system_user }}" group: "{{ paperlessng_system_group }}" mode: g-w,o-rwx when: venv.changed or not reconfigure_only - name: configure ghostscript for PDF lineinfile: path: /etc/ImageMagick-6/policy.xml regexp: '(\s+)' line: '\1' backrefs: yes - name: configure systemd services ini_file: path: "{{ paperlessng_directory }}/scripts/{{ item[0] }}" section: "Service" option: "{{ item[1].option }}" value: "{{ item[1].value }}" with_nested: - [ paperless-consumer.service, paperless-scheduler.service, paperless-webserver.service, ] - [ # https://www.freedesktop.org/software/systemd/man/systemd.exec.html { option: "User", value: "{{ paperlessng_system_user }}" }, { option: "Group", value: "{{ paperlessng_system_group }}" }, { option: "WorkingDirectory", value: "{{ paperlessng_directory }}/src", }, { option: "ProtectSystem", value: "full" }, { option: "NoNewPrivileges", value: "true" }, { option: "PrivateUsers", value: "true" }, { option: "PrivateDevices", value: "true" }, ] - name: configure paperless-consumer service ini_file: path: "{{ paperlessng_directory }}/scripts/paperless-consumer.service" section: "Service" option: "ExecStart" value: "{{ paperlessng_virtualenv }}/bin/python3 manage.py document_consumer" - name: configure paperless-scheduler service ini_file: path: "{{ paperlessng_directory }}/scripts/paperless-scheduler.service" section: "Service" option: "ExecStart" value: "{{ paperlessng_virtualenv }}/bin/python3 manage.py qcluster" - name: configure paperless-webserver service ini_file: path: "{{ paperlessng_directory }}/scripts/paperless-webserver.service" section: "Service" option: "ExecStart" value: "{{ paperlessng_virtualenv }}/bin/gunicorn paperless.asgi:application -w 2 -k uvicorn.workers.UvicornWorker -b {{ paperlessng_listen_address }}:{{ paperlessng_listen_port }}" - name: copy systemd services copy: src: "{{ paperlessng_directory }}/scripts/{{ item }}" remote_src: yes dest: "/etc/systemd/system/{{ item }}" with_items: - paperless-consumer.service - paperless-scheduler.service - paperless-webserver.service register: paperless_services - name: reload systemd daemon systemd: name: "{{ item }}" state: restarted daemon_reload: yes with_items: - paperless-consumer - paperless-scheduler - paperless-webserver when: paperless_services.changed or configuration.changed - name: enable paperlessng services systemd: name: "{{ item }}" enabled: yes masked: no state: started with_items: - paperless-consumer - paperless-scheduler - paperless-webserver