diff --git a/ci-notify.sh b/ci-notify.sh index 4d27098..a0455c4 100755 --- a/ci-notify.sh +++ b/ci-notify.sh @@ -18,6 +18,7 @@ SUMMARY="$4" # e.g., "940 passed, 0 failed, 393 skipped" FEATURES="$5" # e.g., "31/31 features verified" DURATION="$6" # e.g., "12.3s" ACTOR="$7" # who pushed +REPORT_FILE="$8" # optional HTML failure report path if [ "$STATUS" = "pass" ]; then ICON="✅" @@ -58,4 +59,14 @@ curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \ -d "message_thread_id=${TOPIC_ID}" \ -d "text=${MSG}" > /dev/null 2>&1 +# Send HTML failure report as attachment if available +if [ -n "$REPORT_FILE" ] && [ -f "$REPORT_FILE" ]; then + curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendDocument" \ + -F "chat_id=${CHAT_ID}" \ + -F "message_thread_id=${TOPIC_ID}" \ + -F "document=@${REPORT_FILE}" \ + -F "caption=CI Failure Report — ${BRANCH} (${COMMIT})" > /dev/null 2>&1 + rm -f "$REPORT_FILE" +fi + exit 0 diff --git a/ci-runner.sh b/ci-runner.sh index b0a965e..4961487 100755 --- a/ci-runner.sh +++ b/ci-runner.sh @@ -147,9 +147,121 @@ echo "Features: $FEATURES_RESULT" echo "Duration: ${DURATION}s" echo "=========================" +# ─── Generate Failure Summary HTML ─── +REPORT_FILE="" +if [ "$OVERALL" = "fail" ]; then + REPORT_FILE="/tmp/tsharps-ci-report-${BRANCH}-$(date +%Y%m%d-%H%M%S).html" + FAILED_LINES=$(echo "$TEST_OUTPUT" | grep "^FAILED " || true) + ERROR_LINES=$(echo "$TEST_OUTPUT" | grep "^ERROR " || true) + FAILURE_DETAILS=$(echo "$TEST_OUTPUT" | grep -B0 "^FAILED \|^E \|Error\|assert" | head -60 || true) + + python3 -c " +import html, sys +from datetime import datetime, timezone + +branch = '$BRANCH' +commit = '$COMMIT' +actor = '$ACTOR' +duration = '${DURATION}s' +passed = '$PASS_COUNT' +failed = '$FAIL_COUNT' +skipped = '$SKIP_COUNT' +errors = '$ERROR_COUNT' +features = '''$FEATURES_RESULT''' +failed_lines = '''$FAILED_LINES''' +failure_details = '''$FAILURE_DETAILS''' +pkg_output = '''$PKG_OUTPUT''' + +failed_items = [l.replace('FAILED ', '') for l in failed_lines.strip().split('\n') if l.strip()] +error_items = [l for l in '''$ERROR_LINES'''.strip().split('\n') if l.strip()] + +rows = '' +for item in failed_items: + parts = item.rsplit('::', 1) + file_part = parts[0] if len(parts) > 0 else item + test_part = parts[1] if len(parts) > 1 else '' + rows += f'{html.escape(file_part)}{html.escape(test_part)}\n' + +for item in error_items: + rows += f'{html.escape(item)}\n' + +detail_block = html.escape(failure_details) if failure_details.strip() else 'No detailed output captured' +pkg_block = html.escape(pkg_output) if pkg_output.strip() else '' +ts = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC') + +report = f''' + + + + +CI Failure Report — {html.escape(branch)} ({html.escape(commit)}) + + + +

CI Failure Report

+ +
+ + + + + + + +
Branch{html.escape(branch)}
Commit{html.escape(commit)}
Pushed by{html.escape(actor)}
Duration{html.escape(duration)}
Features{html.escape(features)}
Timestamp{ts}
+
+ +
+
{html.escape(passed)}
Passed
+
{html.escape(failed)}
Failed
+
{html.escape(skipped)}
Skipped
+
{html.escape(errors)}
Errors
+
+ +

Failed Tests

+ + + {rows} +
FileTest
+ +

Failure Details

+
{detail_block}
+ +
Generated by TSHARPS CI Runner — {ts}
+ +''' + +with open('$REPORT_FILE', 'w') as f: + f.write(report) +print(f'Report written to $REPORT_FILE') +" 2>&1 + echo "Failure report: $REPORT_FILE" +fi + # ─── Send Notification ─── bash "$SCRIPT_DIR/ci-notify.sh" \ - "$BRANCH" "$COMMIT" "$OVERALL" "$SUMMARY" "$FEATURES_RESULT" "${DURATION}s" "$ACTOR" + "$BRANCH" "$COMMIT" "$OVERALL" "$SUMMARY" "$FEATURES_RESULT" "${DURATION}s" "$ACTOR" "$REPORT_FILE" # Release lock flock -u 200