Add suggestions section to CI failure HTML report + dark mode
When CI fails, the HTML report now includes a "Suggestions" section that analyzes failure patterns and provides actionable fix hints: - Feature manifest path mismatches - Import/module errors → pip install - Auth failures → missing JWT cookie - Route 404s → endpoint moved/renamed - Test count regression → baseline drift - Package check failures → missing deps - Collection errors → broken conftest/syntax Also converted the report to dark mode per golden rule. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b8ad91bbda
commit
e0b6103458
74
ci-runner.sh
74
ci-runner.sh
@ -202,6 +202,41 @@ for item in error_items:
|
||||
detail_block = html.escape(failure_details) if failure_details.strip() else '<em>No detailed output captured</em>'
|
||||
pkg_block = html.escape(pkg_output) if pkg_output.strip() else ''
|
||||
ts = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')
|
||||
baseline = '$BASELINE'
|
||||
|
||||
# Generate suggestions based on failure patterns
|
||||
tips = []
|
||||
all_output = failure_details + ' ' + features + ' ' + pkg_output
|
||||
if 'MISSING' in features and 'features verified' in features:
|
||||
tips.append(('Feature Manifest', 'One or more feature patterns were not found in the expected files. This usually means code was moved or renamed during a refactor. Check .features/*.manifest and update the file paths to match where the code lives now.'))
|
||||
if int(failed) > 0:
|
||||
if 'ImportError' in all_output or 'ModuleNotFoundError' in all_output:
|
||||
tips.append(('Import Error', 'A test failed because a module could not be imported. A file may have been renamed, moved, or a dependency is missing from the venv. Run: pip install -r requirements.txt'))
|
||||
if 'AssertionError' in all_output or 'assert' in all_output:
|
||||
tips.append(('Assertion Failure', 'A test assertion did not match expected values. Check if the API response shape changed, a default value was modified, or test fixtures are stale.'))
|
||||
if '401' in all_output or 'Unauthorized' in all_output:
|
||||
tips.append(('Auth Failure', 'A test got 401 Unauthorized. The endpoint may have been put behind authentication, or the test client is missing a JWT cookie.'))
|
||||
if '404' in all_output:
|
||||
tips.append(('Route Not Found', 'A test got 404. The endpoint may have been renamed, moved to a different router, or removed entirely.'))
|
||||
if '500' in all_output or 'Internal Server Error' in all_output:
|
||||
tips.append(('Server Error', 'A test triggered a 500 error. Check the backend logs for the full traceback: journalctl -u tsharp-scheduler --since \"5 minutes ago\"'))
|
||||
if int(errors) > 0:
|
||||
if 'fixture' in all_output.lower():
|
||||
tips.append(('Fixture Error', 'A test fixture failed during setup. A conftest.py fixture may be broken or a required package (pytest-mock, pytest-asyncio, pytest-timeout) may be missing.'))
|
||||
if 'collection' in all_output.lower():
|
||||
tips.append(('Collection Error', 'pytest failed to collect tests. A test file may have a syntax error or a top-level import that crashes.'))
|
||||
if baseline and int(passed) < int(baseline) and int(passed) > 0:
|
||||
diff = int(baseline) - int(passed)
|
||||
tips.append(('Test Count Regression', f'{diff} fewer tests passed than the baseline ({baseline}). Tests may have been deleted, moved out of backend/tests/, or a new skip/deselect was introduced without updating the baseline.'))
|
||||
if 'FAIL' in pkg_output:
|
||||
tips.append(('Missing Package', 'One or more required Python packages are not installed. Run: source .venv/bin/activate && pip install -r requirements.txt'))
|
||||
if not tips:
|
||||
tips.append(('Unknown', 'No specific pattern matched. Check the failure details above and the backend logs for more context.'))
|
||||
|
||||
suggestions = '<ul style=\"margin:0;padding-left:1.2rem\">'
|
||||
for title, tip in tips:
|
||||
suggestions += f'<li><strong>{html.escape(title)}:</strong> {html.escape(tip)}</li>'
|
||||
suggestions += '</ul>'
|
||||
|
||||
report = f'''<!DOCTYPE html>
|
||||
<html lang=\"en\">
|
||||
@ -210,26 +245,28 @@ report = f'''<!DOCTYPE html>
|
||||
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">
|
||||
<title>CI Failure Report — {html.escape(branch)} ({html.escape(commit)})</title>
|
||||
<style>
|
||||
body {{ font-family: -apple-system, BlinkMacSystemFont, sans-serif; max-width: 800px; margin: 2rem auto; padding: 0 1rem; color: #1a1a2e; background: #f5f5f7; }}
|
||||
h1 {{ color: #e53e3e; border-bottom: 3px solid #e53e3e; padding-bottom: 0.5rem; }}
|
||||
.meta {{ background: #fff; border-radius: 8px; padding: 1rem 1.5rem; margin: 1rem 0; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }}
|
||||
body {{ font-family: -apple-system, BlinkMacSystemFont, sans-serif; max-width: 800px; margin: 2rem auto; padding: 0 1rem; color: #e2e8f0; background: #0d1117; }}
|
||||
h1 {{ color: #ff6b6b; border-bottom: 3px solid #ff6b6b; padding-bottom: 0.5rem; }}
|
||||
h2 {{ color: #58a6ff; margin-top: 1.5rem; }}
|
||||
.meta {{ background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 1rem 1.5rem; margin: 1rem 0; }}
|
||||
.meta table {{ width: 100%; border-collapse: collapse; }}
|
||||
.meta td {{ padding: 0.3rem 0.5rem; }}
|
||||
.meta td:first-child {{ font-weight: 600; width: 120px; color: #4a5568; }}
|
||||
.meta td {{ padding: 0.3rem 0.5rem; color: #c9d1d9; }}
|
||||
.meta td:first-child {{ font-weight: 600; width: 120px; color: #8b949e; }}
|
||||
.meta ul {{ color: #c9d1d9; }}
|
||||
.stats {{ display: flex; gap: 1rem; flex-wrap: wrap; margin: 1rem 0; }}
|
||||
.stat {{ background: #fff; padding: 0.75rem 1.25rem; border-radius: 6px; text-align: center; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }}
|
||||
.stat {{ background: #161b22; border: 1px solid #30363d; padding: 0.75rem 1.25rem; border-radius: 6px; text-align: center; }}
|
||||
.stat .num {{ font-size: 1.5rem; font-weight: 700; }}
|
||||
.stat .label {{ font-size: 0.75rem; color: #718096; text-transform: uppercase; }}
|
||||
.green {{ color: #38a169; }}
|
||||
.red {{ color: #e53e3e; }}
|
||||
.yellow {{ color: #d69e2e; }}
|
||||
table.failures {{ width: 100%; border-collapse: collapse; margin: 1rem 0; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }}
|
||||
table.failures th {{ background: #e53e3e; color: white; padding: 0.6rem 0.8rem; text-align: left; }}
|
||||
table.failures td {{ padding: 0.5rem 0.8rem; border-bottom: 1px solid #e2e8f0; font-size: 0.9em; }}
|
||||
.stat .label {{ font-size: 0.75rem; color: #8b949e; text-transform: uppercase; }}
|
||||
.green {{ color: #40c057; }}
|
||||
.red {{ color: #ff6b6b; }}
|
||||
.yellow {{ color: #f59f00; }}
|
||||
table.failures {{ width: 100%; border-collapse: collapse; margin: 1rem 0; background: #161b22; border: 1px solid #30363d; border-radius: 8px; overflow: hidden; }}
|
||||
table.failures th {{ background: #3d0000; color: #ff6b6b; padding: 0.6rem 0.8rem; text-align: left; }}
|
||||
table.failures td {{ padding: 0.5rem 0.8rem; border-bottom: 1px solid #21262d; font-size: 0.9em; color: #c9d1d9; }}
|
||||
table.failures tr:last-child td {{ border-bottom: none; }}
|
||||
pre {{ background: #2d3748; color: #e2e8f0; padding: 1rem; border-radius: 8px; overflow-x: auto; font-size: 0.85em; line-height: 1.5; }}
|
||||
code {{ background: #edf2f7; padding: 2px 6px; border-radius: 3px; font-size: 0.85em; }}
|
||||
.footer {{ color: #a0aec0; font-size: 0.8rem; margin-top: 2rem; border-top: 1px solid #e2e8f0; padding-top: 0.5rem; }}
|
||||
pre {{ background: #161b22; color: #c9d1d9; padding: 1rem; border-radius: 8px; overflow-x: auto; font-size: 0.85em; line-height: 1.5; border: 1px solid #30363d; }}
|
||||
code {{ background: #21262d; padding: 2px 6px; border-radius: 3px; font-size: 0.85em; color: #79c0ff; }}
|
||||
.footer {{ color: #484f58; font-size: 0.8rem; margin-top: 2rem; border-top: 1px solid #21262d; padding-top: 0.5rem; }}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -262,6 +299,11 @@ report = f'''<!DOCTYPE html>
|
||||
<h2>Failure Details</h2>
|
||||
<pre>{detail_block}</pre>
|
||||
|
||||
<h2>Suggestions</h2>
|
||||
<div class=\"meta\">
|
||||
{suggestions}
|
||||
</div>
|
||||
|
||||
<div class=\"footer\">Generated by TSHARPS CI Runner — {ts}</div>
|
||||
</body>
|
||||
</html>'''
|
||||
|
||||
Loading…
Reference in New Issue
Block a user