diff --git a/core/src/main/java/pl/project13/core/cibuild/BuildServerDataProvider.java b/core/src/main/java/pl/project13/core/cibuild/BuildServerDataProvider.java index 89038e8d..c39f8927 100644 --- a/core/src/main/java/pl/project13/core/cibuild/BuildServerDataProvider.java +++ b/core/src/main/java/pl/project13/core/cibuild/BuildServerDataProvider.java @@ -23,6 +23,7 @@ import pl.project13.core.util.PropertyManager; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.net.InetAddress; import java.net.UnknownHostException; import java.text.SimpleDateFormat; @@ -112,8 +113,8 @@ public static BuildServerDataProvider getBuildServerProvider(@Nonnull Map buildTimeSupplier = () -> { - Date buildDate = new Date(); + Date buildDate = (reproducibleBuildOutputTimestamp == null) ? new Date() : reproducibleBuildOutputTimestamp; SimpleDateFormat smf = new SimpleDateFormat(dateFormat); if (dateFormatTimeZone != null) { smf.setTimeZone(TimeZone.getTimeZone(dateFormatTimeZone)); diff --git a/maven/src/main/java/pl/project13/maven/git/GitCommitIdMojo.java b/maven/src/main/java/pl/project13/maven/git/GitCommitIdMojo.java index de97f1ac..86bee083 100644 --- a/maven/src/main/java/pl/project13/maven/git/GitCommitIdMojo.java +++ b/maven/src/main/java/pl/project13/maven/git/GitCommitIdMojo.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.text.DateFormat; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import java.util.function.Supplier; @@ -372,6 +374,18 @@ public class GitCommitIdMojo extends AbstractMojo { @Parameter(defaultValue = "false") boolean offline; + /** + * Timestamp for reproducible output archive entries + * (https://maven.apache.org/guides/mini/guide-reproducible-builds.html). + * The value from ${project.build.outputTimestamp} is either formatted as ISO 8601 + * yyyy-MM-dd'T'HH:mm:ssXXX or as an int representing seconds since the epoch (like + * SOURCE_DATE_EPOCH. + * + * @since 4.0.2 + */ + @Parameter(defaultValue = "${project.build.outputTimestamp}") + private String projectBuildOutputTimestamp; + /** * Injected {@link BuildContext} to recognize incremental builds. */ @@ -548,7 +562,7 @@ private Properties getContextProperties(MavenProject project) { return null; } - private void loadBuildData(Properties properties) { + private void loadBuildData(Properties properties) throws GitCommitIdExecutionException { Map> additionalProperties = Collections.singletonMap( GitCommitPropertyConstant.BUILD_VERSION, () -> project.getVersion()); BuildServerDataProvider buildServerDataProvider = BuildServerDataProvider.getBuildServerProvider(System.getenv(),log); @@ -560,7 +574,40 @@ private void loadBuildData(Properties properties) { .setExcludeProperties(excludeProperties) .setIncludeOnlyProperties(includeOnlyProperties); - buildServerDataProvider.loadBuildData(properties); + buildServerDataProvider.loadBuildData(properties, parseOutputTimestamp(projectBuildOutputTimestamp)); + } + + /** + * Parse output timestamp configured for Reproducible Builds' archive entries + * (https://maven.apache.org/guides/mini/guide-reproducible-builds.html). + * The value from ${project.build.outputTimestamp} is either formatted as ISO 8601 + * yyyy-MM-dd'T'HH:mm:ssXXX or as an int representing seconds since the epoch (like + * SOURCE_DATE_EPOCH. + * + * Inspired by https://github.com/apache/maven-archiver/blob/a3103d99396cd8d3440b907ef932a33563225265/src/main/java/org/apache/maven/archiver/MavenArchiver.java#L765 + * + * @param outputTimestamp the value of ${project.build.outputTimestamp} (may be null) + * @return the parsed timestamp, may be null if null input or input contains only 1 + * character + */ + private Date parseOutputTimestamp(String outputTimestamp) throws GitCommitIdExecutionException { + if (outputTimestamp != null && !outputTimestamp.trim().isEmpty() && outputTimestamp.chars().allMatch(Character::isDigit)) { + return new Date(Long.parseLong(outputTimestamp) * 1000); + } + + if ((outputTimestamp == null) || (outputTimestamp.length() < 2)) { + // no timestamp configured + return null; + } + + DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); + try { + return df.parse(outputTimestamp); + } catch (ParseException pe) { + throw new GitCommitIdExecutionException( + "Invalid 'project.build.outputTimestamp' value '" + outputTimestamp + "'", + pe); + } } private void publishPropertiesInto(Properties p) {