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)