Skip to content

Commit

Permalink
Implement back publishing support
Browse files Browse the repository at this point in the history
**Problem**
For plugin code, like sbt plugins and Scala compiler plugin,
and even normal libraries, it's common for the maintainers to
want to back publish the current code base against a new version
of Scala (or sbt, Scala Nave etc) instead of bumping the version.

The current "bump the version" approach effectively pushes the work
of figuring out the correct patch version number to the end users.
In addition, if the plugin or the library does not cross publish
all variants, then the end user might need to figure out different
patch version to use effectively the same code.

**Solution**
This implements a mini DSL for tag `version[@command|@3.3.4][#comment]`,
which allows plugin authors to back publish a code base.
  • Loading branch information
eed3si9n committed Oct 13, 2024
1 parent b1fc142 commit fb64a22
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 6 deletions.
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ lazy val plugin = project
case _ => "2.0.0-M2"
}
},
libraryDependencies += "org.scalameta" %% "munit" % "0.7.29" % Test,
addSbtPlugin("com.github.sbt" % "sbt-dynver" % "5.1.0"),
addSbtPlugin("com.github.sbt" % "sbt-git" % "2.1.0"),
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.0"),
Expand Down
59 changes: 53 additions & 6 deletions plugin/src/main/scala/com/geirsson/CiReleasePlugin.scala
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ object CiReleasePlugin extends AutoPlugin {
Some(s"scm:git:[email protected]:$user/$repo.git")
)

lazy val cireleasePublishCommand = settingKey[String]("")

override lazy val buildSettings: Seq[Def.Setting[_]] = List(
dynverSonatypeSnapshots := true,
scmInfo ~= {
Expand All @@ -128,15 +130,29 @@ object CiReleasePlugin extends AutoPlugin {
} catch {
case NonFatal(_) => None
}
}
},
cireleasePublishCommand := {
val gitDescribe = dynverGitDescribeOutput.value
val v = gitDescribe.getVersion(
dynverCurrentDate.value,
dynverSeparator.value,
dynverSonatypeSnapshots.value
)
sys.env.get("CI_RELEASE") match {
case Some(cmd) => cmd
case None => backPubVersionToCommand(v)
}
},
version ~= dropBackPubCommand,
)

override lazy val globalSettings: Seq[Def.Setting[_]] = List(
(Test / publishArtifact) := false,
publishMavenStyle := true,
commands += Command.command("ci-release") { currentState =>
val shouldDeployToSonatypeCentral = isDeploySetToSonatypeCentral(currentState)
val isSnapshot = isSnapshotVersion(currentState)
val version = getVersion(currentState)
val isSnapshot = isSnapshotVersion(version)
if (!isSecure) {
println("No access to secret variables, doing nothing")
currentState
Expand All @@ -150,6 +166,8 @@ object CiReleasePlugin extends AutoPlugin {
val reloadKeyFiles =
"; set pgpSecretRing := pgpSecretRing.value; set pgpPublicRing := pgpPublicRing.value"

val publishCommand = getPublishCommand(currentState)

if (shouldDeployToSonatypeCentral) {
if (isSnapshot) {
println(s"Sonatype Central does not accept snapshots, only official releases. Aborting release.")
Expand All @@ -161,7 +179,7 @@ object CiReleasePlugin extends AutoPlugin {
println("Tag push detected, publishing a stable release")
reloadKeyFiles ::
sys.env.getOrElse("CI_CLEAN", "; clean ; sonatypeBundleClean") ::
sys.env.getOrElse("CI_RELEASE", "+publishSigned") ::
publishCommand ::
sys.env.getOrElse("CI_SONATYPE_RELEASE", "sonatypeCentralRelease") ::
currentState
}
Expand All @@ -184,7 +202,7 @@ object CiReleasePlugin extends AutoPlugin {
println("Tag push detected, publishing a stable release")
reloadKeyFiles ::
sys.env.getOrElse("CI_CLEAN", "; clean ; sonatypeBundleClean") ::
sys.env.getOrElse("CI_RELEASE", "+publishSigned") ::
publishCommand ::
sys.env.getOrElse("CI_SONATYPE_RELEASE", "sonatypeBundleRelease") ::
currentState
}
Expand All @@ -210,10 +228,39 @@ object CiReleasePlugin extends AutoPlugin {
}
}

def isSnapshotVersion(state: State): Boolean = {
def getVersion(state: State): String =
(ThisBuild / version).get(Project.extract(state).structure.data) match {
case Some(v) => v.endsWith("-SNAPSHOT")
case Some(v) => v
case None => throw new NoSuchFieldError("version")
}

def getPublishCommand(state: State): String =
(ThisBuild / cireleasePublishCommand).get(Project.extract(state).structure.data) match {
case Some(v) => v
case None => throw new NoSuchFieldError("cireleasePublishCommand")
}

def isSnapshotVersion(v: String): Boolean = v.endsWith("-SNAPSHOT")

def backPubVersionToCommand(ver: String): String =
if (ver.contains("@")) {
val afterAt = ver.split("@").drop(1).mkString("@")
val cmd =
if (afterAt.contains("#")) afterAt.split("#").head
else afterAt
if (cmd.isEmpty) sys.error(s"Invalid back-publish version: $ver")
else {
if (!cmd.head.isDigit) cmd
else if (cmd.contains(".x")) s";++${cmd};publishSigned"
else s";++${cmd}!;publishSigned"
}
} else "+publishSigned"

def dropBackPubCommand(ver: String): String = {
val nonComment =
if (ver.contains("#")) ver.split("#").head
else ver
if (nonComment.contains("@")) nonComment.split("@").head
else nonComment
}
}
39 changes: 39 additions & 0 deletions plugin/src/test/scala/com/geirsson/CiReleaseTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.geirsson

import CiReleasePlugin.{ backPubVersionToCommand, dropBackPubCommand }

class CiReleaseTest extends munit.FunSuite {
val expectedVer = "1.1.0"

test("Normal version default") {
assertEquals(backPubVersionToCommand("1.1.0"), "+publishSigned")
assertEquals(dropBackPubCommand("1.1.0"), expectedVer)
}

test("Command starting with number is assumed to be a cross version") {
assertEquals(backPubVersionToCommand("[email protected]"), ";++2.12.20!;publishSigned")
assertEquals(dropBackPubCommand("[email protected]"), expectedVer)

assertEquals(backPubVersionToCommand("[email protected]"), ";++3.x;publishSigned")
assertEquals(dropBackPubCommand("[email protected]"), expectedVer)
}

test("Non-number is treated as an alternative publish command") {
assertEquals(backPubVersionToCommand("1.1.0@foo/publishSigned"), "foo/publishSigned")
assertEquals(dropBackPubCommand("1.1.0@foo/publishSigned"), expectedVer)

assertEquals(backPubVersionToCommand("1.1.0@+foo/publishSigned"), "+foo/publishSigned")
assertEquals(dropBackPubCommand("1.1.0@+foo/publishSigned"), expectedVer)
}

test("Treat # as comments") {
assertEquals(backPubVersionToCommand("1.1.0#comment"), "+publishSigned")
assertEquals(dropBackPubCommand("1.1.0#comment"), expectedVer)

assertEquals(backPubVersionToCommand("[email protected]#comment"), ";++2.12.20!;publishSigned")
assertEquals(dropBackPubCommand("[email protected]#comment"), expectedVer)

assertEquals(backPubVersionToCommand("[email protected]#comment"), ";++3.x;publishSigned")
assertEquals(dropBackPubCommand("[email protected]#comment"), expectedVer)
}
}

0 comments on commit fb64a22

Please sign in to comment.