Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build time analytics #33555

Merged
merged 1 commit into from Jun 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions bom/application/pom.xml
Expand Up @@ -6568,6 +6568,11 @@
<artifactId>quarkus-devtools-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-analytics-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-apache-httpclient</artifactId>
Expand Down
24 changes: 24 additions & 0 deletions build-parent/pom.xml
Expand Up @@ -179,6 +179,10 @@
<gcf-invoker.version>1.1.1</gcf-invoker.version>
<!-- Jakarta JMS API -->
<jakarta.jms-api.version>3.1.0</jakarta.jms-api.version>

<!-- Quarkus Analytics -->
<properties-maven-plugin.version>1.1.0</properties-maven-plugin.version>
<quarkus.analytics.disabled>true</quarkus.analytics.disabled>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -431,6 +435,26 @@
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>${properties-maven-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>set-system-properties</goal>
</goals>
<configuration>
<properties>
<property>
<name>quarkus.analytics.disabled</name>
<value>${quarkus.analytics.disabled}</value>
</property>
</properties>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${version.surefire.plugin}</version>
Expand Down
@@ -1,6 +1,8 @@
package io.quarkus.deployment.pkg.builditem;

import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;

import io.quarkus.builder.item.SimpleBuildItem;

Expand Down Expand Up @@ -54,5 +56,14 @@ public String getDistribution() {
public static GraalVMVersion unknown() {
return new GraalVMVersion("unknown", "unknown", -1, "unknown");
}

public Map<String, String> toMap() {
final Map<String, String> graalVMVersion = new HashMap<>();
graalVMVersion.put("graalvm.version.full", fullVersion);
graalVMVersion.put("graalvm.version.version", version);
graalVMVersion.put("graalvm.version.java", String.valueOf(javaVersion));
graalVMVersion.put("graalvm.version.distribution", distribution);
return graalVMVersion;
}
}
}
Expand Up @@ -191,7 +191,8 @@ public AugmentResult createProductionApplication() {
.map(a -> new ArtifactResult(a.getPath(), a.getType(), a.getMetadata()))
.collect(Collectors.toList()),
jarBuildItem != null ? jarBuildItem.toJarResult() : null,
nativeImageBuildItem != null ? nativeImageBuildItem.getPath() : null);
nativeImageBuildItem != null ? nativeImageBuildItem.getPath() : null,
nativeImageBuildItem != null ? nativeImageBuildItem.getGraalVMInfo().toMap() : Collections.emptyMap());
}

private void writeDebugSourceFile(BuildResult result) {
Expand Down
@@ -0,0 +1,34 @@
package io.quarkus.runtime;

import java.util.Optional;

import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigPhase;
import io.quarkus.runtime.annotations.ConfigRoot;

/**
* Build time analytics configuration.
* This is a dummy config class to hide the warnings on the comment line.
* All properties in here are actually used in the build tools.
*/
@ConfigRoot(name = "analytics", phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED)
public class BuildAnalyticsConfig {

/**
* If Build time analytics are disabled.
*/
@ConfigItem
public Optional<Boolean> disabled;

/**
* The Segment base URI.
*/
@ConfigItem
public Optional<String> uriBase;

/**
* The Timeout to send the build time analytics to segment.
*/
@ConfigItem(defaultValue = "3000")
public Optional<Integer> timeout;
brunobat marked this conversation as resolved.
Show resolved Hide resolved
}
1 change: 1 addition & 0 deletions devtools/gradle/gradle-application-plugin/build.gradle.kts
Expand Up @@ -4,6 +4,7 @@ plugins {

dependencies {
implementation(libs.smallrye.config.yaml)
implementation("io.quarkus:quarkus-analytics-common")

testImplementation(libs.quarkus.project.core.extension.codestarts)
}
Expand Down
4 changes: 4 additions & 0 deletions devtools/gradle/gradle-application-plugin/pom.xml
Expand Up @@ -36,6 +36,10 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devtools-common</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-analytics-common</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-project-core-extension-codestarts</artifactId>
Expand Down
@@ -0,0 +1,85 @@
package io.quarkus.gradle;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

import org.aesh.readline.Readline;
import org.aesh.readline.ReadlineBuilder;
import org.aesh.readline.tty.terminal.TerminalConnection;
import org.aesh.terminal.tty.Signal;

/**
* Prompt implementation.
*
* @author <a href="http://escoffier.me">Clement Escoffier</a>
brunobat marked this conversation as resolved.
Show resolved Hide resolved
*/
public class Prompter {

private static class Prompt {
private final String prompt;
private final String defaultValue;
private final Consumer<String> inputConsumer;

public Prompt(String prompt, String defaultValue, Consumer<String> inputConsumer) {
this.prompt = prompt;
this.defaultValue = defaultValue;
this.inputConsumer = inputConsumer;
}
}

private final List<Prompt> prompts = new ArrayList<>();

public Prompter() throws IOException {
}

public Prompter addPrompt(String prompt, Consumer<String> inputConsumer) {
prompts.add(new Prompt(prompt, null, inputConsumer));
return this;
}

public Prompter addPrompt(String prompt, String defaultValue, Consumer<String> inputConsumer) {
prompts.add(new Prompt(prompt, defaultValue, inputConsumer));
return this;
}

public void collectInput() throws IOException {
if (prompts.isEmpty()) {
return;
}
final TerminalConnection connection = new TerminalConnection();
connection.setSignalHandler(interruptionSignalHandler());
try {
read(connection, ReadlineBuilder.builder().enableHistory(false).build(), prompts.iterator());
connection.openBlocking();
} finally {
connection.close();
}
}

private static void read(TerminalConnection connection, Readline readline, Iterator<Prompt> prompts) {
final Prompt prompt = prompts.next();
readline.readline(connection, prompt.prompt, input -> {
prompt.inputConsumer.accept(
(input == null || input.isBlank()) && prompt.defaultValue != null ? prompt.defaultValue : input);
if (!prompts.hasNext()) {
connection.close();
} else {
read(connection, readline, prompts);
}
});
}

private Consumer<Signal> interruptionSignalHandler() {
return new Consumer<Signal>() {
@Override
public void accept(Signal signal) {
if (signal == Signal.INT) {
throw new RuntimeException("Process interrupted");
}
}
};
}
}
Expand Up @@ -222,6 +222,7 @@ void generateBuild() {
params.getBaseName().set(extension().finalName());
params.getTargetDirectory().set(buildDir.toFile());
params.getAppModel().set(appModel);
params.getGradleVersion().set(getProject().getGradle().getGradleVersion());
});

workQueue.await();
Expand Down
@@ -1,5 +1,9 @@
package io.quarkus.gradle.tasks;

import static io.quarkus.analytics.dto.segment.ContextBuilder.CommonSystemProperties.GRADLE_VERSION;
import static io.quarkus.analytics.dto.segment.TrackEventType.*;
import static java.util.Collections.*;
brunobat marked this conversation as resolved.
Show resolved Hide resolved

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
Expand All @@ -15,6 +19,7 @@
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import javax.inject.Inject;

Expand Down Expand Up @@ -52,6 +57,8 @@
import org.gradle.jvm.toolchain.JavaToolchainSpec;
import org.gradle.util.GradleVersion;

import io.quarkus.analytics.AnalyticsService;
import io.quarkus.analytics.config.FileLocationsImpl;
import io.quarkus.bootstrap.BootstrapConstants;
import io.quarkus.bootstrap.app.ConfiguredClassLoading;
import io.quarkus.bootstrap.app.QuarkusBootstrap;
Expand All @@ -63,6 +70,7 @@
import io.quarkus.deployment.dev.DevModeContext;
import io.quarkus.deployment.dev.DevModeMain;
import io.quarkus.deployment.dev.QuarkusDevModeLauncher;
import io.quarkus.gradle.Prompter;
import io.quarkus.gradle.dependency.ApplicationDeploymentClasspathBuilder;
import io.quarkus.gradle.dsl.CompilerOption;
import io.quarkus.gradle.dsl.CompilerOptions;
Expand Down Expand Up @@ -314,9 +322,23 @@ public void startDev() {
"this should not happen as build should have been executed first. " +
"Does the project have any source files?");
}
AnalyticsService analyticsService = new AnalyticsService(FileLocationsImpl.INSTANCE,
new GradleMessageWriter(getLogger()));
analyticsService.buildAnalyticsUserInput((String prompt) -> {
try {
final AtomicReference<String> userInput = new AtomicReference<>("");
final Prompter prompter = new Prompter();
prompter.addPrompt(prompt, input -> userInput.set(input));
prompter.collectInput();
return userInput.get();
} catch (IOException e) {
getLogger().debug("Failed to collect user input for analytics", e);
return "";
}
});

try {
QuarkusDevModeLauncher runner = newLauncher();
QuarkusDevModeLauncher runner = newLauncher(analyticsService);
String outputFile = System.getProperty(IO_QUARKUS_DEVMODE_ARGS);
if (outputFile == null) {
getProject().exec(action -> {
Expand All @@ -337,6 +359,8 @@ public void startDev() {

} catch (Exception e) {
throw new GradleException("Failed to run", e);
} finally {
analyticsService.close();
}
}

Expand Down Expand Up @@ -364,7 +388,7 @@ private boolean classesExist() {
return false;
}

private QuarkusDevModeLauncher newLauncher() throws Exception {
private QuarkusDevModeLauncher newLauncher(final AnalyticsService analyticsService) throws Exception {
final Project project = getProject();
final JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);

Expand Down Expand Up @@ -420,6 +444,13 @@ private QuarkusDevModeLauncher newLauncher() throws Exception {
builder.sourceEncoding(getSourceEncoding());

final ApplicationModel appModel = extension().getApplicationModel(LaunchMode.DEVELOPMENT);

analyticsService.sendAnalytics(
DEV_MODE,
appModel,
Map.of(GRADLE_VERSION, getProject().getGradle().getGradleVersion()),
getProject().getBuildDir().getAbsoluteFile());

final Set<ArtifactKey> projectDependencies = new HashSet<>();
for (ResolvedDependency localDep : DependenciesFilter.getReloadableModules(appModel)) {
addLocalProject(localDep, builder, projectDependencies, appModel.getAppArtifact().getWorkspaceModule().getId()
Expand Down
Expand Up @@ -110,6 +110,7 @@ public void generateCode() {
.setFrom(sourcesDirectories.stream().map(Path::toFile).collect(Collectors.toList()));
params.getOutputPath().set(outputPath);
params.getLaunchMode().set(launchMode);
params.getGradleVersion().set(getProject().getGradle().getGradleVersion());
});

workQueue.await();
Expand Down
Expand Up @@ -21,7 +21,7 @@

public abstract class QuarkusTask extends DefaultTask {
private static final List<String> WORKER_BUILD_FORK_OPTIONS = List.of("quarkus.package.",
"quarkus.application.", "quarkus.gradle-worker.");
"quarkus.application.", "quarkus.gradle-worker.", "quarkus.analytics.");

private final transient QuarkusPluginExtension extension;
protected final File projectDir;
Expand Down