diff --git a/src/ci_orchestrator/commands/build.py b/src/ci_orchestrator/commands/build.py index 2ac3e6a..432c3ac 100644 --- a/src/ci_orchestrator/commands/build.py +++ b/src/ci_orchestrator/commands/build.py @@ -98,6 +98,25 @@ def _sh_quote(value: str) -> str: 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 @@ -173,14 +192,10 @@ def _linux_build( full_cmd = f"{env_prefix}cd {_sh_quote(workdir)} && {cmd}" click.echo(f"[build run] running build (env vars redacted): cd {workdir} && {cmd}") result = t.run(full_cmd, check=False) - # Stream guest stdout/stderr after the fact (paramiko buffers the channel). - if result.stdout: - sys.stdout.write(result.stdout) - if result.stderr: - sys.stderr.write(result.stderr) + tail = _report_build_output(result.stdout, result.stderr) if result.returncode != 0: raise click.ClickException( - f"build command failed (exit {result.returncode})" + f"build command failed (exit {result.returncode}): {tail}" ) if skip_artifact: @@ -333,13 +348,10 @@ def _windows_build( "if ($null -eq $LASTEXITCODE) { exit 0 } else { exit $LASTEXITCODE }" ) result = t.run(full_ps, check=False) - if result.stdout: - sys.stdout.write(result.stdout) - if result.stderr: - sys.stderr.write(result.stderr) + tail = _report_build_output(result.stdout, result.stderr) if result.returncode != 0: raise click.ClickException( - f"build command failed (exit {result.returncode})" + f"build command failed (exit {result.returncode}): {tail}" ) if skip_artifact: