Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#674: change timeformat from RFC822 to ISO 8601 to support maven's reproducible build feature #699

Merged
merged 12 commits into from
Feb 28, 2024
4 changes: 2 additions & 2 deletions .github/workflows/default-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
needs: checkstyle
strategy:
matrix:
java_version: ['11', '12', '13', '14', '15', '16', '17', '18', '19', '20']
java_version: ['11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21']

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -59,7 +59,7 @@ jobs:
strategy:
matrix:
java_version: ['11']
maven_version: ['3.2.5', '3.3.9', '3.5.4', '3.6.3', '3.8.8', '3.9.1', '3.9.2', '4.0.0-alpha-7']
maven_version: ['3.2.5', '3.3.9', '3.5.4', '3.6.3', '3.8.8', '3.9.6', '4.0.0-alpha-12']

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@
<dotGitDirectory>${project.basedir}/.git</dotGitDirectory>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>target/testing.properties</generateGitPropertiesFilename>
<dateFormat>yyyy-MM-dd'T'HH:mm:ssZ</dateFormat>
<!-- <dateFormat>yyyy-MM-dd'T'HH:mm:ssXXX</dateFormat> -->
<dateFormatTimeZone>GMT-08:00</dateFormatTimeZone>
<useNativeGit>false</useNativeGit>
<abbrevLength>7</abbrevLength>
Expand Down
41 changes: 22 additions & 19 deletions src/main/java/pl/project13/maven/git/GitCommitIdMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@

package pl.project13.maven.git;

import com.google.common.annotations.VisibleForTesting;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Collections;
import java.util.Date;
import java.util.List;
Expand All @@ -44,6 +44,7 @@
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.settings.Settings;
import org.joda.time.DateTime;
import org.sonatype.plexus.build.incremental.BuildContext;
import pl.project13.core.CommitIdGenerationMode;
import pl.project13.core.CommitIdPropertiesOutputFormat;
Expand Down Expand Up @@ -497,20 +498,28 @@ public class GitCommitIdMojo extends AbstractMojo {
* represents dates or times exported by this plugin (e.g. {@code git.commit.time}, {@code
* git.build.time}). It should be a valid {@link SimpleDateFormat} string.
*
* <p>The current dateFormat is set to match maven's default {@code yyyy-MM-dd'T'HH:mm:ssZ}.
* Please note that in previous versions (2.2.0 - 2.2.2) the default dateFormat was set to: {@code
* dd.MM.yyyy '@' HH:mm:ss z}. However the {@code RFC 822 time zone} seems to give a more reliable
* option in parsing the date and it's being used in maven as default.
* <p>The current dateFormat will be formatted as ISO 8601
* {@code yyyy-MM-dd'T'HH:mm:ssXXX} and therefore can be used as input to maven's
* <a href="https://maven.apache.org/guides/mini/guide-reproducible-builds.html">
* reproducible build</a> feature.
*
* Please note that in previous versions
* (2.2.2 - 7.0.1) the default format was set to {@code yyyy-MM-dd'T'HH:mm:ssZ}
* which produces a {@code RFC 822 time zone}. While such format gives reliable
* options in parsing the date, it does not comply with the requirements of
* the reproducible build feature.
* (2.2.0 - 2.2.2) the default dateFormat was set to: {@code
* dd.MM.yyyy '@' HH:mm:ss z}.
*
* <p>Example:
*
* <pre>{@code
* <dateFormat>yyyy-MM-dd'T'HH:mm:ssZ</dateFormat>
* <dateFormat>yyyy-MM-dd'T'HH:mm:ssXXX</dateFormat>
* }</pre>
*
* @since 2.2.0
*/
@Parameter(defaultValue = "yyyy-MM-dd'T'HH:mm:ssZ")
@Parameter(defaultValue = "yyyy-MM-dd'T'HH:mm:ssXXX")
String dateFormat;

/**
Expand Down Expand Up @@ -1454,32 +1463,26 @@ private Properties getContextProperties(MavenProject project) {
* href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>.
*
* <p>Inspired by
* https://github.com/apache/maven-archiver/blob/a3103d99396cd8d3440b907ef932a33563225265/src/main/java/org/apache/maven/archiver/MavenArchiver.java#L765
* https://github.com/apache/maven-archiver/blob/7acb1db4a9754beacde3f21a69e5523ee901abd5/src/main/java/org/apache/maven/archiver/MavenArchiver.java#L755
*
* @param outputTimestamp the value of <code>${project.build.outputTimestamp}</code> (may be
* <code>null</code>)
* @return the parsed timestamp, may be <code>null</code> if <code>null</code> input or input
* contains only 1 character
*/
private Date parseOutputTimestamp(String outputTimestamp) throws GitCommitIdExecutionException {
@VisibleForTesting
protected static Date parseOutputTimestamp(String outputTimestamp) {
if (outputTimestamp != null
&& !outputTimestamp.trim().isEmpty()
&& outputTimestamp.chars().allMatch(Character::isDigit)) {
return new Date(Long.parseLong(outputTimestamp) * 1000);
return Date.from(Instant.ofEpochSecond(Long.parseLong(outputTimestamp)));
}

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);
}
return new DateTime(outputTimestamp).toDate();
}

private void publishPropertiesInto(Properties propertiesToPublish, Properties propertiesTarget) {
Expand Down
69 changes: 69 additions & 0 deletions src/test/java/pl/project13/maven/git/GitCommitIdMojoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,19 @@

import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.util.Date;
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.joda.time.DateTime;
import org.junit.Test;
import org.junit.runner.RunWith;
import pl.project13.core.PropertiesFileGenerator;

/**
* Testcases to verify that the git-commit-id works properly.
*/
@RunWith(JUnitParamsRunner.class)
public class GitCommitIdMojoTest {
@Test
public void testCraftPropertiesOutputFileWithRelativePath() throws IOException {
Expand Down Expand Up @@ -66,4 +73,66 @@ public void testCraftPropertiesOutputFileWithFullPath() throws IOException {
.toFile()
.getCanonicalPath());
}

/**
* test cases for output timestamp parsing.
* This timestamp is configured for Reproducible Builds' archive entries
* (https://maven.apache.org/guides/mini/guide-reproducible-builds.html). The value from <code>
* ${project.build.outputTimestamp}</code> is either formatted as ISO 8601 <code>
* yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like <a
* href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>.
* When using ISO 8601 formatting please note that the entire expression must be entirely either
* in the basic format (20240215T135459+0100) or in the
* extended format (e.g. 2024-02-15T13:54:59+01:00).
* The maven plugin only supports the extended format.
*/
private Object[] parametersParseOutputTimestamp() {
return new Object[] {
// long since epoch
new Object[] {
"1644689403"
},
// Date and time with timezone:
new Object[] {
"2022-02-12T15:30+00:00"
},
new Object[] {
"2022-02-12T15:30:45-05:00"
},
new Object[] {
"2022-02-12T15:30:00+00:00"
},
new Object[] {
"2023-11-30T09:17:06+05:30"
},
new Object[] {
"2024-08-15T20:45:30-03:00"
},
new Object[] {
"2022-02-12T15:30:00Z"
},
new Object[] {
"2023-11-30T09:17:06+0100"
},
// Lowercase time designator
new Object[] {
"2019-03-26t14:00Z"
},
// Lowercase UTC designator
new Object[] {
"2019-03-26T14:00z"
},
// Hours-only offset
new Object[] {
"2019-03-26T10:00-04"
},
};
}

@Test
@Parameters(method = "parametersParseOutputTimestamp")
public void testParseOutputTimestamp(String input) {
Date actual = GitCommitIdMojo.parseOutputTimestamp(input);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Tests fail with:

TimeZone.setDefault(TimeZone.getTimeZone("America/Sao_Paulo"));

assertThat(actual).isNotNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ public class NativeAndJGitProviderTest extends GitIntegrationTest {
"git.local.branch.behind",
};

private static final String DEFAULT_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssZ";
private static final String ISO8601_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssZZ";
private static final String DEFAULT_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssXXX";
private static final String ISO8601_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssXXX";

@Test
public void testCompareBasic() throws Exception {
Expand Down