Fix ATVM watcher reboot status recovery
This commit is contained in:
@@ -427,15 +427,15 @@ def extract_host_results_from_run_finished_segment(segment_text: str, inventory:
|
|||||||
host_results: Dict[str, HostResult] = {}
|
host_results: Dict[str, HostResult] = {}
|
||||||
normalized = re.sub(r"\n\s*│\s*s\s*│", "s", segment_text)
|
normalized = re.sub(r"\n\s*│\s*s\s*│", "s", segment_text)
|
||||||
for host_match in re.finditer(
|
for host_match in re.finditer(
|
||||||
r"✔\s+(atvm[^\s]+)\.ts\s+([0-9:hms.\s]+?)\s+(\d+)\s+(\d+)\s+([-\d]+)\s+([-\d]+)\s+([-\d]+)",
|
r"([✔✖])\s+(atvm[^\s]+)\.ts\s+([0-9:hms.\s]+?)\s+(\d+)\s+(\d+)\s+([-\d]+)\s+([-\d]+)\s+([-\d]+)",
|
||||||
normalized,
|
normalized,
|
||||||
re.S,
|
re.S,
|
||||||
):
|
):
|
||||||
host = host_match.group(1)
|
host = host_match.group(2)
|
||||||
duration_seconds = parse_duration_seconds(host_match.group(2))
|
duration_seconds = parse_duration_seconds(host_match.group(3))
|
||||||
tests = int(host_match.group(3))
|
tests = int(host_match.group(4))
|
||||||
passing = int(host_match.group(4))
|
passing = int(host_match.group(5))
|
||||||
failing = 0 if host_match.group(5) == "-" else int(host_match.group(5))
|
failing = 0 if host_match.group(6) == "-" else int(host_match.group(6))
|
||||||
detail = f"{tests} tests, {failing} failures"
|
detail = f"{tests} tests, {failing} failures"
|
||||||
status = "FAIL" if failing else "PASS"
|
status = "FAIL" if failing else "PASS"
|
||||||
host_results[host] = HostResult(
|
host_results[host] = HostResult(
|
||||||
@@ -589,14 +589,11 @@ def collect_latest_host_reporter_artifact(
|
|||||||
if run_ended_at and artifact_mtime >= run_ended_at:
|
if run_ended_at and artifact_mtime >= run_ended_at:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
artifact_ts = reporter_artifact_run_timestamp(artifact_path) or artifact_mtime
|
result = parse_host_reporter_artifact(artifact_path, host, kernels)
|
||||||
result = HostResult(
|
if result is None:
|
||||||
host=host,
|
continue
|
||||||
kernel=kernels.get(host, "unknown"),
|
artifact_ts = result.timestamp or reporter_artifact_run_timestamp(artifact_path) or artifact_mtime
|
||||||
status="PASS",
|
result.timestamp = artifact_ts
|
||||||
detail="completed",
|
|
||||||
timestamp=artifact_ts,
|
|
||||||
)
|
|
||||||
candidate = (host, result)
|
candidate = (host, result)
|
||||||
if latest is None:
|
if latest is None:
|
||||||
latest = candidate
|
latest = candidate
|
||||||
@@ -867,6 +864,36 @@ def get_test_flow(template_name: object) -> List[str]:
|
|||||||
return TEMPLATE_TEST_FLOWS.get(template_name, DEFAULT_TEST_FLOW)
|
return TEMPLATE_TEST_FLOWS.get(template_name, DEFAULT_TEST_FLOW)
|
||||||
|
|
||||||
|
|
||||||
|
def extract_test_flow_from_generated_spec(reporter_root: Path, log_text: str) -> List[str]:
|
||||||
|
spec_match = re.search(r'Extracted specPattern:\s*(\[[^\n]+\])', log_text)
|
||||||
|
if not spec_match:
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
spec_list = ast.literal_eval(spec_match.group(1))
|
||||||
|
except (SyntaxError, ValueError):
|
||||||
|
return []
|
||||||
|
if not isinstance(spec_list, list):
|
||||||
|
return []
|
||||||
|
|
||||||
|
cypress_root = reporter_root.parent
|
||||||
|
for entry in spec_list:
|
||||||
|
if not isinstance(entry, str) or "check-xml-files.ts" in entry:
|
||||||
|
continue
|
||||||
|
spec_path = cypress_root / Path(entry).relative_to("cypress")
|
||||||
|
if not spec_path.exists():
|
||||||
|
continue
|
||||||
|
steps: List[str] = []
|
||||||
|
for line in spec_path.read_text(encoding="utf-8", errors="replace").splitlines():
|
||||||
|
match = re.search(r'it\(\s*`?\$\{numStep\+\+\}\.\s*(.*?)`\s*,', line)
|
||||||
|
if match:
|
||||||
|
step_text = match.group(1).strip()
|
||||||
|
if step_text:
|
||||||
|
steps.append(f"{len(steps) + 1}. {step_text}")
|
||||||
|
if steps:
|
||||||
|
return steps
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
def coverage_lines(metadata: Dict[str, object]) -> List[str]:
|
def coverage_lines(metadata: Dict[str, object]) -> List[str]:
|
||||||
lines = [
|
lines = [
|
||||||
f"- template: `{metadata['template']}`",
|
f"- template: `{metadata['template']}`",
|
||||||
@@ -911,6 +938,8 @@ def build_status_markdown(
|
|||||||
start_ts: Optional[datetime],
|
start_ts: Optional[datetime],
|
||||||
end_ts: Optional[datetime],
|
end_ts: Optional[datetime],
|
||||||
notes: List[str],
|
notes: List[str],
|
||||||
|
reporter_root: Path,
|
||||||
|
log_text: str,
|
||||||
) -> str:
|
) -> str:
|
||||||
ordered_hosts = list(host_results.values())
|
ordered_hosts = list(host_results.values())
|
||||||
infer_missing_host_durations(ordered_hosts, end_ts)
|
infer_missing_host_durations(ordered_hosts, end_ts)
|
||||||
@@ -941,7 +970,8 @@ def build_status_markdown(
|
|||||||
notes = notes + [f"Template command: `{template_command}`"]
|
notes = notes + [f"Template command: `{template_command}`"]
|
||||||
|
|
||||||
notes_block = "\n".join(f"- {note}" for note in notes) if notes else "- none"
|
notes_block = "\n".join(f"- {note}" for note in notes) if notes else "- none"
|
||||||
test_flow_lines = [f"- {step}" for step in get_test_flow(metadata.get("template"))]
|
resolved_flow = extract_test_flow_from_generated_spec(reporter_root, log_text) or get_test_flow(metadata.get("template"))
|
||||||
|
test_flow_lines = [f"- {step}" for step in resolved_flow]
|
||||||
coverage_block = coverage_lines(metadata)
|
coverage_block = coverage_lines(metadata)
|
||||||
|
|
||||||
lines = [
|
lines = [
|
||||||
@@ -1464,25 +1494,19 @@ def determine_state(
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Non-categorized runs often only write a final check-xml reporter XML.
|
# Non-categorized runs often only write a final check-xml reporter XML.
|
||||||
# Fall back to the parent "Cloud Run Finished" summary when host XML is absent.
|
# Prefer the parent "Cloud Run Finished" host summary because it preserves
|
||||||
if not parent_host_results:
|
# final pass/fail counts even when reporter fallback artifacts are sparse.
|
||||||
latest_summary = extract_latest_run_summary(log_text, inventory)
|
latest_summary = extract_latest_run_summary(log_text, inventory)
|
||||||
if latest_summary:
|
if latest_summary:
|
||||||
summary_results = latest_summary["host_results"]
|
summary_results = latest_summary["host_results"]
|
||||||
for host, result in summary_results.items():
|
for host, result in summary_results.items():
|
||||||
parent_host_results[host] = result
|
parent_host_results[host] = result
|
||||||
if subrun_states:
|
if subrun_states:
|
||||||
subrun = subrun_states[-1]
|
subrun = subrun_states[-1]
|
||||||
subrun["host_results"] = summary_results
|
subrun["host_results"] = summary_results
|
||||||
if not subrun.get("currents_url") and latest_summary.get("currents_url"):
|
if not subrun.get("currents_url") and latest_summary.get("currents_url"):
|
||||||
subrun["currents_url"] = latest_summary["currents_url"]
|
subrun["currents_url"] = latest_summary["currents_url"]
|
||||||
notes_list = list(subrun.get("notes", []))
|
subrun["state"] = "FAILED" if any(result.failures for result in summary_results.values()) else "COMPLETED"
|
||||||
fallback_note = "Host result details were derived from the parent run log summary."
|
|
||||||
if fallback_note not in notes_list:
|
|
||||||
notes_list.append(fallback_note)
|
|
||||||
subrun["notes"] = notes_list
|
|
||||||
if subrun["state"] in {"UNKNOWN", "TERMINATED"}:
|
|
||||||
subrun["state"] = "FAILED" if any(result.failures for result in summary_results.values()) else "COMPLETED"
|
|
||||||
|
|
||||||
parent_start_candidates = [subrun["start_ts"] for subrun in subrun_states if subrun["start_ts"]]
|
parent_start_candidates = [subrun["start_ts"] for subrun in subrun_states if subrun["start_ts"]]
|
||||||
parent_end_candidates = [subrun["end_ts"] for subrun in subrun_states if subrun["end_ts"]]
|
parent_end_candidates = [subrun["end_ts"] for subrun in subrun_states if subrun["end_ts"]]
|
||||||
@@ -1548,6 +1572,7 @@ if __name__ == "__main__":
|
|||||||
process_gone_since = now_utc()
|
process_gone_since = now_utc()
|
||||||
if active:
|
if active:
|
||||||
process_gone_since = None
|
process_gone_since = None
|
||||||
|
current_log_text = read_text(run_log)
|
||||||
|
|
||||||
run_state, subrun_states, host_results, start_ts, end_ts, currents_url, notes = determine_state(
|
run_state, subrun_states, host_results, start_ts, end_ts, currents_url, notes = determine_state(
|
||||||
build_name=build_name,
|
build_name=build_name,
|
||||||
@@ -1620,6 +1645,8 @@ if __name__ == "__main__":
|
|||||||
start_ts=subrun["start_ts"],
|
start_ts=subrun["start_ts"],
|
||||||
end_ts=subrun["end_ts"],
|
end_ts=subrun["end_ts"],
|
||||||
notes=subrun["notes"],
|
notes=subrun["notes"],
|
||||||
|
reporter_root=reporter_root,
|
||||||
|
log_text=current_log_text,
|
||||||
)
|
)
|
||||||
print(status_text)
|
print(status_text)
|
||||||
response = post_to_mattermost(status_text)
|
response = post_to_mattermost(status_text)
|
||||||
@@ -1645,6 +1672,8 @@ if __name__ == "__main__":
|
|||||||
start_ts=start_ts,
|
start_ts=start_ts,
|
||||||
end_ts=end_ts,
|
end_ts=end_ts,
|
||||||
notes=notes,
|
notes=notes,
|
||||||
|
reporter_root=reporter_root,
|
||||||
|
log_text=current_log_text,
|
||||||
)
|
)
|
||||||
print(status_text)
|
print(status_text)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user