Files
docker-images/python-tools/README.md
T
ajp_anton 7e6dea8b24
Build custom container images / build (map[base_image:php:8-fpm-alpine build_args:PHP_VERSION=8 context:php8-pgsql fingerprint_command:{ apk info -v | LC_ALL=C sort; find /usr/local/lib/php/extensions /usr/local/etc/php/conf.d -type f -exec sha256sum {} + | LC_ALL=C sort; } name:ph… (push) Successful in 39s
Build custom container images / build (map[base_image:postgres:18 build_args:PG_VERSION=18 POSTGIS_VERSION=3 VCHORD_VERSION=0.5.3 context:postgres fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; find /usr/lib/postgresql -type f -exec sha256su… (push) Successful in 53s
Build custom container images / build (map[base_image:python:3 build_args:PYTHON_VERSION=3 context:python-tools fingerprint_command:{ dpkg-query -W -f='${binary:Package}=${Version}\n' | LC_ALL=C sort; pip freeze | LC_ALL=C sort; } name:python-tools oci_labels:org.opencontainers.ima… (push) Successful in 1m17s
Refine Python tools task interface
2026-08-18 04:21:11 +00:00

157 lines
6.9 KiB
Markdown

# Python Tools
This image is a Python 3 task runner with ExifTool and a small web control panel
for running known, mounted Python maintenance tasks. It starts the control panel
with `python -m app`.
## Control Panel
The application discovers direct `*.py` files in each non-hidden directory
under `/opt/tasks`. Each directory is a task group. Tasks in one group run
sequentially; different groups may run concurrently. It records run history and
combined output in SQLite under `/var/lib/server-maintenance`, and supports
cancellation of active task process groups.
The image contains application code only. Mount these paths at runtime:
- `/opt/tasks` read-only: reviewed task scripts and their input manifests.
- `/opt/credentials` read-only: named credential files used by those scripts.
- `/var/lib/server-maintenance`: persistent SQLite data, logs, and task state.
Task processes receive a minimal environment. To forward non-secret container
settings to tasks, list their names in `SERVER_MAINTENANCE_TASK_ENV`, separated
by commas. This allowlist prevents unrelated container configuration from
being exposed to every task; credentials should remain in `/opt/credentials`.
Tasks are never imported during discovery. Their optional description is the
first line of their module docstring. A task must not require stdin or command
line arguments, should write useful output, and must return a non-zero exit
code on failure.
## Task definitions
Each non-hidden directory directly below `/opt/tasks` is a task group. Its
direct `*.py` files are the exposed tasks. Use an optional
`task-inputs.json` beside those files to define the group's title, task titles
and descriptions, task order, and web form fields. It is parsed as data only
and currently uses version `1`:
```json
{
"version": 1,
"title": "Example Tasks",
"tasks": {
"inspect.py": {
"title": "Inspect item",
"description": "Checks one item without changing it.",
"inputs": [
{"name": "identifier", "label": "Identifier", "type": "text", "required": true},
{"name": "attempts", "label": "Attempts", "type": "integer", "minimum": 1, "maximum": 5, "default": 2},
{"name": "mode", "label": "Mode", "type": "choice", "options": ["standard", "extended"]},
{"name": "alerts", "label": "Alerts", "type": "multi_choice", "options": ["email", "webhook"]},
{"name": "review_csv", "label": "Review CSV", "type": "file", "accept": [".csv"], "maximum_bytes": 10485760}
],
"wait_for_result": true,
"execution": {
"field": "mode",
"dry_run_value": "review",
"dry_run_label": "Review",
"execute_value": "apply",
"execute_label": "Execute"
}
}
}
}
```
`title` is optional on a group and task. A group otherwise uses its directory
name; a task otherwise uses its filename without `.py`, with underscores made
readable. `description` is optional on a task and otherwise comes from the
first non-empty line of its module docstring. The order of keys in `tasks`
controls the listed tasks' display order. Include a task as `{}` when only
ordering is needed. Unlisted direct Python files follow alphabetically.
Each task declaration may contain `title`, `description`, `inputs`,
`wait_for_result`, `download_artifacts`, and `execution`. Set
`wait_for_result` for short tasks where the form should remain blocked until it
reports success or failure. The control panel disables its controls and warns
before page unload while such a task runs. `download_artifacts` additionally
starts the first non-JSON artifact download after a successful browser run;
reserve it for files such as archives that the browser should download
automatically.
Every input has `name`, `label`, and `type`; `required` defaults to `false`.
`default` is allowed for every type except `file`.
- `text`: optional `pattern`, a Python-compatible regular expression.
- `integer`: optional `minimum`, `maximum`, and positive `step`.
- `date` and `datetime`: ISO-formatted browser date controls.
- `choice`: `options` is required, as strings or `{ "value", "label" }` objects.
- `multi_choice`: the same required `options`; its `default` is an array.
- `file`: optional lowercase extension list in `accept` and positive byte cap
in `maximum_bytes`.
Set `sensitive` to `true` on text or file inputs that must not appear in run
history. Text is masked, the database records `[redacted]`, and the original
value is available only through a private execution-input file. Uploaded files
are likewise retained privately for the run and then removed.
For review-first tasks, use `execution`. It produces one split button: click
the main part to use its current mode, or use the arrow to select the other
mode. A successful review run can be repeated with its saved inputs in the
execution mode from the run-detail page. The task must still revalidate the
live filesystem before changing anything.
```json
{
"field": "mode",
"dry_run_value": "review",
"dry_run_label": "Review",
"execute_value": "apply",
"execute_label": "Execute"
}
```
The server validates every submitted value before a run is queued. It records
the validated object in the run database and writes it to a per-run file.
Sensitive fields are redacted in the database and use a private `0600`
execution file instead. Tasks read the applicable file path from
`SERVER_MAINTENANCE_INPUT`; no user value is appended to the command line. A
task without an entry in the manifest gets an empty JSON object. Uploaded files
are stored in a private per-run directory; their absolute paths are provided in
the input JSON and the files are removed when the task finishes, fails, or is
cancelled.
## Artifacts
Tasks can write files to the directory named by
`SERVER_MAINTENANCE_ARTIFACTS`. Run-detail pages show them when the task
finishes. JSON artifacts offer an **Open** link that renders the JSON in a new
tab and leaves the artifact available; other artifacts offer a download link.
The server deletes an artifact after its download transfer ends; any artifact
not downloaded is removed after 24 hours. A browser cannot confirm that a
downloaded file was retained on the client device.
## Background tasks
An optional `background-tasks.json` beside a group's task files declares
internal recurring scripts. These scripts must live below the group's
`internal/` directory, are not exposed in the UI, and are launched at most once
per configured interval. Their latest output is retained below the persistent
state directory rather than being added to normal run history.
```json
{
"version": 1,
"tasks": [
{"id": "poll", "script": "internal/poll.py", "interval_seconds": 60}
]
}
```
`compose.example.yaml` is suitable for adding the service to an existing Compose
stack. Set `PYTHON_TOOLS_VOLUME_ROOT` to the host directory that will hold
`state`, `tasks`, and `credentials`; set
`SERVER_MAINTENANCE_ALLOWED_PROXY_IPS` to the reverse proxy's direct peer
address. Keep the application behind an authenticated reverse proxy.