Three resilience and honesty fixes from the v0.2.8 review. Minor
version bump because redeploy_project and system_prune return
different strings.
M-4: trigger_build_stream now converts every non-ReadTimeout
httpx.HTTPError (ConnectError, ConnectTimeout, WriteError,
RemoteProtocolError, ...) into a SynologyError with a clear
message. Previously only ReadTimeout was handled; everything else
propagated as a raw httpx exception. redeploy_project now tracks
whether stop was actually issued and, when build_stream fails after
a successful stop, tells the user the project is in STOPPED state
and recommends start_project / retry rather than the misleading
"use stop + start separately" workaround.
M-5: _wait_for_project_running exits early on BUILD_FAILED / ERROR
(new _TERMINAL_FAILURE_STATUSES frozenset). DSM signals these
statuses within seconds of a failed image pull; the old polling
loop kept waiting up to 5 minutes for RUNNING. redeploy_project
now surfaces the terminal status with a BUILD_FAILED-specific hint
to update_image_tag.
M-6: system_prune preview now enumerates user-created networks
that have no containers attached (excluding the three built-in
networks bridge/host/none, which Docker never prunes). Previously
the preview noted "Unused networks: (not counted)" even though
SYNO.Docker.Utils/prune does delete them — users could lose
networks they had not been warned about.
Tests:
- 2 new dsm_client tests: ConnectError and RemoteProtocolError
both raise SynologyError, not raw httpx exceptions.
- 2 new project tests: recovery hint after stop+build_stream
failure (RUNNING case); old workaround retained for the
STOPPED case where no stop was issued.
- 3 new polling tests: BUILD_FAILED and ERROR each trigger early
exit; redeploy_project surfaces BUILD_FAILED with update_image_tag
hint.
- 2 new system_prune preview tests: counts unused networks
correctly, excludes built-ins; network-fetch failure is non-fatal.
245 tests pass. ruff check + ruff format clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the delete-before-start workaround with the real mechanism:
SYNO.Docker.Project/build_stream is what the DSM "Erstellen" button calls
(confirmed via DevTools). It pulls updated images and starts the project.
DsmClient.trigger_build_stream(project_id): fires a streaming GET to
build_stream, reads the first SSE chunk to confirm DSM accepted the
request, then closes. ReadTimeout is swallowed (build running server-side).
Immediate JSON error responses are parsed and raised as SynologyError.
redeploy_project simplified from 4 steps to 3:
1. Stop (skip for STOPPED, suppress for BUILD_FAILED)
2. trigger_build_stream — DSM pulls images + starts project
3. Poll for RUNNING (timeout raised from 30s → 5min for large pulls)
build_stream errors are now fatal (abort with clear message).
Removes _read_compose_images_for_project, _try_delete_image and their
json/yaml/re imports — no longer needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
redeploy_project: replace broken SYNO.Docker.Image/pull with a unified
4-step delete-before-start flow for all project states (RUNNING, STOPPED,
BUILD_FAILED). Reads image tags from the project's compose.yaml via
FileStation before stopping, deletes each cached image (non-fatal), then
starts the project so DSM auto-pulls the latest version. Polls for RUNNING
as before.
update_image_tag: auto-update env vars whose value equals the numeric
version prefix of the old tag when the new tag shares the same
<digits>-<suffix> pattern (e.g. JENKINS_VERSION=2.558 → 2.560 when tag
changes 2.558-jdk21 → 2.560-jdk21). Preview mode lists the pending
auto-updates. Only triggers when the var exists and the pattern matches.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove contextlib.suppress from the image pull step in the BUILD_FAILED
redeploy path. A failed pull (e.g. non-existent tag) now immediately
returns an actionable error pointing to update_image_tag instead of
silently continuing and starting the project with stale/missing image.
Also bumps version 0.2.1 → 0.2.2 and adds CHANGELOG entry.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
DSM starts containers asynchronously - start_project returns immediately
while containers are still initialising. Adds _wait_for_project_running:
polls SYNO.Docker.Project/list every 2s up to 30s after issuing start.
Reports RUNNING on success; emits a warning instead of failure on timeout
so callers can still verify with get_project_status.
Applies to all three redeploy paths (RUNNING, STOPPED, BUILD_FAILED).
Also bumps version 0.2.0 → 0.2.1 and adds CHANGELOG entry.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Bug fixes from product test:
1. redeploy_project: BUILD_FAILED now includes explicit image pull (stop → pull → start)
2. delete_image: Distinguishes running vs stopped containers, suggests system_prune for stopped refs
3. New tool delete_container: Verify stopped state before deletion, confirmation required
Tests added for all three paths plus stopped-container edge cases.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Bug 1: Container name hash-prefix (e.g. f93cb8b504f7_jenkins)
- _strip_hash_prefix(): strips 12-char hex prefix and leading slash
- _resolve_container_name(): looks up actual DSM name from container list
- Applied in list_containers (display), container_stats (matching),
get_container_status/get_container_logs/exec_in_container (lookup)
Bug 2: redeploy_project DSM 2101/1202 on wrong project state
- Fetch project status before acting
- RUNNING → stop then start
- STOPPED → start directly (nothing to stop)
- BUILD_FAILED → suppress stop error, then start
- Other → return error with workaround hint
36 tests all passing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>