From dee4948210e743bc9b0b9558315fd2b9b47e5b0d Mon Sep 17 00:00:00 2001 From: jan Date: Mon, 31 May 2021 13:24:11 +0200 Subject: [PATCH 1/2] Add sha1 hashing to manifests Split up manifest to jar-manifest and distributor-manifest --- build.gradle.kts | 10 +- .../net/flintmc/gradle/FlintGradlePlugin.java | 8 + .../gradle/manifest/ManifestConfigurator.java | 67 +++++-- .../cache/MavenArtifactChecksums.java | 123 +++++++++++++ .../tasks/GenerateFlintManifestTask.java | 172 +++++++++++++++--- .../gradle/maven/MavenArtifactDownloader.java | 2 +- .../gradle/maven/pom/MavenArtifact.java | 3 +- .../java/net/flintmc/gradle/util/Util.java | 30 +++ 8 files changed, 360 insertions(+), 55 deletions(-) create mode 100644 src/main/java/net/flintmc/gradle/manifest/cache/MavenArtifactChecksums.java diff --git a/build.gradle.kts b/build.gradle.kts index caaca38..918f789 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -81,11 +81,11 @@ dependencies { implementation(group = "io.github.java-diff-utils", name = "java-diff-utils", version = "4.7") implementation(group = "com.fasterxml.jackson.core", name = "jackson-core", version = "2.12.0-rc1") - implementation(group = "net.flintmc.installer", name = "logic-implementation", version = "1.1.11") - implementation(group = "net.flintmc.installer", name = "frontend-gui", version = "1.1.11") - implementation(group = "net.flintmc.installer", name = "logic", version = "1.1.11") - implementation(group = "net.flintmc.installer", name = "logic-implementation", version = "1.1.11") - implementation(group = "net.flintmc.installer", name = "logic", version = "1.1.11") + implementation(group = "net.flintmc.installer", name = "logic-implementation", version = "2.0.0") + implementation(group = "net.flintmc.installer", name = "frontend-gui", version = "2.0.0") + implementation(group = "net.flintmc.installer", name = "logic", version = "2.0.0") + implementation(group = "net.flintmc.installer", name = "logic-implementation", version = "2.0.0") + implementation(group = "net.flintmc.installer", name = "logic", version = "2.0.0") implementation(group = "com.cloudbees", name = "diff4j", version = "1.2") } diff --git a/src/main/java/net/flintmc/gradle/FlintGradlePlugin.java b/src/main/java/net/flintmc/gradle/FlintGradlePlugin.java index 933d770..02806f4 100644 --- a/src/main/java/net/flintmc/gradle/FlintGradlePlugin.java +++ b/src/main/java/net/flintmc/gradle/FlintGradlePlugin.java @@ -248,6 +248,10 @@ private void handleVersion(String version, EnvironmentType type) { interaction.setupVersioned(compileArtifacts, runtimeArtifacts, version); } + public SimpleMavenRepository getInternalRepository() { + return internalRepository; + } + /** * Retrieves the client artifact for the given version. * @@ -308,4 +312,8 @@ public MavenArtifactURLCache getMavenArtifactURLCache() { public Project getProject() { return project; } + + public MavenArtifactDownloader getDownloader() { + return this.downloader; + } } diff --git a/src/main/java/net/flintmc/gradle/manifest/ManifestConfigurator.java b/src/main/java/net/flintmc/gradle/manifest/ManifestConfigurator.java index dc81228..8ba02c6 100644 --- a/src/main/java/net/flintmc/gradle/manifest/ManifestConfigurator.java +++ b/src/main/java/net/flintmc/gradle/manifest/ManifestConfigurator.java @@ -34,11 +34,10 @@ import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.repositories.MavenArtifactRepository; -import org.gradle.api.credentials.HttpHeaderCredentials; import org.gradle.api.publish.PublishingExtension; import org.gradle.api.publish.maven.MavenPublication; import org.gradle.api.tasks.Copy; -import org.gradle.authentication.http.HttpHeaderAuthentication; +import org.gradle.jvm.tasks.Jar; import java.io.File; import java.net.URI; @@ -47,6 +46,7 @@ public class ManifestConfigurator { private final Project project; private final OkHttpClient httpClient; private final MavenArtifactURLCache mavenArtifactURLCache; + private final FlintGradlePlugin flintGradlePlugin; /** * Constructs a new {@link ManifestConfigurator} for the given plugin. @@ -54,6 +54,7 @@ public class ManifestConfigurator { * @param plugin The plugin to configure the manifest for */ public ManifestConfigurator(FlintGradlePlugin plugin) { + this.flintGradlePlugin = plugin; this.project = plugin.getProject(); this.httpClient = plugin.getHttpClient(); this.mavenArtifactURLCache = plugin.getMavenArtifactURLCache(); @@ -66,13 +67,13 @@ public ManifestConfigurator(FlintGradlePlugin plugin) { * Installs the required gradle tasks to generate the flint manifests. */ public void configure() { - if(!isValidProject(project)) { + if (!isValidProject(project)) { return; } FlintGradleExtension extension = project.getExtensions().getByType(FlintGradleExtension.class); - if(extension.shouldAutoConfigurePublishing() && extension.shouldEnablePublishing()) { + if (extension.shouldAutoConfigurePublishing() && extension.shouldEnablePublishing()) { // Auto configuration is enabled PublishingExtension publishingExtension = project.getExtensions().findByType(PublishingExtension.class); @@ -81,7 +82,7 @@ public void configure() { "Set enablePublishing to false in the flint extension", "Set shouldAutoConfigurePublishing to false in the flint extension"); - if(publishingExtension != null) { + if (publishingExtension != null) { // Found a publishing extension, automatically set the publish target MavenPublication publication = publishingExtension.getPublications().create("flint", MavenPublication.class); @@ -130,29 +131,54 @@ public void configure() { generateStaticFileChecksumsTask.setGroup("publishing"); generateStaticFileChecksumsTask.setDescription("Calculates the checksums of all static files and caches them"); - File manifestFile = new File(Util.getProjectCacheDir(project), "manifest.json"); + File manifestFileJar = new File(Util.getProjectCacheDir(project), "manifestJar.json"); - GenerateFlintManifestTask generateFlintManifestTask = project.getTasks().create( - "generateFlintManifest", + GenerateFlintManifestTask generateFlintManifestJarTask = project.getTasks().create( + "generateFlintManifestJar", GenerateFlintManifestTask.class, - manifestFile, + this.flintGradlePlugin, + GenerateFlintManifestTask.ManifestType.JAR, + manifestFileJar, staticFileInput, packageDependencyInput, resolveArtifactURLsTask.getCacheFile(), - generateStaticFileChecksumsTask.getCacheFile() + generateStaticFileChecksumsTask.getCacheFile(), + repositoryInput ); - generateFlintManifestTask.setGroup("publishing"); - generateFlintManifestTask.setDescription("Generates the flint manifest.json and caches it"); - generateFlintManifestTask.dependsOn(resolveArtifactURLsTask, generateStaticFileChecksumsTask); + generateFlintManifestJarTask.setGroup("publishing"); + generateFlintManifestJarTask.setDescription("Generates the flint manifest.json to include in the jar file and caches it"); + generateFlintManifestJarTask.dependsOn(resolveArtifactURLsTask, generateStaticFileChecksumsTask); + + File manifestFileDistributor = new File(Util.getProjectCacheDir(project), "manifestDistributor.json"); + + GenerateFlintManifestTask generateFlintManifestDistributorTask = project.getTasks().create( + "generateFlintManifestDistributor", + GenerateFlintManifestTask.class, + this.flintGradlePlugin, + GenerateFlintManifestTask.ManifestType.DISTRIBUTOR, + manifestFileDistributor, + staticFileInput, + packageDependencyInput, + resolveArtifactURLsTask.getCacheFile(), + generateStaticFileChecksumsTask.getCacheFile(), + repositoryInput + ); + Jar jar = (Jar) project.getTasks().getByName("jar"); + generateFlintManifestDistributorTask.setGroup("publishing"); + generateFlintManifestDistributorTask.setDescription("Generates the flint manifest.json to publish to the distributor and caches it"); + generateFlintManifestDistributorTask.dependsOn(resolveArtifactURLsTask, generateStaticFileChecksumsTask, jar); + // Retrieve the process resources task so we can include the manifest // The processResources task is a copy task, and as the ProcessResources class is marked unstable, // we cast it to a copy task Copy processResourcesTask = (Copy) project.getTasks().getByName("processResources"); - processResourcesTask.from(manifestFile); - processResourcesTask.dependsOn(generateFlintManifestTask); + processResourcesTask + .from(manifestFileJar) + .rename("manifestJar.json", "manifest.json"); + processResourcesTask.dependsOn(generateFlintManifestJarTask); - if(extension.shouldEnablePublishing()) { + if (extension.shouldEnablePublishing()) { // Generate the URI to publish the manifest to URI manifestURI = Util.concatURI( getProjectPublishURI("Set enablePublishing to false in the flint extension"), @@ -165,12 +191,13 @@ public void configure() { PublishFileTask.class, this, new MaybeNull<>(httpClient), - manifestFile, + manifestFileDistributor, manifestURI ); publishManifestTask.setGroup("publishing"); publishManifestTask.setDescription("Publishes the flint manifest.json to the distributor"); - publishManifestTask.dependsOn(generateFlintManifestTask); + publishManifestTask.dependsOn(generateFlintManifestJarTask); + publishManifestTask.dependsOn(generateFlintManifestDistributorTask); // Create the static files publish task PublishStaticFilesTask publishStaticFilesTask = project.getTasks().create( @@ -214,7 +241,7 @@ private boolean isValidProject(Project project) { * @return The base URI of the distributor publish endpoint including the project namespace */ public URI getProjectPublishURI(String... notAvailableSolutions) { - if(projectPublishURI == null) { + if (projectPublishURI == null) { projectPublishURI = Util.concatURI( FlintPluginProperties.DISTRIBUTOR_URL.require(project, notAvailableSolutions), "api/v1/publish", @@ -235,7 +262,7 @@ public URI getProjectPublishURI(String... notAvailableSolutions) { * @return The base URI of the distributor repository including the project namespace */ public URI getProjectMavenURI(String... notAvailableSolution) { - if(projectMavenURI == null) { + if (projectMavenURI == null) { URI distributorURI = Util.getDistributorMavenURI(project, notAvailableSolution); projectMavenURI = Util.concatURI( diff --git a/src/main/java/net/flintmc/gradle/manifest/cache/MavenArtifactChecksums.java b/src/main/java/net/flintmc/gradle/manifest/cache/MavenArtifactChecksums.java new file mode 100644 index 0000000..69a025f --- /dev/null +++ b/src/main/java/net/flintmc/gradle/manifest/cache/MavenArtifactChecksums.java @@ -0,0 +1,123 @@ +/* + * FlintMC + * Copyright (C) 2020-2021 LabyMedia GmbH and contributors + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package net.flintmc.gradle.manifest.cache; + +import net.flintmc.gradle.maven.pom.MavenArtifact; +import net.flintmc.gradle.util.Util; +import org.gradle.api.Project; + +import java.io.*; +import java.util.HashMap; +import java.util.Map; + +public class MavenArtifactChecksums implements Externalizable { + /** + * Loads the cached static file checksums from a file. + * + * @param file The file to load the cached checksums from + * @return The loaded checksums + * @throws IOException If an I/O error occurs while loading the checksums + */ + public static MavenArtifactChecksums load(File file) throws IOException { + try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) { + return Util.forceCast(in.readObject()); + } catch (ClassNotFoundException e) { + throw new IOException("Failed to read cached checksums of static files due to ClassNotFoundException", e); + } + } + + private Map artifactChecksums; + + /** + * Constructs a new {@link MavenArtifactChecksums} with all values empty. + */ + public MavenArtifactChecksums() { + this.artifactChecksums = new HashMap<>(); + } + + /** + * Saves this instance to the given file. + * + * @param file The file to write the cache to + * @throws IOException If an I/O error occurs + */ + public void save(File file) throws IOException { + try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file))) { + out.writeObject(this); + } + } + + /** + * Adds a checksum to the map of URI checksums. + * + * @param mavenArtifact The maven artifact to add the checksum for + * @param checksum The checksum of the download + */ + public void add(MavenArtifact mavenArtifact, String checksum) { + this.artifactChecksums.put(mavenArtifact, checksum); + } + + /** + * Checks if the checksums contain a checksum for the given URI. + * + * @param mavenArtifact The maven artifact to check for + * @return {@code true} if a checksum for the URI has been cached, {@code false} otherwise + */ + public boolean has(MavenArtifact mavenArtifact) { + return artifactChecksums.containsKey(mavenArtifact); + } + + /** + * Retrieves the checksum for the given URI. + * + * @param mavenArtifact The maven artifact to retrieve the checksum for + * @return The checksum of the download behind the URI, or {@code null}, if the URI has not been cached + */ + public String get(MavenArtifact mavenArtifact) { + return artifactChecksums.get(mavenArtifact); + } + + /** + * Clears all URI checksums. + */ + public void clearArtifacts() { + artifactChecksums.clear(); + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(this.artifactChecksums); + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + this.artifactChecksums = Util.forceCast(in.readObject()); + } + + /** + * Retrieves a project unique file to cache bound dependencies in. + * + * @param project The project to retrieve the cache file for + * @return The cache file + */ + public static File getCacheFile(Project project) { + return new File(Util.getProjectCacheDir(project), "artifact-checksums.bin"); + } +} diff --git a/src/main/java/net/flintmc/gradle/manifest/tasks/GenerateFlintManifestTask.java b/src/main/java/net/flintmc/gradle/manifest/tasks/GenerateFlintManifestTask.java index 91839cb..bf818f1 100644 --- a/src/main/java/net/flintmc/gradle/manifest/tasks/GenerateFlintManifestTask.java +++ b/src/main/java/net/flintmc/gradle/manifest/tasks/GenerateFlintManifestTask.java @@ -20,15 +20,21 @@ package net.flintmc.gradle.manifest.tasks; import net.flintmc.gradle.FlintGradleException; +import net.flintmc.gradle.FlintGradlePlugin; import net.flintmc.gradle.extension.FlintGradleExtension; import net.flintmc.gradle.extension.json.FlintJsonInjectionDescription; import net.flintmc.gradle.json.JsonConverter; import net.flintmc.gradle.manifest.cache.BoundMavenDependencies; +import net.flintmc.gradle.manifest.cache.MavenArtifactChecksums; import net.flintmc.gradle.manifest.cache.StaticFileChecksums; import net.flintmc.gradle.manifest.data.*; +import net.flintmc.gradle.maven.MavenArtifactDownloader; +import net.flintmc.gradle.maven.RemoteMavenRepository; +import net.flintmc.gradle.maven.SimpleMavenRepository; import net.flintmc.gradle.maven.pom.MavenArtifact; import net.flintmc.gradle.minecraft.data.environment.MinecraftVersion; import net.flintmc.gradle.property.FlintPluginProperties; +import net.flintmc.gradle.util.Util; import net.flintmc.installer.impl.repository.models.DependencyDescriptionModel; import net.flintmc.installer.impl.repository.models.PackageModel; import net.flintmc.installer.impl.repository.models.install.InstallInstructionModel; @@ -41,16 +47,22 @@ import net.flintmc.installer.install.json.JsonFileDataInjection; import net.flintmc.installer.install.json.JsonInjectionPath; import net.flintmc.installer.util.OperatingSystem; +import org.apache.commons.io.IOUtils; import org.gradle.api.DefaultTask; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.tasks.*; +import org.gradle.jvm.tasks.Jar; import javax.inject.Inject; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; @@ -59,6 +71,10 @@ * Task generating the flint manifest.json */ public class GenerateFlintManifestTask extends DefaultTask { + + private FlintGradlePlugin flintGradlePlugin; + private ManifestType manifestType; + @OutputFile private final File manifestFile; @@ -74,24 +90,31 @@ public class GenerateFlintManifestTask extends DefaultTask { private final File staticFilesChecksumsCacheFile; private FlintGradleExtension extension; + private MavenArtifactChecksums artifactChecksums; /** * Constructs a new {@link GenerateFlintManifestTask}. * * @param manifestFile The file to write the generated manifest to * @param staticFiles The static files to index in the manifest + * @param repositoryInput * @param packageDependencies The packages the manifest lists as dependencies * @param artifactURLsCacheFile The file to load the cached artifact URLs from * @param staticFilesChecksumsCacheFile The file to load the cached static checksums from */ @Inject public GenerateFlintManifestTask( + FlintGradlePlugin flintGradlePlugin, + ManifestType manifestType, File manifestFile, ManifestStaticFileInput staticFiles, ManifestPackageDependencyInput packageDependencies, File artifactURLsCacheFile, - File staticFilesChecksumsCacheFile + File staticFilesChecksumsCacheFile, + ManifestRepositoryInput repositoryInput ) { + this.flintGradlePlugin = flintGradlePlugin; + this.manifestType = manifestType; this.manifestFile = manifestFile; this.staticFiles = staticFiles; this.packageDependencies = packageDependencies; @@ -280,6 +303,20 @@ public void generate() { throw new FlintGradleException("IOException while loading cached static files checksums", e); } + try { + File cacheFile = MavenArtifactChecksums.getCacheFile(getProject()); + if (!cacheFile.exists()) { + cacheFile.getParentFile().mkdirs(); + cacheFile.createNewFile(); + artifactChecksums = new MavenArtifactChecksums(); + artifactChecksums.save(cacheFile); + } else { + artifactChecksums = MavenArtifactChecksums.load(cacheFile); + } + } catch (IOException e) { + throw new FlintGradleException("IOException while loading cached artifacts checksums", e); + } + // Build package dependencies Set dependencyDescriptionModels = new HashSet<>(); for (ManifestPackageDependency dependency : packageDependencies.getDependencies()) { @@ -296,35 +333,39 @@ public void generate() { Set modifyJsonInstallInstructions = buildJsonInjectionInstructions(); InstallInstructionModel ownInstallInstruction = buildOwnInstallInstruction(); - // Build the runtime classpath - Set runtimeClasspath = new HashSet<>(); - for (InstallInstructionModel mavenInstallInstruction : mavenInstallInstructions) { - // Add all maven dependencies to the runtime classpath - DownloadMavenDependencyDataModel data = mavenInstallInstruction.getData(); - if (ownInstallInstruction == null - || !data.getPath().equals(ownInstallInstruction.getData().getPath())) { - // If the library does not equal ourself, add it - runtimeClasspath.add(data.getPath()); + Set runtimeClasspath = null; + List allInstallInstructions = null; + if (manifestType == ManifestType.DISTRIBUTOR) { + // Collect all install instructions + allInstallInstructions = new ArrayList<>(); + if (ownInstallInstruction != null) { + allInstallInstructions.add(ownInstallInstruction); } - } - - for (InstallInstructionModel staticFileInstallInstruction : staticFileInstallInstructions) { - DownloadFileDataModel data = staticFileInstallInstruction.getData(); - if (data.getPath().endsWith(".jar")) { - // If the static file is a jar, add it to the classpath - runtimeClasspath.add(data.getPath()); + allInstallInstructions.addAll(mavenInstallInstructions); + allInstallInstructions.addAll(staticFileInstallInstructions); + allInstallInstructions.addAll(modifyJsonInstallInstructions); + + } else if (manifestType == ManifestType.JAR) { + // Build the runtime classpath + runtimeClasspath = new HashSet<>(); + for (InstallInstructionModel mavenInstallInstruction : mavenInstallInstructions) { + // Add all maven dependencies to the runtime classpath + DownloadMavenDependencyDataModel data = mavenInstallInstruction.getData(); + if (ownInstallInstruction == null + || !data.getPath().equals(ownInstallInstruction.getData().getPath())) { + // If the library does not equal ourself, add it + runtimeClasspath.add(data.getPath()); + } } - } - // Collect all install instructions - List allInstallInstructions = new ArrayList<>(); - if (ownInstallInstruction != null) { - allInstallInstructions.add(ownInstallInstruction); + for (InstallInstructionModel staticFileInstallInstruction : staticFileInstallInstructions) { + DownloadFileDataModel data = staticFileInstallInstruction.getData(); + if (data.getPath().endsWith(".jar")) { + // If the static file is a jar, add it to the classpath + runtimeClasspath.add(data.getPath()); + } + } } - allInstallInstructions.addAll(mavenInstallInstructions); - allInstallInstructions.addAll(staticFileInstallInstructions); - allInstallInstructions.addAll(modifyJsonInstallInstructions); - // Build package model PackageModel model = new PackageModel( getProjectGroup(), @@ -353,6 +394,12 @@ public void generate() { } catch (IOException e) { throw new FlintGradleException("Failed to write manifest file", e); } + + try { + artifactChecksums.save(MavenArtifactChecksums.getCacheFile(getProject())); + } catch (IOException e) { + throw new FlintGradleException("Failed to write maven artifact cache file", e); + } } /** @@ -379,6 +426,53 @@ private Set buildMavenInstallInstructions( artifact.getClassifier() == null ? "" : "-" + artifact.getClassifier() ); + SimpleMavenRepository internalRepository = this.flintGradlePlugin.getInternalRepository(); + MavenArtifactDownloader downloader = this.flintGradlePlugin.getDownloader(); + + RemoteMavenRepository remoteMavenRepository; + if (flintGradlePlugin.getHttpClient() == null) { + remoteMavenRepository = null; + } else { + remoteMavenRepository = new RemoteMavenRepository(flintGradlePlugin.getHttpClient(), entry.getValue()); + } + + if (!internalRepository.isInstalled(artifact)) { + // The artifact is not installed already, install it + if (remoteMavenRepository == null) { + // Can't download anything in offline mode + throw new RuntimeException("Missing artifact " + artifact + " in local repository, " + + "but working in offline mode"); + } + + boolean setupSource = !downloader.hasSource(remoteMavenRepository); + if (setupSource) { + // The download has the source not set already, add it now + downloader.addSource(remoteMavenRepository); + } + + try { + // Install the artifact including dependencies + downloader.installArtifact(artifact, internalRepository); + } catch (IOException e) { + throw new RuntimeException("Failed to install execution artifact", e); + } + + if (setupSource) { + // We added the source, clean up afterwards + downloader.removeSource(remoteMavenRepository); + } + } + + if (!artifactChecksums.has(artifact)) { + try (InputStream inputStream = internalRepository.getArtifactStream(artifact)) { + MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); + String sha1Hex = Util.sha1Hex(messageDigest.digest(IOUtils.toByteArray(inputStream))); + this.artifactChecksums.add(artifact, sha1Hex); + } catch (IOException | NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + // Add the instruction out.add(new InstallInstructionModel( InstallInstructionTypes.DOWNLOAD_MAVEN_DEPENDENCY, @@ -389,7 +483,8 @@ private Set buildMavenInstallInstructions( artifact.getVersion(), artifact.getClassifier(), entry.getValue().toASCIIString(), - localPath + localPath, + artifactChecksums.get(artifact) ) )); } @@ -421,6 +516,18 @@ private InstallInstructionModel buildOwnInstallInstruction() { targetPath = "${FLINT_PACKAGE_DIR}/" + getProjectName() + ".jar"; } + String sha1Hex; + if (manifestType == ManifestType.DISTRIBUTOR) { + Jar jar = (Jar) getProject().getTasks().getByName("jar"); + File singleFile = jar.getOutputs().getFiles().getSingleFile(); + try (FileInputStream fileInputStream = new FileInputStream(singleFile)) { + sha1Hex = Util.sha1Hex(IOUtils.toByteArray(fileInputStream)); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + sha1Hex = null; + } return new InstallInstructionModel( InstallInstructionTypes.DOWNLOAD_MAVEN_DEPENDENCY, null, @@ -430,9 +537,12 @@ private InstallInstructionModel buildOwnInstallInstruction() { getProjectVersion(), null, "${FLINT_DISTRIBUTOR_URL}" + getChannel(), - targetPath + targetPath, + sha1Hex ) ); + + } /** @@ -538,4 +648,10 @@ private Set buildJsonInjectionInstructions() { .map(model -> new InstallInstructionModel(InstallInstructionTypes.MODIFY_JSON_FILE, OperatingSystem.UNKNOWN, model)) .collect(Collectors.toSet()); } + + public enum ManifestType { + JAR, + DISTRIBUTOR + } + } diff --git a/src/main/java/net/flintmc/gradle/maven/MavenArtifactDownloader.java b/src/main/java/net/flintmc/gradle/maven/MavenArtifactDownloader.java index b0d835b..0cdb4fb 100644 --- a/src/main/java/net/flintmc/gradle/maven/MavenArtifactDownloader.java +++ b/src/main/java/net/flintmc/gradle/maven/MavenArtifactDownloader.java @@ -273,7 +273,7 @@ private MavenPom findPom(MavenArtifact artifact) throws IOException { * @throws IOException If an I/O error occurs while installing the artifact */ @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private boolean installArtifact(MavenArtifact artifact, SimpleMavenRepository target) throws IOException { + public boolean installArtifact(MavenArtifact artifact, SimpleMavenRepository target) throws IOException { try(InputStream stream = findArtifactStream(artifact)) { // Try to find the given artifact if(stream != null) { diff --git a/src/main/java/net/flintmc/gradle/maven/pom/MavenArtifact.java b/src/main/java/net/flintmc/gradle/maven/pom/MavenArtifact.java index 427a74a..ec001e0 100644 --- a/src/main/java/net/flintmc/gradle/maven/pom/MavenArtifact.java +++ b/src/main/java/net/flintmc/gradle/maven/pom/MavenArtifact.java @@ -19,9 +19,10 @@ package net.flintmc.gradle.maven.pom; +import java.io.Serializable; import java.util.Objects; -public class MavenArtifact { +public class MavenArtifact implements Serializable { private String groupId; private String artifactId; private String version; diff --git a/src/main/java/net/flintmc/gradle/util/Util.java b/src/main/java/net/flintmc/gradle/util/Util.java index ba0c176..29e1683 100644 --- a/src/main/java/net/flintmc/gradle/util/Util.java +++ b/src/main/java/net/flintmc/gradle/util/Util.java @@ -572,6 +572,36 @@ public static String md5Hex(byte[] data) { } } + /** + * Hashes a given byte array and writes it as a hex string + * + * @param data the data to convert + * @return the sha1 hash as a hex string + */ + public static String sha1Hex(byte[] data) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + byte[] sha1Sum = digest.digest(data); + + // Build a hexadecimal string from the sha1Sum + StringBuilder buffer = new StringBuilder(); + for(byte b : sha1Sum) { + String hex = Integer.toHexString(b & 0xFF); + + if(hex.length() < 2) { + // Insert a 0 if the string is too short + buffer.append('0'); + } + + buffer.append(hex); + } + + return buffer.toString(); + } catch(NoSuchAlgorithmException e) { + throw new IllegalStateException("MD5 digest not available"); + } + } + /** * Zips the {@code input} to a zip file. * From d379aa5e0c5d86591e89c2360552240d659455f0 Mon Sep 17 00:00:00 2001 From: jan Date: Tue, 1 Jun 2021 20:51:22 +0200 Subject: [PATCH 2/2] Resolve threads --- .../net/flintmc/gradle/FlintGradlePlugin.java | 26 ++++++++--- .../cache/MavenArtifactChecksums.java | 2 +- .../tasks/GenerateFlintManifestTask.java | 43 ++++++++++++++----- 3 files changed, 52 insertions(+), 19 deletions(-) diff --git a/src/main/java/net/flintmc/gradle/FlintGradlePlugin.java b/src/main/java/net/flintmc/gradle/FlintGradlePlugin.java index 02806f4..42d66bc 100644 --- a/src/main/java/net/flintmc/gradle/FlintGradlePlugin.java +++ b/src/main/java/net/flintmc/gradle/FlintGradlePlugin.java @@ -19,12 +19,6 @@ package net.flintmc.gradle; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.URI; -import java.nio.file.Path; -import java.util.Collection; -import javax.annotation.Nonnull; import net.flintmc.gradle.environment.DeobfuscationEnvironment; import net.flintmc.gradle.extension.FlintGradleExtension; import net.flintmc.gradle.extension.FlintPatcherExtension; @@ -53,6 +47,13 @@ import org.gradle.api.invocation.Gradle; import org.gradle.api.tasks.Delete; +import javax.annotation.Nonnull; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URI; +import java.nio.file.Path; +import java.util.Collection; + public class FlintGradlePlugin implements Plugin { public static final String MINECRAFT_TASK_GROUP = "minecraft"; @@ -221,7 +222,7 @@ public void onExtensionConfigured() { * Handles the given minecraft version and sets up all of the required steps for using it with gradle. * * @param version The minecraft version to handle - * @param type The environment type. + * @param type The environment type. */ private void handleVersion(String version, EnvironmentType type) { DeobfuscationEnvironment environment = minecraftRepository.defaultEnvironment(version, type); @@ -248,6 +249,12 @@ private void handleVersion(String version, EnvironmentType type) { interaction.setupVersioned(compileArtifacts, runtimeArtifacts, version); } + /** + * Retrieves the singleton instance of the internal flint maven repository. + * Usually at ~/.gradle/caches/flint-gradle/internal-repository. + * + * @return The internal flint maven repository + */ public SimpleMavenRepository getInternalRepository() { return internalRepository; } @@ -313,6 +320,11 @@ public Project getProject() { return project; } + /** + * Retrieves the singleton instance of the maven artifact downloader. + * + * @return The singleton instance of the maven artifact downloader or null if gradle is in offline mode + */ public MavenArtifactDownloader getDownloader() { return this.downloader; } diff --git a/src/main/java/net/flintmc/gradle/manifest/cache/MavenArtifactChecksums.java b/src/main/java/net/flintmc/gradle/manifest/cache/MavenArtifactChecksums.java index 69a025f..960fb68 100644 --- a/src/main/java/net/flintmc/gradle/manifest/cache/MavenArtifactChecksums.java +++ b/src/main/java/net/flintmc/gradle/manifest/cache/MavenArtifactChecksums.java @@ -39,7 +39,7 @@ public static MavenArtifactChecksums load(File file) throws IOException { try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) { return Util.forceCast(in.readObject()); } catch (ClassNotFoundException e) { - throw new IOException("Failed to read cached checksums of static files due to ClassNotFoundException", e); + throw new IOException("Failed to read cached checksums of maven artifacts due to ClassNotFoundException", e); } } diff --git a/src/main/java/net/flintmc/gradle/manifest/tasks/GenerateFlintManifestTask.java b/src/main/java/net/flintmc/gradle/manifest/tasks/GenerateFlintManifestTask.java index bf818f1..2c993b3 100644 --- a/src/main/java/net/flintmc/gradle/manifest/tasks/GenerateFlintManifestTask.java +++ b/src/main/java/net/flintmc/gradle/manifest/tasks/GenerateFlintManifestTask.java @@ -61,8 +61,6 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.concurrent.CopyOnWriteArraySet; import java.util.stream.Collectors; @@ -97,7 +95,6 @@ public class GenerateFlintManifestTask extends DefaultTask { * * @param manifestFile The file to write the generated manifest to * @param staticFiles The static files to index in the manifest - * @param repositoryInput * @param packageDependencies The packages the manifest lists as dependencies * @param artifactURLsCacheFile The file to load the cached artifact URLs from * @param staticFilesChecksumsCacheFile The file to load the cached static checksums from @@ -110,8 +107,7 @@ public GenerateFlintManifestTask( ManifestStaticFileInput staticFiles, ManifestPackageDependencyInput packageDependencies, File artifactURLsCacheFile, - File staticFilesChecksumsCacheFile, - ManifestRepositoryInput repositoryInput + File staticFilesChecksumsCacheFile ) { this.flintGradlePlugin = flintGradlePlugin; this.manifestType = manifestType; @@ -454,7 +450,7 @@ private Set buildMavenInstallInstructions( // Install the artifact including dependencies downloader.installArtifact(artifact, internalRepository); } catch (IOException e) { - throw new RuntimeException("Failed to install execution artifact", e); + throw new FlintGradleException("Failed to install maven artifact", e); } if (setupSource) { @@ -465,11 +461,10 @@ private Set buildMavenInstallInstructions( if (!artifactChecksums.has(artifact)) { try (InputStream inputStream = internalRepository.getArtifactStream(artifact)) { - MessageDigest messageDigest = MessageDigest.getInstance("SHA-1"); - String sha1Hex = Util.sha1Hex(messageDigest.digest(IOUtils.toByteArray(inputStream))); + String sha1Hex = Util.sha1Hex(IOUtils.toByteArray(inputStream)); this.artifactChecksums.add(artifact, sha1Hex); - } catch (IOException | NoSuchAlgorithmException e) { - throw new RuntimeException(e); + } catch (IOException e) { + throw new FlintGradleException("Could not generate checksum of maven artifact", e); } } @@ -517,15 +512,24 @@ private InstallInstructionModel buildOwnInstallInstruction() { } String sha1Hex; + if (manifestType == ManifestType.DISTRIBUTOR) { Jar jar = (Jar) getProject().getTasks().getByName("jar"); File singleFile = jar.getOutputs().getFiles().getSingleFile(); + try (FileInputStream fileInputStream = new FileInputStream(singleFile)) { sha1Hex = Util.sha1Hex(IOUtils.toByteArray(fileInputStream)); } catch (IOException e) { - throw new RuntimeException(e); + throw new FlintGradleException("Could not hash generated jar file", e); } } else { + /* + * No hash is required when generating the jar manifest because + * it does not contain any install instructions anymore. + * + * It would also not be possible to do that because the manifest would have to + * contain the hash of the jar in which it is embedded. + */ sha1Hex = null; } return new InstallInstructionModel( @@ -649,8 +653,25 @@ private Set buildJsonInjectionInstructions() { .collect(Collectors.toSet()); } + /** + * Used to define how to generate the manifest in {@link GenerateFlintManifestTask#generate()}. + * + * @see PackageModel + */ public enum ManifestType { + /** + * When generating the manifest that will be written into the final jar file it will contain no install instructions. + * + * @see PackageModel#getInstallInstructions() () + */ JAR, + + /** + * When generating the manifest that will be published separately to the distributor it will contain no information + * about the runtime classpath. + * + * @see PackageModel#getRuntimeClasspath() + */ DISTRIBUTOR }