fix(build): flush guest build output + put tail in failure message
Lint / pssa (push) Failing after 23s
Lint / python (push) Successful in 52s

On a non-zero build the guest stdout/stderr was written unflushed
then a ClickException raised, so the cause was lost (only
"build command failed (exit 1)" surfaced). _report_build_output now
emits the output with markers, flushes both streams, and returns the
last lines which are appended to the failure message — diagnosable
even if act_runner buffering drops the body. Applied to Linux + Windows.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Simone
2026-05-17 17:47:17 +02:00
parent 2b8011c35d
commit 67c90f10eb
+23 -11
View File
@@ -98,6 +98,25 @@ def _sh_quote(value: str) -> str:
return shlex.quote(value) return shlex.quote(value)
def _report_build_output(stdout: str, stderr: str) -> str:
"""Emit guest build stdout/stderr to the CI log, flushed.
Returns the last few lines for the failure message so the cause is
visible even if stream buffering hides the body on a non-zero exit.
"""
click.echo("[build run] ----- build output -----")
if stdout:
sys.stdout.write(stdout if stdout.endswith("\n") else stdout + "\n")
sys.stdout.flush()
if stderr:
sys.stderr.write(stderr if stderr.endswith("\n") else stderr + "\n")
sys.stderr.flush()
click.echo("[build run] ----- end build output -----")
combined = (stderr or "").strip() or (stdout or "").strip()
lines = combined.splitlines()
return " | ".join(lines[-5:]) if lines else "(no build output)"
# ---------------------------------------------------------------- Linux flow # ---------------------------------------------------------------- Linux flow
@@ -173,14 +192,10 @@ def _linux_build(
full_cmd = f"{env_prefix}cd {_sh_quote(workdir)} && {cmd}" full_cmd = f"{env_prefix}cd {_sh_quote(workdir)} && {cmd}"
click.echo(f"[build run] running build (env vars redacted): cd {workdir} && {cmd}") click.echo(f"[build run] running build (env vars redacted): cd {workdir} && {cmd}")
result = t.run(full_cmd, check=False) result = t.run(full_cmd, check=False)
# Stream guest stdout/stderr after the fact (paramiko buffers the channel). tail = _report_build_output(result.stdout, result.stderr)
if result.stdout:
sys.stdout.write(result.stdout)
if result.stderr:
sys.stderr.write(result.stderr)
if result.returncode != 0: if result.returncode != 0:
raise click.ClickException( raise click.ClickException(
f"build command failed (exit {result.returncode})" f"build command failed (exit {result.returncode}): {tail}"
) )
if skip_artifact: if skip_artifact:
@@ -333,13 +348,10 @@ def _windows_build(
"if ($null -eq $LASTEXITCODE) { exit 0 } else { exit $LASTEXITCODE }" "if ($null -eq $LASTEXITCODE) { exit 0 } else { exit $LASTEXITCODE }"
) )
result = t.run(full_ps, check=False) result = t.run(full_ps, check=False)
if result.stdout: tail = _report_build_output(result.stdout, result.stderr)
sys.stdout.write(result.stdout)
if result.stderr:
sys.stderr.write(result.stderr)
if result.returncode != 0: if result.returncode != 0:
raise click.ClickException( raise click.ClickException(
f"build command failed (exit {result.returncode})" f"build command failed (exit {result.returncode}): {tail}"
) )
if skip_artifact: if skip_artifact: