From 22563aea6a38929e822d7607c0c27cdb0ea4dcb6 Mon Sep 17 00:00:00 2001 From: Tom Seidel Date: Sat, 13 Jun 2026 01:49:21 +0200 Subject: [PATCH 1/2] feat(versioning): Upgrade Spring Boot to 4.1.0 and migrate to Jackson 3.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump Spring Boot from 4.0.5 to 4.1.0 - Migrate Jackson imports from com.fasterxml.jackson to tools.jackson across all AI clients, MCP services, PR workflow, and model classes - Update Jackson API calls: asText() → asString(), isTextual() → isString() - Fix Thymeleaf templates for Spring Boot 4.1 compatibility: - Replace T() type expressions with .name() string comparisons - Extract inline ternary into th:with variable in ai-integrations form --- pom.xml | 2 +- .../giteabot/agent/critic/CriticAgent.java | 8 ++++---- .../giteabot/ai/google/GoogleAiClient.java | 7 +++---- .../giteabot/ai/google/GoogleAiRequest.java | 1 - .../giteabot/ai/google/GoogleAiResponse.java | 1 - .../giteabot/ai/llamacpp/LlamaCppResponse.java | 1 - .../giteabot/ai/ollama/OllamaRequest.java | 1 - .../giteabot/ai/ollama/OllamaResponse.java | 1 - .../giteabot/ai/openai/OpenAiRequest.java | 2 -- .../remus/giteabot/aiusage/AiUsageService.java | 17 ++++++++--------- .../giteabot/mcp/McpConfigurationParser.java | 13 ++++++------- .../giteabot/mcp/McpOrchestrationService.java | 6 ++---- .../giteabot/mcp/McpToolPromptRenderer.java | 3 +-- .../config/DeploymentTargetService.java | 2 +- .../deployment/CiActionTriggerStrategy.java | 10 +++++----- .../deployment/StaticPreviewUrlStrategy.java | 8 ++++---- .../deployment/WebhookTriggerStrategy.java | 10 +++++----- .../deployment/WorkflowCallbackController.java | 6 +++--- .../deployment/mcp/MCPDeploymentStrategy.java | 8 ++++---- .../deployment/mcp/McpDeploymentConfig.java | 8 ++++---- .../McpConfigurationService.java | 12 ++++++------ .../templates/ai-integrations/form.html | 3 ++- src/main/resources/templates/bots/form.html | 2 +- .../templates/git-integrations/form.html | 2 +- .../deployment/WebhookTriggerStrategyTest.java | 18 +++++++++--------- .../mcp/McpDeploymentConfigTest.java | 2 +- 26 files changed, 71 insertions(+), 83 deletions(-) diff --git a/pom.xml b/pom.xml index 4ebf9b94..cbff9528 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ org.springframework.boot spring-boot-starter-parent - 4.0.5 + 4.1.0 diff --git a/src/main/java/org/remus/giteabot/agent/critic/CriticAgent.java b/src/main/java/org/remus/giteabot/agent/critic/CriticAgent.java index ab8ca07e..d9de7a46 100644 --- a/src/main/java/org/remus/giteabot/agent/critic/CriticAgent.java +++ b/src/main/java/org/remus/giteabot/agent/critic/CriticAgent.java @@ -1,7 +1,7 @@ package org.remus.giteabot.agent.critic; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.remus.giteabot.agent.shared.AgentMetricsHolder; import org.remus.giteabot.ai.AiClient; @@ -120,9 +120,9 @@ ReflectionResult parseResponse(String response) { String json = extractJson(response); try { JsonNode node = objectMapper.readTree(json); - String outcome = node.path("outcome").asText("APPROVE") + String outcome = node.path("outcome").asString("APPROVE") .trim().toUpperCase(Locale.ROOT); - String feedback = node.path("feedback").asText("").trim(); + String feedback = node.path("feedback").asString("").trim(); return switch (outcome) { case "ITERATE" -> ReflectionResult.iterate(feedback); case "ABORT" -> ReflectionResult.abort(feedback); diff --git a/src/main/java/org/remus/giteabot/ai/google/GoogleAiClient.java b/src/main/java/org/remus/giteabot/ai/google/GoogleAiClient.java index 47a3c6c8..efc9e3ed 100644 --- a/src/main/java/org/remus/giteabot/ai/google/GoogleAiClient.java +++ b/src/main/java/org/remus/giteabot/ai/google/GoogleAiClient.java @@ -1,7 +1,7 @@ package org.remus.giteabot.ai.google; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.remus.giteabot.agent.shared.AgentJackson; import org.remus.giteabot.ai.AbstractAiClient; @@ -423,10 +423,9 @@ private String extractGoogleErrorMessage(String responseBody) { } try { JsonNode message = OBJECT_MAPPER.readTree(responseBody).path("error").path("message"); - return message.isTextual() ? message.asText() : null; + return message.isString() ? message.asString() : null; } catch (Exception ignored) { return null; } } } - diff --git a/src/main/java/org/remus/giteabot/ai/google/GoogleAiRequest.java b/src/main/java/org/remus/giteabot/ai/google/GoogleAiRequest.java index cc461414..064990da 100644 --- a/src/main/java/org/remus/giteabot/ai/google/GoogleAiRequest.java +++ b/src/main/java/org/remus/giteabot/ai/google/GoogleAiRequest.java @@ -107,4 +107,3 @@ public static class FunctionDeclaration { private Object parameters; } } - diff --git a/src/main/java/org/remus/giteabot/ai/google/GoogleAiResponse.java b/src/main/java/org/remus/giteabot/ai/google/GoogleAiResponse.java index 2b3cb52c..1669b899 100644 --- a/src/main/java/org/remus/giteabot/ai/google/GoogleAiResponse.java +++ b/src/main/java/org/remus/giteabot/ai/google/GoogleAiResponse.java @@ -73,4 +73,3 @@ public static class UsageMetadata { private int totalTokenCount; } } - diff --git a/src/main/java/org/remus/giteabot/ai/llamacpp/LlamaCppResponse.java b/src/main/java/org/remus/giteabot/ai/llamacpp/LlamaCppResponse.java index 9e2d27ef..8c748177 100644 --- a/src/main/java/org/remus/giteabot/ai/llamacpp/LlamaCppResponse.java +++ b/src/main/java/org/remus/giteabot/ai/llamacpp/LlamaCppResponse.java @@ -82,4 +82,3 @@ public static class Timings { private Double predictedMs; } } - diff --git a/src/main/java/org/remus/giteabot/ai/ollama/OllamaRequest.java b/src/main/java/org/remus/giteabot/ai/ollama/OllamaRequest.java index 45b7cd0d..0ea68797 100644 --- a/src/main/java/org/remus/giteabot/ai/ollama/OllamaRequest.java +++ b/src/main/java/org/remus/giteabot/ai/ollama/OllamaRequest.java @@ -93,4 +93,3 @@ public static class FunctionCall { private Map arguments; } } - diff --git a/src/main/java/org/remus/giteabot/ai/ollama/OllamaResponse.java b/src/main/java/org/remus/giteabot/ai/ollama/OllamaResponse.java index 26e022df..597ca1f7 100644 --- a/src/main/java/org/remus/giteabot/ai/ollama/OllamaResponse.java +++ b/src/main/java/org/remus/giteabot/ai/ollama/OllamaResponse.java @@ -57,4 +57,3 @@ public static class FunctionResponse { private Map arguments; } } - diff --git a/src/main/java/org/remus/giteabot/ai/openai/OpenAiRequest.java b/src/main/java/org/remus/giteabot/ai/openai/OpenAiRequest.java index b5492fcb..5b744f93 100644 --- a/src/main/java/org/remus/giteabot/ai/openai/OpenAiRequest.java +++ b/src/main/java/org/remus/giteabot/ai/openai/OpenAiRequest.java @@ -90,5 +90,3 @@ public static class FunctionCall { private String arguments; } } - - diff --git a/src/main/java/org/remus/giteabot/aiusage/AiUsageService.java b/src/main/java/org/remus/giteabot/aiusage/AiUsageService.java index 38305a68..8f7ed391 100644 --- a/src/main/java/org/remus/giteabot/aiusage/AiUsageService.java +++ b/src/main/java/org/remus/giteabot/aiusage/AiUsageService.java @@ -1,7 +1,5 @@ package org.remus.giteabot.aiusage; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; @@ -9,6 +7,7 @@ import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import tools.jackson.core.JsonGenerator; import java.io.IOException; import java.io.OutputStream; @@ -119,11 +118,11 @@ public Page findErrors(Instant from, Instant to, int page, * the correct pattern for streaming responses that outlive a single * transaction.

*/ - public void exportErrors(Instant from, Instant to, OutputStream outputStream) throws IOException { + public void exportErrors(Instant from, Instant to, OutputStream outputStream) { Instant f = effectiveFrom(from); Instant t = effectiveTo(to); - try (JsonGenerator gen = new ObjectMapper().getFactory().createGenerator(outputStream)) { + try (JsonGenerator gen = tools.jackson.core.ObjectWriteContext.empty().createGenerator(outputStream)) { gen.writeStartArray(); int page = 0; Page result; @@ -132,11 +131,11 @@ public void exportErrors(Instant from, Instant to, OutputStream outputStream) th f, t, PageRequest.of(page, EXPORT_PAGE_SIZE)); for (AiErrorLog entry : result.getContent()) { gen.writeStartObject(); - gen.writeObjectField("timestamp", entry.getTimestamp()); - gen.writeStringField("aiIntegration", entry.getAiIntegrationName()); - gen.writeStringField("sessionId", entry.getSessionId()); - gen.writeStringField("errorMessage", entry.getErrorMessage()); - gen.writeStringField("stackTrace", entry.getStackTrace()); + gen.writePOJOProperty("timestamp", entry.getTimestamp()); + gen.writeStringProperty("aiIntegration", entry.getAiIntegrationName()); + gen.writeStringProperty("sessionId", entry.getSessionId()); + gen.writeStringProperty("errorMessage", entry.getErrorMessage()); + gen.writeStringProperty("stackTrace", entry.getStackTrace()); gen.writeEndObject(); } gen.flush(); diff --git a/src/main/java/org/remus/giteabot/mcp/McpConfigurationParser.java b/src/main/java/org/remus/giteabot/mcp/McpConfigurationParser.java index b04fa887..19d9b23e 100644 --- a/src/main/java/org/remus/giteabot/mcp/McpConfigurationParser.java +++ b/src/main/java/org/remus/giteabot/mcp/McpConfigurationParser.java @@ -1,7 +1,7 @@ package org.remus.giteabot.mcp; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; @@ -62,8 +62,8 @@ private McpServerDefinition toServerDefinition(String fallbackName, JsonNode nod JsonNode headersNode = node.get("headers"); if (headersNode != null && headersNode.isObject()) { headersNode.properties().forEach(field -> { - if (field.getValue().isTextual()) { - headers.put(field.getKey(), field.getValue().asText()); + if (field.getValue().isString()) { + headers.put(field.getKey(), field.getValue().asString()); } }); } @@ -72,10 +72,9 @@ private McpServerDefinition toServerDefinition(String fallbackName, JsonNode nod private String text(JsonNode node, String field, String defaultValue) { JsonNode value = node.get(field); - if (value != null && value.isTextual() && !value.asText().isBlank()) { - return value.asText().strip(); + if (value != null && value.isString() && !value.asString().isBlank()) { + return value.asString().strip(); } return defaultValue; } } - diff --git a/src/main/java/org/remus/giteabot/mcp/McpOrchestrationService.java b/src/main/java/org/remus/giteabot/mcp/McpOrchestrationService.java index 75a8a11c..bcea261c 100644 --- a/src/main/java/org/remus/giteabot/mcp/McpOrchestrationService.java +++ b/src/main/java/org/remus/giteabot/mcp/McpOrchestrationService.java @@ -1,7 +1,7 @@ package org.remus.giteabot.mcp; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.ObjectMapper; import io.modelcontextprotocol.client.McpClient; import io.modelcontextprotocol.client.McpSyncClient; import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport; @@ -474,5 +474,3 @@ String describe() { } } } - - diff --git a/src/main/java/org/remus/giteabot/mcp/McpToolPromptRenderer.java b/src/main/java/org/remus/giteabot/mcp/McpToolPromptRenderer.java index e0694ded..9c63759e 100644 --- a/src/main/java/org/remus/giteabot/mcp/McpToolPromptRenderer.java +++ b/src/main/java/org/remus/giteabot/mcp/McpToolPromptRenderer.java @@ -1,6 +1,6 @@ package org.remus.giteabot.mcp; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import java.util.Map; @@ -50,4 +50,3 @@ private String schemaToString(Map schema) { } } } - diff --git a/src/main/java/org/remus/giteabot/prworkflow/config/DeploymentTargetService.java b/src/main/java/org/remus/giteabot/prworkflow/config/DeploymentTargetService.java index 7e0e4177..68838f23 100644 --- a/src/main/java/org/remus/giteabot/prworkflow/config/DeploymentTargetService.java +++ b/src/main/java/org/remus/giteabot/prworkflow/config/DeploymentTargetService.java @@ -1,6 +1,6 @@ package org.remus.giteabot.prworkflow.config; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectMapper; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/org/remus/giteabot/prworkflow/deployment/CiActionTriggerStrategy.java b/src/main/java/org/remus/giteabot/prworkflow/deployment/CiActionTriggerStrategy.java index 2498e65c..eb982d9b 100644 --- a/src/main/java/org/remus/giteabot/prworkflow/deployment/CiActionTriggerStrategy.java +++ b/src/main/java/org/remus/giteabot/prworkflow/deployment/CiActionTriggerStrategy.java @@ -1,8 +1,8 @@ package org.remus.giteabot.prworkflow.deployment; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.node.ObjectNode; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.remus.giteabot.admin.Bot; @@ -242,7 +242,7 @@ private Map renderInputs(JsonNode inputsNode, DeploymentRequest Map out = new LinkedHashMap<>(); inputsNode.properties().forEach(entry -> { String value = entry.getValue() == null || entry.getValue().isNull() - ? "" : entry.getValue().asText(); + ? "" : entry.getValue().asString(); out.put(entry.getKey(), applyPlaceholders(value, request)); }); return out; @@ -270,7 +270,7 @@ static int clampPollInterval(int seconds) { private static String textOrNull(JsonNode node, String field) { if (node == null) return null; JsonNode v = node.get(field); - return (v == null || v.isNull()) ? null : v.asText(); + return (v == null || v.isNull()) ? null : v.asString(); } private static String textOr(JsonNode node, String field, String fallback) { diff --git a/src/main/java/org/remus/giteabot/prworkflow/deployment/StaticPreviewUrlStrategy.java b/src/main/java/org/remus/giteabot/prworkflow/deployment/StaticPreviewUrlStrategy.java index b14d5a5f..3766b615 100644 --- a/src/main/java/org/remus/giteabot/prworkflow/deployment/StaticPreviewUrlStrategy.java +++ b/src/main/java/org/remus/giteabot/prworkflow/deployment/StaticPreviewUrlStrategy.java @@ -1,7 +1,7 @@ package org.remus.giteabot.prworkflow.deployment; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -109,7 +109,7 @@ public DeploymentResult trigger(DeploymentRequest request) { JsonNode extra = config.get(CONFIG_EXTRA_HEADERS); if (extra != null && extra.isObject()) { extra.properties().iterator().forEachRemaining(entry -> - builder.header(entry.getKey(), entry.getValue().asText())); + builder.header(entry.getKey(), entry.getValue().asString())); } HttpResponse response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.discarding()); @@ -140,7 +140,7 @@ public DeploymentResult trigger(DeploymentRequest request) { private static String textOr(JsonNode node, String field, String fallback) { JsonNode v = node == null ? null : node.get(field); - return (v == null || v.isNull()) ? fallback : v.asText(); + return (v == null || v.isNull()) ? fallback : v.asString(); } private static int intOr(JsonNode node, String field, int fallback) { diff --git a/src/main/java/org/remus/giteabot/prworkflow/deployment/WebhookTriggerStrategy.java b/src/main/java/org/remus/giteabot/prworkflow/deployment/WebhookTriggerStrategy.java index 4fdb7147..02a90cd1 100644 --- a/src/main/java/org/remus/giteabot/prworkflow/deployment/WebhookTriggerStrategy.java +++ b/src/main/java/org/remus/giteabot/prworkflow/deployment/WebhookTriggerStrategy.java @@ -1,8 +1,8 @@ package org.remus.giteabot.prworkflow.deployment; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -130,7 +130,7 @@ public DeploymentResult trigger(DeploymentRequest request) { JsonNode headers = config.get(CONFIG_HEADERS); if (headers != null && headers.isObject()) { headers.properties().iterator().forEachRemaining(entry -> - builder.header(entry.getKey(), entry.getValue().asText())); + builder.header(entry.getKey(), entry.getValue().asString())); } try { @@ -168,7 +168,7 @@ private static String textOrNull(JsonNode node, String field) { return null; } JsonNode v = node.get(field); - return (v == null || v.isNull()) ? null : v.asText(); + return (v == null || v.isNull()) ? null : v.asString(); } private static String truncate(String body) { diff --git a/src/main/java/org/remus/giteabot/prworkflow/deployment/WorkflowCallbackController.java b/src/main/java/org/remus/giteabot/prworkflow/deployment/WorkflowCallbackController.java index 662991d3..6662253c 100644 --- a/src/main/java/org/remus/giteabot/prworkflow/deployment/WorkflowCallbackController.java +++ b/src/main/java/org/remus/giteabot/prworkflow/deployment/WorkflowCallbackController.java @@ -1,7 +1,7 @@ package org.remus.giteabot.prworkflow.deployment; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.remus.giteabot.prworkflow.PrWorkflowRun; @@ -161,7 +161,7 @@ private static ResponseEntity unauthorized() { private static String textOrNull(JsonNode node, String field) { JsonNode v = node == null ? null : node.get(field); - return (v == null || v.isNull()) ? null : v.asText(); + return (v == null || v.isNull()) ? null : v.asString(); } private static boolean constantTimeEquals(String expected, String provided) { diff --git a/src/main/java/org/remus/giteabot/prworkflow/deployment/mcp/MCPDeploymentStrategy.java b/src/main/java/org/remus/giteabot/prworkflow/deployment/mcp/MCPDeploymentStrategy.java index 89524ccc..0c85df37 100644 --- a/src/main/java/org/remus/giteabot/prworkflow/deployment/mcp/MCPDeploymentStrategy.java +++ b/src/main/java/org/remus/giteabot/prworkflow/deployment/mcp/MCPDeploymentStrategy.java @@ -1,8 +1,8 @@ package org.remus.giteabot.prworkflow.deployment.mcp; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.core.type.TypeReference; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.remus.giteabot.agent.validation.ToolResult; @@ -363,7 +363,7 @@ static HandleSummary parseHandle(String handleJson) { private static String textOrNull(JsonNode node, String field) { JsonNode v = node.get(field); - return (v == null || v.isNull()) ? null : v.asText(); + return (v == null || v.isNull()) ? null : v.asString(); } } diff --git a/src/main/java/org/remus/giteabot/prworkflow/deployment/mcp/McpDeploymentConfig.java b/src/main/java/org/remus/giteabot/prworkflow/deployment/mcp/McpDeploymentConfig.java index c6c2d991..dcc5319c 100644 --- a/src/main/java/org/remus/giteabot/prworkflow/deployment/mcp/McpDeploymentConfig.java +++ b/src/main/java/org/remus/giteabot/prworkflow/deployment/mcp/McpDeploymentConfig.java @@ -1,8 +1,8 @@ package org.remus.giteabot.prworkflow.deployment.mcp; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.databind.node.ObjectNode; import java.util.Collections; import java.util.LinkedHashMap; @@ -138,6 +138,6 @@ public String handleJson(ObjectMapper objectMapper, Map remoteHa private static String textOrNull(JsonNode node, String field) { JsonNode v = node.get(field); - return (v == null || v.isNull()) ? null : v.asText(); + return (v == null || v.isNull()) ? null : v.asString(); } } diff --git a/src/main/java/org/remus/giteabot/systemsettings/McpConfigurationService.java b/src/main/java/org/remus/giteabot/systemsettings/McpConfigurationService.java index 283fa6ad..8859fb56 100644 --- a/src/main/java/org/remus/giteabot/systemsettings/McpConfigurationService.java +++ b/src/main/java/org/remus/giteabot/systemsettings/McpConfigurationService.java @@ -2,8 +2,8 @@ import lombok.RequiredArgsConstructor; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import org.remus.giteabot.admin.Bot; import org.remus.giteabot.admin.BotRepository; import org.springframework.stereotype.Service; @@ -83,8 +83,8 @@ private boolean containsStdioTransport(JsonNode node) { if (node.isObject()) { for (var field : node.properties()) { if (("transport".equalsIgnoreCase(field.getKey()) || "type".equalsIgnoreCase(field.getKey())) - && field.getValue().isTextual() - && "stdio".equalsIgnoreCase(field.getValue().asText())) { + && field.getValue().isString() + && "stdio".equalsIgnoreCase(field.getValue().asString())) { return true; } if ("command".equalsIgnoreCase(field.getKey())) { @@ -108,8 +108,8 @@ private boolean containsRemoteEndpoint(JsonNode node) { if (node == null) { return false; } - if (node.isTextual()) { - String value = node.asText().toLowerCase(); + if (node.isString()) { + String value = node.asString().toLowerCase(); return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("ws://") diff --git a/src/main/resources/templates/ai-integrations/form.html b/src/main/resources/templates/ai-integrations/form.html index 1be13b9a..4ba078dc 100644 --- a/src/main/resources/templates/ai-integrations/form.html +++ b/src/main/resources/templates/ai-integrations/form.html @@ -2,7 +2,8 @@ -
+
diff --git a/src/main/resources/templates/bots/form.html b/src/main/resources/templates/bots/form.html index 97141896..11f744ad 100644 --- a/src/main/resources/templates/bots/form.html +++ b/src/main/resources/templates/bots/form.html @@ -43,7 +43,7 @@

diff --git a/src/main/resources/templates/git-integrations/form.html b/src/main/resources/templates/git-integrations/form.html index 6ebec334..7718c45c 100644 --- a/src/main/resources/templates/git-integrations/form.html +++ b/src/main/resources/templates/git-integrations/form.html @@ -81,7 +81,7 @@

diff --git a/src/test/java/org/remus/giteabot/prworkflow/deployment/WebhookTriggerStrategyTest.java b/src/test/java/org/remus/giteabot/prworkflow/deployment/WebhookTriggerStrategyTest.java index c304a69e..6061eef9 100644 --- a/src/test/java/org/remus/giteabot/prworkflow/deployment/WebhookTriggerStrategyTest.java +++ b/src/test/java/org/remus/giteabot/prworkflow/deployment/WebhookTriggerStrategyTest.java @@ -1,7 +1,7 @@ package org.remus.giteabot.prworkflow.deployment; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.remus.giteabot.prworkflow.PrWorkflowRun; @@ -127,7 +127,7 @@ void hmacSignatureIsStableAndCorrect() throws Exception { } @Test - void payloadShapeMatchesContract() throws Exception { + void payloadShapeMatchesContract() { RecordingClient client = new RecordingClient(); WebhookTriggerStrategy strategy = new WebhookTriggerStrategy(client); DeploymentResult result = strategy.trigger(request( @@ -136,12 +136,12 @@ void payloadShapeMatchesContract() throws Exception { JsonNode body = MAPPER.readTree(client.lastBody); assertThat(body.get("runId").asLong()).isEqualTo(42L); assertThat(body.get("prNumber").asLong()).isEqualTo(1234L); - assertThat(body.get("sha").asText()).isEqualTo("abc123"); - assertThat(body.get("branch").asText()).isEqualTo("feature/x"); - assertThat(body.get("repoOwner").asText()).isEqualTo("acme"); - assertThat(body.get("repoName").asText()).isEqualTo("web"); - assertThat(body.get("callbackUrl").asText()).contains("/api/workflow-callback/42/"); - assertThat(body.get("callbackSecret").asText()).isEqualTo("cbsecret"); + assertThat(body.get("sha").asString()).isEqualTo("abc123"); + assertThat(body.get("branch").asString()).isEqualTo("feature/x"); + assertThat(body.get("repoOwner").asString()).isEqualTo("acme"); + assertThat(body.get("repoName").asString()).isEqualTo("web"); + assertThat(body.get("callbackUrl").asString()).contains("/api/workflow-callback/42/"); + assertThat(body.get("callbackSecret").asString()).isEqualTo("cbsecret"); } /** Minimal HttpClient that records the request body sent — avoids deep Mockito stubbing. */ diff --git a/src/test/java/org/remus/giteabot/prworkflow/deployment/mcp/McpDeploymentConfigTest.java b/src/test/java/org/remus/giteabot/prworkflow/deployment/mcp/McpDeploymentConfigTest.java index b6faff22..db7ece96 100644 --- a/src/test/java/org/remus/giteabot/prworkflow/deployment/mcp/McpDeploymentConfigTest.java +++ b/src/test/java/org/remus/giteabot/prworkflow/deployment/mcp/McpDeploymentConfigTest.java @@ -1,6 +1,6 @@ package org.remus.giteabot.prworkflow.deployment.mcp; -import com.fasterxml.jackson.databind.ObjectMapper; +import tools.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; import java.util.Map; From 8d43da9642588e5a7dd5e175baf9caf3615d059d Mon Sep 17 00:00:00 2001 From: Tom Seidel Date: Sat, 13 Jun 2026 01:49:32 +0200 Subject: [PATCH 2/2] feat(versioning): Upgrade Spring Boot to 4.1.0 and migrate to Jackson 3.x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Bump Spring Boot from 4.0.5 to 4.1.0 - Migrate Jackson imports from com.fasterxml.jackson to tools.jackson across all AI clients, MCP services, PR workflow, and model classes - Update Jackson API calls: asText() → asString(), isTextual() → isString() - Fix Thymeleaf templates for Spring Boot 4.1 compatibility: - Replace T() type expressions with .name() string comparisons - Extract inline ternary into th:with variable in ai-integrations form --- .../agent/issueimpl/AiResponseParser.java | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/src/main/java/org/remus/giteabot/agent/issueimpl/AiResponseParser.java b/src/main/java/org/remus/giteabot/agent/issueimpl/AiResponseParser.java index b32acecd..4bde7584 100644 --- a/src/main/java/org/remus/giteabot/agent/issueimpl/AiResponseParser.java +++ b/src/main/java/org/remus/giteabot/agent/issueimpl/AiResponseParser.java @@ -137,63 +137,6 @@ public ImplementationPlan parseAiResponse(String aiResponse) { } } - /** - * Parses the AI's response for requested files, validating them against the repository tree. - * - * @param aiResponse The raw AI response - * @param tree The repository file tree - * @return List of valid requested file paths - */ - public List parseRequestedFiles(String aiResponse, List> tree) { - List requestedFiles = new ArrayList<>(); - - // Build set of valid paths - Set validPaths = new HashSet<>(); - for (Map entry : tree) { - String path = (String) entry.getOrDefault("path", ""); - String type = (String) entry.getOrDefault("type", "blob"); - if ("blob".equals(type)) { - validPaths.add(path); - } - } - - // Try to extract JSON from response - String jsonStr = extractJsonFromResponse(aiResponse); - if (jsonStr != null) { - jsonStr = truncateToFirstJsonObject(jsonStr); - try { - FileRequestResponse response = objectMapper.readValue(jsonStr, FileRequestResponse.class); - if (response != null && response.getRequestedFiles() != null) { - for (String file : response.getRequestedFiles()) { - if (validPaths.contains(file)) { - requestedFiles.add(file); - } else { - log.debug("Requested file not found in tree: {}", file); - } - } - } - } catch (JacksonException e) { - log.warn("Failed to parse file request response: {}", e.getMessage()); - } - } - - // If parsing failed, fall back to pattern matching - if (requestedFiles.isEmpty()) { - for (String path : validPaths) { - if (aiResponse.contains(path)) { - requestedFiles.add(path); - } - } - } - - // Limit to 30 files - if (requestedFiles.size() > 30) { - requestedFiles = requestedFiles.subList(0, 30); - } - - return requestedFiles; - } - /** * Extracts the non-JSON (thinking/reasoning) text from an AI response. *