ru.fix.gradle.release.plugin.BranchGardener.kt Maven / Gradle / Ivy
package ru.fix.gradle.release.plugin
import org.gradle.api.GradleException
import org.gradle.api.Project
import ru.fix.gradle.release.plugin.PluginProperties.CREATE_DEFAULT_RELEASE_BRANCH
class BranchGardener(
private val project: Project,
private val userInteractor: UserInteractor,
private val projectFileSystemLookup: ProjectFilesLookup) {
/**
* Search for released versions based on existing tag names and creates
* new tag with incremented version
*/
fun createRelease() {
val git = projectFileSystemLookup.openGitRepository()
val versionManager = VersionManager(git, userInteractor)
if (git.isUncommittedChangesExist()) {
userInteractor.error("" +
"Could not create new release due to uncommitted changes. " +
"Please commit your current work before creating new release.")
return
}
git.fetchTags()
val extension = project.extensions.findByType(ReleaseExtension::class.java)
checkNotNull(extension) { "Failed to find ReleaseExtension" }
// by default current branch is used as release branch
// but user can specify explicitly which major and minor version to use to create release
val userDefinedMajorMinorVersion: String? =
if (project.hasProperty(PluginProperties.RELEASE_MAJOR_MINOR_VERSION)) {
val majorMinorVersion = project.property(PluginProperties.RELEASE_MAJOR_MINOR_VERSION).toString()
versionManager.assertValidMajorMinorVersion(majorMinorVersion)
majorMinorVersion
} else {
null
}
val branch: String
val fullVersion: String
when (extension.nextReleaseVersionDeterminationSchema) {
ReleaseDetection.MAJOR_MINOR_FROM_BRANCH_NAME_PATCH_FROM_TAG -> {
if (userDefinedMajorMinorVersion != null) {
switchToUserDefinedReleaseBranch(extension.releaseBranchPrefix + userDefinedMajorMinorVersion, git)
}
branch = git.getCurrentBranch()
assertBranchIsAReleaseBranch(extension.releaseBranchPrefix, branch)
val majorMinorVersionFromBranch = versionManager.extractVersionFromBranch(branch)
fullVersion = versionManager.supposeReleaseVersion(majorMinorVersionFromBranch)
}
ReleaseDetection.MAJOR_MINOR_PATCH_FROM_TAG -> {
branch = git.getCurrentBranch()
fullVersion = if (userDefinedMajorMinorVersion != null)
versionManager.supposeReleaseVersion(userDefinedMajorMinorVersion)
else
versionManager.supposeReleaseVersion()
}
}
userInteractor.info("Creating release for version $fullVersion")
val gradlePropertiesFile = projectFileSystemLookup.findGradlePropertiesFile()
val tempBranch = "temp_gradle_release_plugin/$fullVersion"
if (git.isLocalBranchExists(tempBranch)) {
throw GradleException("Temporary branch $tempBranch already exists. Please delete it first")
}
git.createBranch(tempBranch, true)
versionManager.updateVersionInFile(gradlePropertiesFile.toAbsolutePath(), fullVersion)
git.commitFilesInIndex(extension.commitMessage(fullVersion))
val tagRef = git.createTag(extension.tagName(fullVersion), extension.commitMessage(fullVersion))
if (project.hasProperty(PluginProperties.CHECKOUT_TAG) &&
project.property(PluginProperties.CHECKOUT_TAG).toString().toBoolean()) {
git.checkoutTag(fullVersion)
} else {
git.checkoutLocalBranch(branch)
}
git.deleteBranch(tempBranch)
git.pushTag(tagRef)
}
private fun switchToUserDefinedReleaseBranch(releaseBranch: String, git: GitRepository) {
userInteractor.info("Using user defined branch: $releaseBranch")
if (git.getCurrentBranch() != releaseBranch) {
userInteractor.info("Switching to release branch $releaseBranch")
if (git.isLocalBranchExists(releaseBranch)) {
git.checkoutLocalBranch(releaseBranch)
} else {
git.checkoutRemoteBranch(releaseBranch)
}
}
}
private fun assertBranchIsAReleaseBranch(branchPrefix: String, currentBranch: String) {
val pattern = "$branchPrefix(\\d+)\\.(\\d+)"
userInteractor.info("Checking that branch '$currentBranch' matches release branch naming pattern '$pattern'")
if (!Regex(pattern).matches(currentBranch)) {
throw BranchDoesNotMatchReleaseBranchNamingConvention(
"Current branch $currentBranch does not match pattern '$pattern'")
}
}
fun createReleaseBranch() {
val git = projectFileSystemLookup.openGitRepository()
val versionManager = VersionManager(git, userInteractor)
if (git.isUncommittedChangesExist()) {
userInteractor.error("" +
"Could not create new release branch due to uncommitted changes. " +
"Please commit your current work before creating new release branch.")
return
}
val extension = project.extensions.findByType(ReleaseExtension::class.java)
checkNotNull(extension) { "Failed to find ReleaseExtension" }
val currentBranch = git.getCurrentBranch()
userInteractor.info("Creating new release branch based on: $currentBranch")
val supposedVersion = versionManager.supposeBranchVersion()
val userVersion = if (
project.hasProperty(CREATE_DEFAULT_RELEASE_BRANCH) &&
project.property(CREATE_DEFAULT_RELEASE_BRANCH).toString().toBoolean()) {
supposedVersion
} else {
userInteractor.promptQuestion(
"Please specify release version in x.y format (Default: $supposedVersion)",
supposedVersion)
}
if (versionManager.branchVersionExists(userVersion)) {
userInteractor.info("Version $userVersion already exists")
return
}
versionManager.assertValidMajorMinorVersion(userVersion)
val branch = "${extension.releaseBranchPrefix}$userVersion"
if (git.isLocalBranchExists(branch)) {
userInteractor.info("Branch with name $branch already exists")
return
}
git.createBranch(branch, true)
userInteractor.info("Branch $branch was successfully created based on $currentBranch")
}
}
class BranchDoesNotMatchReleaseBranchNamingConvention(message: String) : Exception(message)