diff --git a/src/integTest/groovy/nebula/plugin/release/ReleasePluginIntegrationSpec.groovy b/src/integTest/groovy/nebula/plugin/release/ReleasePluginIntegrationSpec.groovy index fa5e257..7e8dc10 100644 --- a/src/integTest/groovy/nebula/plugin/release/ReleasePluginIntegrationSpec.groovy +++ b/src/integTest/groovy/nebula/plugin/release/ReleasePluginIntegrationSpec.groovy @@ -329,36 +329,6 @@ class ReleasePluginIntegrationSpec extends GitVersioningIntegrationTestKitSpec { version.toString().startsWith("1.0.0-snapshot." + getUtcDateForComparison()) } - def 'allow create final from a commit when some of its candidates are before the commit'() { - given: - def file = new File(projectDir, "test_file.txt") - file.text = "DUMMY" - git.add(patterns: ['.'] as Set) - git.commit(message: "Add file") - git.push(all: true) - runTasks('candidate') - git.branch.add(name: "0.1.x") - file.text = "Updated dummy" - git.add(patterns: ['.'] as Set) - git.commit(message: "Update file") - git.push(all: true) - runTasks('candidate') - - when: - git.checkout(branch: '0.1.x') - def version = inferredVersionForTask('final') - - then: - version.toString() == normal('0.1.0').toString() - - when: - git.checkout(branch: 'master') - version = inferredVersionForTask('candidate') - - then: - version.toString() == normal('0.2.0-rc.1').toString() - } - def 'create new major release branch in git-flow style and have branch name respected on version'() { def oneX = 'release/1.x' git.branch.add(name: oneX) diff --git a/src/main/groovy/nebula/plugin/release/git/command/GitCommandParameters.groovy b/src/main/groovy/nebula/plugin/release/git/command/GitCommandParameters.groovy index 4c2ff8e..ba2dfb4 100644 --- a/src/main/groovy/nebula/plugin/release/git/command/GitCommandParameters.groovy +++ b/src/main/groovy/nebula/plugin/release/git/command/GitCommandParameters.groovy @@ -5,8 +5,8 @@ import org.gradle.api.provider.ValueSourceParameters interface GitCommandParameters extends ValueSourceParameters { Property getRootDir() - Property getTagForSearch() Property getGitConfigScope() Property getGitConfigKey() Property getGitConfigValue() + Property getCommit() } diff --git a/src/main/groovy/nebula/plugin/release/git/command/GitReadCommand.groovy b/src/main/groovy/nebula/plugin/release/git/command/GitReadCommand.groovy index 973355b..07c65d5 100644 --- a/src/main/groovy/nebula/plugin/release/git/command/GitReadCommand.groovy +++ b/src/main/groovy/nebula/plugin/release/git/command/GitReadCommand.groovy @@ -58,11 +58,25 @@ abstract class CurrentBranch extends GitReadCommand { * Uses git describe to find a given tag in the history of the current branch * ex. git describe HEAD --tags --match v10.0.0 -> v10.0.0-220-ga00baaa */ -abstract class DescribeTagForHead extends GitReadCommand { +abstract class DescribeHeadWithTag extends GitReadCommand { @Override String obtain() { try { - return executeGitCommand( "describe", "HEAD", "--tags", "--match", parameters.getTagForSearch().get()) + return executeGitCommand( "describe", "HEAD", "--tags", "--long") + } catch (Exception e) { + return null + } + } +} +/** + * Uses git describe to find a given tag in the history of the current branch + * ex. git describe HEAD --tags --match v10.0.0 -> v10.0.0-220-ga00baaa + */ +abstract class TagsPointingAt extends GitReadCommand { + @Override + String obtain() { + try { + return executeGitCommand( "tag", "--points-at", parameters.commit.get()) } catch (Exception e) { return null } diff --git a/src/main/groovy/nebula/plugin/release/git/command/GitReadOnlyCommandUtil.groovy b/src/main/groovy/nebula/plugin/release/git/command/GitReadOnlyCommandUtil.groovy index 35d2eb4..52331ce 100644 --- a/src/main/groovy/nebula/plugin/release/git/command/GitReadOnlyCommandUtil.groovy +++ b/src/main/groovy/nebula/plugin/release/git/command/GitReadOnlyCommandUtil.groovy @@ -120,19 +120,6 @@ class GitReadOnlyCommandUtil implements Serializable { .collect { new TagRef(it) } } - /** - * Returns the tags that point to the current HEAD - */ - List refTags() { - try { - return refTagsProvider.get().toString() - .split("\n") - .findAll { String tag -> !tag?.replaceAll("\n", "")?.isEmpty() } - .toList() - } catch (Exception e) { - return Collections.emptyList() - } - } Integer getCommitCountForHead() { try { @@ -144,11 +131,10 @@ class GitReadOnlyCommandUtil implements Serializable { } } - String describeTagForHead(String tagName) { + String describeHeadWithTags() { try { - def describeTagInHeadProvider = providers.of(DescribeTagForHead.class) { + def describeTagInHeadProvider = providers.of(DescribeHeadWithTag.class) { it.parameters.rootDir.set(rootDir) - it.parameters.tagForSearch.set(tagName) } return describeTagInHeadProvider.get().toString() .split("\n") @@ -158,6 +144,21 @@ class GitReadOnlyCommandUtil implements Serializable { } } + List getTagsPointingAt(String commit) { + try { + def tagsPointingAtProvider = providers.of(TagsPointingAt.class) { + it.parameters.rootDir.set(rootDir) + it.parameters.commit.set(commit) + } + return tagsPointingAtProvider.get().toString() + .split("\n") + .findAll { String tag -> !tag?.replaceAll("\n", "")?.isEmpty() } + .collect() + } catch(Exception e) { + return null + } + } + /** * Checks if the repo has changes * @return diff --git a/src/main/groovy/nebula/plugin/release/git/command/GitWriteCommandsUtil.groovy b/src/main/groovy/nebula/plugin/release/git/command/GitWriteCommandsUtil.groovy index 00ce6eb..d6b52c4 100644 --- a/src/main/groovy/nebula/plugin/release/git/command/GitWriteCommandsUtil.groovy +++ b/src/main/groovy/nebula/plugin/release/git/command/GitWriteCommandsUtil.groovy @@ -81,10 +81,6 @@ class GitWriteCommandsUtil implements Serializable { it.standardOutput = output it.errorOutput = error } - def errorMsg = new String(error.toByteArray(), Charset.defaultCharset()) - if(errorMsg) { - throw new GradleException(errorMsg) - } return new String(output.toByteArray(), Charset.defaultCharset()) } } diff --git a/src/main/groovy/nebula/plugin/release/git/semver/NearestVersionLocator.groovy b/src/main/groovy/nebula/plugin/release/git/semver/NearestVersionLocator.groovy index 105d365..9c145b4 100644 --- a/src/main/groovy/nebula/plugin/release/git/semver/NearestVersionLocator.groovy +++ b/src/main/groovy/nebula/plugin/release/git/semver/NearestVersionLocator.groovy @@ -18,13 +18,13 @@ package nebula.plugin.release.git.semver import com.github.zafarkhaja.semver.Version import groovy.transform.CompileDynamic import nebula.plugin.release.git.command.GitReadOnlyCommandUtil -import nebula.plugin.release.git.model.TagRef import nebula.plugin.release.git.base.TagStrategy +import org.gradle.api.GradleException import org.slf4j.Logger import org.slf4j.LoggerFactory /** - * Locates the nearest {@link nebula.plugin.release.git.model.TagRef}s whose names can be + * Locates the nearest Tag whose names can be * parsed as a {@link com.github.zafarkhaja.semver.Version version}. Both the * absolute nearest version tag and the nearest "normal version" tag are * included. @@ -54,12 +54,6 @@ class NearestVersionLocator { * starting from the current HEAD. * *

- * All tag names are parsed to determine if they are valid - * version strings. Tag names can begin with "v" (which will - * be stripped off). - *

- * - *

* The nearest tag is determined by getting a commit log between * the tag and {@code HEAD}. The version tag with the smallest * log from a pure count of commits will have its version returned. If two @@ -81,52 +75,58 @@ class NearestVersionLocator { */ NearestVersion locate() { logger.debug('Locate beginning on branch: {}', gitCommandUtil.currentBranch()) - // Reuse a single walk to make use of caching. - List tagRefs = gitCommandUtil.refTags() - List allTags = tagRefs.collect { ref -> - TagRef.fromRef(ref) - }.findAll { - it.version - } - - List normalTags = allTags.findAll { !it.version.preReleaseVersion } - def normal = findNearestVersion(normalTags) - def any = findNearestVersion(allTags) - + def normal = getLatestTagWithDistance(true) + def any = getLatestTagWithDistance(false) logger.debug('Nearest release: {}, nearest any: {}.', normal, any) return new NearestVersion(any.version, normal.version, any.distance, normal.distance) } - private Map findNearestVersion(List tagList) { - List tagsWithDistance = tagList.collect { TagRef tag -> - getTagWithDistance(tag) - } - if (tagsWithDistance) { - tagsWithDistance.sort {} - return tagsWithDistance.min { a, b -> - def distanceCompare = a.distance <=> b.distance - def versionCompare = (a.version <=> b.version) * -1 - distanceCompare == 0 ? versionCompare : distanceCompare - } - } else { - return [version: UNKNOWN, distance: gitCommandUtil.getCommitCountForHead()] - } - } - - private getTagWithDistance(TagRef tag) { + private getLatestTagWithDistance(boolean excludePreReleases) { try { - String result = gitCommandUtil.describeTagForHead(tag.name) + String result = gitCommandUtil.describeHeadWithTags() if(!result) { return [version: UNKNOWN, distance: gitCommandUtil.getCommitCountForHead()] } String[] parts = result.split('-') if(parts.size() < 3) { - return [version: tag.version, distance: 0] + return [version: parseTag(parts[0], true), distance: 0] } - return [version: tag.version, distance: parts[parts.size() - 2]?.toInteger()] + + String commit = parts[parts.size() -1].drop(1) + List allTagsForCommit = gitCommandUtil.getTagsPointingAt(commit).collect { + parseTag(it) + }.findAll { + it && excludePreReleases ? !it.preReleaseVersion : true + } + + if(!allTagsForCommit || allTagsForCommit.every { !it }) { + String tag = parts.size() == 4 ? parts[0..1].join('-') : parts[0] + Version version = parseTag(tag, true) + if(version.preReleaseVersion && excludePreReleases) { + return [version: UNKNOWN, distance: gitCommandUtil.getCommitCountForHead()] + } + return [version: parseTag(tag, true), distance: parts[parts.size() - 2]?.toInteger()] + } + + def highest = allTagsForCommit.min { a, b -> + (a <=> b) * -1 + } + return [version: highest, distance: parts[parts.size() - 2]?.toInteger()] } catch (Exception e) { return [version: UNKNOWN, distance: gitCommandUtil.getCommitCountForHead()] } } + + private static Version parseTag(String name, boolean failOnInvalid = false) { + try { + Version.valueOf(name[0] == 'v' ? name[1..-1] : name) + } catch (Exception e) { + if(!failOnInvalid) { + return null + } + + throw new GradleException("Current commit has following tags: ${name} but they were not recognized as valid versions" ) + } + } }