# Upgrade a Quarkus App to Java 25

Yes. An agent moves your Quarkus build, Dockerfiles and CI to Java 25, runs the tests on JDK 25, fixes what breaks and opens a pull request.

[Run this on Rush](https://cloud.getrush.ai/run/upgrade-java-version/quarkus/), in a Project that has your repository.

## What the agent does

The agent clones the repository into its own cloud sandbox and lists every place a Java version is written, because a Quarkus project spreads it across the build, the container images and CI. In a project shaped like the `getting-started` module of quarkusio/quarkus-quickstarts, `pom.xml` sets `<maven.compiler.source>17</maven.compiler.source>` and `<maven.compiler.target>17</maven.compiler.target>` next to `quarkus.platform.version` `3.40.1`, `compiler-plugin.version` `3.15.0` and `surefire-plugin.version` `3.5.4`. Across the whole quickstarts repository, 102 modules use the `source` and `target` pair and 11 use `maven.compiler.release`, so the agent changes each one in the form it already has rather than mixing the two.

It then reads the container images, which are easy to miss because the build never touches them. `src/main/docker/Dockerfile.jvm` starts `FROM registry.access.redhat.com/ubi9/openjdk-21-runtime:1.24`, and `Dockerfile.legacy-jar` sits beside it, while `Dockerfile.native` and `Dockerfile.native-micro` ship a native binary with no JVM at all. A class file compiled for Java 25 will not load on a Java 21 runtime, so the JVM images move to the matching Java 25 runtime image and the native ones are left alone, with a line in the pull request saying why. A Kotlin module needs one more change: `hibernate-orm-panache-kotlin-quickstart/pom.xml` sets `<jvmTarget>11</jvmTarget>` for `kotlin.version` `2.3.0`, and the agent decides whether that target should follow the Java version.

CI comes next. `.github/workflows/build-pr-development.yml` installs the JDK with `actions/setup-java@v4`, `distribution: temurin` and a `java: [ 21 ]` matrix, and keys the Maven cache on the JDK as `q2maven-pr-jdk-${{ matrix.java }}`. The agent changes the matrix to 25, so the cache key follows by itself. The Maven wrapper, `.mvn/wrapper/maven-wrapper.properties`, points at Maven `3.9.11`; the agent checks that it runs on JDK 25 before it builds anything.

Then it installs JDK 25 in the sandbox and runs the build the workflow runs: `./mvnw -e -B clean install`, which compiles, runs the `@QuarkusTest` classes such as `GreetingResourceTest` through Surefire, and the `GreetingResourceIT` integration tests through Failsafe. When Quarkus Dev Services needs a container for a database or a broker and none can start in the sandbox, the agent says which modules it could not test there. For each compile error, removed API or failing test it fixes the cause, reruns, and only then opens the pull request.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Upgrade this Quarkus project to Java 25. Find every place the Java version is set: maven.compiler.source, target or release in each pom.xml (keep the form each module already uses), the Kotlin jvmTarget if any, the runtime image in each src/main/docker/Dockerfile.jvm and Dockerfile.legacy-jar, and the java-version matrix in the GitHub Actions workflows. Leave native Dockerfiles alone. Check that the Maven wrapper and the Quarkus, compiler and Surefire plugins run on JDK 25 and update only what needs it. Install JDK 25 in the sandbox, run ./mvnw -B clean install, fix what breaks, and say which tests could not run there and why. Open a pull request listing each file changed, each fix, and anything left for a separate change.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is a pull request on a new branch. In a Quarkus service it changes the compiler properties in `pom.xml` to 25, the `FROM` line of `Dockerfile.jvm` and `Dockerfile.legacy-jar` to a Java 25 runtime image, and the workflow matrix to `[ 25 ]`, plus any source or test changes the new JDK forced. The description lists each file with the reason, each failure the agent hit with its fix, the modules whose tests could not run in the sandbox, and follow-ups it kept out, such as a Quarkus platform upgrade.

The checks are the repository's own: the pull request workflow builds every module on JDK 25 on GitHub's runners, where Dev Services can start its containers. The session's transcript keeps each command the agent ran, from `java -version` to every `./mvnw` run, with its output.

Before merging, a reviewer checks that the new runtime image is one the team is allowed to deploy, that the Kotlin `jvmTarget` choice is deliberate, and that nothing in the description says a test was skipped without a reason. If the service is built as a native image, they confirm the native build still passes, since the JVM version change does not cover it.

## Run it on every new JDK release

Java ships a feature release every six months and Quarkus follows with support for it. A scheduled job can send this request with the new version in the prompt once the platform supports it, one request per service, each with its repository in `resources`. Each run has its own sandbox and branch, so a team with many Quarkus services sees which ones move cleanly.

Set a webhook URL on the API key and Rush posts `run.completed` or `run.failed` when each run's turn finishes, with `session_url` to read the result. A key runs at most `max_concurrent` sessions at once, 4 by default, and answers `429` past that, so the job should queue its requests.

## Why run it on Rush?

A JDK upgrade is mostly about the machine it runs on: a fresh JDK, the Maven wrapper downloading its distribution, the whole dependency tree resolved again and every test run on the new version. In a Rush sandbox the agent installs JDK 25 on its own disk, so the build cannot quietly run on whatever `JAVA_HOME` a laptop has, and nobody's local toolchain changes.

The result carries its own proof. The transcript keeps every Maven run and its output, and the pull request's workflow builds again on JDK 25 with Dev Services available. Because each run is separate, a team can send one request per service at the same time and review the pull requests as they arrive.

## Frequently asked questions

### Does it upgrade the Quarkus platform version too?

Not unless you ask. The prompt changes the Java version and only the plugins that need it to run on JDK 25. A Quarkus upgrade has its own migration guide and risks, so it belongs in its own pull request.

### What happens to my native image Dockerfiles?

They stay as they are. `Dockerfile.native` and `Dockerfile.native-micro` copy a native executable and need no JVM, so the Java runtime version does not apply to them. The pull request says so, and notes that the native build itself should be checked on CI.

### Can it run tests that need Dev Services?

Dev Services starts databases and brokers in containers. When those cannot start in the sandbox, the agent lists the modules it could not test and the pull request's workflow runs them on GitHub's runners.

### Should I use maven.compiler.release instead of source and target?

`release` also checks that the code uses only APIs from that Java version, which `source` and `target` do not. The agent keeps the form each module already uses so the diff stays small, and suggests the switch as a follow-up if you want it.
