Quarkus deployment module that provides automatic configuration and orchestration of development services using Docker Compose
—
Development-time CLI commands for listing DevServices, viewing logs, and managing container lifecycle during development.
Parent group command that provides the main entry point for DevServices CLI operations with subcommand management.
/**
* Group command definition for DevServices CLI operations
*/
@GroupCommandDefinition(name = "devservices", description = "Dev Service Commands")
public class DevServicesCommand implements GroupCommand {
/**
* Creates a DevServices command group with service descriptions
* @param serviceDescriptions List of available dev service descriptions
*/
public DevServicesCommand(List<DevServiceDescriptionBuildItem> serviceDescriptions);
/**
* Gets the list of available subcommands
* @return List containing list and logs commands
*/
@Override
public List<Command> getCommands();
/**
* Executes the group command (shows help)
* @param commandInvocation Command invocation context
* @return SUCCESS status
*/
@Override
public CommandResult execute(CommandInvocation commandInvocation);
/**
* Finds a dev service by name
* @param devServiceName Name of the service to find
* @return Optional containing the service description if found
*/
static Optional<DevServiceDescriptionBuildItem> findDevService(String devServiceName);
}Usage Examples:
// Creating DevServices command in build step
@BuildStep
public ConsoleCommandBuildItem createDevServicesCommand(
List<DevServiceDescriptionBuildItem> serviceDescriptions) {
DevServicesCommand devServicesCommand = new DevServicesCommand(serviceDescriptions);
return new ConsoleCommandBuildItem(devServicesCommand);
}
// Using the command programmatically
List<DevServiceDescriptionBuildItem> services = getAvailableServices();
DevServicesCommand command = new DevServicesCommand(services);
// Get available subcommands
List<Command> subcommands = command.getCommands();
for (Command cmd : subcommands) {
System.out.println("Available command: " + cmd.getClass().getSimpleName());
}
// Find specific service
Optional<DevServiceDescriptionBuildItem> postgres =
DevServicesCommand.findDevService("PostgreSQL Dev Service");
if (postgres.isPresent()) {
DevServiceDescriptionBuildItem service = postgres.get();
System.out.println("Found service: " + service.getName());
System.out.println("Description: " + service.getDescription());
if (service.hasContainerInfo()) {
String containerId = service.getContainerInfo().id();
System.out.println("Container ID: " + containerId);
}
}Command completion support for DevService names, providing autocomplete functionality in the CLI.
/**
* Command completer for DevService names
*/
public static class DevServiceCompleter extends SetCompleter {
/**
* Provides completion options for DevService names
* @param soFar Current input string
* @return Set of matching service names
*/
@Override
protected Set<String> allOptions(String soFar);
}Command implementation that lists all running DevServices with their status and configuration information.
/**
* Command to list all running dev services
*/
@CommandDefinition(name = "list", description = "List of dev services")
public class DevServicesListCommand implements Command {
/**
* Executes the list command, displaying all dev services
* @param commandInvocation Command invocation context
* @return SUCCESS status
*/
@Override
public CommandResult execute(CommandInvocation commandInvocation);
}Usage Examples:
// Example output when running 'devservices list' command:
/*
PostgreSQL Dev Service
Container: 1a2b3c4d5e6f /postgres-dev postgres:13
Network: bridge - 0.0.0.0:5432->5432/tcp
Exec command: docker exec -it 1a2b3c4d /bin/bash
Injected config: - quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/quarkus
Redis Dev Service
Container: 9f8e7d6c5b4a /redis-dev redis:6-alpine
Network: bridge - 0.0.0.0:6379->6379/tcp
Exec command: docker exec -it 9f8e7d6c /bin/bash
Injected config: - quarkus.redis.hosts=redis://localhost:6379
Compose Dev Services
Project: my-app-devservices, Services: database, message-queue
Injected config: - DATABASE_URL=postgresql://localhost:5433/mydb
- RABBITMQ_URL=amqp://localhost:5672
*/
// Programmatic usage
DevServicesListCommand listCommand = new DevServicesListCommand();
CommandInvocation mockInvocation = createMockInvocation();
CommandResult result = listCommand.execute(mockInvocation);
if (result == CommandResult.SUCCESS) {
System.out.println("Successfully listed dev services");
}Command implementation that displays container logs for a specific DevService with optional following and tailing support.
/**
* Command to display container logs for a dev service
*/
@CommandDefinition(name = "logs", description = "Print container logs")
public class DevServicesLogsCommand implements Command {
/**
* Dev Service name (required argument with completion support)
*/
@Argument(required = true, description = "Dev Service name", completer = DevServiceCompleter.class)
private String devService;
/**
* Follow logs in real-time
*/
@Option(name = "follow", shortName = 'f', description = "Follow container logs", hasValue = false, defaultValue = "false")
private boolean follow;
/**
* Number of lines to tail (-1 for all)
*/
@Option(name = "tail", shortName = 't', description = "Tail container logs", defaultValue = "-1")
private int tail;
/**
* Executes the logs command
* @param commandInvocation Command invocation context
* @return SUCCESS if service found and logs displayed, FAILURE otherwise
*/
@Override
public CommandResult execute(CommandInvocation commandInvocation);
}Usage Examples:
// Command line usage examples:
// Show all logs for a service
// > devservices logs "PostgreSQL Dev Service"
// Follow logs in real-time
// > devservices logs -f "PostgreSQL Dev Service"
// Show last 50 lines and follow
// > devservices logs -t 50 -f "Redis Dev Service"
// Just last 100 lines
// > devservices logs --tail 100 "Compose Dev Services"
// Programmatic usage
DevServicesLogsCommand logsCommand = new DevServicesLogsCommand();
// Set command arguments programmatically (normally done by CLI framework)
setCommandArgument(logsCommand, "devService", "PostgreSQL Dev Service");
setCommandOption(logsCommand, "follow", true);
setCommandOption(logsCommand, "tail", 50);
CommandInvocation invocation = createCommandInvocation();
CommandResult result = logsCommand.execute(invocation);
if (result == CommandResult.SUCCESS) {
System.out.println("Successfully displayed logs");
} else {
System.err.println("Failed to find service or display logs");
}
// Example log output:
/*
2024-01-15 10:30:25.123 UTC [1] LOG: database system was shut down at 2024-01-15 10:30:20 UTC
2024-01-15 10:30:25.125 UTC [1] LOG: database system is ready to accept connections
2024-01-15 10:30:25.126 UTC [7] LOG: autovacuum launcher started
2024-01-15 10:30:30.445 UTC [8] LOG: connection received: host=172.17.0.1 port=54321
2024-01-15 10:30:30.447 UTC [8] LOG: connection authorized: user=quarkus database=quarkus
*/Manages log forwarding from containers to the application logging system with start/stop lifecycle control.
/**
* Forwards container logs to application logging system
*/
public class ContainerLogForwarder implements Closeable {
/**
* Creates a log forwarder for a dev service
* @param devService Dev service description containing container info
*/
public ContainerLogForwarder(DevServiceDescriptionBuildItem devService);
/**
* Gets the associated dev service
* @return Dev service description
*/
public DevServiceDescriptionBuildItem getDevService();
/**
* Checks if log forwarding is currently active
* @return true if logs are being forwarded
*/
public boolean isRunning();
/**
* Starts log forwarding from the container
*/
public void start();
/**
* Stops log forwarding and cleans up resources
*/
@Override
public void close();
}Usage Examples:
// Managing log forwarders for dev services
Map<String, ContainerLogForwarder> logForwarders = new HashMap<>();
// Create log forwarders for all services with containers
for (DevServiceDescriptionBuildItem service : serviceDescriptions) {
if (service.hasContainerInfo()) {
String containerId = service.getContainerInfo().id();
ContainerLogForwarder forwarder = new ContainerLogForwarder(service);
logForwarders.put(containerId, forwarder);
}
}
// Start log forwarding for all services
for (ContainerLogForwarder forwarder : logForwarders.values()) {
if (!forwarder.isRunning()) {
forwarder.start();
System.out.println("Started log forwarding for: " +
forwarder.getDevService().getName());
}
}
// Toggle log forwarding based on user preference
boolean enableLogForwarding = getUserPreference();
for (ContainerLogForwarder forwarder : logForwarders.values()) {
if (enableLogForwarding && !forwarder.isRunning()) {
forwarder.start();
} else if (!enableLogForwarding && forwarder.isRunning()) {
forwarder.close();
}
}
// Cleanup when shutting down
try {
for (ContainerLogForwarder forwarder : logForwarders.values()) {
if (forwarder.isRunning()) {
forwarder.close();
}
}
} catch (Exception e) {
System.err.println("Error during log forwarder cleanup: " + e.getMessage());
}
// Example logged output format:
/*
2024-01-15 10:30:25.123 INFO [PostgreSQL Dev Service] [1a2b3c4d] 2024-01-15 10:30:25.123 UTC [1] LOG: database system is ready to accept connections
2024-01-15 10:30:26.234 ERROR [Redis Dev Service] [9f8e7d6c] 1:M 15 Jan 2024 10:30:26.234 # Warning: no config file specified, using the default config
2024-01-15 10:30:27.345 INFO [Message Queue] [5a6b7c8d] 2024-01-15 10:30:27.345 [info] <0.123.0> accepting AMQP connection <0.456.0> (127.0.0.1:54321 -> 127.0.0.1:5672)
*/
// Using with try-with-resources
try (ContainerLogForwarder forwarder = new ContainerLogForwarder(serviceDescription)) {
forwarder.start();
// Do work while logs are being forwarded
performDevelopmentTasks();
// Forwarder automatically closed when leaving try block
}These commands integrate with the Quarkus development console through build steps:
@BuildStep(onlyIf = { IsDevelopment.class })
public void registerConsoleCommands(
List<DevServiceDescriptionBuildItem> serviceDescriptions,
BuildProducer<ConsoleCommandBuildItem> commandProducer) {
// Register the DevServices command group
DevServicesCommand devServicesCommand = new DevServicesCommand(serviceDescriptions);
commandProducer.produce(new ConsoleCommandBuildItem(devServicesCommand));
}The commands are available in the Quarkus development console and provide:
When running Quarkus in development mode (mvn quarkus:dev), the DevServices commands become available:
2024-01-15 10:30:25,123 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2024-01-15 10:30:25,124 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, devservices-deployment, ...]
Tests paused
Press [SPACE] to restart, [e] to edit command line args (currently ''), [r] to resume testing, [o] Toggle test output, [h] for more options>
# Available DevServices commands:
# devservices list - List all running dev services
# devservices logs <service-name> - Show logs for a specific serviceThe console provides immediate feedback and allows developers to inspect and manage their development services without leaving the development environment.
Install with Tessl CLI
npx tessl i tessl/maven-io-quarkus--quarkus-devservices-deployment