Files
paperless-ngx/docs/migration-v3.md
Trenton H 8e67828bd7 Feature: Redesign the task system (#12584)
* feat(tasks): replace PaperlessTask model with structured redesign

Drop the old string-based PaperlessTask table and recreate it with
Status/TaskType/TriggerSource enums, JSONField result storage, and
duration tracking fields. Update all call sites to use the new API.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(tasks): rewrite signal handlers to track all task types

Replace the old consume_file-only handler with a full rewrite that tracks
6 task types (consume_file, train_classifier, sanity_check, index_optimize,
llm_index, mail_fetch) with proper trigger source detection, input data
extraction, legacy result string parsing, duration/wait time recording,
and structured error capture on failure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(tasks): add traceback and revoked state coverage to signal tests

* refactor(tasks): remove manual PaperlessTask creation and scheduled/auto params

All task records are now created exclusively via Celery signals (Task 2).
Removed PaperlessTask creation/update from train_classifier, sanity_check,
llmindex_index, and check_sanity. Removed scheduled= and auto= parameters
from all 7 call sites. Updated apply_async callers to use trigger_source
headers instead. Exceptions now propagate naturally from task functions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(tasks): auto-inject trigger_source=scheduled header for all beat tasks

Inject `headers: {"trigger_source": "scheduled"}` into every Celery beat
schedule entry so signal handlers can identify scheduler-originated tasks
without per-task instrumentation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(tasks): update serializer, filter, and viewset with v9 backwards compat

- Replace TasksViewSerializer/RunTaskViewSerializer with TaskSerializerV10
  (new field names), TaskSerializerV9 (v9 compat), TaskSummarySerializer,
  and RunTaskSerializer
- Add AcknowledgeTasksViewSerializer unchanged (kept existing validation)
- Expand PaperlessTaskFilterSet with MultipleChoiceFilter for task_type,
  trigger_source, status; add is_complete, date_created_after/before filters
- Replace TasksViewSet.get_serializer_class() to branch on request.version
- Add get_queryset() v9 compat for task_name/type query params
- Add acknowledge_all, summary, active actions to TasksViewSet
- Rewrite run action to use apply_async with trigger_source header
- Add timedelta import to views.py; add MultipleChoiceFilter/DateTimeFilter
  to filters.py imports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tasks): add read_only_fields to TaskSerializerV9, enforce admin via permission_classes on run action

* test(tasks): rewrite API task tests for redesigned model and v9 compat

Replaces the old Django TestCase-based tests with pytest-style classes using
PaperlessTaskFactory. Covers v10 field names, v9 backwards-compat field
mapping, filtering, ordering, acknowledge, acknowledge_all, summary, active,
and run endpoints. Also adds PaperlessTaskFactory to factories.py and fixes
a redundant source= kwarg in TaskSerializerV10.related_document_ids.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(tasks): fix two spec gaps in task API test suite

Move test_list_is_owner_aware to TestGetTasksV10 (it tests GET /api/tasks/,
not acknowledge). Add test_related_document_ids_includes_duplicate_of to
cover the duplicate_of path in the related_document_ids property.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(tasks): address code quality review findings

Remove trivial field-existence tests per project conventions. Fix
potentially flaky ordering test to use explicit date_created values.
Add is_complete=false filter test, v9 type filter input direction test,
and tighten TestActive second test to target REVOKED specifically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(tasks): update TaskAdmin for redesigned model

Add date_created, duration_seconds to list_display; add trigger_source
to list_filter; add input_data, duration_seconds, wait_time_seconds to
readonly_fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(tasks): update Angular types and service for task redesign

Replace PaperlessTaskName/PaperlessTaskType/PaperlessTaskStatus enums
with new PaperlessTaskType, PaperlessTaskTriggerSource, PaperlessTaskStatus
enums. Update PaperlessTask interface to new field names (task_type,
trigger_source, input_data, result_message, related_document_ids).
Update TasksService to filter by task_type instead of task_name.
Update tasks component and system-status-dialog to use new field names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(tasks): remove django-celery-results

PaperlessTask now tracks all task results via Celery signals. The
django-celery-results DB backend was write-only -- nothing reads
from it. Drop the package and add a migration to clean up the
orphaned tables.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: fix remaining tests broken by task system redesign

Update all tests that created PaperlessTask objects with old field names
to use PaperlessTaskFactory and new field names (task_type, trigger_source,
status, result_message). Use apply_async instead of delay where mocked.
Drop TestCheckSanityTaskRecording — tests PaperlessTask creation that was
intentionally removed from check_sanity().

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(tasks): improve test_api_tasks.py structure and add api marker

- Move admin_client, v9_client, user_client fixtures to conftest.py so
  they can be reused by other API tests; all three now build on the
  rest_api_client fixture instead of creating APIClient() directly
- Move regular_user fixture to conftest.py (was already done, now also
  used by the new client fixtures)
- Add docstrings to every test method describing the behaviour under test
- Move timedelta/timezone imports to module level
- Register 'api' pytest marker in pyproject.toml and apply pytestmark to
  the entire file so all 40 tests are selectable via -m api

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(tasks): simplify task tracking code after redesign

- Extract COMPLETE_STATUSES as a class constant on PaperlessTask,
  eliminating the repeated status tuple across models.py, views.py (3×),
  and filters.py
- Extract _CELERY_STATE_TO_STATUS as a module-level constant instead of
  rebuilding the dict on every task_postrun
- Extract _V9_TYPE_TO_TRIGGER_SOURCE and _RUNNABLE_TASKS as class
  constants on TasksViewSet instead of rebuilding on every request
- Extract _TRIGGER_SOURCE_TO_V9_TYPE as a class constant on
  TaskSerializerV9 instead of rebuilding per serialized object
- Extract _get_consume_args helper to deduplicate identical arg
  extraction logic in _extract_input_data, _determine_trigger_source,
  and _extract_owner_id
- Move inline imports (re, traceback) and Avg to module level
- Fix _DOCUMENT_SOURCE_TO_TRIGGER type annotation key type to
  DocumentSource instead of Any
- Remove redundant truthiness checks in SystemStatusView branches
  already guarded by an is-None check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(tasks): add docstrings and rename _parse_legacy_result

- Add docstrings to _extract_input_data, _determine_trigger_source,
  _extract_owner_id explaining what each helper does and why
- Rename _parse_legacy_result -> _parse_consume_result: the function
  parses current consume_file string outputs (consumer.py returns
  "New document id N created" and "It is a duplicate of X (#N)"),
  not legacy data; the old name was misleading

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(tasks): extend and harden the task system redesign

- TaskType: add EMPTY_TRASH, CHECK_WORKFLOWS, CLEANUP_SHARE_LINKS;
  remove INDEX_REBUILD (no backing task — beat schedule uses index_optimize)
- TRACKED_TASKS: wire up all nine task types including the three new ones
  and llmindex_index / process_mail_accounts
- Add task_revoked_handler so cancelled/expired tasks are marked REVOKED
- Fix double-write: task_postrun_handler no longer overwrites result_data
  when status is already FAILURE (task_failure_handler owns that write)
- v9 serialiser: map EMAIL_CONSUME and FOLDER_CONSUME to AUTO_TASK
- views: scope task list to owner for regular users, admins see all;
  validate ?days= query param and return 400 on bad input
- tests: add test_list_admin_sees_all_tasks; rename/fix
  test_parses_duplicate_string (duplicates produce SUCCESS, not FAILURE);
  use PaperlessTaskFactory in modified tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tasks): fix MAIL_FETCH null input_data and postrun double-query

- _extract_input_data: return {} instead of {"account_ids": None} when
  process_mail_accounts is called without an explicit account list (the
  normal beat-scheduled path); add test to cover this path
- task_postrun_handler: replace filter().first() + filter().update() with
  get() + save(update_fields=[...]) — single fetch, single write,
  consistent with task_prerun_handler

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tasks): add queryset stub to satisfy drf-spectacular schema generation

TasksViewSet.get_queryset() accesses request.user, which drf-spectacular
cannot provide during static schema generation.  Adding a class-level
queryset = PaperlessTask.objects.none() gives spectacular a model to
introspect without invoking get_queryset(), eliminating both warnings
and the test_valid_schema failure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(tasks): fill coverage gaps in task system

- test_task_signals: add TestTaskRevokedHandler (marks REVOKED, ignores
  None request, ignores unknown id); switch existing direct
  PaperlessTask.objects.create calls to PaperlessTaskFactory; import
  pytest_mock and use MockerFixture typing on mocker params
- test_api_tasks: add test_rejects_invalid_days_param to TestSummary
- tasks.service.spec: add dismissAllTasks test (POST acknowledge_all +
  reload)
- models: add pragma: no cover to __str__, is_complete, and
  related_document_ids (trivial delegates, covered indirectly)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Well, that was a bad push.

* Fixes v9 API compatability with testing coverage

* fix(tasks): restore INDEX_OPTIMIZE enum and remove no-op run button

INDEX_OPTIMIZE was dropped from the TaskType enum but still referenced
in _RUNNABLE_TASKS (views.py) and the frontend system-status-dialog,
causing an AttributeError at import time. Restore the enum value in the
model and migration so the serializer accepts it, but remove it from
_RUNNABLE_TASKS since index_optimize is a Tantivy no-op. Remove the
frontend "Run Task" button for index optimization accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tasks): v9 type filter now matches all equivalent trigger sources

The v9 ?type= query param mapped each value to a single TriggerSource,
but the serializer maps multiple sources to the same v9 type value.
A task serialized as "auto_task" would not appear when filtering by
?type=auto_task if its trigger_source was email_consume or
folder_consume. Same issue for "manual_task" missing web_ui and
api_upload sources. Changed to trigger_source__in with the full set
of sources for each v9 type value.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tasks): give task_failure_handler full ownership of FAILURE path

task_postrun_handler now early-returns for FAILURE states instead of
redundantly writing status and date_done. task_failure_handler now
computes duration_seconds and wait_time_seconds so failed tasks get
complete timing data. This eliminates a wasted .get() + .save() round
trip on every failed task and gives each handler a clean, non-overlapping
responsibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tasks): resolve trigger_source header via TriggerSource enum lookup

Replace two hardcoded string comparisons ("scheduled", "system") with a
single TriggerSource(header_source) lookup so the enum values are the
single source of truth. Any valid TriggerSource DB value passed in the
header is accepted; invalid values fall through to the document-source /
MANUAL logic. Update tests to pass enum values in headers rather than raw
strings, and add a test for the invalid-header fallback path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(tasks): use TriggerSource enum values at all apply_async call sites

Replace raw strings ("system", "manual") with PaperlessTask.TriggerSource
enum values in the three callers that can import models. The settings
file remains a raw string (models cannot be imported at settings load
time) with a comment pointing to the enum value it must match.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(tasks): parametrize repetitive test cases in task test files

test_api_tasks.py:
- Collapse six trigger_source->v9-type tests into one parametrized test,
  adding the previously untested API_UPLOAD case
- Collapse three task_name mapping tests (two remaps + pass-through)
  into one parametrized test
- Collapse two acknowledge_all status tests into one parametrized test
- Collapse two run-endpoint 400 tests into one parametrized test
- Update run/ assertions to use TriggerSource enum values

test_task_signals.py:
- Collapse three trigger_source header tests into one parametrized test
- Collapse two DocumentSource->TriggerSource mapping tests into one
  parametrized test
- Collapse two prerun ignore-invalid-id tests into one parametrized test

All parametrize cases use pytest.param with descriptive ids.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Handle JSON serialization for datetime and Path.  Further restrist the v9 permissions as Copilot suggests

* That should fix the generated schema/browser

* Use XSerializer for the schema

* A few more basic cases I see no value in covering

* Drops the migration related stuff too.  Just in case we want it again or it confuses people

* fix: annotate tasks_summary_retrieve as array of TaskSummarySerializer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: annotate tasks_active_retrieve as array of TaskSerializerV10

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Restore task running to superuser only

* Removes the acknowledge/dismiss all stuff

* Aligns v10 and v9 task permissions with each other

* Short blurb just to warn users about the tasks being cleared

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 09:28:41 -07:00

14 KiB

v3 Migration Guide

Secret Key is Now Required

The PAPERLESS_SECRET_KEY environment variable is now required. This is a critical security setting used for cryptographic signing and should be set to a long, random value.

Action Required

If you are upgrading an existing installation, you must now set PAPERLESS_SECRET_KEY explicitly.

If your installation was relying on the previous built-in default key, you have two options:

  • Set PAPERLESS_SECRET_KEY to that previous value to preserve existing sessions and tokens.
  • Set PAPERLESS_SECRET_KEY to a new random value to improve security, understanding that this will invalidate existing sessions and other signed tokens.

For new installations, or if you choose to rotate the key, you may generate a new secret key with:

python3 -c "import secrets; print(secrets.token_urlsafe(64))"

Consumer Settings Changes

The v3 consumer command uses a different library to unify the watching for new files in the consume directory. For the user, this removes several configuration options related to delays and retries and replaces with a single unified setting. It also adjusts how the consumer ignore filtering happens, replaced fnmatch with regex and separating the directory ignore from the file ignore.

Summary

Old Setting New Setting Notes
CONSUMER_POLLING CONSUMER_POLLING_INTERVAL Renamed for clarity
CONSUMER_INOTIFY_DELAY CONSUMER_STABILITY_DELAY Unified for all modes
CONSUMER_POLLING_DELAY Removed Use CONSUMER_STABILITY_DELAY
CONSUMER_POLLING_RETRY_COUNT Removed Automatic with stability tracking
CONSUMER_IGNORE_PATTERNS CONSUMER_IGNORE_PATTERNS Now regex, not fnmatch; user patterns are added to (not replacing) default ones
New CONSUMER_IGNORE_DIRS Additional directories to ignore; user entries are added to (not replacing) defaults

Encryption Support

Document and thumbnail encryption is no longer supported. This was previously deprecated in paperless-ng 0.9.3

Users must decrypt their document using the decrypt_documents command before upgrading.

Barcode Scanner Changes

Support for pyzbar has been removed. The underlying libzbar library has seen no updates in 16 years and is largely unmaintained, and the pyzbar Python wrapper last saw a release in March 2022. In practice, pyzbar struggled with barcode detection reliability, particularly on skewed, low-contrast, or partially obscured barcodes. zxing-cpp is actively maintained, significantly more reliable at finding barcodes, and now ships pre-built wheels for both x86_64 and arm64, removing the need to build the library.

The CONSUMER_BARCODE_SCANNER setting has been removed. zxing-cpp is now the only backend.

Summary

Old Setting New Setting Notes
CONSUMER_BARCODE_SCANNER Removed zxing-cpp is now the only backend

Action Required

  • If you were already using CONSUMER_BARCODE_SCANNER=ZXING, simply remove the setting.
  • If you had CONSUMER_BARCODE_SCANNER=PYZBAR or were using the default, no functional changes are needed beyond removing the setting. zxing-cpp supports all the same barcode formats and you should see improved detection reliability.
  • The libzbar0 / libzbar-dev system packages are no longer required and can be removed from any custom Docker images or host installations.

Database Engine

PAPERLESS_DBENGINE is now required to use PostgreSQL or MariaDB. Previously, the engine was inferred from the presence of PAPERLESS_DBHOST, with PAPERLESS_DBENGINE only needed to select MariaDB over PostgreSQL.

SQLite users require no changes, though they may explicitly set their engine if desired.

Action Required

PostgreSQL and MariaDB users must add PAPERLESS_DBENGINE to their environment:

# v2 (PostgreSQL inferred from PAPERLESS_DBHOST)
PAPERLESS_DBHOST: postgres

# v3 (engine must be explicit)
PAPERLESS_DBENGINE: postgresql
PAPERLESS_DBHOST: postgres

See PAPERLESS_DBENGINE for accepted values.

Database Advanced Options

The individual SSL, timeout, and pooling variables have been removed in favor of a single PAPERLESS_DB_OPTIONS string. This consolidates a growing set of engine-specific variables into one place, and allows any option supported by the underlying database driver to be set without requiring a dedicated environment variable for each.

The removed variables and their replacements are:

Removed Variable Replacement in PAPERLESS_DB_OPTIONS
PAPERLESS_DBSSLMODE sslmode=<value> (PostgreSQL) or ssl_mode=<value> (MariaDB)
PAPERLESS_DBSSLROOTCERT sslrootcert=<path> (PostgreSQL) or ssl.ca=<path> (MariaDB)
PAPERLESS_DBSSLCERT sslcert=<path> (PostgreSQL) or ssl.cert=<path> (MariaDB)
PAPERLESS_DBSSLKEY sslkey=<path> (PostgreSQL) or ssl.key=<path> (MariaDB)
PAPERLESS_DB_POOLSIZE pool.max_size=<value> (PostgreSQL only)
PAPERLESS_DB_TIMEOUT timeout=<value> (SQLite) or connect_timeout=<value> (PostgreSQL/MariaDB)

The deprecated variables will continue to function for now but will be removed in a future release. A deprecation warning is logged at startup for each deprecated variable that is still set.

Action Required

Users with any of the deprecated variables set should migrate to PAPERLESS_DB_OPTIONS. Multiple options are combined in a single value:

PAPERLESS_DB_OPTIONS="sslmode=require,sslrootcert=/certs/ca.pem,pool.max_size=10"

OCR and Archive File Generation Settings

The settings that control OCR behaviour and archive file generation have been redesigned. The old settings that coupled these two concerns together are removed — old values are not silently honoured; a startup warning is logged if any removed variable is still set in your environment.

Removed settings

Removed Setting Replacement
PAPERLESS_OCR_MODE=skip PAPERLESS_OCR_MODE=auto (new default)
PAPERLESS_OCR_MODE=skip_noarchive PAPERLESS_OCR_MODE=auto + PAPERLESS_ARCHIVE_FILE_GENERATION=never
PAPERLESS_OCR_SKIP_ARCHIVE_FILE=never PAPERLESS_ARCHIVE_FILE_GENERATION=always
PAPERLESS_OCR_SKIP_ARCHIVE_FILE=with_text PAPERLESS_ARCHIVE_FILE_GENERATION=auto (new default)
PAPERLESS_OCR_SKIP_ARCHIVE_FILE=always PAPERLESS_ARCHIVE_FILE_GENERATION=never

What changed and why

Previously, OCR_MODE conflated two independent concerns: whether to run OCR and whether to produce an archive. skip meant "skip OCR if text exists, but always produce an archive". skip_noarchive meant "skip OCR if text exists, and also skip the archive". This made it impossible to, for example, disable OCR entirely while still producing archives.

The new settings are independent:

Database configuration

If you changed OCR settings via the admin UI (ApplicationConfiguration), the database values are migrated automatically during the upgrade. mode values (skip / skip_noarchive) are mapped to their new equivalents and skip_archive_file values are converted to the new archive_file_generation field. After upgrading, review the OCR settings in the admin UI to confirm the migrated values match your intent.

Action Required

Remove any PAPERLESS_OCR_SKIP_ARCHIVE_FILE variable from your environment. If you relied on OCR_MODE=skip or OCR_MODE=skip_noarchive, update accordingly:

# v2: skip OCR when text present, always archive
PAPERLESS_OCR_MODE=skip
# v3: equivalent (auto is the new default)
# No change needed — auto is the default

# v2: skip OCR when text present, skip archive too
PAPERLESS_OCR_MODE=skip_noarchive
# v3: equivalent
PAPERLESS_OCR_MODE=auto
PAPERLESS_ARCHIVE_FILE_GENERATION=never

# v2: always skip archive
PAPERLESS_OCR_SKIP_ARCHIVE_FILE=always
# v3: equivalent
PAPERLESS_ARCHIVE_FILE_GENERATION=never

# v2: skip archive only for born-digital docs
PAPERLESS_OCR_SKIP_ARCHIVE_FILE=with_text
# v3: equivalent (auto is the new default)
PAPERLESS_ARCHIVE_FILE_GENERATION=auto

Remote OCR parser

If you use the remote OCR parser (Azure AI), note that it always produces a searchable PDF and stores it as the archive copy. ARCHIVE_FILE_GENERATION=never has no effect for documents handled by the remote parser — the archive is produced unconditionally by the remote engine.

Search Index (Whoosh -> Tantivy)

The full-text search backend has been replaced with Tantivy. The index format is incompatible with Whoosh, so the search index is automatically rebuilt from scratch on first startup after upgrading. No manual action is required for the rebuild itself.

Note and custom field search syntax

The old Whoosh index exposed note and custom_field as flat text fields that were included in unqualified searches (e.g. just typing invoice would match note content). With Tantivy these are now structured JSON fields accessed via dotted paths:

Old syntax New syntax
note:query notes.note:query
custom_field:query custom_fields.value:query

Saved views are migrated automatically. Any saved view filter rule that used an explicit note: or custom_field: field prefix in a fulltext query is rewritten to the new syntax by a data migration that runs on upgrade.

Unqualified queries are not migrated. If you had a saved view with a plain search term (e.g. invoice) that happened to match note content or custom field values, it will no longer return those matches. Update those queries to use the explicit prefix, for example:

invoice OR notes.note:invoice OR custom_fields.value:invoice

Custom field names can also be searched with custom_fields.name:fieldname.

OpenID Connect Token Endpoint Authentication

Some existing OpenID Connect setups may require an explicit token endpoint authentication method after upgrading to v3.

Action Required

If OIDC login fails at the callback with an invalid_client error, add token_auth_method to the provider settings in PAPERLESS_SOCIALACCOUNT_PROVIDERS.

For example:

{
  "openid_connect": {
    "APPS": [
      {
        ...
        "settings": {
          "server_url": "https://login.example.com",
          "token_auth_method": "client_secret_basic"
        }
      }
    ]
  }
}

Task History Cleared on Upgrade

The task tracking system has been redesigned in this release. All existing task history records are dropped from the database during the upgrade. Previously completed, failed, or acknowledged tasks will no longer appear in the task list after upgrading.

No user action is required.

Consume Script Positional Arguments Removed

Pre- and post-consumption scripts no longer receive positional arguments. All information is now passed exclusively via environment variables, which have been available since earlier versions.

Pre-consumption script

Previously, the original file path was passed as $1. It is now only available as DOCUMENT_SOURCE_PATH.

Before:

#!/usr/bin/env bash
# $1 was the original file path
process_document "$1"

After:

#!/usr/bin/env bash
process_document "${DOCUMENT_SOURCE_PATH}"

Post-consumption script

Previously, document metadata was passed as positional arguments $1 through $8:

Argument Environment Variable Equivalent
$1 DOCUMENT_ID
$2 DOCUMENT_FILE_NAME
$3 DOCUMENT_SOURCE_PATH
$4 DOCUMENT_THUMBNAIL_PATH
$5 DOCUMENT_DOWNLOAD_URL
$6 DOCUMENT_THUMBNAIL_URL
$7 DOCUMENT_CORRESPONDENT
$8 DOCUMENT_TAGS

Before:

#!/usr/bin/env bash
DOCUMENT_ID=$1
CORRESPONDENT=$7
TAGS=$8

After:

#!/usr/bin/env bash
# Use environment variables directly
echo "Document ${DOCUMENT_ID} from ${DOCUMENT_CORRESPONDENT} tagged: ${DOCUMENT_TAGS}"

Action Required

Update any pre- or post-consumption scripts that read $1, $2, etc. to use the corresponding environment variables instead. Environment variables have been the preferred option since v1.8.0.