diff --git a/src/fromager/external_commands.py b/src/fromager/external_commands.py index f23b20aa..cd661e11 100644 --- a/src/fromager/external_commands.py +++ b/src/fromager/external_commands.py @@ -7,6 +7,8 @@ import typing from io import TextIOWrapper +from . import log + logger = logging.getLogger(__name__) HERE = pathlib.Path(__file__).absolute().parent @@ -98,8 +100,31 @@ def run( stdin=stdin, ) output = completed.stdout.decode("utf-8") if completed.stdout else "" + + # Get package name from context to add to each line for greppability + try: + req = log.requirement_ctxvar.get() + line_prefix = f"{req.name}: " + except LookupError: + line_prefix = "" + + # Prefix output lines with visual marker and package name for greppability + formatted_output = None + if output: + output_prefix = line_prefix + " > " + prefixed_output = " > " + output.replace("\n", f"\n{output_prefix}") + formatted_output = f"{line_prefix}{prefixed_output}" + if completed.returncode != 0: - logger.error("%s failed with %s", cmd, output) + # Build complete error message with command, exit code, and output + message = ( + f"command failed with exit code {completed.returncode}: {shlex.join(cmd)}" + ) + if formatted_output: + message += f"\n{formatted_output}" + + logger.error(message) + err_type = subprocess.CalledProcessError if network_isolation: # Look for a few common messages that mean there is a network @@ -113,5 +138,9 @@ def run( if substr in output: err_type = NetworkIsolationError raise err_type(completed.returncode, cmd, output) - logger.debug("output: %s", output) + + # Log command output with visual prefix to distinguish from fromager output + if formatted_output: + logger.debug(formatted_output) + return output diff --git a/tests/test_external_commands.py b/tests/test_external_commands.py index 87d2bae0..91b94720 100644 --- a/tests/test_external_commands.py +++ b/tests/test_external_commands.py @@ -29,7 +29,10 @@ def test_external_commands_log_file(tmp_path: pathlib.Path) -> None: assert "test\n" == file_contents -@mock.patch("subprocess.run", return_value=mock.Mock(returncode=0)) +@mock.patch( + "subprocess.run", + return_value=mock.Mock(returncode=0, stdout=b"test output\n"), +) @mock.patch( "fromager.external_commands.network_isolation_cmd", return_value=["/bin/unshare", "--net", "--map-current-user"],