* 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>
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_KEYto that previous value to preserve existing sessions and tokens. - Set
PAPERLESS_SECRET_KEYto 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=PYZBARor 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-devsystem 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:
PAPERLESS_OCR_MODEcontrols OCR:auto(default),force,redo,off.PAPERLESS_ARCHIVE_FILE_GENERATIONcontrols archive production:auto(default),always,never.
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.