Skip to content

Commit

Permalink
Dump all Quarkus app dependencies along with their checksums as part …
Browse files Browse the repository at this point in the history
…of track-config-changes goal
  • Loading branch information
aloubyansky committed Dec 8, 2023
1 parent af6d3d7 commit f035e8d
Showing 1 changed file with 105 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package io.quarkus.maven;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import java.util.zip.Adler32;
import java.util.zip.Checksum;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
Expand All @@ -18,6 +25,7 @@
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.maven.dependency.DependencyFlags;
import io.quarkus.runtime.LaunchMode;

/**
Expand Down Expand Up @@ -58,6 +66,18 @@ public class TrackConfigChangesMojo extends QuarkusBootstrapMojo {
@Parameter(defaultValue = "false", property = "quarkus.track-config-changes.dump-current-when-recorded-unavailable")
boolean dumpCurrentWhenRecordedUnavailable;

/**
* Whether to dump Quarkus application dependencies along with their checksums
*/
@Parameter(defaultValue = "true", property = "quarkus.track-config-changes.dump-dependencies")
boolean dumpDependencies;

/**
* Dependency dump file
*/
@Parameter(property = "quarkus.track-config-changes.dependenciesFile")
File dependenciesFile;

@Override
protected boolean beforeExecute() throws MojoExecutionException, MojoFailureException {
if (skip) {
Expand All @@ -82,16 +102,6 @@ protected void doExecute() throws MojoExecutionException, MojoFailureException {
getLog().debug("Bootstrapping Quarkus application in mode " + launchMode);
}

Path targetFile;
if (outputFile == null) {
targetFile = outputDirectory.toPath()
.resolve("quarkus-" + launchMode.getDefaultProfile() + "-config-check");
} else if (outputFile.isAbsolute()) {
targetFile = outputFile.toPath();
} else {
targetFile = outputDirectory.toPath().resolve(outputFile.toPath());
}

Path compareFile;
if (this.recordedBuildConfigFile == null) {
compareFile = recordedBuildConfigDirectory.toPath()
Expand All @@ -102,34 +112,60 @@ protected void doExecute() throws MojoExecutionException, MojoFailureException {
compareFile = recordedBuildConfigDirectory.toPath().resolve(this.recordedBuildConfigFile.toPath());
}

final Properties compareProps = new Properties();
if (Files.exists(compareFile)) {
try (BufferedReader reader = Files.newBufferedReader(compareFile)) {
compareProps.load(reader);
} catch (IOException e) {
throw new RuntimeException("Failed to read " + compareFile, e);
}
} else if (!dumpCurrentWhenRecordedUnavailable) {
getLog().info(compareFile + " not found");
final boolean prevConfigExists = Files.exists(compareFile);
if (!prevConfigExists && !dumpCurrentWhenRecordedUnavailable && !dumpDependencies) {
getLog().info("Config dump from the previous build does not exist at " + compareFile);
return;
}

CuratedApplication curatedApplication = null;
QuarkusClassLoader deploymentClassLoader = null;
final ClassLoader originalCl = Thread.currentThread().getContextClassLoader();
Properties actualProps;
final boolean clearPackageTypeSystemProperty = setPackageTypeSystemPropertyIfNativeProfileEnabled();
try {
curatedApplication = bootstrapApplication(launchMode);
deploymentClassLoader = curatedApplication.createDeploymentClassLoader();
Thread.currentThread().setContextClassLoader(deploymentClassLoader);

final Class<?> codeGenerator = deploymentClassLoader.loadClass("io.quarkus.deployment.CodeGenerator");
final Method dumpConfig = codeGenerator.getMethod("dumpCurrentConfigValues", ApplicationModel.class, String.class,
Properties.class, QuarkusClassLoader.class, Properties.class, Path.class);
dumpConfig.invoke(null, curatedApplication.getApplicationModel(),
launchMode.name(), getBuildSystemProperties(true),
deploymentClassLoader, compareProps, targetFile);
if (prevConfigExists || dumpCurrentWhenRecordedUnavailable) {
final Path targetFile = getOutputFile(outputFile, launchMode.getDefaultProfile(), "-config-check");
Properties compareProps = null;
if (prevConfigExists) {
compareProps = new Properties();
try (BufferedReader reader = Files.newBufferedReader(compareFile)) {
compareProps.load(reader);
} catch (IOException e) {
throw new RuntimeException("Failed to read " + compareFile, e);
}
}

deploymentClassLoader = curatedApplication.createDeploymentClassLoader();
Thread.currentThread().setContextClassLoader(deploymentClassLoader);

final Class<?> codeGenerator = deploymentClassLoader.loadClass("io.quarkus.deployment.CodeGenerator");
final Method dumpConfig = codeGenerator.getMethod("dumpCurrentConfigValues", ApplicationModel.class,
String.class,
Properties.class, QuarkusClassLoader.class, Properties.class, Path.class);
dumpConfig.invoke(null, curatedApplication.getApplicationModel(),
launchMode.name(), getBuildSystemProperties(true),
deploymentClassLoader, compareProps, targetFile);
}

if (dumpDependencies) {
final List<String> deps = new ArrayList<>();
for (var d : curatedApplication.getApplicationModel().getDependencies(DependencyFlags.DEPLOYMENT_CP)) {
var adler32 = new Adler32();
updateChecksum(adler32, d.getResolvedPaths());
deps.add(d.toGACTVString() + " " + adler32.getValue());
}
Collections.sort(deps);
final Path targetFile = getOutputFile(dependenciesFile, launchMode.getDefaultProfile(),
"-dependency-checksums.txt");
Files.createDirectories(targetFile.getParent());
try (BufferedWriter writer = Files.newBufferedWriter(targetFile)) {
for (var s : deps) {
writer.write(s);
writer.newLine();
}
}
}
} catch (Exception any) {
throw new MojoExecutionException("Failed to bootstrap Quarkus application", any);
} finally {
Expand All @@ -142,4 +178,44 @@ protected void doExecute() throws MojoExecutionException, MojoFailureException {
}
}
}

private Path getOutputFile(File outputFile, String profile, String fileNameSuffix) {
if (outputFile == null) {
return outputDirectory.toPath().resolve("quarkus-" + profile + fileNameSuffix);
}
if (outputFile.isAbsolute()) {
return outputFile.toPath();
}
return outputDirectory.toPath().resolve(outputFile.toPath());
}

private static void updateChecksum(Checksum checksum, Iterable<Path> pc) throws IOException {
for (var path : sort(pc)) {
if (Files.isDirectory(path)) {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
updateChecksum(checksum, stream);
}
} else {
checksum.update(Files.readAllBytes(path));
}
}
}

private static Iterable<Path> sort(Iterable<Path> original) {
var i = original.iterator();
if (!i.hasNext()) {
return List.of();
}
var o = i.next();
if (!i.hasNext()) {
return List.of(o);
}
final List<Path> sorted = new ArrayList<>();
sorted.add(o);
while (i.hasNext()) {
sorted.add(i.next());
}
Collections.sort(sorted);
return sorted;
}
}

0 comments on commit f035e8d

Please sign in to comment.