From e74722426f052465eb229bb1a8adfc93b6d2a50f Mon Sep 17 00:00:00 2001 From: Piotr Galar Date: Sat, 29 Aug 2026 16:57:08 +0100 Subject: [PATCH] feat(ci): embed devnet status in scenario reports --- .github/workflows/ci_run.yml | 5 ++++ scenarios/report.py | 28 ++++++++++++++++++++- scripts/tests/test_scenario_report.py | 35 +++++++++++++++++++++++++++ src/logger.rs | 3 ++- 4 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 scripts/tests/test_scenario_report.py diff --git a/.github/workflows/ci_run.yml b/.github/workflows/ci_run.yml index 54b66960..c6f8bc13 100644 --- a/.github/workflows/ci_run.yml +++ b/.github/workflows/ci_run.yml @@ -446,6 +446,11 @@ jobs: ./foc-devnet version 2>&1 || echo "version command failed" echo '```' echo "" + echo "## Devnet status" + echo '```' + ./foc-devnet status 2>&1 || echo "status command failed" + echo '```' + echo "" echo "## Resolved dependencies" if [ -f "$CI_DEPENDENCY_METADATA" ]; then echo '```json' diff --git a/scenarios/report.py b/scenarios/report.py index facab24d..a3f370f6 100644 --- a/scenarios/report.py +++ b/scenarios/report.py @@ -43,6 +43,24 @@ def get_version_info(): return "foc-devnet version: not available" +def get_status_info(): + """Capture the current devnet status for inclusion in the report.""" + for binary in ["./foc-devnet", "foc-devnet"]: + try: + result = subprocess.run( + [binary, "status"], + capture_output=True, + text=True, + timeout=30, + ) + output = (result.stdout + result.stderr).strip() + if output: + return output + except (FileNotFoundError, subprocess.TimeoutExpired): + continue + return "foc-devnet status: not available" + + _REPORT_TEMPLATE = Template(""" # Scenarios Tests @@ -56,6 +74,11 @@ def get_version_info(): ## Versions info $version_info +## Devnet status +``` +$status_info +``` + ## Resolved dependencies $dependency_table @@ -101,12 +124,15 @@ def write_report(results: list[TestResult] | None = None, elapsed: int = 0): passed = sum(1 for r in results if r.is_passed) content = _REPORT_TEMPLATE.substitute( run_type=os.environ.get("SCENARIO_RUN_TYPE", "local"), - date=datetime.datetime.now(datetime.UTC).strftime("%d-%B-%Y %H:%M:%S GMT +0"), + date=datetime.datetime.now(datetime.timezone.utc).strftime( + "%d-%B-%Y %H:%M:%S GMT +0" + ), pass_count=passed, fail_count=total - passed, total_count=total, ci_run_link=_build_ci_run_link(), version_info=f"```\n{get_version_info()}\n```", + status_info=get_status_info(), dependency_table=format_markdown_table(), test_summary=_build_test_summary(results), ) diff --git a/scripts/tests/test_scenario_report.py b/scripts/tests/test_scenario_report.py new file mode 100644 index 00000000..44a1c819 --- /dev/null +++ b/scripts/tests/test_scenario_report.py @@ -0,0 +1,35 @@ +import subprocess +import tempfile +import unittest +from pathlib import Path +from unittest.mock import patch + +from scenarios import report + + +class ScenarioReportTests(unittest.TestCase): + def test_report_embeds_status(self): + with tempfile.TemporaryDirectory() as directory: + report_path = Path(directory) / "scenario_report.md" + with patch.object(report, "REPORT_MD", str(report_path)): + with patch.object(report, "get_version_info", return_value="version"): + with patch.object( + report, "get_status_info", return_value="status output" + ): + report.write_report() + + content = report_path.read_text() + + self.assertIn("## Devnet status", content) + self.assertIn("status output", content) + + @patch("scenarios.report.subprocess.run") + def test_status_output_includes_stderr(self, run): + run.return_value = subprocess.CompletedProcess( + ["./foc-devnet", "status"], + 1, + stdout="status output\n", + stderr="status warning\n", + ) + + self.assertEqual(report.get_status_info(), "status output\nstatus warning") diff --git a/src/logger.rs b/src/logger.rs index aac845d7..992d2e55 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,5 +1,6 @@ use crate::paths::foc_devnet_run_log_file; use std::fs; +use std::io::IsTerminal; use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; /// Initializes the logging system. @@ -20,7 +21,7 @@ pub fn init_logging(run_id: &str) -> Result<(), Box> { let file_layer = fmt::layer().with_ansi(false).with_writer(log_file); let stdout_layer = fmt::layer() - .with_ansi(true) + .with_ansi(std::io::stdout().is_terminal()) .with_writer(std::io::stdout) .with_target(false) .with_file(true)