diff --git a/.teamcity/_Self/buildTypes/ReleaseEapFromBranch.kt b/.teamcity/_Self/buildTypes/ReleaseEapFromBranch.kt new file mode 100644 index 000000000..e094f2c78 --- /dev/null +++ b/.teamcity/_Self/buildTypes/ReleaseEapFromBranch.kt @@ -0,0 +1,106 @@ +package _Self.buildTypes + +import _Self.Constants.EAP_CHANNEL +import _Self.Constants.RELEASE_EAP +import _Self.IdeaVimBuildType +import jetbrains.buildServer.configs.kotlin.v2019_2.CheckoutMode +import jetbrains.buildServer.configs.kotlin.v2019_2.DslContext +import jetbrains.buildServer.configs.kotlin.v2019_2.ParameterDisplay +import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.sshAgent +import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.gradle +import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.script +import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnMetric +import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnMetricChange + +object ReleaseEapFromBranch : IdeaVimBuildType({ + name = "EXP: Publish EAP Build from branch" + description = "Build and publish EAP of IdeaVim plugin" + + artifactRules = "build/distributions/*" + + params { + param("env.ORG_GRADLE_PROJECT_ideaVersion", RELEASE_EAP) + password( + "env.ORG_GRADLE_PROJECT_publishToken", + "credentialsJSON:61a36031-4da1-4226-a876-b8148bf32bde", + label = "Password" + ) + param("env.ORG_GRADLE_PROJECT_publishChannels", EAP_CHANNEL) + password( + "env.ORG_GRADLE_PROJECT_slackUrl", + "credentialsJSON:a8ab8150-e6f8-4eaf-987c-bcd65eac50b5", + label = "Slack Token" + ) + password( + "env.YOUTRACK_TOKEN", + "credentialsJSON:2479995b-7b60-4fbb-b095-f0bafae7f622", + display = ParameterDisplay.HIDDEN + ) + } + + vcs { + root(DslContext.settingsRoot) + branchFilter = """ + +:heads/releases/* + """.trimIndent() + + checkoutMode = CheckoutMode.AUTO + } + + steps { + script { + name = "Pull git tags" + scriptContent = "git fetch --tags origin" + } + script { + name = "Pull git history" + scriptContent = "git fetch --unshallow" + } + gradle { + name = "Calculate new eap version from branch" + tasks = "scripts:calculateNewEapVersionFromBranch" + } + gradle { + name = "Set TeamCity build number" + tasks = "scripts:setTeamCityBuildNumber" + } + gradle { + name = "Add release tag" + tasks = "scripts:addReleaseTag" + } + gradle { + name = "Publish plugin" + tasks = "publishPlugin" + } + script { + name = "Push changes to the repo" + scriptContent = """ + branch=$(git branch --show-current) + echo current branch is ${'$'}branch + git push origin %build.number% + """.trimIndent() + } + gradle { + name = "YouTrack post release actions" + tasks = "scripts:eapReleaseActions" + } + } + + features { + sshAgent { + teamcitySshKey = "IdeaVim ssh keys" + } + } + + failureConditions { + failOnMetricChange { + metric = BuildFailureOnMetric.MetricType.ARTIFACT_SIZE + threshold = 5 + units = BuildFailureOnMetric.MetricUnit.PERCENTS + comparison = BuildFailureOnMetric.MetricComparison.DIFF + compareTo = build { + buildRule = lastSuccessful() + } + } + } +}) diff --git a/.teamcity/_Self/subprojects/Releases.kt b/.teamcity/_Self/subprojects/Releases.kt index e64d3bc57..0a01f6dd7 100644 --- a/.teamcity/_Self/subprojects/Releases.kt +++ b/.teamcity/_Self/subprojects/Releases.kt @@ -5,6 +5,7 @@ import _Self.buildTypes.PrintReleaseBranch import _Self.buildTypes.PublishVimEngine import _Self.buildTypes.ReleaseDev import _Self.buildTypes.ReleaseEap +import _Self.buildTypes.ReleaseEapFromBranch import _Self.buildTypes.ReleaseMajor import _Self.buildTypes.ReleaseMinor import _Self.buildTypes.ReleasePatch @@ -43,4 +44,5 @@ object Releases : Project({ buildType(CreateNewReleaseBranchFromMaster) buildType(PrintReleaseBranch) + buildType(ReleaseEapFromBranch) }) diff --git a/scripts/build.gradle.kts b/scripts/build.gradle.kts index a6d74e5bf..5c877bbb3 100644 --- a/scripts/build.gradle.kts +++ b/scripts/build.gradle.kts @@ -107,6 +107,13 @@ tasks.register("calculateNewEapVersion", JavaExec::class) { args = listOf("${rootProject.rootDir}") } +tasks.register("calculateNewEapVersionFromBranch", JavaExec::class) { + group = "release" + mainClass.set("scripts.release.CalculateNewEapVersionFromBranchKt") + classpath = sourceSets["main"].runtimeClasspath + args = listOf("${rootProject.rootDir}") +} + tasks.register("calculateNewDevVersion", JavaExec::class) { group = "release" mainClass.set("scripts.release.CalculateNewDevVersionKt") diff --git a/scripts/src/main/kotlin/scripts/release/calculateNewEapVersionFromBranch.kt b/scripts/src/main/kotlin/scripts/release/calculateNewEapVersionFromBranch.kt new file mode 100644 index 000000000..e65a67f99 --- /dev/null +++ b/scripts/src/main/kotlin/scripts/release/calculateNewEapVersionFromBranch.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2003-2023 The IdeaVim authors + * + * Use of this source code is governed by an MIT-style + * license that can be found in the LICENSE.txt file or at + * https://opensource.org/licenses/MIT. + */ + +package scripts.release + +fun main(args: Array<String> ) { + val projectDir = args[0] + println("Working directory: $projectDir") + val branch = withRepo(projectDir) { it.branch } + val (majorBranchVersion, minorBranchVersion) = versions(branch) + val versions = getVersionsExistingVersionsFor(majorBranchVersion, minorBranchVersion, projectDir) + val maxExistingVersion = versions.keys.maxOrNull() + + val nextVersion = if (maxExistingVersion != null) { + if (maxExistingVersion.suffixTokens.isEmpty()) { + maxExistingVersion.nextPatch().withSuffix("eap.1").value + } + else { + check(maxExistingVersion.suffixTokens.size == 2) { + "We should have exactly two suffix tokens. Current tokens: ${maxExistingVersion.suffixTokens.toList()}" + } + check(maxExistingVersion.suffixTokens[0] == "eap") { + "First suffix token must be eap. Current tokens: ${maxExistingVersion.suffixTokens.toList()}" + } + + val newEapNumber = maxExistingVersion.suffixTokens[1].toInt().inc() + maxExistingVersion.withSuffix("eap.$newEapNumber").value + } + } else { + "$majorBranchVersion.$minorBranchVersion.0-eap.1" + } + + + println("Next eap version: $nextVersion") + println("##teamcity[setParameter name='env.ORG_GRADLE_PROJECT_version' value='$nextVersion']") +} + +private val regex = "releases/(\\d+)\\.(\\d+)\\.x".toRegex() +private fun versions(branchName: String): Pair<Int, Int> { + val match = regex.matchEntire(branchName) ?: error("Cannot match branch: $branchName") + val major = match.groups[1] + val minor = match.groups[2] + return major!!.value.toInt() to minor!!.value.toInt() +} \ No newline at end of file diff --git a/scripts/src/main/kotlin/scripts/release/util.kt b/scripts/src/main/kotlin/scripts/release/util.kt index dc0f278b0..2131a60e4 100644 --- a/scripts/src/main/kotlin/scripts/release/util.kt +++ b/scripts/src/main/kotlin/scripts/release/util.kt @@ -64,6 +64,31 @@ enum class ReleaseType { STABLE_NO_PATCH, // Version that ends on 0. Like 2.5.0 } +internal fun getVersionsExistingVersionsFor( + majorVersion: Int, + minorVersion: Int, + projectDir: String, +): Map<Semver, ObjectId> { + val repository = RepositoryBuilder().setGitDir(File("$projectDir/.git")).build() + val git = Git(repository) + println(git.log().call().first()) + println(git.tagList().call().first()) + + return git.tagList().call().mapNotNull { ref -> + runCatching { + // Git has two types of tags: light and annotated. This code detect hash of the commit for both types of tags + val revWalk = RevWalk(repository) + val tag = revWalk.parseAny(ref.objectId) + val commitHash = revWalk.peel(tag).id + val semver = Semver(ref.name.removePrefix("refs/tags/")) + if (semver.major == majorVersion && semver.minor == minorVersion) { + semver to commitHash + } else null + }.getOrNull() + } + .toMap() +} + internal fun getVersion(projectDir: String, releaseType: ReleaseType): Pair<Semver, ObjectId> { val repository = RepositoryBuilder().setGitDir(File("$projectDir/.git")).build() val git = Git(repository)