Adds services.arrInit.<name>.configXml for declaratively ensuring XML
elements exist in a Servarr config.xml before the service starts.
Generates a preStart hook on the main service that runs a Python helper
to patch or create config.xml. Undeclared elements are preserved;
declared elements are written with exact values.
Primary use case: preventing recurring Prowlarr 'not listening on port'
failures when config.xml loses the <Port> element — now guaranteed to
exist before Prowlarr starts.
Hardening:
- Atomic writes (tmp + rename): power loss cannot corrupt config.xml
- Malformed XML recovery: fresh <Config> root instead of blocking boot
- Secure default mode (0600) for new files containing ApiKey
- Preserves existing file mode on rewrite
- Assertion against duplicate serviceName targeting
Tests (10 subtests): creates-from-missing, patches-existing, preserves-
undeclared, corrects-tampered, idempotent, malformed-recovery,
ownership-preserved, not-world-readable.
Verifies the service enters failed state after exhausting all
StartLimitBurst retries when the API never becomes available.
Checks StartLimitIntervalSec/Burst configuration and confirms
repeated timeout messages appear in the journal.
Tests networkNamespacePath and networkNamespaceService options.
Creates a network namespace, runs a mock Servarr inside it, verifies
namespace isolation (mock unreachable from default ns), and confirms
the init service provisions resources through the namespace.
Exercises the naming option which was previously untested.
Verifies fields are applied to Sonarr via config/naming API
and validates idempotency (second run reports 'already correct').
Move embedded Python scripts out of Nix string interpolation into
standalone files under scripts/. Each script reads its configuration
from a JSON file passed as the first CLI argument.
Shared utilities (API key reading, API polling, health check loop)
are consolidated into common.py, eliminating three copies of
read_api_key and wait_for_api.
Implementation improvements included in the extraction:
- Remove pyarr dependency; all HTTP calls use raw requests
- Add update semantics: download clients and synced apps are now
compared against desired state and updated on drift via PUT
- Bazarr configure_provider compares API keys and updates stale ones
- Narrow health_check_loop exception clause from bare Exception to
(RequestException, ValueError, KeyError)
- Fix double resp.json() call in resolve_profile_id (jellyseerr)
- Replace os.system with subprocess.run for Jellyseerr restart
- Handle Servarr fields with missing 'value' key
- Skip masked fields (privacy=apiKey/password) in drift detection
to prevent spurious updates every run