fix: detect flash failure from output even when returncode is 0
program_flash can return exit code 0 while outputting errors like 'uboot failed' or 'cannot open'. Previously only returncode was checked, so these silent failures caused the workflow to continue past a failed erase/program step. Now scans combined stdout+stderr for failure keywords (uboot, failed, cannot, abort, invalid, unsupported, timeout) and returns success=False. Also checks returncode != 0 as fallback.
This commit is contained in:
+60
-8
@@ -10,6 +10,7 @@ Compatible with Xilinx 2018.3 — 2023.2.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -35,6 +36,27 @@ ProgressCallback = Callable[[str, str], None] | None
|
|||||||
|
|
||||||
# ── Helpers ─────────────────────────────────────────────────────────
|
# ── Helpers ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Keywords in program_flash output that indicate failure even with rc=0
|
||||||
|
_FAIL_KEYWORDS = [
|
||||||
|
"uboot", "u-boot", "failed", "cannot", "abort",
|
||||||
|
"invalid", "unsupported", "timeout",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _output_has_error(output: str) -> str:
|
||||||
|
"""Check combined stdout+stderr for known failure keywords.
|
||||||
|
|
||||||
|
Returns the first matching line, or empty string if clean.
|
||||||
|
"""
|
||||||
|
lowered = output.lower()
|
||||||
|
for kw in _FAIL_KEYWORDS:
|
||||||
|
if kw in lowered:
|
||||||
|
# Find the actual line
|
||||||
|
for line in output.splitlines():
|
||||||
|
if kw in line.lower():
|
||||||
|
return line.strip()[:200]
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def _run_command(
|
def _run_command(
|
||||||
cmd: list[str],
|
cmd: list[str],
|
||||||
@@ -167,12 +189,27 @@ def wipe_flash(
|
|||||||
callback=callback,
|
callback=callback,
|
||||||
)
|
)
|
||||||
|
|
||||||
success = result.returncode == 0
|
combined = result.stdout + result.stderr
|
||||||
|
err_line = _output_has_error(combined)
|
||||||
|
if err_line:
|
||||||
|
return FlashResult(
|
||||||
|
step="wipe",
|
||||||
|
success=False,
|
||||||
|
message=f"Flash erase failed: {err_line}",
|
||||||
|
output=combined[:4000],
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
return FlashResult(
|
||||||
|
step="wipe",
|
||||||
|
success=False,
|
||||||
|
message=f"Flash erase failed (exit {result.returncode})",
|
||||||
|
output=combined[:4000],
|
||||||
|
)
|
||||||
return FlashResult(
|
return FlashResult(
|
||||||
step="wipe",
|
step="wipe",
|
||||||
success=success,
|
success=True,
|
||||||
message="Flash erased successfully" if success else "Flash erase failed",
|
message="Flash erased successfully",
|
||||||
output=(result.stdout + result.stderr)[:4000],
|
output=combined[:4000],
|
||||||
)
|
)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
return FlashResult(step="wipe", success=False, message="Flash erase timed out")
|
return FlashResult(step="wipe", success=False, message="Flash erase timed out")
|
||||||
@@ -252,12 +289,27 @@ def program_flash_bin(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
result = _run_command(cmd, timeout=config.step_timeouts.get("flash_program", 600), callback=callback)
|
result = _run_command(cmd, timeout=config.step_timeouts.get("flash_program", 600), callback=callback)
|
||||||
success = result.returncode == 0
|
combined = result.stdout + result.stderr
|
||||||
|
err_line = _output_has_error(combined)
|
||||||
|
if err_line:
|
||||||
|
return FlashResult(
|
||||||
|
step="program",
|
||||||
|
success=False,
|
||||||
|
message=f"Flash programming failed: {err_line}",
|
||||||
|
output=combined[:4000],
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
return FlashResult(
|
||||||
|
step="program",
|
||||||
|
success=False,
|
||||||
|
message=f"Flash programming failed (exit {result.returncode})",
|
||||||
|
output=combined[:4000],
|
||||||
|
)
|
||||||
return FlashResult(
|
return FlashResult(
|
||||||
step="program",
|
step="program",
|
||||||
success=success,
|
success=True,
|
||||||
message="Flash programmed successfully" if success else "Flash programming failed",
|
message="Flash programmed successfully",
|
||||||
output=(result.stdout + result.stderr)[:4000],
|
output=combined[:4000],
|
||||||
)
|
)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
return FlashResult(step="program", success=False, message="Flash programming timed out (600s)")
|
return FlashResult(step="program", success=False, message="Flash programming timed out (600s)")
|
||||||
|
|||||||
Reference in New Issue
Block a user