diff --git a/pom.xml b/pom.xml index cd115e5525..85c35698b2 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ io.jenkins.tools.bom bom-${jenkins.baseline}.x - 5857.vb_f3dd0731f44 + 5933.vcf06f7b_5d1a_2 pom import @@ -227,5 +227,4 @@ https://repo.jenkins-ci.org/public/ - diff --git a/src/main/java/jenkins/plugins/git/AbstractGitSCMSource.java b/src/main/java/jenkins/plugins/git/AbstractGitSCMSource.java index 1504297965..268ec30e2c 100644 --- a/src/main/java/jenkins/plugins/git/AbstractGitSCMSource.java +++ b/src/main/java/jenkins/plugins/git/AbstractGitSCMSource.java @@ -110,6 +110,7 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevTree; import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.RefSpec; @@ -425,6 +426,34 @@ private , R extends GitSCMSourceRequest> } } + private long getTagTimestamp(RevWalk walk, ObjectId objectId) throws IOException { + try { + // Annotated tag object + RevTag tag = walk.parseTag(objectId); + + if (tag.getTaggerIdent() != null + && tag.getTaggerIdent().getWhen() != null) { + return tag.getTaggerIdent().getWhen().getTime(); + } + + // No tagger ident (or weird tag) — walk the tag chain to a commit, if any + Object target = tag.getObject(); + for (int i = 0; i < 32 && target instanceof RevTag; i++) { + target = ((RevTag) target).getObject(); + } + + if (target instanceof RevCommit) { + return TimeUnit.SECONDS.toMillis(((RevCommit) target).getCommitTime()); + } + + throw new IOException("Tag does not ultimately reference a commit: " + objectId.name()); + } catch (org.eclipse.jgit.errors.IncorrectObjectTypeException e) { + // Lightweight tag (or direct commit id) + RevCommit commit = walk.parseCommit(objectId); + return TimeUnit.SECONDS.toMillis(commit.getCommitTime()); + } + } + /** * {@inheritDoc} */ @@ -786,8 +815,7 @@ private void discoverTags(final Repository repository, } count++; final String tagName = StringUtils.removeStart(ref.getKey(), Constants.R_TAGS); - RevCommit commit = walk.parseCommit(ref.getValue()); - final long lastModified = TimeUnit.SECONDS.toMillis(commit.getCommitTime()); + final long lastModified = getTagTimestamp(walk, ref.getValue()); if (request.process(new GitTagSCMHead(tagName, lastModified), new SCMSourceRequest.IntermediateLambda() { @Nullable @@ -804,7 +832,7 @@ public SCMSourceCriteria.Probe create(@NonNull GitTagSCMHead head, @Nullable ObjectId revisionInfo) throws IOException, InterruptedException { RevCommit commit = walk.parseCommit(revisionInfo); - final long lastModified = TimeUnit.SECONDS.toMillis(commit.getCommitTime()); + final long lastModified = getTagTimestamp(walk, revisionInfo); final RevTree tree = commit.getTree(); return new TreeWalkingSCMProbe(tagName, lastModified, repository, tree); } @@ -1012,8 +1040,7 @@ public SCMRevision run(GitClient client, String remoteName) throws GitException, final Repository repository = client.getRepository(); RevWalk walk = new RevWalk(repository)) { ObjectId ref = client.revParse(tagRef); - RevCommit commit = walk.parseCommit(ref); - long lastModified = TimeUnit.SECONDS.toMillis(commit.getCommitTime()); + long lastModified = getTagTimestamp(walk, ref); listener.getLogger().printf("Resolved tag %s revision %s%n", revision, ref.getName()); return new GitTagSCMRevision(new GitTagSCMHead(revision, lastModified), diff --git a/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceWantTagsTest.java b/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceWantTagsTest.java index 2a7b87ca76..18140f1f90 100644 --- a/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceWantTagsTest.java +++ b/src/test/java/jenkins/plugins/git/AbstractGitSCMSourceWantTagsTest.java @@ -3,6 +3,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -169,6 +170,25 @@ void indexingHasBranchAndTagDiscoveryTraitIgnoreTagDiscoveryTrait() throws Excep assertTrue(tagsFetched); } + @Test + public void tagTimestampsAreValid() throws Exception { + source.setTraits(Collections.singletonList(new TagDiscoveryTrait())); + Set heads = source.fetch(LISTENER); + + Set tags = heads.stream() + .filter(h -> h instanceof GitTagSCMHead) + .map(h -> (GitTagSCMHead) h) + .collect(Collectors.toSet()); + + assertThat("Should discover both tags", tags.size(), is(2)); + + long year2000 = 946684800000L; // Jan 1, 2000 + for (GitTagSCMHead tag : tags) { + assertThat("Tag " + tag.getName() + " should have valid timestamp", + tag.getTimestamp(), greaterThan(year2000)); + } + } + static boolean tagsFetched; public static class MockGitClientForTags extends TestJGitAPIImpl {