Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 59 additions & 40 deletions .github/scripts/tests/templates/summary.html
Original file line number Diff line number Diff line change
Expand Up @@ -491,11 +491,33 @@

// Configuration constants
const MAX_URL_LENGTH = 8000; // GitHub URL limit is 8201, stay under 8000 to be safe
const LOG_ENTRY_ESTIMATE = 150; // Estimated characters per log entry
const MIN_REMAINING_SPACE = 100; // Minimum space needed for log section
const ERROR_BUFFER_SPACE = 50; // Buffer space when adding error details
const GITHUB_ISSUE_BASE_URL = "https://github.com/ydb-platform/ydb/issues/new"; // Base URL for GitHub issues

// Utility functions
function buildGitHubIssueUrl(title, body, labels) {
return GITHUB_ISSUE_BASE_URL + "?title=" + title + "&body=" + body + "&labels=" + labels + "&type=Bug";
}

// Show tooltip stub to avoid runtime errors (visual feedback provided by icon swap)
function showCopiedTooltip() {}

// Safe percent-encoded truncation helpers
function isDecodable(s) {
Copy link

Copilot AI Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function should handle the case where the input parameter is null or undefined to prevent potential runtime errors.

Suggested change
function isDecodable(s) {
function isDecodable(s) {
if (typeof s !== 'string') return false;

Copilot uses AI. Check for mistakes.
try { decodeURIComponent(s); return true; } catch (e) { return false; }
}

function safeTruncateEncoded(encoded, maxLen) {
if (encoded.length <= maxLen) return encoded;
let cut = encoded.slice(0, maxLen);
// Remove incomplete % sequence at the tail
cut = cut.replace(/%(?:[0-9A-Fa-f]{0,2})$/, '');
while (cut && !isDecodable(cut)) {
if (/%[0-9A-Fa-f]{2}$/.test(cut)) cut = cut.slice(0, -3);
else cut = cut.slice(0, -1);
Comment on lines +514 to +516
Copy link

Copilot AI Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This while loop could potentially run for a long time with malformed input. Consider adding a maximum iteration limit to prevent infinite loops.

Suggested change
while (cut && !isDecodable(cut)) {
if (/%[0-9A-Fa-f]{2}$/.test(cut)) cut = cut.slice(0, -3);
else cut = cut.slice(0, -1);
let maxIterations = 1000, iter = 0;
while (cut && !isDecodable(cut) && iter < maxIterations) {
if (/%[0-9A-Fa-f]{2}$/.test(cut)) cut = cut.slice(0, -3);
else cut = cut.slice(0, -1);
iter++;

Copilot uses AI. Check for mistakes.
}
return cut;
}

function parseTestName(full_name) {
const trimmed = full_name.trim();
let pieces;
Expand Down Expand Up @@ -611,46 +633,47 @@
return body;
}

function addOptionalContent(baseBody, logUrls, errorText, remainingSpace) {
function buildBodyWithLengthLimit(baseBody, logUrls, errorText, title, labels, maxUrlLength) {
let body = baseBody;
let availableSpace = remainingSpace;

// Add logs if space allows
if (logUrls && Object.keys(logUrls).length > 0 && availableSpace > MIN_REMAINING_SPACE) {
// Add logs first
if (logUrls && Object.keys(logUrls).length > 0) {
let logsSection = "**Logs:**%0A";
let logCount = 0;
const maxLogs = Math.min(Object.keys(logUrls).length, Math.floor(availableSpace / LOG_ENTRY_ESTIMATE));

for (const [logName, logUrl] of Object.entries(logUrls)) {
if (logCount >= maxLogs) break;
const logEntry = "- [" + encodeURIComponent(logName) + "](" + encodeURIComponent(logUrl) + ")%0A";
if (logsSection.length + logEntry.length < availableSpace - ERROR_BUFFER_SPACE) {
logsSection += logEntry;
logCount++;
} else {
break;
}
}

if (Object.keys(logUrls).length > logCount) {
logsSection += "- (and " + (Object.keys(logUrls).length - logCount) + " more logs in report)%0A";
logsSection += "- [" + encodeURIComponent(logName) + "](" + encodeURIComponent(logUrl) + ")%0A";
}
logsSection += "%0A";

body += logsSection;
availableSpace -= logsSection.length;
}

// Add error details if space allows
if (errorText && errorText.trim() !== "" && availableSpace > ERROR_BUFFER_SPACE) {
const maxErrorLength = Math.min(errorText.trim().length, availableSpace - ERROR_BUFFER_SPACE);
const errorToShow = errorText.trim().substring(0, maxErrorLength);
let errorSection = "**Error Summary:**%0A```%0A" + encodeURIComponent(errorToShow);
if (errorText.trim().length > maxErrorLength) {
errorSection += "... (see full error in test report)";
// Try to add error text, checking actual URL length
if (errorText && errorText.trim() !== "") {
const errorPrefix = "**Error Summary:**%0A```%0A";
const errorSuffix = "%0A```%0A%0A";
const truncationMsg = "%0A<TRUNCATED>";

const fullErrorText = errorText.trim();
const encodedFullError = encodeURIComponent(fullErrorText);

// Compute minimal overhead URL length (without error content)
const minimalSection = errorPrefix + errorSuffix;
const minimalUrl = buildGitHubIssueUrl(title, body + minimalSection, labels);
if (minimalUrl.length > maxUrlLength) {
// Can't include error section at all
} else {
const allowedDelta = maxUrlLength - minimalUrl.length;
if (encodedFullError.length <= allowedDelta) {
body += errorPrefix + encodedFullError + errorSuffix;
} else {
const allowedForEncoded = allowedDelta - truncationMsg.length;
if (allowedForEncoded > 0) {
const truncatedEncoded = safeTruncateEncoded(encodedFullError, allowedForEncoded);
if (truncatedEncoded) {
body += errorPrefix + truncatedEncoded + truncationMsg + errorSuffix;
}
}
}
}
errorSection += "%0A```%0A%0A";
body += errorSection;
}

return body;
Expand Down Expand Up @@ -691,15 +714,11 @@
// Build labels
const labels = buildLabels(actualIssueType, buildPreset, ownerAreaLabel);

// Calculate base URL length
const baseUrl = "https://github.com/ydb-platform/ydb/issues/new?title=" + title + "&body=" + body + "&labels=" + labels + "&type=Bug";
const remainingSpace = MAX_URL_LENGTH - baseUrl.length;

// Add optional content based on available space
const finalBody = addOptionalContent(body, logUrls, errorText, remainingSpace);
// Build body with exact length limit checking
const finalBody = buildBodyWithLengthLimit(body, logUrls, errorText, title, labels, MAX_URL_LENGTH);

// Create final URL and open
const finalUrl = "https://github.com/ydb-platform/ydb/issues/new?title=" + title + "&body=" + finalBody + "&labels=" + labels + "&type=Bug";
const finalUrl = buildGitHubIssueUrl(title, finalBody, labels);
window.open(finalUrl, '_blank');
}

Expand Down