This is an automated email from the ASF dual-hosted git repository.

fmariani pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 8feb64214ca Update Camel application with openrewrite via camel jbang
8feb64214ca is described below

commit 8feb64214ca1591a541eee65b94171e3ebbb8ae8
Author: Croway <[email protected]>
AuthorDate: Tue Jan 21 14:55:29 2025 +0100

    Update Camel application with openrewrite via camel jbang
    
    Handle quarkus
    
    Handle extra repos
    
    Handle downloads concurrently
    
    Refactor camel update
    
    Define extra recipes and artifacts
    
    Use cache + List test
    
    Add documentation
---
 .../org/apache/camel/catalog/components/kafka.json |   4 +-
 .../org/apache/camel/component/kafka/kafka.json    |   4 +-
 .../camel/component/kafka/KafkaConfiguration.java  |   3 +-
 .../modules/ROOT/pages/camel-jbang.adoc            |  83 +++++-
 .../dsl/KafkaComponentBuilderFactory.java          |   1 -
 .../endpoint/dsl/KafkaEndpointBuilderFactory.java  |   2 -
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   6 +
 .../core/commands/update/CamelQuarkusUpdate.java   |  95 +++++++
 .../jbang/core/commands/update/CamelUpdate.java    | 169 ++++++++++++
 .../core/commands/update/CamelUpdateException.java |  32 +++
 .../core/commands/update/CamelUpdateMixin.java     |  91 +++++++
 .../dsl/jbang/core/commands/update/Update.java     |  55 ++++
 .../jbang/core/commands/update/UpdateCommand.java  |  37 +++
 .../dsl/jbang/core/commands/update/UpdateList.java | 299 +++++++++++++++++++++
 .../dsl/jbang/core/commands/update/UpdateRun.java  | 136 ++++++++++
 .../jbang/core/commands/update/UpdateListTest.java |  39 +++
 16 files changed, 1046 insertions(+), 10 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json
index e01c39e68ee..81a556f89f7 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/kafka.json
@@ -37,7 +37,7 @@
     "autoCommitEnable": { "index": 10, "kind": "property", "displayName": 
"Auto Commit Enable", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", 
"configurationField": "configuration", "description": "If true, periodically 
commit to ZooKeeper the offset of messages already fetched  [...]
     "autoCommitIntervalMs": { "index": 11, "kind": "property", "displayName": 
"Auto Commit Interval Ms", "group": "consumer", "label": "consumer", 
"required": false, "type": "integer", "javaType": "java.lang.Integer", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 
"5000", "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "The frequency in ms that the consumer offsets 
are committed  [...]
     "autoOffsetReset": { "index": 12, "kind": "property", "displayName": "Auto 
Offset Reset", "group": "consumer", "label": "consumer", "required": false, 
"type": "string", "javaType": "java.lang.String", "enum": [ "latest", 
"earliest", "none" ], "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": "latest", "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "What to do when there is no i [...]
-    "batching": { "index": 13, "kind": "property", "displayName": "Batching", 
"group": "consumer", "label": "consumer", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "Whether to use batching for processing or 
streaming. The default is false, which uses streaming. I [...]
+    "batching": { "index": 13, "kind": "property", "displayName": "Batching", 
"group": "consumer", "label": "consumer", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "Whether to use batching for processing or 
streaming. The default is false, which uses streaming. I [...]
     "batchingIntervalMs": { "index": 14, "kind": "property", "displayName": 
"Batching Interval Ms", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "In consumer batching mode, then this option is 
specifying a time in millis, to trigger ba [...]
     "breakOnFirstError": { "index": 15, "kind": "property", "displayName": 
"Break On First Error", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", 
"configurationField": "configuration", "description": "This options controls 
what happens when a consumer is processing an exchange  [...]
     "bridgeErrorHandler": { "index": 16, "kind": "property", "displayName": 
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Allows for bridging the consumer to the Camel routing Error Handler, which 
mean any exceptions (if possible) occurred while the Camel consumer is trying 
to pickup incoming messages, or the lik [...]
@@ -171,7 +171,7 @@
     "autoCommitEnable": { "index": 10, "kind": "parameter", "displayName": 
"Auto Commit Enable", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", 
"configurationField": "configuration", "description": "If true, periodically 
commit to ZooKeeper the offset of messages already fetched [...]
     "autoCommitIntervalMs": { "index": 11, "kind": "parameter", "displayName": 
"Auto Commit Interval Ms", "group": "consumer", "label": "consumer", 
"required": false, "type": "integer", "javaType": "java.lang.Integer", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 
"5000", "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "The frequency in ms that the consumer offsets 
are committed [...]
     "autoOffsetReset": { "index": 12, "kind": "parameter", "displayName": 
"Auto Offset Reset", "group": "consumer", "label": "consumer", "required": 
false, "type": "string", "javaType": "java.lang.String", "enum": [ "latest", 
"earliest", "none" ], "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": "latest", "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "What to do when there is no  [...]
-    "batching": { "index": 13, "kind": "parameter", "displayName": "Batching", 
"group": "consumer", "label": "consumer", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "Whether to use batching for processing or 
streaming. The default is false, which uses streaming.  [...]
+    "batching": { "index": 13, "kind": "parameter", "displayName": "Batching", 
"group": "consumer", "label": "consumer", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "Whether to use batching for processing or 
streaming. The default is false, which uses streaming.  [...]
     "batchingIntervalMs": { "index": 14, "kind": "parameter", "displayName": 
"Batching Interval Ms", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "In consumer batching mode, then this option is 
specifying a time in millis, to trigger b [...]
     "breakOnFirstError": { "index": 15, "kind": "parameter", "displayName": 
"Break On First Error", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", 
"configurationField": "configuration", "description": "This options controls 
what happens when a consumer is processing an exchange [...]
     "checkCrcs": { "index": 16, "kind": "parameter", "displayName": "Check 
Crcs", "group": "consumer", "label": "consumer", "required": false, "type": 
"boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "true", "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "Automatically check the CRC32 of the records 
consumed. This ensures no on-the-wire  [...]
diff --git 
a/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json
 
b/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json
index e01c39e68ee..81a556f89f7 100644
--- 
a/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json
+++ 
b/components/camel-kafka/src/generated/resources/META-INF/org/apache/camel/component/kafka/kafka.json
@@ -37,7 +37,7 @@
     "autoCommitEnable": { "index": 10, "kind": "property", "displayName": 
"Auto Commit Enable", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", 
"configurationField": "configuration", "description": "If true, periodically 
commit to ZooKeeper the offset of messages already fetched  [...]
     "autoCommitIntervalMs": { "index": 11, "kind": "property", "displayName": 
"Auto Commit Interval Ms", "group": "consumer", "label": "consumer", 
"required": false, "type": "integer", "javaType": "java.lang.Integer", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 
"5000", "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "The frequency in ms that the consumer offsets 
are committed  [...]
     "autoOffsetReset": { "index": 12, "kind": "property", "displayName": "Auto 
Offset Reset", "group": "consumer", "label": "consumer", "required": false, 
"type": "string", "javaType": "java.lang.String", "enum": [ "latest", 
"earliest", "none" ], "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": "latest", "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "What to do when there is no i [...]
-    "batching": { "index": 13, "kind": "property", "displayName": "Batching", 
"group": "consumer", "label": "consumer", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "Whether to use batching for processing or 
streaming. The default is false, which uses streaming. I [...]
+    "batching": { "index": 13, "kind": "property", "displayName": "Batching", 
"group": "consumer", "label": "consumer", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "Whether to use batching for processing or 
streaming. The default is false, which uses streaming. I [...]
     "batchingIntervalMs": { "index": 14, "kind": "property", "displayName": 
"Batching Interval Ms", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "In consumer batching mode, then this option is 
specifying a time in millis, to trigger ba [...]
     "breakOnFirstError": { "index": 15, "kind": "property", "displayName": 
"Break On First Error", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", 
"configurationField": "configuration", "description": "This options controls 
what happens when a consumer is processing an exchange  [...]
     "bridgeErrorHandler": { "index": 16, "kind": "property", "displayName": 
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Allows for bridging the consumer to the Camel routing Error Handler, which 
mean any exceptions (if possible) occurred while the Camel consumer is trying 
to pickup incoming messages, or the lik [...]
@@ -171,7 +171,7 @@
     "autoCommitEnable": { "index": 10, "kind": "parameter", "displayName": 
"Auto Commit Enable", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, 
"configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", 
"configurationField": "configuration", "description": "If true, periodically 
commit to ZooKeeper the offset of messages already fetched [...]
     "autoCommitIntervalMs": { "index": 11, "kind": "parameter", "displayName": 
"Auto Commit Interval Ms", "group": "consumer", "label": "consumer", 
"required": false, "type": "integer", "javaType": "java.lang.Integer", 
"deprecated": false, "autowired": false, "secret": false, "defaultValue": 
"5000", "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "The frequency in ms that the consumer offsets 
are committed [...]
     "autoOffsetReset": { "index": 12, "kind": "parameter", "displayName": 
"Auto Offset Reset", "group": "consumer", "label": "consumer", "required": 
false, "type": "string", "javaType": "java.lang.String", "enum": [ "latest", 
"earliest", "none" ], "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": "latest", "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "What to do when there is no  [...]
-    "batching": { "index": 13, "kind": "parameter", "displayName": "Batching", 
"group": "consumer", "label": "consumer", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "Whether to use batching for processing or 
streaming. The default is false, which uses streaming.  [...]
+    "batching": { "index": 13, "kind": "parameter", "displayName": "Batching", 
"group": "consumer", "label": "consumer", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "Whether to use batching for processing or 
streaming. The default is false, which uses streaming.  [...]
     "batchingIntervalMs": { "index": 14, "kind": "parameter", "displayName": 
"Batching Interval Ms", "group": "consumer", "label": "consumer", "required": 
false, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "In consumer batching mode, then this option is 
specifying a time in millis, to trigger b [...]
     "breakOnFirstError": { "index": 15, "kind": "parameter", "displayName": 
"Break On First Error", "group": "consumer", "label": "consumer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, 
"configurationClass": "org.apache.camel.component.kafka.KafkaConfiguration", 
"configurationField": "configuration", "description": "This options controls 
what happens when a consumer is processing an exchange [...]
     "checkCrcs": { "index": 16, "kind": "parameter", "displayName": "Check 
Crcs", "group": "consumer", "label": "consumer", "required": false, "type": 
"boolean", "javaType": "java.lang.Boolean", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "true", "configurationClass": 
"org.apache.camel.component.kafka.KafkaConfiguration", "configurationField": 
"configuration", "description": "Automatically check the CRC32 of the records 
consumed. This ensures no on-the-wire  [...]
diff --git 
a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConfiguration.java
 
b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConfiguration.java
index 32ee8aef9d0..0ee358a5689 100755
--- 
a/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConfiguration.java
+++ 
b/components/camel-kafka/src/main/java/org/apache/camel/component/kafka/KafkaConfiguration.java
@@ -2003,8 +2003,7 @@ public class KafkaConfiguration implements Cloneable, 
HeaderFilterStrategyAware
      * In streaming mode, then a single kafka record is processed per Camel 
exchange in the message body.
      *
      * In batching mode, then Camel groups many kafka records together as a 
List<Exchange> objects in the message body.
-     * The option maxPollRecords is used to define the number of records to 
group together in batching mode. See also
-     * the batchingIntervalMs option.
+     * The option maxPollRecords is used to define the number of records to 
group together in batching mode.
      */
     public void setBatching(boolean batching) {
         this.batching = batching;
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
index 1c81b8c0371..98020c933cc 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang.adoc
@@ -4110,4 +4110,85 @@ $ camel infra log service
 [service] ...
 [service] ...
 [service] ...
----
\ No newline at end of file
+---
+
+== Update
+
+Apache Camel applications can be automatically updated using Camel JBang. The 
update command provides two main operations:
+
+- `list`: Shows available Apache Camel versions for updating
+- `run`: Executes the actual update process 
+
+The update process leverages the 
https://github.com/apache/camel-upgrade-recipes[Apache Camel Open Rewrite 
recipes] and supports three application types:
+
+- Plain Camel (camel-main)
+- Camel Quarkus
+- Camel Spring Boot
+
+While Camel and Camel Spring Boot updates primarily use camel-upgrade-recipes, 
+Camel Quarkus updates involve both the Quarkus runtime (via 
https://github.com/openrewrite/rewrite-quarkus[Rewrite Quarkus]) and Apache 
Camel recipes.
+
+=== Listing Available Updates
+
+To see which versions are available for updating, use:
+
+[source,bash]
+----
+$ camel update list
+
+ VERSION                       RUNTIME            RUNTIME VERSION  DESCRIPTION 
                                                                   
+ 4.4.4                         Camel Quarkus      3.8.x            Migrates 
`camel 4.0` quarkus application to `camel 4.4`.                       
+ 4.8.0                         Camel                               Migrates 
Apache Camel 4 application to Apache Camel 4.8.0                      
+ 4.8.3                         Camel Quarkus      3.15.x           Migrates 
`camel 4.4` quarkus application to `camel 4.8`.                       
+ 4.9.0                         Camel                               Migrates 
Apache Camel 4 application to Apache Camel 4.9.0                      
+ 4.9.0                         Camel Spring Boot  3.4.0            Migrates 
Apache Camel Spring Boot 4 application to Apache Camel Spring Boot 4.9.0
+----
+
+=== Running Updates
+
+To perform an update to a specific version:
+
+[source,bash]
+----
+$ camel update run $VERSION
+----
+
+NOTE: The update commands must be executed in the project directory containing 
the pom.xml file.
+
+==== Configuration Options
+
+The update process can be customized with several options:
+`--runtime`: Specifies the application type:
+
+- `camel-main` - Plain Camel applications
+- `spring-boot` - Camel Spring Boot applications
+- `quarkus` - Camel Quarkus applications
+
+`--repos`: Additional Maven repositories to use during the update
+`--dry-run`: Preview the changes without applying them
+`--extraActiveRecipes`: Comma-separated list of additional recipe names to 
apply
+`--extraRecipeArtifactCoordinates`: Comma-separated list of Maven coordinates 
for extra recipes (format: groupId:artifactId:version)
+Use `--help` to see all available options.
+
+==== Examples
+
+Update a Camel Quarkus application
+
+[source,bash]
+----
+$ camel update run 4.8.3 --runtime=quarkus --dryRun
+----
+
+Update a plain Camel application
+
+[source,bash]
+----
+$ camel update run 4.9.0 --runtime=camel-main --repos=https://myMaven/repo 
--extraActiveRecipes=my.first.Recipe,my.second.Recipe 
--extraRecipeArtifactCoordinates=ex.my.org:recipes:1.0.0
+----
+
+Update a Spring Boot application with and extra Spring Boot 3.3 upgrade recipe
+
+[source,bash]
+----
+$ camel update run 4.9.0 --runtime=spring-boot 
--extraActiveRecipes=org.openrewrite.java.spring.boot3.UpgradeSpringBoot_3_3 
--extraRecipeArtifactCoordinates=org.openrewrite.recipe:rewrite-spring:6.0.2
+----
\ No newline at end of file
diff --git 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KafkaComponentBuilderFactory.java
 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KafkaComponentBuilderFactory.java
index 44160b56f69..8c51614c7fd 100644
--- 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KafkaComponentBuilderFactory.java
+++ 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/KafkaComponentBuilderFactory.java
@@ -313,7 +313,6 @@ public interface KafkaComponentBuilderFactory {
          * batching mode, then Camel groups many kafka records together as a
          * List objects in the message body. The option maxPollRecords is used
          * to define the number of records to group together in batching mode.
-         * See also the batchingIntervalMs option.
          * 
          * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
          * 
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KafkaEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KafkaEndpointBuilderFactory.java
index 5b1dec8dcab..1f47c78e78f 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KafkaEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/KafkaEndpointBuilderFactory.java
@@ -444,7 +444,6 @@ public interface KafkaEndpointBuilderFactory {
          * batching mode, then Camel groups many kafka records together as a
          * List objects in the message body. The option maxPollRecords is used
          * to define the number of records to group together in batching mode.
-         * See also the batchingIntervalMs option.
          * 
          * The option is a: <code>boolean</code> type.
          * 
@@ -465,7 +464,6 @@ public interface KafkaEndpointBuilderFactory {
          * batching mode, then Camel groups many kafka records together as a
          * List objects in the message body. The option maxPollRecords is used
          * to define the number of records to group together in batching mode.
-         * See also the batchingIntervalMs option.
          * 
          * The option will be converted to a <code>boolean</code> type.
          * 
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index f127b1d052a..d168a0b66f7 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -49,6 +49,9 @@ import 
org.apache.camel.dsl.jbang.core.commands.plugin.PluginCommand;
 import org.apache.camel.dsl.jbang.core.commands.plugin.PluginDelete;
 import org.apache.camel.dsl.jbang.core.commands.plugin.PluginGet;
 import org.apache.camel.dsl.jbang.core.commands.process.*;
+import org.apache.camel.dsl.jbang.core.commands.update.UpdateCommand;
+import org.apache.camel.dsl.jbang.core.commands.update.UpdateList;
+import org.apache.camel.dsl.jbang.core.commands.update.UpdateRun;
 import org.apache.camel.dsl.jbang.core.commands.version.VersionCommand;
 import org.apache.camel.dsl.jbang.core.commands.version.VersionGet;
 import org.apache.camel.dsl.jbang.core.commands.version.VersionList;
@@ -175,6 +178,9 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("get", new CommandLine(new 
InfraGet(main)))
                         .addSubcommand("ps", new CommandLine(new 
InfraPs(main)))
                         .addSubcommand("log", new CommandLine(new 
InfraLog(main))))
+                .addSubcommand("update", new CommandLine(new 
UpdateCommand(main))
+                        .addSubcommand("list", new CommandLine(new 
UpdateList(main)))
+                        .addSubcommand("run", new CommandLine(new 
UpdateRun(main))))
                 .setParameterExceptionHandler(new 
MissingPluginParameterExceptionHandler());
 
         PluginHelper.addPlugins(commandLine, main, args);
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelQuarkusUpdate.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelQuarkusUpdate.java
new file mode 100644
index 00000000000..d587dfa3840
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelQuarkusUpdate.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.update;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.camel.main.download.MavenDependencyDownloader;
+
+public final class CamelQuarkusUpdate implements Update {
+
+    private List<String> commands = new ArrayList<>();
+    private CamelUpdateMixin updateMixin;
+    private MavenDependencyDownloader downloader;
+
+    private final String QUARKUS_UPDATE_ARTIFACTID = "camel-quarkus-catalog";
+
+    public CamelQuarkusUpdate(CamelUpdateMixin updateMixin, 
MavenDependencyDownloader downloader) {
+        this.updateMixin = updateMixin;
+        this.downloader = downloader;
+    }
+
+    /**
+     * Quarkus updates are in the form 3.8, 3.15, 3.17... Downloads Camel 
Quarkus catalog for the given Camel version
+     * and get the Quarkus stream version
+     *
+     * @return
+     */
+    public String getQuarkusStream() {
+        // Assume that the quarkus updates are in the form 3.8, 3.15, 3.16...
+        List<String[]> qVersions
+                = 
downloader.resolveAvailableVersions("org.apache.camel.quarkus", 
QUARKUS_UPDATE_ARTIFACTID,
+                        updateMixin.version,
+                        updateMixin.repos);
+        String streamVersion = null;
+        for (String[] qVersion : qVersions) {
+            if (qVersion[0].equals(updateMixin.version)) {
+                streamVersion = qVersion[1].substring(0, 
qVersion[1].lastIndexOf('.'));
+            }
+        }
+
+        return streamVersion;
+    }
+
+    @Override
+    public String debug() {
+        String result = "--no-transfer-progress";
+        if (updateMixin.debug) {
+            result = "-X";
+        }
+
+        return result;
+    }
+
+    @Override
+    public String runMode() {
+        String result = "-DrewriteFullRun";
+        if (updateMixin.dryRun) {
+            result = "-DrewriteDryRun";
+        }
+
+        return result;
+    }
+
+    @Override
+    public List<String> command() throws CamelUpdateException {
+        commands.add(mvnProgramCall());
+        commands.add(debug());
+        commands.add(String.format("%s:quarkus-maven-plugin:%s:update", 
updateMixin.quarkusMavenPluginGroupId,
+                updateMixin.quarkusMavenPluginVersion));
+        commands.add("-Dstream=" + getQuarkusStream());
+        commands.add(runMode());
+
+        return commands;
+    }
+
+    @Override
+    public String getArtifactCoordinates() {
+        return QUARKUS_UPDATE_ARTIFACTID;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelUpdate.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelUpdate.java
new file mode 100644
index 00000000000..2469a55e2fd
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelUpdate.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.update;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.camel.dsl.jbang.core.common.RuntimeType;
+import org.apache.camel.main.download.MavenDependencyDownloader;
+import org.apache.camel.tooling.maven.MavenArtifact;
+
+public final class CamelUpdate implements Update {
+
+    private List<String> commands = new ArrayList<>();
+    private CamelUpdateMixin updateMixin;
+    private MavenDependencyDownloader downloader;
+
+    public CamelUpdate(CamelUpdateMixin updateMixin, MavenDependencyDownloader 
downloader) {
+        this.updateMixin = updateMixin;
+        this.downloader = downloader;
+    }
+
+    /**
+     * Download the jar containing the recipes and extract the recipe name to 
be used in the maven command
+     *
+     * @return
+     */
+    public List<String> activeRecipes() throws CamelUpdateException {
+        List<Recipe> recipes;
+        MavenArtifact mavenArtifact
+                = downloader.downloadArtifact("org.apache.camel.upgrade", 
getArtifactCoordinates(), updateMixin.version);
+
+        try {
+            recipes = getRecipesInJar(mavenArtifact.getFile());
+        } catch (IOException ex) {
+            throw new CamelUpdateException(ex);
+        }
+
+        List<String> activeRecipes = new ArrayList<>();
+        for (Recipe recipe : recipes) {
+            // The recipe named latest.yaml contains all the recipe for the 
update up to the selected version
+            if (recipe.name().contains("latest")) {
+                activeRecipes.clear();
+                recipe.recipeName().ifPresent(name -> activeRecipes.add(name));
+                break;
+            }
+
+            recipe.recipeName().ifPresent(name -> activeRecipes.add(name));
+        }
+
+        return activeRecipes;
+    }
+
+    private List<Recipe> getRecipesInJar(File jar) throws IOException {
+        List<Recipe> recipes = new ArrayList<>();
+        Path jarPath = jar.toPath();
+
+        try (FileSystem fileSystem = FileSystems.newFileSystem(jarPath, 
(ClassLoader) null)) {
+            Path recipePath = fileSystem.getPath("META-INF", "rewrite");
+            if (Files.exists(recipePath)) {
+                try (Stream<Path> walk = Files.walk(recipePath)) {
+                    walk.filter(p -> p.toString().endsWith(".yaml"))
+                            .forEach(p -> {
+                                try {
+                                    recipes.add(new Recipe(
+                                            p.getFileName().toString(),
+                                            Files.readString(p)));
+                                } catch (IOException e) {
+                                    throw new UncheckedIOException(e);
+                                }
+                            });
+                }
+            }
+        }
+        return recipes;
+    }
+
+    @Override
+    public String debug() {
+        String result = "--no-transfer-progress";
+        if (updateMixin.debug) {
+            result = "-X";
+        }
+
+        return result;
+    }
+
+    @Override
+    public String runMode() {
+        String task = "run";
+        if (updateMixin.dryRun) {
+            task = "dryRun";
+        }
+
+        return task;
+    }
+
+    @Override
+    public List<String> command() throws CamelUpdateException {
+        commands.add(mvnProgramCall());
+        commands.add(debug());
+        commands.add("org.openrewrite.maven:rewrite-maven-plugin:" + 
updateMixin.openRewriteVersion + ":"
+                     + runMode());
+
+        List<String> coordinates = new ArrayList<>();
+        coordinates.add(String.format("org.apache.camel.upgrade:%s:%s", 
getArtifactCoordinates(), updateMixin.version));
+        if (updateMixin.extraRecipeArtifactCoordinates != null && 
!updateMixin.extraRecipeArtifactCoordinates.isEmpty()) {
+            coordinates.addAll(updateMixin.extraRecipeArtifactCoordinates);
+        }
+
+        commands.add("-Drewrite.recipeArtifactCoordinates=" +
+                     coordinates.stream().collect(Collectors.joining(",")));
+
+        List<String> recipes = new ArrayList<>();
+        recipes.addAll(activeRecipes());
+        if (updateMixin.extraActiveRecipes != null && 
!updateMixin.extraActiveRecipes.isEmpty()) {
+            recipes.addAll(updateMixin.extraActiveRecipes);
+        }
+        commands.add("-Drewrite.activeRecipes=" + recipes
+                .stream().collect(Collectors.joining(",")));
+
+        return commands;
+    }
+
+    public String getArtifactCoordinates() {
+        return updateMixin.runtime == RuntimeType.springBoot
+                ? updateMixin.camelSpringBootArtifactCoordinates : 
updateMixin.camelArtifactCoordinates;
+    }
+
+    record Recipe(String name, String content) {
+
+        /**
+         * Retrieves the name of the recipe if it is a Camel upgrade recipe.
+         *
+         * @return an Optional containing the recipe name if it is a Camel 
upgrade recipe, otherwise empty
+         */
+        public Optional<String> recipeName() {
+            return Arrays.stream(content().split(System.lineSeparator()))
+                    .filter(l -> l.startsWith("name") && 
l.contains("org.apache.camel.upgrade"))
+                    .map(l -> 
l.substring(l.indexOf("org.apache.camel.upgrade")))
+                    .findFirst();
+        }
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelUpdateException.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelUpdateException.java
new file mode 100644
index 00000000000..2c7e5c1097e
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelUpdateException.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.update;
+
+public class CamelUpdateException extends Exception {
+
+    public CamelUpdateException(String message) {
+        super(message);
+    }
+
+    public CamelUpdateException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public CamelUpdateException(Throwable cause) {
+        super(cause);
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelUpdateMixin.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelUpdateMixin.java
new file mode 100644
index 00000000000..500447167d3
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/CamelUpdateMixin.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.update;
+
+import java.util.List;
+
+import org.apache.camel.dsl.jbang.core.common.RuntimeCompletionCandidates;
+import org.apache.camel.dsl.jbang.core.common.RuntimeType;
+import org.apache.camel.dsl.jbang.core.common.RuntimeTypeConverter;
+import picocli.CommandLine;
+
+public class CamelUpdateMixin {
+
+    @CommandLine.Parameters(description = "The version to which the Camel 
project should be updated.", arity = "1")
+    String version;
+
+    @CommandLine.Option(names = { "--openRewriteVersion" },
+                        description = "The version of OpenRewrite to use 
during the update process.",
+                        defaultValue = "6.0.4")
+    String openRewriteVersion;
+
+    @CommandLine.Option(names = { "--camelArtifact" },
+                        description = "The Maven artifact coordinates for the 
Camel upgrade recipes.",
+                        defaultValue = "camel-upgrade-recipes")
+    String camelArtifactCoordinates;
+
+    @CommandLine.Option(names = { "--camelSpringBootArtifact" },
+                        description = "The Maven artifact coordinates for the 
Camel Spring Boot upgrade recipes.",
+                        defaultValue = "camel-spring-boot-upgrade-recipes")
+    String camelSpringBootArtifactCoordinates;
+
+    @CommandLine.Option(names = { "--debug" },
+                        defaultValue = "false",
+                        description = "Enables debug logging if set to true.")
+    boolean debug;
+
+    @CommandLine.Option(names = { "--quarkusMavenPluginVersion" },
+                        description = "The version of the Quarkus Maven plugin 
to use.",
+                        defaultValue = RuntimeType.QUARKUS_VERSION)
+    String quarkusMavenPluginVersion;
+
+    @CommandLine.Option(names = { "--quarkusMavenPluginGroupId" },
+                        description = "The group ID of the Quarkus Maven 
plugin.",
+                        defaultValue = "io.quarkus")
+    String quarkusMavenPluginGroupId;
+
+    @CommandLine.Option(names = { "--dryRun" },
+                        description = "If set to true, performs a dry run of 
the update process without making any changes.",
+                        defaultValue = "false")
+    boolean dryRun;
+
+    @CommandLine.Option(names = { "--runtime" },
+                        completionCandidates = 
RuntimeCompletionCandidates.class,
+                        defaultValue = "camel-main",
+                        converter = RuntimeTypeConverter.class,
+                        description = "Runtime (${COMPLETION-CANDIDATES})")
+    RuntimeType runtime = RuntimeType.main;
+
+    @CommandLine.Option(names = { "--repos" },
+                        description = "Additional maven repositories for 
download on-demand (Use commas to separate multiple repositories)")
+    String repos;
+
+    @CommandLine.Option(names = { "--extraActiveRecipes" },
+                        description = "Comma separated list of recipes to be 
executed after the Camel one, " +
+                                      "make sure the artifact containing the 
recipes is added via extraRecipeArtifactCoordinates")
+    List<String> extraActiveRecipes;
+
+    @CommandLine.Option(names = { "--extraRecipeArtifactCoordinates" },
+                        description = "Comma separated list of artifact 
coordinates containing extraActiveRecipes, " +
+                                      "ex.my.org:recipes:1.0.0")
+    List<String> extraRecipeArtifactCoordinates;
+
+    @CommandLine.Option(names = { "--upgradeTimeout" },
+                        description = "Time to wait, in seconds, before 
shutting down the upgrade process",
+                        defaultValue = "240")
+    int upgradeTimeout;
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/Update.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/Update.java
new file mode 100644
index 00000000000..9bd5714d230
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/Update.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.update;
+
+import java.util.List;
+
+import org.apache.camel.util.FileUtil;
+
+/**
+ * Interface defining the contract for generating the command to update Camel 
artifacts. This interface provides common
+ * methods to Camel, Camel Spring Boot and Camel Quarkus applications.
+ *
+ * @see CamelUpdateException
+ */
+public sealed interface Update permits CamelUpdate, CamelQuarkusUpdate {
+
+    String debug();
+
+    String runMode();
+
+    /**
+     * Returns the command to execute that updates Apache Camel.
+     *
+     * @return                      a list of strings representing the command 
to execute.
+     * @throws CamelUpdateException if an error occurs while generating the 
command.
+     */
+    List<String> command() throws CamelUpdateException;
+
+    String getArtifactCoordinates();
+
+    default String mvnProgramCall() {
+        String mvnProgramCall;
+        if (FileUtil.isWindows()) {
+            mvnProgramCall = "cmd /c mvn";
+        } else {
+            mvnProgramCall = "mvn";
+        }
+
+        return mvnProgramCall;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateCommand.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateCommand.java
new file mode 100644
index 00000000000..c3a8752a018
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateCommand.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.update;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import picocli.CommandLine;
+
[email protected](name = "update",
+                     description = "Update Camel project")
+public class UpdateCommand extends CamelCommand {
+
+    public UpdateCommand(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer doCall() throws Exception {
+        // defaults to list
+        new CommandLine(new UpdateList(getMain())).execute();
+        return 0;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateList.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateList.java
new file mode 100644
index 00000000000..01433e93bd0
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateList.java
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.update;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Collectors;
+
+import com.github.freva.asciitable.AsciiTable;
+import com.github.freva.asciitable.Column;
+import com.github.freva.asciitable.HorizontalAlign;
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.main.download.MavenDependencyDownloader;
+import org.apache.camel.tooling.maven.MavenArtifact;
+import org.apache.camel.util.json.Jsoner;
+import picocli.CommandLine;
+
+/**
+ * A command-line tool for listing available update versions for Apache Camel 
and its runtime variants.
+ *
+ * <p>
+ * The command supports listing versions in both human-readable ASCII table 
format and JSON format. It downloads version
+ * information from Maven repositories and presents available upgrade paths 
for different Camel runtime variants.
+ * </p>
+ *
+ * <p>
+ * Command usage: list [--repos=<repos>] [--json]
+ * </p>
+ *
+ * <h3>Features:</h3>
+ * <ul>
+ * <li>Lists available update versions for Plain Camel, Camel Spring Boot, and 
Camel Quarkus</li>
+ * <li>Supports additional Maven repositories for dependency resolution</li>
+ * <li>Provides output in both ASCII table and JSON formats</li>
+ * <li>Includes runtime version information and upgrade descriptions</li>
+ * </ul>
+ *
+ * <h3>Command Options:</h3>
+ * <ul>
+ * <li>--repos: Specifies additional Maven repositories for downloading 
dependencies (comma-separated)</li>
+ * <li>--json: Outputs the version information in JSON format</li>
+ * </ul>
+ *
+ * <h3>Version Support:</h3>
+ * <ul>
+ * <li>Plain Camel: Supports versions from 4.8.0 onwards</li>
+ * <li>Spring Boot: Supports versions from 4.8.0 onwards</li>
+ * <li>Quarkus: Supports versions from 4.4.0 onwards with recipes from 
1.0.22</li>
+ * </ul>
+ *
+ * @see org.apache.camel.dsl.jbang.core.commands.CamelCommand
+ * @see org.apache.camel.dsl.jbang.core.commands.CamelJBangMain
+ */
[email protected](name = "list",
+                     description = "List available update versions for Apache 
Camel and its runtime variants")
+public class UpdateList extends CamelCommand {
+
+    @CommandLine.Option(names = { "--repos" },
+                        description = "Additional maven repositories for 
download on-demand (Use commas to separate multiple repositories)")
+    String repos;
+
+    @CommandLine.Option(names = { "--json" },
+                        description = "Output in JSON Format")
+    boolean jsonOutput;
+
+    @CommandLine.Option(names = { "--use-cache" },
+                        description = "Use Maven cache")
+    boolean useCache;
+
+    private static final String CAMEL_UPGRADE_GROUPID = 
"org.apache.camel.upgrade";
+    private static final String CAMEL_UPGRADE_ARTIFACTID = 
"camel-upgrade-recipes";
+    private static final String CAMEL_SB_UPGRADE_ARTIFACTID = 
"camel-spring-boot-upgrade-recipes";
+    private static final String FIRST_RECIPE_VERSION = "4.8.0";
+    private static final String QUARKUS_FIRST_RECIPE_VERSION = "1.0.22";
+    private static final String QUARKUS_FIRST_UPDATABLE_VERSION = "4.4.0";
+
+    public UpdateList(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer doCall() throws Exception {
+        List<Row> rows = new ArrayList<>();
+        try (MavenDependencyDownloader downloader = new 
MavenDependencyDownloader();) {
+            downloader.setRepositories(repos);
+            downloader.setFresh(!useCache);
+            downloader.start();
+
+            RecipeVersions recipesVersions = 
collectRecipesVersions(downloader);
+
+            // Convert recipes versions into Rows for table and json 
visualization
+            recipesVersions.plainCamelRecipesVersion()
+                    .forEach(l -> rows
+                            .add(new Row(l[0], "Camel", "", "Migrates Apache 
Camel 4 application to Apache Camel " + l[0])));
+            recipesVersions.camelSpringBootRecipesVersion().forEach(l -> {
+                String[] runtimeVersion
+                        = recipesVersions.sbVersions().stream().filter(v -> 
v[0].equals(l[0])).findFirst().orElseThrow();
+
+                rows.add(new Row(
+                        l[0], "Camel Spring Boot", runtimeVersion[1],
+                        "Migrates Apache Camel Spring Boot 4 application to 
Apache Camel Spring Boot " + l[0]));
+            });
+            // Translate quarkus versions to Camel
+            recipesVersions.camelQuarkusRecipesVersions();
+            recipesVersions.quarkusUpdateRecipes().forEach(l -> {
+                List<String[]> runtimeVersions = 
recipesVersions.qVersions().stream().filter(v -> v[1].startsWith(l.version()))
+                        .collect(Collectors.toList());
+                runtimeVersions.sort(Comparator.comparing(o -> o[1]));
+                String[] runtimeVersion = 
runtimeVersions.get(runtimeVersions.size() - 1);
+                // Quarkus may release patches independently, therefore, we do 
not know the real micro version
+                String quarkusVersion = runtimeVersion[1];
+                quarkusVersion = quarkusVersion.substring(0, 
quarkusVersion.lastIndexOf('.')) + ".x";
+
+                rows.add(new Row(runtimeVersion[0], "Camel Quarkus", 
quarkusVersion, l.description()));
+            });
+        }
+
+        rows.sort(Comparator.comparing(Row::version));
+
+        if (jsonOutput) {
+            printer().println(
+                    Jsoner.serialize(
+                            rows.stream().map(row -> Map.of(
+                                    "version", row.version(),
+                                    "runtime", row.runtime(),
+                                    "runtimeVersion", row.runtimeVersion(),
+                                    "description", row.description()))
+                                    .collect(Collectors.toList())));
+        } else {
+            printer().println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, 
Arrays.asList(
+                    new 
Column().header("VERSION").minWidth(30).dataAlign(HorizontalAlign.LEFT)
+                            .with(r -> r.version()),
+                    new Column().header("RUNTIME")
+                            .dataAlign(HorizontalAlign.LEFT).with(r -> 
r.runtime()),
+                    new Column().header("RUNTIME VERSION")
+                            .dataAlign(HorizontalAlign.LEFT).with(r -> 
r.runtimeVersion()),
+                    new Column().header("DESCRIPTION")
+                            .dataAlign(HorizontalAlign.LEFT).with(r -> 
r.description()))));
+        }
+
+        return 0;
+    }
+
+    /**
+     * Download camel, camel-spring-boot and quarkus upgrade-recipes 
dependencies and collect existing versions
+     *
+     * @param  downloader
+     * @return
+     * @throws ExecutionException
+     * @throws InterruptedException
+     */
+    private RecipeVersions collectRecipesVersions(MavenDependencyDownloader 
downloader)
+            throws ExecutionException, InterruptedException {
+        CompletableFuture<List<String[]>> plainCamelRecipesVersionFuture
+                = CompletableFuture.supplyAsync(() -> 
downloader.resolveAvailableVersions(
+                        CAMEL_UPGRADE_GROUPID,
+                        CAMEL_UPGRADE_ARTIFACTID,
+                        FIRST_RECIPE_VERSION,
+                        repos));
+
+        final List<String[]> sbVersions = new ArrayList<>();
+        CompletableFuture<List<String[]>> camelSpringBootRecipesVersionFuture 
= CompletableFuture.supplyAsync(() -> {
+            List<String[]> camelSpringBootRecipesVersion = 
downloader.resolveAvailableVersions(
+                    CAMEL_UPGRADE_GROUPID,
+                    CAMEL_SB_UPGRADE_ARTIFACTID,
+                    FIRST_RECIPE_VERSION,
+                    repos);
+            if (!camelSpringBootRecipesVersion.isEmpty()) {
+                // 4.8.0 is the first version with update recipes
+                sbVersions.addAll(
+                        downloader.resolveAvailableVersions(
+                                "org.apache.camel.springboot",
+                                "camel-spring-boot",
+                                FIRST_RECIPE_VERSION,
+                                repos));
+            }
+
+            return camelSpringBootRecipesVersion;
+        });
+
+        final Set<QuarkusUpdates> quarkusUpdateRecipes = new LinkedHashSet<>();
+        CompletableFuture<List<String[]>> camelQuarkusRecipesVersionsFuture = 
CompletableFuture
+                .supplyAsync(() -> {
+                    List<String[]> camelQuarkusRecipesVersions = 
downloader.resolveAvailableVersions(
+                            "io.quarkus",
+                            "quarkus-update-recipes",
+                            QUARKUS_FIRST_RECIPE_VERSION,
+                            repos);
+
+                    for (String[] camelQuarkusRecipeVersion : 
camelQuarkusRecipesVersions) {
+                        String version = camelQuarkusRecipeVersion[0];
+                        MavenArtifact artifact = 
downloader.downloadArtifact("io.quarkus",
+                                "quarkus-update-recipes",
+                                version);
+
+                        try {
+                            
quarkusUpdateRecipes.addAll(getCamelQuarkusRecipesInJar(artifact.getFile()));
+                        } catch (IOException e) {
+                            throw new RuntimeException(e);
+                        }
+                    }
+
+                    return camelQuarkusRecipesVersions;
+                });
+
+        CompletableFuture<List<String[]>> qVersionsFuture
+                = CompletableFuture.supplyAsync(() -> 
downloader.resolveAvailableVersions(
+                        "org.apache.camel.quarkus",
+                        "camel-quarkus-catalog",
+                        QUARKUS_FIRST_UPDATABLE_VERSION,
+                        repos));
+
+        return new RecipeVersions(
+                plainCamelRecipesVersionFuture.get(),
+                sbVersions,
+                camelSpringBootRecipesVersionFuture.get(),
+                quarkusUpdateRecipes,
+                camelQuarkusRecipesVersionsFuture.get(),
+                qVersionsFuture.get());
+    }
+
+    record RecipeVersions(List<String[]> plainCamelRecipesVersion,
+            List<String[]> sbVersions,
+            List<String[]> camelSpringBootRecipesVersion,
+            Set<QuarkusUpdates> quarkusUpdateRecipes,
+            List<String[]> camelQuarkusRecipesVersions,
+            List<String[]> qVersions) {
+    }
+
+    record Row(String version, String runtime, String runtimeVersion, String 
description) {
+    }
+
+    record QuarkusUpdates(String version, String description) {
+    }
+
+    /**
+     * Extracts Camel Quarkus recipe information from a JAR file.
+     *
+     * @param  jar         The JAR file containing Quarkus update recipes
+     * @return             Collection of QuarkusUpdates containing version and 
description information
+     * @throws IOException if an error occurs while reading the JAR file
+     */
+    private Collection<QuarkusUpdates> getCamelQuarkusRecipesInJar(File jar) 
throws IOException {
+        List<QuarkusUpdates> quarkusUpdateRecipes = new ArrayList<>();
+        try (JarFile jarFile = new JarFile(jar)) {
+            Enumeration<JarEntry> e = jarFile.entries();
+            while (e.hasMoreElements()) {
+                JarEntry jarEntry = e.nextElement();
+                String name = jarEntry.getName();
+                if 
(name.contains("quarkus-updates/org.apache.camel.quarkus/camel-quarkus/")
+                        && name.endsWith(".yaml")
+                        /* Quarkus specific, maybe in the future can be 
removed */
+                        && !name.contains("alpha")) {
+
+                    String content = new 
String(jarFile.getInputStream(jarEntry).readAllBytes());
+                    String description = 
Arrays.stream(content.split(System.lineSeparator()))
+                            .filter(l -> l.startsWith("description"))
+                            .map(l -> l.substring(l.indexOf(":") + 1).trim())
+                            .findFirst().orElse("");
+
+                    quarkusUpdateRecipes.add(new QuarkusUpdates(
+                            name.substring(name.lastIndexOf("/") + 1, 
name.indexOf(".yaml")),
+                            description));
+                }
+            }
+
+            return quarkusUpdateRecipes;
+        }
+    }
+
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateRun.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateRun.java
new file mode 100644
index 00000000000..a31d302f825
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateRun.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.update;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.RuntimeType;
+import org.apache.camel.main.download.DownloadException;
+import org.apache.camel.main.download.MavenDependencyDownloader;
+import picocli.CommandLine;
+
+/**
+ * Command to update a Camel project to a specified version. This command 
supports updating projects for different
+ * runtimes such as Camel Main, Spring Boot, and Quarkus. It uses Maven and 
OpenRewrite to apply the necessary updates.
+ */
[email protected](name = "run",
+                     description = "Update Camel project")
+public class UpdateRun extends CamelCommand {
+
+    @CommandLine.Mixin
+    private CamelUpdateMixin updateMixin;
+
+    public UpdateRun(CamelJBangMain main) {
+        super(main);
+    }
+
+    /**
+     * Executes the update command for a Camel project. This method determines 
the appropriate Maven command based on
+     * the runtime type (Camel Main, Spring Boot, or Quarkus) and executes it 
to update the project to the specified
+     * version.
+     *
+     * @return           the exit code of the update process (0 for success, 
-1 for failure)
+     * @throws Exception if an error occurs during the update process
+     */
+    @Override
+    public Integer doCall() throws Exception {
+        // Check if the current directory contains a Maven project (i.e., a 
pom.xml file)
+        if (Files.list(Path.of("."))
+                .noneMatch(f -> f.getFileName().toString().equals("pom.xml"))) 
{
+            printer().println("No Maven Project found in the current 
directory, " +
+                              "please execute camel upgrade run command in the 
directory containing the Maven project to update");
+
+            return -1;
+        }
+
+        List<String> command = new ArrayList<>();
+        try (MavenDependencyDownloader downloader = new 
MavenDependencyDownloader();) {
+            downloader.setRepositories(updateMixin.repos);
+            downloader.start();
+
+            Update update = null;
+            try {
+                if (updateMixin.runtime == RuntimeType.quarkus) {
+                    update = new CamelQuarkusUpdate(updateMixin, downloader);
+
+                    command = update.command();
+                } else {
+                    update = new CamelUpdate(updateMixin, downloader);
+
+                    command = update.command();
+                }
+            } catch (CamelUpdateException ex) {
+                printer().println(ex.getMessage());
+
+                return -1;
+            } catch (DownloadException e) {
+                printer().println(String.format("Cannot find Camel Upgrade 
Recipes %s:%s:%s",
+                        "org.apache.camel.upgrade", 
update.getArtifactCoordinates(), updateMixin.version));
+
+                return -1;
+            }
+        }
+
+        executeCommand(command);
+
+        return 0;
+    }
+
+    /**
+     * Executes a shell command and prints its output.
+     *
+     * @param  command              the command to execute
+     * @return                      the exit code of the command execution
+     * @throws IOException          if an I/O error occurs
+     * @throws InterruptedException if the command execution is interrupted
+     */
+    private int executeCommand(List<String> command) throws IOException, 
InterruptedException {
+        ProcessBuilder pb = new ProcessBuilder(command);
+        Process p = pb.redirectErrorStream(true)
+                .start();
+
+        try (BufferedReader stdInput = new BufferedReader(new 
InputStreamReader(p.getInputStream()));
+             BufferedReader stdError = new BufferedReader(new 
InputStreamReader(p.getErrorStream()))) {
+
+            String line;
+            while ((line = stdInput.readLine()) != null || (line = 
stdError.readLine()) != null) {
+                printer().println(line);
+            }
+
+            if (!p.waitFor(updateMixin.upgradeTimeout, TimeUnit.SECONDS)) {
+                p.destroyForcibly();
+                printer().println("Update execution timed out");
+
+                return -1;
+            }
+
+            int exitCode = p.exitValue();
+
+            return exitCode;
+
+        }
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateListTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateListTest.java
new file mode 100644
index 00000000000..f5d6569ce3a
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/update/UpdateListTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.update;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelCommandBaseTest;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class UpdateListTest extends CamelCommandBaseTest {
+
+    @Test
+    public void listUpdateVersions() throws Exception {
+        UpdateList infraList = new UpdateList(new 
CamelJBangMain().withPrinter(printer));
+
+        infraList.doCall();
+
+        List<String> lines = printer.getLines();
+        Assertions.assertThat(lines.stream().collect(Collectors.joining("\n")))
+                .contains("Migrates Apache Camel 4 application to Apache Camel 
4.9.0");
+    }
+}

Reply via email to