From 9c597c25d3881c00b096901b0712b2f5780daa97 Mon Sep 17 00:00:00 2001 From: Stefan Koch Date: Thu, 4 Jun 2026 08:53:03 +0200 Subject: [PATCH] fix(orchestrator): Drop subprocess stdout from woodpecker-apply phase result MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `_phase_woodpecker_apply` was the only CalledProcessError handler in the file that surfaced exc.output into the PhaseResult.detail (as a sliced "tail" of the last stdout line, truncated to 120 chars). The six other handlers (infisical-bootstrap, services-configure, gitea-configure, seed, kestra-register, woodpecker-oauth) all stick to `type(exc).__name__` + rc and let `docker logs ` / `docker compose logs` be the source of truth for the full output. exc.output here is the captured stdout of `docker compose up -d` for the woodpecker stack. Modern compose tends to print only the start sequence ("Network … Creating", "Container … Started", etc.), but its error branches can echo back values from the rendered env block (e.g. when interpolation fails for a malformed variable). The woodpecker stack's .env contains Infisical-managed secrets — the risk of those landing in a PhaseResult that gets logged / surfaced to the operator is non-zero. Aligned this handler with the rest of the file: report the rc and the exception type, no stdout excerpt. Existing test test_phase_woodpecker_apply_partial_on_compose_up_nonzero still passes — it only asserts on "docker compose up -d failed" and "rc=1", both of which remain in the new detail string. --- src/nexus_deploy/orchestrator.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/nexus_deploy/orchestrator.py b/src/nexus_deploy/orchestrator.py index fe9ead04..edc3a486 100644 --- a/src/nexus_deploy/orchestrator.py +++ b/src/nexus_deploy/orchestrator.py @@ -1889,14 +1889,22 @@ def _phase_woodpecker_apply(self, ssh: SSHClient) -> PhaseResult: except subprocess.CalledProcessError as exc: # docker compose returned non-zero — service-level failure # (image pull failed, container crashed at startup, port - # collision, …). Forward the captured stdout to the - # detail so operators see the compose error inline. - stdout_excerpt = (exc.output or "").strip().splitlines()[-1:] if exc.output else [] - tail = stdout_excerpt[0] if stdout_excerpt else "no stdout captured" + # collision, …). Earlier versions of this handler appended + # the tail of exc.output to the detail "so operators see + # the compose error inline", but exc.output is the captured + # stdout of `docker compose up -d` for the woodpecker stack + # — which can include env-var values quoted back inside + # compose's own error messages (e.g. when a service env + # block references a value that fails interpolation). + # Aligned with the other CalledProcessError handlers in this + # file (infisical-bootstrap, gitea-configure, kestra-register, + # …) which all surface only `type(exc).__name__` + rc and + # leave the full output to `docker logs woodpecker` on the + # server. return PhaseResult( name="woodpecker-apply", status="partial", - detail=f"docker compose up -d failed (rc={exc.returncode}): {tail[:120]}", + detail=f"docker compose up -d failed (rc={exc.returncode}, {type(exc).__name__})", ) except subprocess.TimeoutExpired as exc: # Genuine ssh transport timeout — operator should