Skip to content

Commit 98940db

Browse files
committed
add configureReproducible( Date lastModifiedDate ) API
this closes #121
1 parent e42b62e commit 98940db

File tree

6 files changed

+98
-0
lines changed

6 files changed

+98
-0
lines changed

src/main/java/org/codehaus/plexus/archiver/AbstractArchiver.java

+41
Original file line numberDiff line numberDiff line change
@@ -1257,4 +1257,45 @@ public String getOverrideGroupName()
12571257
{
12581258
return overrideGroupName;
12591259
}
1260+
1261+
@Override
1262+
public void configureReproducible( Date lastModifiedDate )
1263+
{
1264+
// 1. force last modified date
1265+
setLastModifiedDate( normalizeLastModifiedDate( lastModifiedDate ) );
1266+
1267+
// 2. sort filenames in each directory when scanning filesystem
1268+
setFilenameComparator( new Comparator<String>()
1269+
{
1270+
@Override
1271+
public int compare( String s1, String s2 )
1272+
{
1273+
return s1.compareTo( s2 );
1274+
}
1275+
} );
1276+
1277+
// 3. ignore file/directory mode from filesystem, since they may vary based on local user umask
1278+
// notice: this overrides execute bit on Unix (that is already ignored on Windows)
1279+
setFileMode( Archiver.DEFAULT_FILE_MODE );
1280+
setDirectoryMode( Archiver.DEFAULT_DIR_MODE );
1281+
1282+
// 4. ignore uid/gid from filesystem (for tar)
1283+
setOverrideUid( 0 );
1284+
setOverrideUserName( "root" ); // is it possible to avoid this, like "tar --numeric-owner"?
1285+
setOverrideGid( 0 );
1286+
setOverrideGroupName( "root" );
1287+
}
1288+
1289+
/**
1290+
* Normalize last modified time value to get reproducible archive entries, based on
1291+
* archive binary format (tar uses UTC timestamp but zip uses local time then requires
1292+
* tweaks to make the value reproducible whatever the current timezone is).
1293+
*
1294+
* @param lastModifiedDate
1295+
* @return
1296+
*/
1297+
protected Date normalizeLastModifiedDate( Date lastModifiedDate )
1298+
{
1299+
return lastModifiedDate;
1300+
}
12601301
}

src/main/java/org/codehaus/plexus/archiver/Archiver.java

+17
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,9 @@ ResourceIterator getResources()
415415
Date getLastModifiedDate();
416416

417417
/**
418+
* Set filename comparator, used to sort file entries when scanning directories since File.list() does not
419+
* guarantee any order.
420+
*
418421
* @since 4.2.0
419422
*/
420423
void setFilenameComparator( Comparator<String> filenameComparator );
@@ -458,4 +461,18 @@ ResourceIterator getResources()
458461
* @since 4.2.0
459462
*/
460463
String getOverrideGroupName();
464+
465+
/**
466+
* Configure the archiver to create archives in a reproducible way (see <a
467+
* href="https://reproducible-builds.org/>Reproducible Builds</a>). This will configure:
468+
* <ul>
469+
* <li>reproducible archive entries order,</li>
470+
* <li>defined entries timestamp</li>
471+
* <li>and reproducible entries Unix mode.</li>
472+
* <ul>
473+
*
474+
* @param lastModifiedDate the date to use for archive entries last modified time
475+
* @since 4.2.0
476+
*/
477+
void configureReproducible( Date lastModifiedDate );
461478
}

src/main/java/org/codehaus/plexus/archiver/diags/DelgatingArchiver.java

+7
Original file line numberDiff line numberDiff line change
@@ -398,4 +398,11 @@ public String getOverrideGroupName()
398398
{
399399
return target.getOverrideGroupName();
400400
}
401+
402+
@Override
403+
public void configureReproducible( Date lastModifiedDate )
404+
{
405+
target.configureReproducible( lastModifiedDate );
406+
}
407+
401408
}

src/main/java/org/codehaus/plexus/archiver/diags/NoOpArchiver.java

+6
Original file line numberDiff line numberDiff line change
@@ -414,4 +414,10 @@ public String getOverrideGroupName()
414414
return null;
415415
}
416416

417+
@Override
418+
public void configureReproducible( Date lastModifiedDate )
419+
{
420+
421+
}
422+
417423
}

src/main/java/org/codehaus/plexus/archiver/diags/TrackingArchiver.java

+5
Original file line numberDiff line numberDiff line change
@@ -468,4 +468,9 @@ public String getOverrideGroupName()
468468
{
469469
return null;
470470
}
471+
472+
@Override
473+
public void configureReproducible( Date lastModifiedDate )
474+
{
475+
}
471476
}

src/main/java/org/codehaus/plexus/archiver/zip/AbstractZipArchiver.java

+22
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import java.io.OutputStream;
2525
import java.nio.ByteBuffer;
2626
import java.nio.charset.Charset;
27+
import java.util.Calendar;
28+
import java.util.Date;
2729
import java.util.Hashtable;
2830
import java.util.Stack;
2931
import java.util.concurrent.ExecutionException;
@@ -833,4 +835,24 @@ protected String getArchiveType()
833835
return archiveType;
834836
}
835837

838+
@Override
839+
protected Date normalizeLastModifiedDate( Date lastModifiedDate )
840+
{
841+
// timestamp of zip entries at zip storage level ignores timezone: managed in ZipEntry.setTime,
842+
// that turns javaToDosTime: need to revert the operation here to get reproducible
843+
// zip entry time
844+
return new Date( dosToJavaTime( lastModifiedDate.getTime() ) );
845+
}
846+
847+
/**
848+
* Converts DOS time to Java time (number of milliseconds since epoch).
849+
*
850+
* @see java.util.zip.ZipEntry#setTime
851+
* @see java.util.zip.ZipUtils#dosToJavaTime
852+
*/
853+
private static long dosToJavaTime( long dosTime )
854+
{
855+
Calendar cal = Calendar.getInstance();
856+
return dosTime - ( cal.get( Calendar.ZONE_OFFSET ) + cal.get( Calendar.DST_OFFSET ) );
857+
}
836858
}

0 commit comments

Comments
 (0)