Quarkus with bleep
This tutorial walks through a working Quarkus 3.x service that
compiles, runs @QuarkusTest suites, runs in dev mode, and packages to
the production fast-jar layout — all through bleep. If you are here to
convert an existing Maven build, skip to
importing a Maven Quarkus build:
bleep import-maven recognizes Quarkus projects and emits all of the
wiring below on its own.
Like Spring Boot, the whole
integration lands in bleep's four primitives — projects, dependencies,
scripts, sourcegen. There is no Quarkus code inside bleep itself; the
machinery lives in bleep-plugin-quarkus, a regular library your
scripts project depends on.
How it works
Quarkus bootstraps by building an "application model" of your app —
its classpath, which dependencies are extensions, where classes and
resources live. Its stock resolvers parse pom.xml or drive the Gradle
tooling API. Neither exists in a bleep workspace, but Quarkus has an
official escape hatch (its Gradle plugin uses it for IDE runs): a
pre-serialized model handed over via system property, checked before
any build-tool detection.
bleep-plugin-quarkus produces that file as sourcegen: before the
test project compiles, QuarkusTestModelGen writes the model to
<target dir>/quarkus/test-app-model.dat and declares the JVM option
that points Quarkus at it (plus the jboss LogManager) by writing them
to the project's fork-options file, which bleep appends when it starts
the test fork — so the template needs no platform block. The writer
forks a JVM whose
classpath carries the app's own Quarkus version, so the serialization
format always matches what the test JVM reads back — bleep does not pin
you to any particular Quarkus release. A content hash next to the model
means it regenerates on classpath or layout changes, not on every run.
The build file
$schema: https://raw.githubusercontent.com/oyvindberg/bleep/master/schema.json
$version: 1.0.0-M14
jvm:
name: graalvm-community:25.0.1
projects:
myapp:
dependencies:
- io.quarkus:quarkus-rest:3.39.1
extends: template-common
myapp-test:
dependencies:
- io.quarkus:quarkus-junit5:3.39.1
dependsOn: myapp
extends:
- template-common
- template-quarkus-test
scripts:
dependencies:
- build.bleep:bleep-plugin-quarkus:${BLEEP_VERSION}
java:
options: -proc:none --release 17
platform:
name: jvm
scripts:
run-myapp-dev:
main: scripts.RunMyappDev
project: scripts
package-myapp:
main: scripts.PackageMyapp
project: scripts
templates:
template-common:
java:
options: -proc:none --release 17 -parameters
platform:
name: jvm
template-quarkus-test:
isTestProject: true
sourcegen:
main: bleep.plugin.quarkus.QuarkusTestModelGen
project: scripts
Three projects (myapp, myapp-test, scripts), two named scripts,
and one template-quarkus-test template holding all Quarkus test
wiring. Worth pointing out:
myapp-testis just a project that depends onmyapp— there is noTestscope. The template it extends carries the Quarkus specifics, so twenty Quarkus apps in one build share these five lines.sourcegenreferencesbleep.plugin.quarkus.QuarkusTestModelGendirectly — the plugin ships aBleepCodegenScriptusable as-is.- No
platformblock. The sourcegen declares the two JVM options the fork needs — the serialized-model path and the jboss LogManager (which must be set before any JUL initialization; Quarkus requires the same under surefire) — by writing them to the project's fork-options file, which bleep appends when it starts the fork. Everything else, including registering the workspace's projects as application archives (the rolejandex-maven-pluginplays under Maven), travels inside the model itself. - Suites share one warm fork — by default. bleep's defaults,
testFork: per-projectandmaxConcurrentSuites: 1, already give Maven's one-JVM-per-module semantics: a project's suites run sequentially through one reused fork. Quarkus DevServices containers are labeled per test JVM, so suites that share a booted application — and a database schema an earlier suite created — need exactly this, and get it with no extra configuration. (bleep import-mavenwrites both fields onto the template explicitly, so the intent is visible in the generated build.) For projects whose suites are independent, settestFork: per-suiteand you get bleep's usual suite-per-fork parallelism.
The application code
Plain JAX-RS beans; nothing bleep-specific.
The resource — GreetingResource.java
package com.example.myapp;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
//start
@Path("/hello")
public class GreetingResource {
private final GreetingService service;
@Inject
public GreetingResource(GreetingService service) {
this.service = service;
}
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return service.greeting();
}
}
//stop
The service — GreetingService.java
package com.example.myapp;
import jakarta.enterprise.context.ApplicationScoped;
//start
@ApplicationScoped
public class GreetingService {
public String greeting() {
return "Hello from Quarkus";
}
}
//stop
The test — GreetingResourceTest.java
package com.example.myapp;
import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
//start
@QuarkusTest
class GreetingResourceTest {
@TestHTTPResource("/hello")
URL helloUrl;
@Inject GreetingService service;
@Test
void helloEndpointServesGreeting() throws Exception {
HttpResponse<String> response =
HttpClient.newHttpClient()
.send(
HttpRequest.newBuilder(helloUrl.toURI()).GET().build(),
HttpResponse.BodyHandlers.ofString());
Assertions.assertEquals(200, response.statusCode());
Assertions.assertEquals("Hello from Quarkus", response.body());
}
@Test
void serviceIsInjectable() {
Assertions.assertEquals("Hello from Quarkus", service.greeting());
}
}
//stop
A real @QuarkusTest: the application boots, @Inject and
@TestHTTPResource work, RestAssured hits the live HTTP server.
bleep test myapp-test
Dev mode and packaging
Two six-line scripts wrap the plugin's runtime entry points:
package scripts;
import bleep.plugin.quarkus.QuarkusRun;
import bleepscript.BleepScript;
import bleepscript.Commands;
import bleepscript.Started;
import java.util.List;
/** Run myapp in Quarkus dev mode: augmentation in-process, live reload from bleep's source dirs. */
public class RunMyappDev extends BleepScript {
public RunMyappDev() {
super("run-myapp-dev");
}
@Override
public void run(Started started, Commands commands, List<String> args) {
new QuarkusRun().withJvmArgs("-Xmx512m").runOn(started, commands, "myapp");
}
}
package scripts;
import bleep.plugin.quarkus.QuarkusPackage;
import bleepscript.BleepScript;
import bleepscript.Commands;
import bleepscript.Started;
import java.util.List;
/** Production build: writes the fast-jar quarkus-app/ layout plus quarkus-artifact.properties. */
public class PackageMyapp extends BleepScript {
public PackageMyapp() {
super("package-myapp");
}
@Override
public void run(Started started, Commands commands, List<String> args) {
new QuarkusPackage().packageOn(started, commands, "myapp");
}
}
bleep run-myapp-dev starts Quarkus dev mode with live reload wired to
bleep's source dirs. bleep package-myapp produces the standard
quarkus-app/ fast-jar layout plus quarkus-artifact.properties,
identical to Maven's output — downstream tooling (Dockerfiles,
quarkus-run.jar launchers) works unchanged.
Importing a Maven Quarkus build
bleep import-maven detects Quarkus test projects (anything depending
on quarkus-junit5, or its 3.3x name quarkus-junit) and emits the
full wiring automatically:
- the
template-quarkus-testtemplate shown above, extendson every detected Quarkus test project,- a
scriptsproject depending onbleep-plugin-quarkus, - kotlin's
<javaParameters>and surefire JVM flags carried over, dependencyManagementBOM imports asboms:entries, inherited by downstream projects viadependsOn.
The result is a build where bleep test runs @QuarkusTest suites —
DevServices containers, @InjectMock, Testcontainers and all — with no
hand-editing. This flow is exercised against a production monorepo of
22 Quarkus services (Kotlin, Maven, reactive Hibernate, Kafka, CXF);
the imported build runs its ~900-test suite green.
Notes
- Quarkus reads
application.properties/application.yamlfrom the project's resource dirs like any other classpath resource — bleep serves resources from the source tree, no copy step involved. - No directory-layout mapping is needed: bleep compiles a test
project into a dir named
test-classes, which Quarkus'sPathTestHelperalready recognizes, and the application's real paths travel in the serialized model.