From 12c8d9224b26143d371204b4c7ac542ec6c18de9 Mon Sep 17 00:00:00 2001 From: covers1624 Date: Thu, 23 Jan 2025 17:50:49 +1030 Subject: [PATCH] Copy published data files prior to publishing. --- .../internal/DataFileCollections.java | 27 +++++- .../moddevgradle/tasks/CopyDataFile.java | 37 ++++++++ .../DataFileCollectionFunctionalTest.java | 86 +++++++++++++++++++ 3 files changed, 148 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/neoforged/moddevgradle/tasks/CopyDataFile.java diff --git a/src/main/java/net/neoforged/moddevgradle/internal/DataFileCollections.java b/src/main/java/net/neoforged/moddevgradle/internal/DataFileCollections.java index b935b2f..55bff8d 100644 --- a/src/main/java/net/neoforged/moddevgradle/internal/DataFileCollections.java +++ b/src/main/java/net/neoforged/moddevgradle/internal/DataFileCollections.java @@ -4,6 +4,8 @@ import java.util.function.Consumer; import net.neoforged.moddevgradle.dsl.DataFileCollection; import net.neoforged.moddevgradle.internal.utils.ExtensionUtils; +import net.neoforged.moddevgradle.internal.utils.StringUtils; +import net.neoforged.moddevgradle.tasks.CopyDataFile; import org.gradle.api.Project; import org.gradle.api.artifacts.ConfigurablePublishArtifact; import org.gradle.api.artifacts.Configuration; @@ -93,6 +95,9 @@ private static CollectionWrapper createCollection(Project project, String name, } }); + var copyTaskName = "copy" + StringUtils.capitalize(name) + "DataPublications"; + var copyTask = project.getTasks().register(copyTaskName, CopyDataFile.class); + var depFactory = project.getDependencyFactory(); Consumer publishCallback = new Consumer<>() { ConfigurablePublishArtifact firstArtifact; @@ -100,8 +105,26 @@ private static CollectionWrapper createCollection(Project project, String name, @Override public void accept(Object artifactNotation) { - elementsConfiguration.getDependencies().add(depFactory.create(project.files(artifactNotation))); - project.getArtifacts().add(elementsConfiguration.getName(), artifactNotation, artifact -> { + // We copy the provided artifact somewhere in the build directory in order to avoid + // plugins which generate companion files (gpg 'signing'), from generating these inside the user's source directories. + + // We have to resolve this as a collection because `project.files` doesn't support resolving the outputs of a task. + var inputFiles = project.files(artifactNotation); + if (inputFiles.isEmpty()) return; // what? + if (inputFiles.getFiles().size() > 1) { + throw new RuntimeException("More than one file specified. Not supported by the publishing api."); + } + var copyInput = inputFiles.iterator().next(); + var copyOutput = project.getLayout().getBuildDirectory().file(copyTaskName + "/" + artifactCount + "/" + copyInput.getName()); + copyTask.configure(t -> { + t.getRawInputs().getFrom().add(inputFiles); + t.getInputFiles().add(project.getLayout().file(project.provider(() -> copyInput))); + t.getOutputFiles().add(copyOutput); + }); + + elementsConfiguration.getDependencies().add(depFactory.create(project.files(copyOutput))); + project.getArtifacts().add(elementsConfiguration.getName(), copyOutput, artifact -> { + artifact.builtBy(copyTask); if (firstArtifact == null) { firstArtifact = artifact; artifact.setClassifier(category); diff --git a/src/main/java/net/neoforged/moddevgradle/tasks/CopyDataFile.java b/src/main/java/net/neoforged/moddevgradle/tasks/CopyDataFile.java new file mode 100644 index 0000000..dc7bb56 --- /dev/null +++ b/src/main/java/net/neoforged/moddevgradle/tasks/CopyDataFile.java @@ -0,0 +1,37 @@ +package net.neoforged.moddevgradle.tasks; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.RegularFile; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.OutputFiles; +import org.gradle.api.tasks.TaskAction; + +public abstract class CopyDataFile extends DefaultTask { + @InputFiles // Exists to implicitly declare task dependencies. + public abstract ConfigurableFileCollection getRawInputs(); + + @InputFiles + public abstract ListProperty getInputFiles(); + + @OutputFiles + public abstract ListProperty getOutputFiles(); + + @TaskAction + public void doCopy() throws IOException { + var inputs = getInputFiles().get(); + var outputs = getOutputFiles().get(); + if (inputs.size() != outputs.size()) throw new RuntimeException("Lists length dont match."); + + for (int i = 0; i < inputs.size(); i++) { + var in = inputs.get(i).getAsFile().toPath(); + var out = outputs.get(i).getAsFile().toPath(); + Files.createDirectories(out.getParent()); + Files.copy(in, out, StandardCopyOption.REPLACE_EXISTING); + } + } +} diff --git a/src/test/java/net/neoforged/moddevgradle/functional/DataFileCollectionFunctionalTest.java b/src/test/java/net/neoforged/moddevgradle/functional/DataFileCollectionFunctionalTest.java index 7d69ceb..65cb228 100644 --- a/src/test/java/net/neoforged/moddevgradle/functional/DataFileCollectionFunctionalTest.java +++ b/src/test/java/net/neoforged/moddevgradle/functional/DataFileCollectionFunctionalTest.java @@ -37,6 +37,90 @@ public void testPublishAccessTransformerFile() throws IOException { entry("publish-at-1.0-accesstransformer.cfg", "# hello world")); } + @Test + public void testPublishSignedAccessTransformerFileDoesntProduceGarbageNextToTheFile() throws IOException { + var atFile = testProjectDir.toPath().resolve("accesstransformer.cfg"); + Files.writeString(atFile, "# hello world"); + + publishDataFiles("test", "publish-at", "1.0", """ + neoForge { + accessTransformers { + publish(project.file("accesstransformer.cfg")) + } + } + signing { + useInMemoryPgpKeys( + ""\" + -----BEGIN PGP PRIVATE KEY BLOCK----- + Version: Keybase OpenPGP v1.0.0 + Comment: https://keybase.io/crypto + + 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 + Zg== + =QLGC + -----END PGP PRIVATE KEY BLOCK----- + ""\", + "password" + ) + sign publishing.publications.maven + } + """); + + assertThat(Files.exists(atFile.resolveSibling(atFile.getFileName() + ".asc"))) + .isFalse(); + assertThat(consumeDataFilePublication("accessTransformers", "test:publish-at:1.0")).containsOnly( + entry("publish-at-1.0-accesstransformer.cfg", "# hello world")); + } + @Test public void testPublishInterfaceInjectionFile() throws IOException { writeProjectFile("interfaces.json", "[]"); @@ -133,10 +217,12 @@ private void publishDataFiles(String groupId, } rootProject.name = "{0}" """, artifactId); + // TODO we should probably not just always apply the signing plugin? writeGroovyBuildScript(""" plugins { id "net.neoforged.moddev" id "maven-publish" + id "signing" } group = "{0}" version = "{1}"