Skip to content

Commit

Permalink
Introduce CleanProperties
Browse files Browse the repository at this point in the history
PropertiesFileTransformer uses `java.utils.Properties` internally as a storage.

`java.utils.Properties` `store0()` contains `bw.write("#" + new Date().toString());` that prepends current timestamp before any content (after comments).

 This effectively breaks reproducible builds that use PropertiesFileTransformer because every new build has different timestamp in transformed files.

 CleanProperties implementation is introduced in order to remove prepended timestamp when creating output stream.
  • Loading branch information
simPod committed Feb 15, 2023
1 parent d8117e9 commit e46bd20
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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

StripFirstLineStream(final OutputStream out) {
super(out)
}

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

firstLineSeen = true;
}
}

}

@Override
void store(final OutputStream out, final String comments) throws IOException {
super.store(new StripFirstLineStream(out), null)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package com.github.jengelman.gradle.plugins.shadow.transformers

import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties
import org.apache.tools.zip.ZipEntry
import org.apache.tools.zip.ZipOutputStream
import org.codehaus.plexus.util.IOUtil
Expand Down Expand Up @@ -117,7 +118,7 @@ import static groovy.lang.Closure.IDENTITY
class PropertiesFileTransformer implements Transformer {
private static final String PROPERTIES_SUFFIX = '.properties'

private Map<String, Properties> propertiesEntries = [:]
private Map<String, CleanProperties> propertiesEntries = [:]

@Input
List<String> paths = []
Expand Down Expand Up @@ -179,15 +180,17 @@ class PropertiesFileTransformer implements Transformer {
}

private Properties loadAndTransformKeys(InputStream is) {
Properties props = new Properties()
props.load(is)
Properties props = new CleanProperties()
if (is != null) {
props.load(is)
}
return transformKeys(props)
}

private Properties transformKeys(Properties properties) {
if (keyTransformer == IDENTITY)
return properties
def result = new Properties()
def result = new CleanProperties()
properties.each { key, value ->
result.put(keyTransformer.call(key), value)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package com.github.jengelman.gradle.plugins.shadow.transformers

import com.github.jengelman.gradle.plugins.shadow.ShadowStats
import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator
import org.apache.tools.zip.ZipOutputStream
import org.junit.Before
import org.junit.Test

import java.util.zip.ZipFile

import static java.util.Arrays.asList
import static org.junit.Assert.*

/**
* Test for {@link PropertiesFileTransformer}.
*/
final class PropertiesFileTransformerTest extends TransformerTestSupport {
static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"

private PropertiesFileTransformer transformer

@Before
void setUp() {
transformer = new PropertiesFileTransformer()
}

@Test
void testHasTransformedResource() {
transformer.transform(new TransformerContext(MANIFEST_NAME))

assertTrue(transformer.hasTransformedResource())
}

@Test
void testHasNotTransformedResource() {
assertFalse(transformer.hasTransformedResource())
}

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

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)
} finally {
zipOutputStream.close()
}
def targetLines = readFrom(testableZipFile, MANIFEST_NAME)

assertFalse(targetLines.isEmpty())

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

static List<String> readFrom(File jarFile, String resourceName) {
def zip = new ZipFile(jarFile)
try {
def entry = zip.getEntry(resourceName)
if (!entry) {
return Collections.emptyList()
}
return zip.getInputStream(entry).readLines()
} finally {
zip.close()
}
}

InputStream getResourceStream(String resource) {
this.class.classLoader.getResourceAsStream(resource)
}
}

0 comments on commit e46bd20

Please sign in to comment.