Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.

Fix for PropertiesFileTransformer breaks Reproducible builds in 8.1.1 #102

Merged
merged 6 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
/*
* Source https://stackoverflow.com/a/39043903/519333
*/
package com.github.jengelman.gradle.plugins.shadow.internal

class CleanProperties extends Properties {
private static class StripFirstLineStream extends FilterOutputStream {

private boolean firstLineSeen = false
private static class StripCommentsWithTimestampBufferedWriter extends BufferedWriter {

StripFirstLineStream(final OutputStream out) {
private final int lengthOfExpectedTimestamp

StripCommentsWithTimestampBufferedWriter(final Writer out) {
super(out)

lengthOfExpectedTimestamp = ("#" + new Date().toString()).length()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Date is related to your locale, how can we make sure the device locale is matched with the jar locale?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of this is to just remove the comment lines which include the timestamp, which is generated when the properties file is written, so I assume locale is the same. Timestamp is not coming from other jar files. Either way I agree is a bit tricky.

Another option could be try to parse the date to see if it's a date or not, or maybe keep it simple and strip all comment lines.

Probably these changes are a mitigation as the real solution should come from the component writing the properties file, but anyway is more solid than the previous one which was removing only the first line.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Timestamp is not coming from other jar files.

In this case, seems the fix is solid.

Another option could be try to parse the date to see if it's a date or not

In this case, seems we still have to determine the real locale is.

}

@Override
void write(final int b) throws IOException {
if (firstLineSeen) {
super.write(b)
} else if (b == '\n') {
super.write(b)

firstLineSeen = true
void write(final String str) throws IOException {
if (couldBeCommentWithTimestamp(str)) {
return
}
super.write(str)
}

private boolean couldBeCommentWithTimestamp(final String str) {
return str != null &&
str.startsWith("#") &&
str.length() == lengthOfExpectedTimestamp
}
}

@Override
void store(final OutputStream out, final String comments) throws IOException {
super.store(new StripFirstLineStream(out), null)
void store(final Writer writer, final String comments) throws IOException {
super.store(new StripCommentsWithTimestampBufferedWriter(writer), comments)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,42 @@ final class PropertiesFileTransformerTest extends TransformerTestSupport {
void testTransformation() {
transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections.<Relocator>emptyList(), new ShadowStats()))

def testableZipFile = doTransformAndGetTransformedFile(transformer, false)
def targetLines = readFrom(testableZipFile, MANIFEST_NAME)

assertFalse(targetLines.isEmpty())

assertTrue(targetLines.contains("Manifest-Version=1.0"))
}

@Test
void testTransformationPropertiesAreReproducible() {
transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections.<Relocator>emptyList(), new ShadowStats()))

def firstRunTransformedFile = doTransformAndGetTransformedFile(transformer, true)
def firstRunTargetLines = readFrom(firstRunTransformedFile, MANIFEST_NAME)

Thread.sleep(1000) // wait for 1sec to ensure timestamps in properties would change

def secondRunTransformedFile = doTransformAndGetTransformedFile(transformer, true)
def secondRunTargetLines = readFrom(secondRunTransformedFile, MANIFEST_NAME)

assertEquals(firstRunTargetLines, secondRunTargetLines)
}

static File doTransformAndGetTransformedFile(final PropertiesFileTransformer transformer, final boolean preserveFileTimestamps) {
def testableZipFile = File.createTempFile("testable-zip-file-", ".jar")
def fileOutputStream = new FileOutputStream(testableZipFile)
def bufferedOutputStream = new BufferedOutputStream(fileOutputStream)
def zipOutputStream = new ZipOutputStream(bufferedOutputStream)

try {
transformer.modifyOutputStream(zipOutputStream, false)
transformer.modifyOutputStream(zipOutputStream, preserveFileTimestamps)
} finally {
zipOutputStream.close()
}
def targetLines = readFrom(testableZipFile, MANIFEST_NAME)

assertFalse(targetLines.isEmpty())

assertTrue(targetLines.contains("Manifest-Version=1.0"))
return testableZipFile
}

static List<String> readFrom(File jarFile, String resourceName) {
Expand Down