From 53d25fc27355fcd68725f24cf45c8aedc361243c Mon Sep 17 00:00:00 2001 From: Kirill Rysin Date: Tue, 9 Sep 2025 22:41:34 +0200 Subject: [PATCH 01/10] max url lenght now will be find with binaty search --- .github/scripts/tests/templates/summary.html | 86 ++++++++++++-------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/.github/scripts/tests/templates/summary.html b/.github/scripts/tests/templates/summary.html index 65e7a02d514b..e7a7bfa3823c 100644 --- a/.github/scripts/tests/templates/summary.html +++ b/.github/scripts/tests/templates/summary.html @@ -611,46 +611,64 @@ 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 = "... (see full error in test report)"; + + // Start with full error text and try to fit it + const fullErrorText = errorText.trim(); + let testErrorSection = errorPrefix + encodeURIComponent(fullErrorText) + errorSuffix; + let testBody = body + testErrorSection; + let testUrl = "https://github.com/ydb-platform/ydb/issues/new?title=" + title + "&body=" + testBody + "&labels=" + labels + "&type=Bug"; + + if (testUrl.length <= maxUrlLength) { + // Full error fits, use it + body += testErrorSection; + } else { + // Need to truncate - binary search for maximum length that fits (max 4 iterations) + let left = 0; + let right = fullErrorText.length; + let bestLength = 0; + let iterations = 0; + const maxIterations = 4; + + while (left <= right && iterations < maxIterations) { + const mid = Math.floor((left + right) / 2); + const errorToTest = fullErrorText.substring(0, mid); + testErrorSection = errorPrefix + encodeURIComponent(errorToTest) + truncationMsg + errorSuffix; + testBody = body + testErrorSection; + testUrl = "https://github.com/ydb-platform/ydb/issues/new?title=" + title + "&body=" + testBody + "&labels=" + labels + "&type=Bug"; + + if (testUrl.length <= maxUrlLength) { + bestLength = mid; + left = mid + 1; + } else { + right = mid - 1; + } + iterations++; + } + + if (bestLength > 0) { + const errorToShow = fullErrorText.substring(0, bestLength); + const errorSection = errorPrefix + encodeURIComponent(errorToShow) + truncationMsg + errorSuffix; + body += errorSection; + } } - errorSection += "%0A```%0A%0A"; - body += errorSection; } return body; @@ -691,12 +709,8 @@ // 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"; From dd457221566369d549176bbd1d55b4482eb721b5 Mon Sep 17 00:00:00 2001 From: Kirill Rysin Date: Wed, 10 Sep 2025 07:19:43 +0200 Subject: [PATCH 02/10] trigger ci --- ydb/tests/functional/tpc/medium/test_workload_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ydb/tests/functional/tpc/medium/test_workload_manager.py b/ydb/tests/functional/tpc/medium/test_workload_manager.py index c7260e51b602..c5f3bd1af6f1 100644 --- a/ydb/tests/functional/tpc/medium/test_workload_manager.py +++ b/ydb/tests/functional/tpc/medium/test_workload_manager.py @@ -6,7 +6,7 @@ class TestClickbenchWM(wm.TestWorkloadMangerClickbenchConcurentQueryLimit, FunctionalTestBase): iterations: int = 2 verify_data: bool = False - + #-- @classmethod def setup_class(cls) -> None: cls.setup_cluster() From 09f775ccb6b53439ccef49c89084d2d1a12e4cd7 Mon Sep 17 00:00:00 2001 From: Kirill Rysin Date: Wed, 10 Sep 2025 07:25:14 +0200 Subject: [PATCH 03/10] copilot improvments --- .github/scripts/tests/templates/summary.html | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/scripts/tests/templates/summary.html b/.github/scripts/tests/templates/summary.html index e7a7bfa3823c..3dc66ab1c4e0 100644 --- a/.github/scripts/tests/templates/summary.html +++ b/.github/scripts/tests/templates/summary.html @@ -494,8 +494,14 @@ 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 MAX_BINARY_SEARCH_ITERATIONS = 4; // Limit binary search to 4 iterations for performance + 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"; + } + function parseTestName(full_name) { const trimmed = full_name.trim(); let pieces; @@ -634,7 +640,7 @@ const fullErrorText = errorText.trim(); let testErrorSection = errorPrefix + encodeURIComponent(fullErrorText) + errorSuffix; let testBody = body + testErrorSection; - let testUrl = "https://github.com/ydb-platform/ydb/issues/new?title=" + title + "&body=" + testBody + "&labels=" + labels + "&type=Bug"; + let testUrl = buildGitHubIssueUrl(title, testBody, labels); if (testUrl.length <= maxUrlLength) { // Full error fits, use it @@ -644,15 +650,14 @@ let left = 0; let right = fullErrorText.length; let bestLength = 0; - let iterations = 0; - const maxIterations = 4; - - while (left <= right && iterations < maxIterations) { + let iterations = 0; + + while (left <= right && iterations < MAX_BINARY_SEARCH_ITERATIONS) { const mid = Math.floor((left + right) / 2); const errorToTest = fullErrorText.substring(0, mid); testErrorSection = errorPrefix + encodeURIComponent(errorToTest) + truncationMsg + errorSuffix; testBody = body + testErrorSection; - testUrl = "https://github.com/ydb-platform/ydb/issues/new?title=" + title + "&body=" + testBody + "&labels=" + labels + "&type=Bug"; + testUrl = buildGitHubIssueUrl(title, testBody, labels); if (testUrl.length <= maxUrlLength) { bestLength = mid; @@ -713,7 +718,7 @@ 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'); } From dbb22ab61cee75a574e2def39824764c23c93343 Mon Sep 17 00:00:00 2001 From: Kirill Rysin Date: Wed, 10 Sep 2025 07:30:36 +0200 Subject: [PATCH 04/10] delete unused const --- .github/scripts/tests/templates/summary.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/scripts/tests/templates/summary.html b/.github/scripts/tests/templates/summary.html index 3dc66ab1c4e0..4209a42d7aa8 100644 --- a/.github/scripts/tests/templates/summary.html +++ b/.github/scripts/tests/templates/summary.html @@ -491,9 +491,6 @@ // 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 MAX_BINARY_SEARCH_ITERATIONS = 4; // Limit binary search to 4 iterations for performance const GITHUB_ISSUE_BASE_URL = "https://github.com/ydb-platform/ydb/issues/new"; // Base URL for GitHub issues From 518b0f4039d881b0a8a65fb92b8dc46118d53851 Mon Sep 17 00:00:00 2001 From: Kirill Rysin Date: Wed, 10 Sep 2025 07:42:11 +0200 Subject: [PATCH 05/10] fix --- .github/scripts/tests/templates/summary.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/tests/templates/summary.html b/.github/scripts/tests/templates/summary.html index 4209a42d7aa8..4001e31149b7 100644 --- a/.github/scripts/tests/templates/summary.html +++ b/.github/scripts/tests/templates/summary.html @@ -647,9 +647,9 @@ let left = 0; let right = fullErrorText.length; let bestLength = 0; - let iterations = 0; + let iterations = 0; - while (left <= right && iterations < MAX_BINARY_SEARCH_ITERATIONS) { + while (left <= right && iterations < MAX_BINARY_SEARCH_ITERATIONS) { const mid = Math.floor((left + right) / 2); const errorToTest = fullErrorText.substring(0, mid); testErrorSection = errorPrefix + encodeURIComponent(errorToTest) + truncationMsg + errorSuffix; From 7ae223dd6107e73613dd81a4809a04ab485d8d88 Mon Sep 17 00:00:00 2001 From: Kirill Rysin Date: Wed, 10 Sep 2025 10:20:13 +0200 Subject: [PATCH 06/10] rollback --- ydb/tests/functional/tpc/medium/test_workload_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ydb/tests/functional/tpc/medium/test_workload_manager.py b/ydb/tests/functional/tpc/medium/test_workload_manager.py index c5f3bd1af6f1..c7260e51b602 100644 --- a/ydb/tests/functional/tpc/medium/test_workload_manager.py +++ b/ydb/tests/functional/tpc/medium/test_workload_manager.py @@ -6,7 +6,7 @@ class TestClickbenchWM(wm.TestWorkloadMangerClickbenchConcurentQueryLimit, FunctionalTestBase): iterations: int = 2 verify_data: bool = False - #-- + @classmethod def setup_class(cls) -> None: cls.setup_cluster() From 64c201f2305601ef49a330f91f5f629dfe059cfe Mon Sep 17 00:00:00 2001 From: Kirill Rysin Date: Wed, 10 Sep 2025 10:50:22 +0200 Subject: [PATCH 07/10] fix trunk message --- .github/scripts/tests/templates/summary.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/tests/templates/summary.html b/.github/scripts/tests/templates/summary.html index 4001e31149b7..2e66b5cd4aa2 100644 --- a/.github/scripts/tests/templates/summary.html +++ b/.github/scripts/tests/templates/summary.html @@ -631,7 +631,7 @@ if (errorText && errorText.trim() !== "") { const errorPrefix = "**Error Summary:**%0A```%0A"; const errorSuffix = "%0A```%0A%0A"; - const truncationMsg = "... (see full error in test report)"; + const truncationMsg = "%0A"; // Start with full error text and try to fit it const fullErrorText = errorText.trim(); From 6b9aa25764a2565d71b52e3c144f28066405c2d4 Mon Sep 17 00:00:00 2001 From: Kirill Rysin Date: Mon, 15 Sep 2025 22:13:06 +0200 Subject: [PATCH 08/10] remove bisect --- .github/scripts/tests/templates/summary.html | 71 ++++++++++---------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/.github/scripts/tests/templates/summary.html b/.github/scripts/tests/templates/summary.html index 2e66b5cd4aa2..e01e945f41db 100644 --- a/.github/scripts/tests/templates/summary.html +++ b/.github/scripts/tests/templates/summary.html @@ -491,7 +491,6 @@ // Configuration constants const MAX_URL_LENGTH = 8000; // GitHub URL limit is 8201, stay under 8000 to be safe - const MAX_BINARY_SEARCH_ITERATIONS = 4; // Limit binary search to 4 iterations for performance const GITHUB_ISSUE_BASE_URL = "https://github.com/ydb-platform/ydb/issues/new"; // Base URL for GitHub issues // Utility functions @@ -499,6 +498,26 @@ 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) { + 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); + } + return cut; + } + function parseTestName(full_name) { const trimmed = full_name.trim(); let pieces; @@ -633,42 +652,26 @@ const errorSuffix = "%0A```%0A%0A"; const truncationMsg = "%0A"; - // Start with full error text and try to fit it const fullErrorText = errorText.trim(); - let testErrorSection = errorPrefix + encodeURIComponent(fullErrorText) + errorSuffix; - let testBody = body + testErrorSection; - let testUrl = buildGitHubIssueUrl(title, testBody, labels); - - if (testUrl.length <= maxUrlLength) { - // Full error fits, use it - body += testErrorSection; + 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 { - // Need to truncate - binary search for maximum length that fits (max 4 iterations) - let left = 0; - let right = fullErrorText.length; - let bestLength = 0; - let iterations = 0; - - while (left <= right && iterations < MAX_BINARY_SEARCH_ITERATIONS) { - const mid = Math.floor((left + right) / 2); - const errorToTest = fullErrorText.substring(0, mid); - testErrorSection = errorPrefix + encodeURIComponent(errorToTest) + truncationMsg + errorSuffix; - testBody = body + testErrorSection; - testUrl = buildGitHubIssueUrl(title, testBody, labels); - - if (testUrl.length <= maxUrlLength) { - bestLength = mid; - left = mid + 1; - } else { - right = mid - 1; + 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; + } } - iterations++; - } - - if (bestLength > 0) { - const errorToShow = fullErrorText.substring(0, bestLength); - const errorSection = errorPrefix + encodeURIComponent(errorToShow) + truncationMsg + errorSuffix; - body += errorSection; } } } From 2a19d74ccd9632b73b04be1064edbbcbbe89f83d Mon Sep 17 00:00:00 2001 From: Kirill Rysin Date: Mon, 15 Sep 2025 22:19:28 +0200 Subject: [PATCH 09/10] trigger ci --- ydb/tests/fq/streaming_optimize/test_sql_streaming.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ydb/tests/fq/streaming_optimize/test_sql_streaming.py b/ydb/tests/fq/streaming_optimize/test_sql_streaming.py index a2c0b304a8c6..0dbbfa7a3e3b 100644 --- a/ydb/tests/fq/streaming_optimize/test_sql_streaming.py +++ b/ydb/tests/fq/streaming_optimize/test_sql_streaming.py @@ -2,6 +2,9 @@ import pytest import yatest.common +# Trigger CI run for issue creation report verification + + from test_utils import pytest_generate_tests_for_run, get_case_file from ydb.tests.fq.tools.fqrun import FqRun from yql_utils import yql_binary_path From ff5e871c03771b59d5a52d3e73340c578bac8f99 Mon Sep 17 00:00:00 2001 From: Kirill Rysin Date: Mon, 15 Sep 2025 23:59:19 +0200 Subject: [PATCH 10/10] rollback --- ydb/tests/fq/streaming_optimize/test_sql_streaming.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/ydb/tests/fq/streaming_optimize/test_sql_streaming.py b/ydb/tests/fq/streaming_optimize/test_sql_streaming.py index 0dbbfa7a3e3b..a2c0b304a8c6 100644 --- a/ydb/tests/fq/streaming_optimize/test_sql_streaming.py +++ b/ydb/tests/fq/streaming_optimize/test_sql_streaming.py @@ -2,9 +2,6 @@ import pytest import yatest.common -# Trigger CI run for issue creation report verification - - from test_utils import pytest_generate_tests_for_run, get_case_file from ydb.tests.fq.tools.fqrun import FqRun from yql_utils import yql_binary_path