Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,41 @@ public String getTestFailsAiPrompt(
@QueryParam("serverId") String srvCodeOrAlias,
@QueryParam("buildId") Integer buildId,
@Nullable @QueryParam("maxDetailsChars") Integer maxDetailsChars,
@Nullable @QueryParam("waitForTc") Boolean waitForTc,
@Nullable @QueryParam("background") Boolean background,
@Nullable @QueryParam("processId") Long processId) throws ServiceUnauthorizedException {
return CtxListener.getApplicationContext(ctx)
.getInstance(SingleBuildResultsService.class)
.getSingleBuildFailuresAiPrompt(srvCodeOrAlias, buildId, maxDetailsChars, SyncMode.RELOAD_QUEUED,
SingleBuildResultsService processor = CtxListener.getApplicationContext(ctx)
.getInstance(SingleBuildResultsService.class);

if (Boolean.TRUE.equals(background)) {
return processor.startSingleBuildFailuresAiPromptRefresh(srvCodeOrAlias, buildId, SyncMode.RELOAD_QUEUED,
ITcBotUserCreds.get(req), processId);
}

SyncMode mode = waitForTc == null || waitForTc ? SyncMode.RELOAD_QUEUED : SyncMode.NONE;

return processor.getSingleBuildFailuresAiPrompt(srvCodeOrAlias, buildId, maxDetailsChars, mode,
ITcBotUserCreds.get(req), processId);
}

@GET
@Path("failures/analyzeLogs")
@Produces(MediaType.TEXT_PLAIN)
public String analyzeBuildLogs(
@QueryParam("serverId") String srvCodeOrAlias,
@QueryParam("buildId") Integer buildId,
@Nullable @QueryParam("background") Boolean background,
@Nullable @QueryParam("processId") Long processId) throws ServiceUnauthorizedException {
SingleBuildResultsService processor = CtxListener.getApplicationContext(ctx)
.getInstance(SingleBuildResultsService.class);

if (Boolean.TRUE.equals(background)) {
return processor.startSingleBuildLogAnalysis(srvCodeOrAlias, buildId, SyncMode.RELOAD_QUEUED,
ITcBotUserCreds.get(req), processId);
}

return processor.analyzeSingleBuildLogs(srvCodeOrAlias, buildId, SyncMode.RELOAD_QUEUED,
ITcBotUserCreds.get(req), processId);
}

@GET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ public DsSummaryUi getPrFailsWithSyncMode(
* @param testName Optional full test name filter.
* @param promptSuiteId Optional suite id filter.
* @param waitForTc Wait for fresh TeamCity context and build log processing.
* @param background Start fresh context refresh in background and return immediately.
*/
@GET
@Path("results/aiPrompt")
Expand All @@ -246,10 +247,26 @@ public DsSummaryUi getPrFailsWithSyncMode(
@Nullable @QueryParam("testName") String testName,
@Nullable @QueryParam("promptSuiteId") String promptSuiteId,
@Nullable @QueryParam("waitForTc") Boolean waitForTc,
@Nullable @QueryParam("background") Boolean background,
@Nullable @QueryParam("processId") Long processId) {
final TcBotApplicationContext appCtx = CtxListener.getApplicationContext(ctx);
final PrChainsProcessor processor = appCtx.getInstance(PrChainsProcessor.class);

return appCtx.getInstance(PrChainsProcessor.class).getPrFailuresAiPrompt(
if (Boolean.TRUE.equals(background)) {
return processor.startPrFailuresAiPromptRefresh(
ITcBotUserCreds.get(req),
srvId,
suiteId,
branchForTc,
act,
cnt,
baseBranchForTc,
testName,
promptSuiteId,
processId);
}

return processor.getPrFailuresAiPrompt(
ITcBotUserCreds.get(req),
srvId,
suiteId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,25 @@ public String getTestFailsAiPrompt(@Nullable @QueryParam("branch") String branch
@Nullable @QueryParam("testName") String testName,
@Nullable @QueryParam("promptSuiteId") String promptSuiteId,
@Nullable @QueryParam("waitForTc") Boolean waitForTc,
@Nullable @QueryParam("background") Boolean background,
@Nullable @QueryParam("processId") Long processId) {
int actualMergeBuilds = (mergeCnt == null || mergeCnt < 1) ? 1 : mergeCnt;
TrackedBranchChainsProcessor processor = CtxListener.getApplicationContext(ctx)
.getInstance(TrackedBranchChainsProcessor.class);

return CtxListener.getApplicationContext(ctx)
.getInstance(TrackedBranchChainsProcessor.class)
.getTrackedBranchFailuresAiPrompt(branchOrNull,
if (Boolean.TRUE.equals(background)) {
return processor.startTrackedBranchFailuresAiPromptRefresh(branchOrNull,
actualMergeBuilds,
ITcBotUserCreds.get(req),
SyncMode.RELOAD_QUEUED,
tagForHistSelected,
SortOption.parseStringValue(sortOption),
testName,
promptSuiteId,
processId);
}

return processor.getTrackedBranchFailuresAiPrompt(branchOrNull,
actualMergeBuilds,
ITcBotUserCreds.get(req),
SyncMode.RELOAD_QUEUED,
Expand Down
8 changes: 6 additions & 2 deletions ignite-tc-helper-web/src/main/webapp/build.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>

<script src="js/common-1.7.js?v=20260509-6"></script>
<script src="js/common-1.7.js?v=20260529-1"></script>
<script src="js/testfails-2.4.js?v=20260528-1"></script>
</head>
<body>
Expand Down Expand Up @@ -126,9 +126,13 @@

function showData(result) {
var aiPromptUrl = "rest/build/failures/aiPrompt" + parmsForRest();
var logAnalysisUrl = "rest/build/failures/analyzeLogs" + parmsForRest();

$("#divFailures").html(showChainOnServersResults(result)
+ " <a href='"+ aiPromptUrl + "' title='Download AI prompt with TeamCity failure context'>AI prompt</a>"
+ " <a href='javascript:void(0);' onclick='" + jsCallAttr("openAiPrompt", [aiPromptUrl])
+ "' title='Open AI prompt with TeamCity failure context'>AI prompt</a>"
+ " | <a href='javascript:void(0);' onclick='" + jsCallAttr("openBuildLogAnalysis", [logAnalysisUrl])
+ "' title='Download and analyze build logs'>Analyze logs</a>"
);
}

Expand Down
2 changes: 1 addition & 1 deletion ignite-tc-helper-web/src/main/webapp/current.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>

<script src="js/common-1.7.js?v=20260509-6"></script>
<script src="js/common-1.7.js?v=20260529-1"></script>
<script src="js/testfails-2.4.js?v=20260528-1"></script>
</head>
<body>
Expand Down
92 changes: 88 additions & 4 deletions ignite-tc-helper-web/src/main/webapp/js/common-1.7.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ function botProcessStatusText(status) {
: "Waiting for the bot to publish status.";
}

function botProcessFailed(status) {
return status && (status.failed === true ||
(isDefinedAndFilled(status.status) && status.status.indexOf("Failed:") === 0));
}

function startBotProcessPolling(processId, onStatus, options) {
if (!isDefinedAndFilled(processId))
return function () {};
Expand Down Expand Up @@ -251,8 +256,9 @@ function openAiPrompt(url) {
initialMode: true,
processKind: "aiPrompt",
requestUrl: function (waitForTc, processId) {
return aiPromptUrlWithWaitForTc(url, waitForTc, processId);
return aiPromptUrlWithWaitForTc(url, waitForTc, processId, waitForTc);
},
backgroundInitial: true,
timeoutMs: 70000,
skip: {
isVisible: function (waitForTc) {
Expand All @@ -277,6 +283,33 @@ function openAiPrompt(url) {
});
}

function openBuildLogAnalysis(url) {
openTextCommandDialog({
dialogId: "buildLogAnalysisDialog",
statusId: "buildLogAnalysisStatus",
logId: "buildLogAnalysisProgressLog",
errorId: "buildLogAnalysisError",
title: "Analyzing build logs",
initialMode: true,
processKind: "buildLogAnalysis",
requestUrl: function (mode, processId, background) {
return commandUrlWithProcess(url, processId, background);
},
backgroundInitial: true,
backgroundFollowupStep: "Reading cached log analysis result.",
timeoutMs: 70000,
statusText: function () {
return "Analyzing build logs...";
},
showOpenButton: false,
showDownloadButton: false,
readyStatusText: "Build log analysis is ready.",
readyStepText: "Build log analysis completed.",
failureStatusText: "Build log analysis failed.",
failureMessagePrefix: "Build log analysis failed: "
});
}

function openTextCommandDialog(options) {
let state = createTextCommandDialog(options);

Expand Down Expand Up @@ -317,10 +350,21 @@ function requestTextCommand(options, state, mode, firstStep) {

startTextCommandProgress(options, state, mode, firstStep);

let backgroundInitial = options.backgroundInitial === true && mode === true;

state.xhr = $.ajax({
url: options.requestUrl(mode, state.processId),
url: options.requestUrl(mode, state.processId, backgroundInitial),
timeout: options.timeoutMs == null ? 70000 : options.timeoutMs,
success: function (result) {
if (backgroundInitial) {
state.xhr = null;

if (isDefinedAndFilled(result))
appendTextCommandStep(state, result);

return;
}

finishTextCommandDialog(options, state, result);
},
error: function (jqXHR, status, error) {
Expand Down Expand Up @@ -499,9 +543,25 @@ function restoreWindowScroll(scrollLeft, scrollTop) {
window.scrollTo(scrollLeft, scrollTop);
}

function aiPromptUrlWithWaitForTc(url, waitForTc, processId) {
function aiPromptUrlWithWaitForTc(url, waitForTc, processId, background) {
return url + (url.indexOf("?") >= 0 ? "&" : "?") + "waitForTc=" + waitForTc +
(isDefinedAndFilled(processId) ? "&processId=" + encodeURIComponent(processId) : "");
(isDefinedAndFilled(processId) ? "&processId=" + encodeURIComponent(processId) : "") +
(background === true ? "&background=true" : "");
}

function commandUrlWithProcess(url, processId, background) {
let params = [];

if (isDefinedAndFilled(processId))
params.push("processId=" + encodeURIComponent(processId));

if (background === true)
params.push("background=true");

if (params.length === 0)
return url;

return url + (url.indexOf("?") >= 0 ? "&" : "?") + params.join("&");
}

function startTextCommandProgress(options, state, mode, firstStep) {
Expand All @@ -520,6 +580,17 @@ function startTextCommandProgress(options, state, mode, firstStep) {

state.processPollStop = startBotProcessPolling(state.processId, function (status) {
appendTextCommandStep(state, botProcessStatusText(status));

if (options.backgroundInitial === true && mode === true && status.running === false) {
if (botProcessFailed(status)) {
failTextCommandDialogMessage(options, state, botProcessStatusText(status));

return;
}

requestTextCommand(options, state, false,
options.backgroundFollowupStep || "Building prompt from refreshed context.");
}
});

return;
Expand Down Expand Up @@ -612,6 +683,19 @@ function failTextCommandDialog(options, state, jqXHR, status, error) {
showErrInLoadStatus(jqXHR, status);
}

function failTextCommandDialogMessage(options, state, message) {
if (state.timer)
clearInterval(state.timer);

if (state.processPollStop)
state.processPollStop();

state.status.text(options.failureStatusText || "Command request failed.");
state.skipBtn.hide();
state.errorBlock.text((options.failureMessagePrefix || "Command request failed: ") + message).show();
appendTextCommandStep(state, message);
}

function openTextCommandResult(state) {
if (!state.resultUrl)
return false;
Expand Down
2 changes: 1 addition & 1 deletion ignite-tc-helper-web/src/main/webapp/pr.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<!-- production version, optimized for size and speed -->
<!--<script src="https://cdn.jsdelivr.net/npm/vue"></script>-->
<script src="js/common-1.7.js?v=20260509-6"></script>
<script src="js/common-1.7.js?v=20260529-1"></script>
<script src="js/testfails-2.4.js?v=20260528-1"></script>
</head>
<body>
Expand Down
2 changes: 1 addition & 1 deletion ignite-tc-helper-web/src/main/webapp/prs.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.2/css/all.css"
integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" crossorigin="anonymous">

<script src="js/common-1.7.js?v=20260510-1"></script>
<script src="js/common-1.7.js?v=20260529-1"></script>
<script src="js/testfails-2.4.js?v=20260528-1"></script>
<script src="js/prs-1.3.js?v=20260510-5"></script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,11 @@ public void aiPromptSkipButtonRequestsCurrentContext() throws IOException {
assertTrue(js.contains("buttonText: \"Use current context now\""));
assertTrue(js.contains("nextMode: false"));
assertTrue(js.contains("requestTextCommand(options, state, nextMode, skip.stepText)"));
assertTrue(js.contains("return aiPromptUrlWithWaitForTc(url, waitForTc, processId)"));
assertTrue(js.contains("return aiPromptUrlWithWaitForTc(url, waitForTc, processId, waitForTc)"));
assertTrue(js.contains("\"waitForTc=\" + waitForTc"));
assertTrue(js.contains("backgroundInitial: true"));
assertTrue(js.contains("\"&background=true\""));
assertTrue(js.contains("Building prompt from refreshed context."));
}

private static Path commonJs() {
Expand Down
Loading