diff --git a/.gitignore b/.gitignore index 805e89e3..8e81fb93 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,12 @@ *.ipr *.iws +# Eclipse project files +.classpath +.project +.settings/ +bin/ + # gradle config .gradle diff --git a/src/main/groovy/pl/allegro/tech/build/axion/release/domain/TagNameSerializationConfig.groovy b/src/main/groovy/pl/allegro/tech/build/axion/release/domain/TagNameSerializationConfig.groovy index 1397203f..f762187f 100644 --- a/src/main/groovy/pl/allegro/tech/build/axion/release/domain/TagNameSerializationConfig.groovy +++ b/src/main/groovy/pl/allegro/tech/build/axion/release/domain/TagNameSerializationConfig.groovy @@ -2,6 +2,7 @@ package pl.allegro.tech.build.axion.release.domain import pl.allegro.tech.build.axion.release.domain.properties.TagProperties import pl.allegro.tech.build.axion.release.domain.scm.ScmPosition +import pl.allegro.tech.build.axion.release.domain.scm.ScmRepository; class TagNameSerializationConfig { @@ -20,10 +21,16 @@ class TagNameSerializationConfig { Closure deserialize = TagNameSerializer.DEFAULT.deserializer Closure initialVersion = defaultInitialVersion() + + Closure tagSelector = defaultTagSelector() private static Closure defaultInitialVersion() { return { TagProperties rules, ScmPosition position -> return '0.1.0' } } + + private static Closure defaultTagSelector() { + return ScmRepository.LAST_TAG_SELECTOR + } } diff --git a/src/main/groovy/pl/allegro/tech/build/axion/release/domain/VersionResolver.groovy b/src/main/groovy/pl/allegro/tech/build/axion/release/domain/VersionResolver.groovy index ce321b16..41c4f0fa 100644 --- a/src/main/groovy/pl/allegro/tech/build/axion/release/domain/VersionResolver.groovy +++ b/src/main/groovy/pl/allegro/tech/build/axion/release/domain/VersionResolver.groovy @@ -8,16 +8,16 @@ import pl.allegro.tech.build.axion.release.domain.scm.ScmPosition import pl.allegro.tech.build.axion.release.domain.scm.ScmRepository class VersionResolver { - + private final ScmRepository repository private final VersionFactory versionFactory - + VersionResolver(ScmRepository repository, VersionFactory versionFactory) { this.repository = repository this.versionFactory = versionFactory } - + VersionWithPosition resolveVersion(VersionProperties versionRules, TagProperties tagRules, NextVersionProperties nextVersionRules) { Map positions = readPositions(tagRules, nextVersionRules) @@ -29,27 +29,27 @@ class VersionResolver { if(positions.currentPosition.nextVersionTag) { position = position.asNotOnTagPosition() } - + return new VersionWithPosition(currentVersion, previousVersion, position) } private Map readPositions(TagProperties tagRules, NextVersionProperties nextVersionRules) { - ScmPosition currentPosition = repository.currentPosition(~/^${tagRules.prefix}.*(|${nextVersionRules.suffix})$/) + ScmPosition currentPosition = repository.currentPosition(~/^${tagRules.prefix}.*(|${nextVersionRules.suffix})$/, tagRules.tagSelector) ScmPositionContext currentPositionContext = new ScmPositionContext(currentPosition, nextVersionRules) ScmPosition lastReleasePosition if(currentPositionContext.nextVersionTag) { - lastReleasePosition = repository.currentPosition(~/^${tagRules.prefix}.*/, ~/.*${nextVersionRules.suffix}$/).asOnTagPosition() + lastReleasePosition = repository.currentPosition(~/^${tagRules.prefix}.*/, ~/.*${nextVersionRules.suffix}$/, tagRules.tagSelector).asOnTagPosition() } else { lastReleasePosition = currentPosition.asOnTagPosition() } - + return [ currentPosition: currentPositionContext, lastReleasePosition: new ScmPositionContext(lastReleasePosition, nextVersionRules) ] } - + } diff --git a/src/main/groovy/pl/allegro/tech/build/axion/release/domain/properties/TagProperties.groovy b/src/main/groovy/pl/allegro/tech/build/axion/release/domain/properties/TagProperties.groovy index 133cb968..9db88472 100644 --- a/src/main/groovy/pl/allegro/tech/build/axion/release/domain/properties/TagProperties.groovy +++ b/src/main/groovy/pl/allegro/tech/build/axion/release/domain/properties/TagProperties.groovy @@ -15,4 +15,6 @@ class TagProperties { final Closure initialVersion + final Closure tagSelector + } diff --git a/src/main/groovy/pl/allegro/tech/build/axion/release/domain/scm/ScmRepository.groovy b/src/main/groovy/pl/allegro/tech/build/axion/release/domain/scm/ScmRepository.groovy index a3d9cffe..379cb92f 100644 --- a/src/main/groovy/pl/allegro/tech/build/axion/release/domain/scm/ScmRepository.groovy +++ b/src/main/groovy/pl/allegro/tech/build/axion/release/domain/scm/ScmRepository.groovy @@ -2,7 +2,12 @@ package pl.allegro.tech.build.axion.release.domain.scm import java.util.regex.Pattern +import groovy.lang.Closure; + interface ScmRepository { + public static final Closure LAST_TAG_SELECTOR = { List tags -> + tags && tags.size() > 0 ? tags[-1] : null + } void fetchTags(ScmIdentity identity, String remoteName) @@ -17,8 +22,10 @@ interface ScmRepository { String currentBranch() ScmPosition currentPosition(Pattern tagPattern) - - ScmPosition currentPosition(Pattern tagPattern, Pattern inversePattern) + + ScmPosition currentPosition(Pattern tagPattern, Closure tagSelector) + + ScmPosition currentPosition(Pattern tagPattern, Pattern inversePattern, Closure tagSelector) boolean remoteAttached(String remoteName); diff --git a/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/DryRepository.groovy b/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/DryRepository.groovy index 1c1b2ffe..45b0cb31 100644 --- a/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/DryRepository.groovy +++ b/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/DryRepository.groovy @@ -9,7 +9,7 @@ import pl.allegro.tech.build.axion.release.domain.scm.ScmRepository import java.util.regex.Pattern class DryRepository implements ScmRepository { - + private static final ReleaseLogger logger = ReleaseLogger.Factory.logger(DryRepository) private final ScmRepository delegateRepository @@ -50,15 +50,20 @@ class DryRepository implements ScmRepository { } @Override - ScmPosition currentPosition(Pattern tagPattern, Pattern inversePattern) { - return currentPosition(tagPattern) + ScmPosition currentPosition(Pattern tagPattern, Closure tagSelector) { + ScmPosition position = delegateRepository.currentPosition(tagPattern, tagSelector) + log("scm position: $position") + return position } - + + @Override + ScmPosition currentPosition(Pattern tagPattern, Pattern inversePattern, Closure tagSelector) { + return currentPosition(tagPattern, tagSelector) + } + @Override ScmPosition currentPosition(Pattern tagPattern) { - ScmPosition position = delegateRepository.currentPosition(tagPattern) - log("scm position: $position") - return position + return currentPosition(tagPattern, LAST_TAG_SELECTOR) } @Override diff --git a/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/DummyRepository.groovy b/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/DummyRepository.groovy index aed4d0b7..da045be4 100644 --- a/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/DummyRepository.groovy +++ b/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/DummyRepository.groovy @@ -14,7 +14,7 @@ class DummyRepository implements ScmRepository { DummyRepository() { } - + private void log(String commandName) { logger.quiet("Couldn't perform $commandName command on uninitialized repository") } @@ -54,9 +54,14 @@ class DummyRepository implements ScmRepository { logger.quiet("Could not resolve current position on uninitialized repository, returning default") return ScmPosition.defaultPosition() } - + + @Override + ScmPosition currentPosition(Pattern tagPattern, Closure tagSelector) { + return currentPosition(tagPattern) + } + @Override - ScmPosition currentPosition(Pattern tagPattern, Pattern inversePattern) { + ScmPosition currentPosition(Pattern tagPattern, Pattern inversePattern, Closure tagSelector) { return currentPosition(tagPattern) } diff --git a/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/config/TagPropertiesFactory.groovy b/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/config/TagPropertiesFactory.groovy index 32bde5c4..768f0cd2 100644 --- a/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/config/TagPropertiesFactory.groovy +++ b/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/config/TagPropertiesFactory.groovy @@ -13,7 +13,8 @@ class TagPropertiesFactory { versionSeparator: config.versionSeparator, serialize: config.serialize, deserialize: config.deserialize, - initialVersion: config.initialVersion + initialVersion: config.initialVersion, + tagSelector: config.tagSelector ) } diff --git a/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/GitRepository.groovy b/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/GitRepository.groovy index 69f358ad..7e4d1b17 100644 --- a/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/GitRepository.groovy +++ b/src/main/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/GitRepository.groovy @@ -32,9 +32,9 @@ class GitRepository implements ScmRepository { private static final String GIT_TAG_PREFIX = 'refs/tags/' private final TransportConfigFactory transportConfigFactory = new TransportConfigFactory() - + private final File repositoryDir - + private final Grgit repository private final ScmProperties properties @@ -159,38 +159,47 @@ class GitRepository implements ScmRepository { @Override ScmPosition currentPosition(Pattern pattern) { - return currentPosition(pattern, Pattern.compile('$a^')) + return currentPosition(pattern, Pattern.compile('$a^'), LAST_TAG_SELECTOR) } - + @Override - ScmPosition currentPosition(Pattern pattern, Pattern inversePattern) { + ScmPosition currentPosition(Pattern pattern, Closure tagSelector) { + return currentPosition(pattern, Pattern.compile('$a^'), tagSelector) + } + + @Override + ScmPosition currentPosition(Pattern pattern, Pattern inversePattern, Closure tagSelector) { if(!hasCommits()) { return ScmPosition.defaultPosition() } Map tags = repository.tag.list() .grep({ def tag = it.fullName.substring(GIT_TAG_PREFIX.length()); tag ==~ pattern && !(tag ==~ inversePattern) }) - .inject([:], { map, entry -> map[entry.commit.id] = entry.fullName.substring(GIT_TAG_PREFIX.length()); return map; }) + .inject([:].withDefault {p -> []}, { map, entry -> + map[entry.commit.id] << entry.fullName.substring(GIT_TAG_PREFIX.length()) + return map + }) ObjectId headId = repository.repository.jgit.repository.resolve(Constants.HEAD) String branch = repository.branch.current.name RevWalk walk = new RevWalk(repository.repository.jgit.repository) walk.sort(RevSort.TOPO) + walk.sort(RevSort.COMMIT_TIME_DESC, true) RevCommit head = walk.parseCommit(headId) - String tagName = null + List tagNameList = null walk.markStart(head) RevCommit commit for (commit = walk.next(); commit != null; commit = walk.next()) { - tagName = tags[commit.id.name()] - if (tagName != null) { + tagNameList = tags[commit.id.name()] + if (tagNameList) { break } } walk.dispose() - + String tagName = tagSelector(tagNameList) boolean onTag = (commit == null) ? null : commit.id.name() == headId.name() return new ScmPosition(branch, tagName, onTag, checkUncommittedChanges()) } diff --git a/src/test/groovy/pl/allegro/tech/build/axion/release/domain/properties/TagPropertiesBuilder.groovy b/src/test/groovy/pl/allegro/tech/build/axion/release/domain/properties/TagPropertiesBuilder.groovy index 8cbfe12b..f5b2b237 100644 --- a/src/test/groovy/pl/allegro/tech/build/axion/release/domain/properties/TagPropertiesBuilder.groovy +++ b/src/test/groovy/pl/allegro/tech/build/axion/release/domain/properties/TagPropertiesBuilder.groovy @@ -1,6 +1,7 @@ package pl.allegro.tech.build.axion.release.domain.properties import pl.allegro.tech.build.axion.release.domain.TagNameSerializer +import pl.allegro.tech.build.axion.release.domain.scm.ScmRepository; class TagPropertiesBuilder { @@ -17,7 +18,8 @@ class TagPropertiesBuilder { deserialize: TagNameSerializer.DEFAULT.deserializer, prefix: 'release', versionSeparator: '-', - initialVersion: { r, p -> '0.1.0' } + initialVersion: { r, p -> '0.1.0' }, + tagSelector: ScmRepository.LAST_TAG_SELECTOR ) } diff --git a/src/test/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/DryRepositoryTest.groovy b/src/test/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/DryRepositoryTest.groovy index 9aa8e903..b857c395 100644 --- a/src/test/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/DryRepositoryTest.groovy +++ b/src/test/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/DryRepositoryTest.groovy @@ -40,7 +40,7 @@ class DryRepositoryTest extends Specification { def "should return currentPosition from real scm"() { given: ScmPosition expectedPosition = new ScmPosition("master", "latest", true) - scm.currentPosition(_) >> expectedPosition + scm.currentPosition(_, _) >> expectedPosition when: ScmPosition currentPosition = dryRepository.currentPosition(~/^release.*/) diff --git a/src/test/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/GitRepositoryTest.groovy b/src/test/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/GitRepositoryTest.groovy index 110b10d5..9f9926bd 100644 --- a/src/test/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/GitRepositoryTest.groovy +++ b/src/test/groovy/pl/allegro/tech/build/axion/release/infrastructure/git/GitRepositoryTest.groovy @@ -30,10 +30,10 @@ class GitRepositoryTest extends Specification { void setup() { Project remoteProject = ProjectBuilder.builder().build() remoteRawRepository = GitProjectBuilder.gitProject(remoteProject).withInitialCommit().build()[Grgit] - + project = ProjectBuilder.builder().build() Map repositories = GitProjectBuilder.gitProject(project, remoteProject).build() - + rawRepository = repositories[Grgit] repository = repositories[GitRepository] } @@ -111,10 +111,30 @@ class GitRepositoryTest extends Specification { !position.onTag } + def "should use the given closure to select one of multiple tags on the same commit"() { + given: + repository.tag('release-1.0.0-rc1') + repository.commit(['*'], "commit after release") + repository.tag('release-1.0.0-rc2') + repository.tag('release-1.0.0') + + Closure tagSelector = { tags -> + assert tags == ['release-1.0.0', 'release-1.0.0-rc2'] + return 'release-1.0.0' + } + + when: + ScmPosition position = repository.currentPosition(~/^release.*/, tagSelector) + + then: + position.latestTag == 'release-1.0.0' + position.onTag + } + def "should return default position when no commit in repository"() { given: GitRepository commitlessRepository = GitProjectBuilder.gitProject(ProjectBuilder.builder().build()).build()[GitRepository] - + when: ScmPosition position = commitlessRepository.currentPosition(~/^release.*/) @@ -215,7 +235,7 @@ class GitRepositoryTest extends Specification { def "should not push commits if the pushTagsOnly flag is set to true"() { repository.tag('release-push') repository.commit(['*'], 'commit after release-push') - + when: repository.push(ScmIdentity.defaultIdentity(), new ScmPushOptions(remote: 'origin', pushTagsOnly: true))