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..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,16 @@ 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; + } + /** * Retrieves the client artifact for the given version. * @@ -308,4 +319,13 @@ public MavenArtifactURLCache getMavenArtifactURLCache() { 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/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..960fb68 --- /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 maven artifacts 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..2c993b3 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,13 +47,17 @@ 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; @@ -59,6 +69,10 @@ * Task generating the flint manifest.json */ public class GenerateFlintManifestTask extends DefaultTask { + + private FlintGradlePlugin flintGradlePlugin; + private ManifestType manifestType; + @OutputFile private final File manifestFile; @@ -74,6 +88,7 @@ public class GenerateFlintManifestTask extends DefaultTask { private final File staticFilesChecksumsCacheFile; private FlintGradleExtension extension; + private MavenArtifactChecksums artifactChecksums; /** * Constructs a new {@link GenerateFlintManifestTask}. @@ -86,12 +101,16 @@ public class GenerateFlintManifestTask extends DefaultTask { */ @Inject public GenerateFlintManifestTask( + FlintGradlePlugin flintGradlePlugin, + ManifestType manifestType, File manifestFile, ManifestStaticFileInput staticFiles, ManifestPackageDependencyInput packageDependencies, File artifactURLsCacheFile, File staticFilesChecksumsCacheFile ) { + this.flintGradlePlugin = flintGradlePlugin; + this.manifestType = manifestType; this.manifestFile = manifestFile; this.staticFiles = staticFiles; this.packageDependencies = packageDependencies; @@ -280,6 +299,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 +329,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 +390,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 +422,52 @@ 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 FlintGradleException("Failed to install maven artifact", e); + } + + if (setupSource) { + // We added the source, clean up afterwards + downloader.removeSource(remoteMavenRepository); + } + } + + if (!artifactChecksums.has(artifact)) { + try (InputStream inputStream = internalRepository.getArtifactStream(artifact)) { + String sha1Hex = Util.sha1Hex(IOUtils.toByteArray(inputStream)); + this.artifactChecksums.add(artifact, sha1Hex); + } catch (IOException e) { + throw new FlintGradleException("Could not generate checksum of maven artifact", e); + } + } + // Add the instruction out.add(new InstallInstructionModel( InstallInstructionTypes.DOWNLOAD_MAVEN_DEPENDENCY, @@ -389,7 +478,8 @@ private Set buildMavenInstallInstructions( artifact.getVersion(), artifact.getClassifier(), entry.getValue().toASCIIString(), - localPath + localPath, + artifactChecksums.get(artifact) ) )); } @@ -421,6 +511,27 @@ 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 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( InstallInstructionTypes.DOWNLOAD_MAVEN_DEPENDENCY, null, @@ -430,9 +541,12 @@ private InstallInstructionModel buildOwnInstallInstruction() { getProjectVersion(), null, "${FLINT_DISTRIBUTOR_URL}" + getChannel(), - targetPath + targetPath, + sha1Hex ) ); + + } /** @@ -538,4 +652,27 @@ private Set buildJsonInjectionInstructions() { .map(model -> new InstallInstructionModel(InstallInstructionTypes.MODIFY_JSON_FILE, OperatingSystem.UNKNOWN, model)) .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 + } + } 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. *