fix #187 Add facilities to list and filter tags, find closest previous tag
This commit is contained in:
committed by
Marcin Grzejszczak
parent
e980b1f74e
commit
4b01dbea64
@@ -19,9 +19,13 @@ package releaser.internal.git;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.jcraft.jsch.IdentityRepository;
|
||||
import com.jcraft.jsch.JSch;
|
||||
@@ -44,9 +48,12 @@ import org.eclipse.jgit.api.ResetCommand;
|
||||
import org.eclipse.jgit.api.TransportConfigCallback;
|
||||
import org.eclipse.jgit.api.errors.EmptyCommitException;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevCommit;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.transport.CredentialsProvider;
|
||||
import org.eclipse.jgit.transport.FetchResult;
|
||||
import org.eclipse.jgit.transport.JschConfigSessionFactory;
|
||||
@@ -247,11 +254,47 @@ class GitRepo {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all the tags in the repository.
|
||||
* @return a {@link List} of all the tags in the repository.
|
||||
*/
|
||||
Stream<String> listTags() {
|
||||
try (Git git = this.gitFactory.open(file(this.basedir))) {
|
||||
final RevWalk walk = new RevWalk(git.getRepository());
|
||||
List<Ref> allTagsNewestFirst = git.tagList().call();
|
||||
Collections.sort(allTagsNewestFirst, (Comparator<Ref>) (o1, o2) -> {
|
||||
Date d1;
|
||||
Date d2;
|
||||
try {
|
||||
d1 = walk.parseTag(o1.getObjectId()).getTaggerIdent().getWhen();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
return 1; // put at end
|
||||
}
|
||||
try {
|
||||
d2 = walk.parseTag(o2.getObjectId()).getTaggerIdent().getWhen();
|
||||
}
|
||||
catch (IOException ioe) {
|
||||
return -1; // put ahead
|
||||
}
|
||||
return d2.compareTo(d1); // more recent first
|
||||
});
|
||||
return allTagsNewestFirst.stream()
|
||||
.map(ref -> Repository.shortenRefName(ref.getName()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to fetch git tags", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a tag with the given name, and if not found looks for a branch.
|
||||
*/
|
||||
private Optional<Ref> findTagOrBranchHeadRevision(Git git, String tagOrBranch)
|
||||
throws GitAPIException {
|
||||
throws GitAPIException, IOException {
|
||||
if (tagOrBranch.equals("HEAD")) {
|
||||
return Optional.of(git.getRepository().exactRef(Constants.HEAD).getTarget());
|
||||
}
|
||||
final Optional<Ref> tag = git.tagList().call().stream()
|
||||
.filter(ref -> ref.getName().equals("refs/tags/" + tagOrBranch))
|
||||
.findFirst();
|
||||
|
||||
@@ -23,7 +23,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.transport.URIish;
|
||||
@@ -318,4 +320,15 @@ public class ProjectGitHandler implements Closeable {
|
||||
CACHE.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the tag names that match a {@link Pattern}.
|
||||
* @param clonedProject the base dir for the cloned repository
|
||||
* @param tagPattern the {@link Pattern} to use to filter tag names
|
||||
* @return a {@link Stream} of the tags whose name match the given {@link Pattern}
|
||||
*/
|
||||
public Stream<String> findTagNamesMatching(File clonedProject, Pattern tagPattern) {
|
||||
return gitRepo(clonedProject).listTags()
|
||||
.filter(tagName -> tagPattern.matcher(tagName).matches());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.maven.model.Model;
|
||||
@@ -266,6 +267,109 @@ public class ProjectVersion implements Comparable<ProjectVersion>, Serializable
|
||||
return this.assertVersion().major;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the previous patch's version, without any prefix or suffix (ie
|
||||
* MAJOR.MINOR.PATCH only). If the patch number is already at 0, returns an empty
|
||||
* {@link Optional} instead.
|
||||
* @param prefix the prefix to prepend to the tag pattern (eg. if tags are in the form
|
||||
* vVERSION)
|
||||
* @param suffix the suffix to append to the tag pattern instead (eg. if tags are in
|
||||
* the form VERSION.RELEASE). this replaces the suffix in the version if any.
|
||||
* @return an {@link Optional} valued with the previous MAJOR.MINOR.PATCH, or empty if
|
||||
* PATCH is already 0
|
||||
* @see #computePreviousMinorTagPattern(String,String)
|
||||
*/
|
||||
public Optional<String> computePreviousPatchTag(String prefix, String suffix) {
|
||||
SplitVersion splitVersion = this.assertVersion();
|
||||
if (suffix.isEmpty()) {
|
||||
suffix = splitVersion.suffix;
|
||||
}
|
||||
int patch;
|
||||
try {
|
||||
patch = Integer.parseInt(splitVersion.patch);
|
||||
}
|
||||
catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException("Version " + this.version
|
||||
+ " doesn't contain a numerical PATCH number", nfe);
|
||||
}
|
||||
if (patch == 0) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(prefix + splitVersion.major + splitVersion.delimiter
|
||||
+ splitVersion.minor + splitVersion.delimiter + (patch - 1)
|
||||
+ splitVersion.delimiter + suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a {@link Pattern} that allows to identify all the version tags of the
|
||||
* previous MAJOR.MINOR (MAJOR.MINOR.*), provided MINOR is not 0. If MINOR is 0,
|
||||
* {@link Optional#empty()} is returned.
|
||||
* @param prefix the prefix to prepend to the tag pattern (eg. if tags are in the form
|
||||
* vVERSION)
|
||||
* @param suffix the suffix to append to the tag pattern (eg. if tags are in the form
|
||||
* VERSION.RELEASE)
|
||||
* @return a {@link Optional} of {@link Pattern} to identify all the versions in the
|
||||
* previous MAJOR.MINOR, or empty if current MINOR is 0
|
||||
* @see #computePreviousMajorTagPattern(String, String)
|
||||
*/
|
||||
public Optional<Pattern> computePreviousMinorTagPattern(String prefix,
|
||||
String suffix) {
|
||||
SplitVersion splitVersion = this.assertVersion();
|
||||
if (suffix.isEmpty()) {
|
||||
suffix = splitVersion.suffix;
|
||||
}
|
||||
String quotedSuffix = Pattern.quote(splitVersion.delimiter + suffix);
|
||||
int minor;
|
||||
try {
|
||||
minor = Integer.parseInt(splitVersion.minor);
|
||||
}
|
||||
catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException("Version " + this.version
|
||||
+ " doesn't contain a numerical MINOR number", nfe);
|
||||
}
|
||||
if (minor == 0) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Pattern p = Pattern
|
||||
.compile(Pattern
|
||||
.quote(prefix + splitVersion.major + splitVersion.delimiter
|
||||
+ (minor - 1) + splitVersion.delimiter)
|
||||
+ "\\d+" + quotedSuffix);
|
||||
return Optional.of(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a {@link Pattern} that allows to identify all the version tags of the
|
||||
* previous MAJOR. Throws {@link IllegalArgumentException} if MAJOR is 0.
|
||||
* @param prefix the prefix to prepend to the tag pattern (eg. if tags are in the form
|
||||
* vVERSION)
|
||||
* @param suffix the suffix to append to the tag pattern (eg. if tags are in the form
|
||||
* VERSION.RELEASE)
|
||||
* @return a {@link Pattern} to identify all the versions in the previous MAJOR
|
||||
*/
|
||||
public Pattern computePreviousMajorTagPattern(String prefix, String suffix) {
|
||||
SplitVersion splitVersion = this.assertVersion();
|
||||
if (suffix.isEmpty()) {
|
||||
suffix = splitVersion.suffix;
|
||||
}
|
||||
String quotedSuffix = Pattern.quote(splitVersion.delimiter + suffix);
|
||||
int major;
|
||||
try {
|
||||
major = Integer.parseInt(splitVersion.major);
|
||||
}
|
||||
catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException("Version " + this.version
|
||||
+ " doesn't contain a numerical MAJOR number", nfe);
|
||||
}
|
||||
if (major == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot compute previous MAJOR pattern with MAJOR of 0");
|
||||
}
|
||||
return Pattern.compile(
|
||||
Pattern.quote(prefix + (major - 1) + splitVersion.delimiter) + "\\d+"
|
||||
+ Pattern.quote(splitVersion.delimiter) + "\\d+" + quotedSuffix);
|
||||
}
|
||||
|
||||
public boolean isSnapshot() {
|
||||
return this.version != null && this.version.contains("SNAPSHOT");
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.junit.Test;
|
||||
import releaser.internal.git.GitRepoTests;
|
||||
import releaser.internal.project.ProjectVersion;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.assertj.core.api.BDDAssertions.thenThrownBy;
|
||||
|
||||
@@ -620,6 +621,69 @@ public class ProjectVersionTests {
|
||||
then(projectVersion("1.0.0.BUILD-SNAPSHOT").releaseTagName()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_decremented_patch_when_patch_above_zero() {
|
||||
then(projectVersion("1.2.3.RELEASE").computePreviousPatchTag("v", "RELEASE"))
|
||||
.contains("v1.2.2.RELEASE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_use_current_suffix_if_no_forced_suffix() {
|
||||
then(projectVersion("1.2.3.WHATEVER").computePreviousPatchTag("v", ""))
|
||||
.contains("v1.2.2.WHATEVER");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_replace_suffix() {
|
||||
then(projectVersion("1.2.3.WHATEVER").computePreviousPatchTag("v", "RELEASE"))
|
||||
.contains("v1.2.2.RELEASE");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_empty_when_patch_at_zero() {
|
||||
then(projectVersion("1.2.0.WHATEVER").computePreviousPatchTag("v", "RELEASE"))
|
||||
.isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_minor_pattern_when_minor_above_zero() {
|
||||
then(projectVersion("1.2.0.WHATEVER")
|
||||
.computePreviousMinorTagPattern("v", "RELEASE").get().pattern())
|
||||
.isEqualTo("\\Qv1.1.\\E\\d+\\Q.RELEASE\\E");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_compute_minor_pattern_with_current_suffix_if_no_forced_suffix() {
|
||||
then(projectVersion("1.2.0.WHATEVER").computePreviousMinorTagPattern("v", "")
|
||||
.get().pattern()).isEqualTo("\\Qv1.1.\\E\\d+\\Q.WHATEVER\\E");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_empty_minor_pattern_when_minor_zero() {
|
||||
then(projectVersion("1.0.0.WHATEVER").computePreviousMinorTagPattern("v",
|
||||
"RELEASE")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_return_major_pattern_when_major_above_zero() {
|
||||
then(projectVersion("1.0.0.WHATEVER")
|
||||
.computePreviousMajorTagPattern("v", "RELEASE").pattern())
|
||||
.isEqualTo("\\Qv0.\\E\\d+\\Q.\\E\\d+\\Q.RELEASE\\E");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_compute_major_pattern_with_current_suffix_if_no_forced_suffix() {
|
||||
then(projectVersion("1.0.0.WHATEVER").computePreviousMajorTagPattern("v", "")
|
||||
.pattern()).isEqualTo("\\Qv0.\\E\\d+\\Q.\\E\\d+\\Q.WHATEVER\\E");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_throw_major_pattern_when_major_zero() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> projectVersion("0.0.1.WHATEVER")
|
||||
.computePreviousMajorTagPattern("v", "RELEASE"));
|
||||
}
|
||||
|
||||
private void thenPatternsForSnapshotMilestoneAndReleaseCandidateArePresent(
|
||||
List<Pattern> unknownTypeOfVersion) {
|
||||
then(unknownTypeOfVersion).isNotEmpty();
|
||||
|
||||
Reference in New Issue
Block a user