Skip to content

Commit 14b260c

Browse files
authored
Merge pull request #513 from TheSnoozer/issue-509
#509: Allow initial 'git.build.time' from 'project.build.outputTimestamp' to support reproducible builds
2 parents 5f1dea4 + e1576fd commit 14b260c

File tree

2 files changed

+54
-6
lines changed

2 files changed

+54
-6
lines changed

core/src/main/java/pl/project13/core/cibuild/BuildServerDataProvider.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import pl.project13.core.util.PropertyManager;
2424

2525
import javax.annotation.Nonnull;
26+
import javax.annotation.Nullable;
2627
import java.net.InetAddress;
2728
import java.net.UnknownHostException;
2829
import java.text.SimpleDateFormat;
@@ -112,8 +113,8 @@ public static BuildServerDataProvider getBuildServerProvider(@Nonnull Map<String
112113
return new UnknownBuildServerData(log, env);
113114
}
114115

115-
public void loadBuildData(@Nonnull Properties properties) {
116-
loadBuildVersionAndTimeData(properties);
116+
public void loadBuildData(@Nonnull Properties properties, @Nullable Date reproducibleBuildOutputTimestamp) {
117+
loadBuildVersionAndTimeData(properties, reproducibleBuildOutputTimestamp);
117118
loadBuildHostData(properties);
118119
loadBuildNumber(properties);
119120
}
@@ -132,9 +133,9 @@ public void loadBuildData(@Nonnull Properties properties) {
132133
*/
133134
public abstract String getBuildBranch();
134135

135-
private void loadBuildVersionAndTimeData(@Nonnull Properties properties) {
136+
private void loadBuildVersionAndTimeData(@Nonnull Properties properties, @Nullable Date reproducibleBuildOutputTimestamp) {
136137
Supplier<String> buildTimeSupplier = () -> {
137-
Date buildDate = new Date();
138+
Date buildDate = (reproducibleBuildOutputTimestamp == null) ? new Date() : reproducibleBuildOutputTimestamp;
138139
SimpleDateFormat smf = new SimpleDateFormat(dateFormat);
139140
if (dateFormatTimeZone != null) {
140141
smf.setTimeZone(TimeZone.getTimeZone(dateFormatTimeZone));

maven/src/main/java/pl/project13/maven/git/GitCommitIdMojo.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.io.IOException;
2222
import java.nio.charset.Charset;
2323
import java.nio.charset.StandardCharsets;
24+
import java.text.DateFormat;
25+
import java.text.ParseException;
2426
import java.text.SimpleDateFormat;
2527
import java.util.*;
2628
import java.util.function.Supplier;
@@ -372,6 +374,18 @@ public class GitCommitIdMojo extends AbstractMojo {
372374
@Parameter(defaultValue = "false")
373375
boolean offline;
374376

377+
/**
378+
* Timestamp for reproducible output archive entries
379+
* (https://maven.apache.org/guides/mini/guide-reproducible-builds.html).
380+
* The value from <code>${project.build.outputTimestamp}</code> is either formatted as ISO 8601
381+
* <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like
382+
* <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>.
383+
*
384+
* @since 4.0.2
385+
*/
386+
@Parameter(defaultValue = "${project.build.outputTimestamp}")
387+
private String projectBuildOutputTimestamp;
388+
375389
/**
376390
* Injected {@link BuildContext} to recognize incremental builds.
377391
*/
@@ -548,7 +562,7 @@ private Properties getContextProperties(MavenProject project) {
548562
return null;
549563
}
550564

551-
private void loadBuildData(Properties properties) {
565+
private void loadBuildData(Properties properties) throws GitCommitIdExecutionException {
552566
Map<String, Supplier<String>> additionalProperties = Collections.singletonMap(
553567
GitCommitPropertyConstant.BUILD_VERSION, () -> project.getVersion());
554568
BuildServerDataProvider buildServerDataProvider = BuildServerDataProvider.getBuildServerProvider(System.getenv(),log);
@@ -560,7 +574,40 @@ private void loadBuildData(Properties properties) {
560574
.setExcludeProperties(excludeProperties)
561575
.setIncludeOnlyProperties(includeOnlyProperties);
562576

563-
buildServerDataProvider.loadBuildData(properties);
577+
buildServerDataProvider.loadBuildData(properties, parseOutputTimestamp(projectBuildOutputTimestamp));
578+
}
579+
580+
/**
581+
* Parse output timestamp configured for Reproducible Builds' archive entries
582+
* (https://maven.apache.org/guides/mini/guide-reproducible-builds.html).
583+
* The value from <code>${project.build.outputTimestamp}</code> is either formatted as ISO 8601
584+
* <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like
585+
* <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>.
586+
*
587+
* Inspired by https://github.com/apache/maven-archiver/blob/a3103d99396cd8d3440b907ef932a33563225265/src/main/java/org/apache/maven/archiver/MavenArchiver.java#L765
588+
*
589+
* @param outputTimestamp the value of <code>${project.build.outputTimestamp}</code> (may be <code>null</code>)
590+
* @return the parsed timestamp, may be <code>null</code> if <code>null</code> input or input contains only 1
591+
* character
592+
*/
593+
private Date parseOutputTimestamp(String outputTimestamp) throws GitCommitIdExecutionException {
594+
if (outputTimestamp != null && !outputTimestamp.trim().isEmpty() && outputTimestamp.chars().allMatch(Character::isDigit)) {
595+
return new Date(Long.parseLong(outputTimestamp) * 1000);
596+
}
597+
598+
if ((outputTimestamp == null) || (outputTimestamp.length() < 2)) {
599+
// no timestamp configured
600+
return null;
601+
}
602+
603+
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");
604+
try {
605+
return df.parse(outputTimestamp);
606+
} catch (ParseException pe) {
607+
throw new GitCommitIdExecutionException(
608+
"Invalid 'project.build.outputTimestamp' value '" + outputTimestamp + "'",
609+
pe);
610+
}
564611
}
565612

566613
private void publishPropertiesInto(Properties p) {

0 commit comments

Comments
 (0)