[ci] Group component test output into collapsible CI log sections (#17536)

This commit is contained in:
Jesse Hills
2026-07-13 15:29:35 +12:00
committed by GitHub
parent 07460ebee4
commit 4a82b10783
2 changed files with 330 additions and 72 deletions
+92 -72
View File
@@ -88,6 +88,38 @@ def show_disk_space_if_ci(esphome_command: str) -> None:
sys.stdout.flush()
def start_log_group(title: str) -> None:
"""Begin a collapsible log group in the GitHub Actions log viewer.
Everything printed until the matching :func:`end_log_group` is folded away
by default, so the full ``esphome config``/``compile`` dump for one
configuration no longer pushes the pass/fail result thousands of lines down
the log. Outside CI this is a no-op so local runs stay plain.
Args:
title: Text shown on the (collapsed) group header line.
"""
if not os.environ.get("GITHUB_ACTIONS"):
return
# Flush so the marker is ordered correctly relative to the child process
# output that follows (the subprocess writes straight to our stdout).
sys.stdout.flush()
print(f"::group::{title}")
sys.stdout.flush()
def end_log_group() -> None:
"""Close the collapsible log group opened by :func:`start_log_group`.
Outside CI this is a no-op.
"""
if not os.environ.get("GITHUB_ACTIONS"):
return
sys.stdout.flush()
print("::endgroup::")
sys.stdout.flush()
def find_component_tests(
components_dir: Path,
component_pattern: str = "*",
@@ -383,54 +415,48 @@ def run_esphome_test(
# Build command string for display/logging
cmd_str = " ".join(cmd)
# Run command
print(f"> [{component}] [{test_name}] [{platform_with_version}]")
# Run command inside a collapsible CI log group so the full esphome output
# for this configuration can be folded away by default.
group_title = f"[{component}] [{test_name}] [{platform_with_version}]"
start_log_group(group_title)
print(f"> {group_title}")
if use_testing_mode:
print(" (using --testing-mode)")
start_time = time.time()
test_id = f"{component}.{test_name}.{platform_with_version}"
# Always close the group, even if the subprocess or disk-space reporting
# raises, so later output is never folded into the wrong CI log section.
try:
result = subprocess.run(cmd, check=False)
success = result.returncode == 0
duration = time.time() - start_time
# Show disk space after build in CI during compile
show_disk_space_if_ci(esphome_command)
finally:
end_log_group()
if not success and not continue_on_fail:
# Print command immediately for failed tests
print(f"\n{'=' * 80}")
print("FAILED - Command to reproduce:")
print(f"{'=' * 80}")
print(cmd_str)
print()
raise subprocess.CalledProcessError(result.returncode, cmd)
success = result.returncode == 0
duration = time.time() - start_time
return TestResult(
test_id=test_id,
components=[component],
platform=platform_with_version,
success=success,
duration=duration,
command=cmd_str,
test_type=esphome_command,
)
except subprocess.CalledProcessError:
duration = time.time() - start_time
# Re-raise if we're not continuing on fail
if not continue_on_fail:
raise
return TestResult(
test_id=test_id,
components=[component],
platform=platform_with_version,
success=False,
duration=duration,
command=cmd_str,
test_type=esphome_command,
)
if not success and not continue_on_fail:
# Print command immediately for failed tests. The group is already
# closed, so the failure and reproduce command stay visible.
print(f"\n{'=' * 80}")
print("FAILED - Command to reproduce:")
print(f"{'=' * 80}")
print(cmd_str)
print()
raise subprocess.CalledProcessError(result.returncode, cmd)
return TestResult(
test_id=test_id,
components=[component],
platform=platform_with_version,
success=success,
duration=duration,
command=cmd_str,
test_type=esphome_command,
)
def run_grouped_test(
@@ -534,54 +560,48 @@ def run_grouped_test(
# Build command string for display/logging
cmd_str = " ".join(cmd)
# Run command
# Run command inside a collapsible CI log group so the full esphome output
# for this grouped configuration can be folded away by default.
components_str = ", ".join(components)
print(f"> [GROUPED: {components_str}] [{platform_with_version}]")
group_title = f"[GROUPED: {components_str}] [{platform_with_version}]"
start_log_group(group_title)
print(f"> {group_title}")
print(" (using --testing-mode)")
start_time = time.time()
test_id = f"GROUPED[{','.join(components)}].{platform_with_version}"
# Always close the group, even if the subprocess or disk-space reporting
# raises, so later output is never folded into the wrong CI log section.
try:
result = subprocess.run(cmd, check=False)
success = result.returncode == 0
duration = time.time() - start_time
# Show disk space after build in CI during compile
show_disk_space_if_ci(esphome_command)
finally:
end_log_group()
if not success and not continue_on_fail:
# Print command immediately for failed tests
print(f"\n{'=' * 80}")
print("FAILED - Command to reproduce:")
print(f"{'=' * 80}")
print(cmd_str)
print()
raise subprocess.CalledProcessError(result.returncode, cmd)
success = result.returncode == 0
duration = time.time() - start_time
return TestResult(
test_id=test_id,
components=components,
platform=platform_with_version,
success=success,
duration=duration,
command=cmd_str,
test_type=esphome_command,
)
except subprocess.CalledProcessError:
duration = time.time() - start_time
# Re-raise if we're not continuing on fail
if not continue_on_fail:
raise
return TestResult(
test_id=test_id,
components=components,
platform=platform_with_version,
success=False,
duration=duration,
command=cmd_str,
test_type=esphome_command,
)
if not success and not continue_on_fail:
# Print command immediately for failed tests. The group is already
# closed, so the failure and reproduce command stay visible.
print(f"\n{'=' * 80}")
print("FAILED - Command to reproduce:")
print(f"{'=' * 80}")
print(cmd_str)
print()
raise subprocess.CalledProcessError(result.returncode, cmd)
return TestResult(
test_id=test_id,
components=components,
platform=platform_with_version,
success=success,
duration=duration,
command=cmd_str,
test_type=esphome_command,
)
def run_grouped_component_tests(