From 62cd0e971310bf53b317872d15ea00f32beedacf Mon Sep 17 00:00:00 2001 From: Eric Haag Date: Mon, 8 Jul 2024 13:33:36 -0500 Subject: [PATCH 1/2] Remove volatility from schema manifest file The schema paths written to the schema manifest are full absolute paths. This introduces volatility in the build, makes it non-reproducible and non-relocatable, since the full absolute path to a schema will vary between machines, e.g., between a local user and a CI machine. An additional source of volatility is the timestamp written to the file which will vary between every invocation even on the same machine. With this change, the schema paths written to the schema manifest are relativized to the base project directory. The relative path to a schema is all that's required locate it and will always be consistent between machines. Additionally, the java-ordered-properties library is used to ensure the properties file is always written in a deterministic order and removes the unnecessary timestamp. --- pom.xml | 5 ++ .../deweyjose/graphqlcodegen/Codegen.java | 2 +- .../graphqlcodegen/SchemaFileManifest.java | 51 +++++++++++-------- .../SchemaFileManifestTest.java | 14 ++--- 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/pom.xml b/pom.xml index 982ee30..abe259f 100644 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,11 @@ logback-classic 1.5.6 + + nu.studer + java-ordered-properties + 1.0.4 + org.junit.jupiter junit-jupiter-engine diff --git a/src/main/java/io/github/deweyjose/graphqlcodegen/Codegen.java b/src/main/java/io/github/deweyjose/graphqlcodegen/Codegen.java index 3eba6f4..828f402 100644 --- a/src/main/java/io/github/deweyjose/graphqlcodegen/Codegen.java +++ b/src/main/java/io/github/deweyjose/graphqlcodegen/Codegen.java @@ -209,7 +209,7 @@ public void execute() { Set schemaPaths = getSchemaPaths(); - SchemaFileManifest manifest = new SchemaFileManifest(new File(outputDir, "schema-manifest.props")); + SchemaFileManifest manifest = new SchemaFileManifest(new File(outputDir, "schema-manifest.props"), project.getBasedir()); if (onlyGenerateChanged) { manifest.setFiles(new HashSet<>(schemaPaths)); diff --git a/src/main/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifest.java b/src/main/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifest.java index a7ae9f3..5edd931 100644 --- a/src/main/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifest.java +++ b/src/main/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifest.java @@ -2,6 +2,8 @@ import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import nu.studer.java.util.OrderedProperties; +import nu.studer.java.util.OrderedProperties.OrderedPropertiesBuilder; import java.io.File; import java.io.FileInputStream; @@ -11,14 +13,13 @@ import java.nio.file.Paths; import java.security.MessageDigest; import java.util.HashSet; -import java.util.Properties; import java.util.Set; @Slf4j public class SchemaFileManifest { private Set files; - private final Properties manifest; private final File manifestPath; + private final File projectPath; /** * Manifest constructor loads the properties file into memory. @@ -27,27 +28,17 @@ public class SchemaFileManifest { * * @param files * @param manifestPath + * @param projectPath */ - public SchemaFileManifest(Set files, File manifestPath) { + public SchemaFileManifest(Set files, File manifestPath, File projectPath) { this.files = files; this.manifestPath = manifestPath; - manifest = loadManifest(manifestPath); + this.projectPath = projectPath; } - public SchemaFileManifest(File manifestPath) { + public SchemaFileManifest(File manifestPath, File projectPath) { this.manifestPath = manifestPath; - manifest = loadManifest(manifestPath); - } - - @SneakyThrows - public static Properties loadManifest(File manifestPath) { - Properties properties = new Properties(); - if (manifestPath.exists()) { - try (FileInputStream fis = new FileInputStream(manifestPath)) { - properties.load(fis); - } - } - return properties; + this.projectPath = projectPath; } @SneakyThrows @@ -105,8 +96,9 @@ public void setFiles(Set files) { */ public Set getChangedFiles() { Set changed = new HashSet<>(); + OrderedProperties manifest = loadManifest(); for (File file : files) { - String oldChecksum = manifest.getProperty(file.getPath()); + String oldChecksum = manifest.getProperty(relativizeToProject(file)); if (oldChecksum == null) { log.info("{} is new, will generate code", file.getName()); } else if (!oldChecksum.equals(generateChecksum(file))) { @@ -126,9 +118,11 @@ public Set getChangedFiles() { */ @SneakyThrows public void syncManifest() { - manifest.clear(); + OrderedProperties manifest = new OrderedPropertiesBuilder() + .withSuppressDateInComment(true) + .build(); for (File file : files) { - manifest.put(file.getPath(), generateChecksum(file)); + manifest.setProperty(relativizeToProject(file), generateChecksum(file)); } try (FileOutputStream fos = new FileOutputStream(manifestPath)) { @@ -136,4 +130,21 @@ public void syncManifest() { fos.flush(); } } + + @SneakyThrows + private OrderedProperties loadManifest() { + OrderedProperties properties = new OrderedPropertiesBuilder() + .withSuppressDateInComment(true) + .build(); + if (manifestPath.exists()) { + try (FileInputStream fis = new FileInputStream(manifestPath)) { + properties.load(fis); + } + } + return properties; + } + + private String relativizeToProject(File file) { + return projectPath.toPath().relativize(file.toPath()).toString(); + } } diff --git a/src/test/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifestTest.java b/src/test/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifestTest.java index 50cc766..9ce6796 100644 --- a/src/test/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifestTest.java +++ b/src/test/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifestTest.java @@ -52,21 +52,21 @@ void testManifestNoChange() { File foo = getFile("schema/foo.graphqls"); Properties properties = new Properties(); - properties.put(bar.getPath(), "7cada13b5b8770e46f7a69e8856abdb9"); - properties.put(foo.getPath(), "61bbd2d58c22dfb3c664829ad116f7e9"); + properties.put(tempFolder.relativize(bar.toPath()).toString(), "7cada13b5b8770e46f7a69e8856abdb9"); + properties.put(tempFolder.relativize(foo.toPath()).toString(), "61bbd2d58c22dfb3c664829ad116f7e9"); File manifest = tempFolder.resolve("manifest.props").toFile(); try (FileOutputStream fis = new FileOutputStream(manifest)) { properties.store(fis, "Schema Manifest"); } - SchemaFileManifest sfm = new SchemaFileManifest(new HashSet<>(Arrays.asList(foo, bar)), manifest); + SchemaFileManifest sfm = new SchemaFileManifest(new HashSet<>(Arrays.asList(foo, bar)), manifest, tempFolder.toFile()); Assertions.assertTrue(sfm.getChangedFiles().isEmpty()); sfm.syncManifest(); - sfm = new SchemaFileManifest(new HashSet<>(Arrays.asList(foo, bar)), manifest); + sfm = new SchemaFileManifest(new HashSet<>(Arrays.asList(foo, bar)), manifest, tempFolder.toFile()); Assertions.assertTrue(sfm.getChangedFiles().isEmpty()); } @@ -79,18 +79,18 @@ void testManifestRequiresChange() { File foo = getFile("schema/foo.graphqls"); File manifest = tempFolder.resolve("manifest.props").toFile(); - SchemaFileManifest sfm = new SchemaFileManifest(new HashSet<>(Arrays.asList(foo, bar)), manifest); + SchemaFileManifest sfm = new SchemaFileManifest(new HashSet<>(Arrays.asList(foo, bar)), manifest, tempFolder.toFile()); Assertions.assertTrue(sfm.getChangedFiles().contains(foo)); sfm.syncManifest(); Assertions.assertTrue(sfm.getChangedFiles().isEmpty()); - sfm = new SchemaFileManifest(new HashSet<>(Arrays.asList(foo, bar)), manifest); + sfm = new SchemaFileManifest(new HashSet<>(Arrays.asList(foo, bar)), manifest, tempFolder.toFile()); Assertions.assertTrue(sfm.getChangedFiles().isEmpty()); sfm.syncManifest(); - sfm = new SchemaFileManifest(new HashSet<>(Arrays.asList(foo, bar)), manifest); + sfm = new SchemaFileManifest(new HashSet<>(Arrays.asList(foo, bar)), manifest, tempFolder.toFile()); Assertions.assertTrue(sfm.getChangedFiles().isEmpty()); } From ca4d922474f07fc9ca38bd47d2b75ff145c1634d Mon Sep 17 00:00:00 2001 From: Eric Haag Date: Tue, 9 Jul 2024 14:42:49 -0500 Subject: [PATCH 2/2] Do not write schema-manifest.props file to sources directory --- README.md | 18 +++++++++++++++--- .../deweyjose/graphqlcodegen/Codegen.java | 5 ++++- .../graphqlcodegen/SchemaFileManifest.java | 4 ++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5ce8204..201a37a 100644 --- a/README.md +++ b/README.md @@ -300,7 +300,7 @@ Example - Type: string - Required: false -- Default: `${project.basedir}/target/generated-sources` +- Default: `${project.build.directory}/generated-sources` Example: @@ -312,12 +312,24 @@ Example: - Type: string - Required: false -- Default: `${project.basedir}/target/generated-examples` +- Default: `${project.build.directory}/generated-examples` Example: ```xml -${project.build.directory}/generated-examples +${project.build.directory}/generated-examples +``` + +## schemaManifestOutputDir + +- Type: string +- Required: false +- Default: `${project.build.directory}/graphqlcodegen` + +Example: + +```xml +${project.build.directory}/graphqlcodegen ``` ## includeQueries diff --git a/src/main/java/io/github/deweyjose/graphqlcodegen/Codegen.java b/src/main/java/io/github/deweyjose/graphqlcodegen/Codegen.java index 828f402..706be6b 100644 --- a/src/main/java/io/github/deweyjose/graphqlcodegen/Codegen.java +++ b/src/main/java/io/github/deweyjose/graphqlcodegen/Codegen.java @@ -95,6 +95,9 @@ public class Codegen extends AbstractMojo { @Parameter(property = "exampleOutputDir", defaultValue = "${project.build.directory}/generated-examples") private File exampleOutputDir; + @Parameter(property = "schemaManifestOutputDir", defaultValue = "${project.build.directory}/graphqlcodegen") + private File schemaManifestOutputDir; + @Parameter(property = "includeQueries") private String[] includeQueries; @@ -209,7 +212,7 @@ public void execute() { Set schemaPaths = getSchemaPaths(); - SchemaFileManifest manifest = new SchemaFileManifest(new File(outputDir, "schema-manifest.props"), project.getBasedir()); + SchemaFileManifest manifest = new SchemaFileManifest(new File(schemaManifestOutputDir, "schema-manifest.props"), project.getBasedir()); if (onlyGenerateChanged) { manifest.setFiles(new HashSet<>(schemaPaths)); diff --git a/src/main/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifest.java b/src/main/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifest.java index 5edd931..fd276fb 100644 --- a/src/main/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifest.java +++ b/src/main/java/io/github/deweyjose/graphqlcodegen/SchemaFileManifest.java @@ -125,6 +125,10 @@ public void syncManifest() { manifest.setProperty(relativizeToProject(file), generateChecksum(file)); } + if (!manifestPath.exists()) { + manifestPath.getParentFile().mkdirs(); + } + try (FileOutputStream fos = new FileOutputStream(manifestPath)) { manifest.store(fos, "Schema Manifest"); fos.flush();