-
Notifications
You must be signed in to change notification settings - Fork 76
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
**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
Showing
5 changed files
with
162 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,4 +13,4 @@ jobs: | |
java-version: 8 | ||
cache: sbt | ||
- uses: sbt/setup-sbt@v1 | ||
- run: sbt +compile | ||
- run: sbt +test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 ~= { | ||
|
@@ -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 | ||
|
@@ -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.") | ||
|
@@ -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 | ||
} | ||
|
@@ -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 | ||
} | ||
|
@@ -210,10 +228,49 @@ 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 nonComment = | ||
if (ver.contains("#")) ver.split("#").head | ||
else ver | ||
val commands0 = nonComment.split("@").toList.drop(1) | ||
var nonDigit = false | ||
val commands = (commands0.map { cmd => | ||
if (cmd.isEmpty) sys.error(s"Invalid back-publish version: $ver") | ||
else { | ||
if (!cmd.head.isDigit) { | ||
nonDigit = true | ||
cmd | ||
} | ||
else if (cmd.contains(".x")) s"++${cmd}" | ||
else s"++${cmd}!" | ||
} | ||
}) ::: (if (nonDigit) Nil else List("publishSigned")) | ||
commands match { | ||
case x :: Nil => x | ||
case xs => xs.mkString(";", ";", "") | ||
} | ||
} 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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
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("Commands can be chained") { | ||
assertEquals(backPubVersionToCommand("[email protected]@foo/publishSigned"), ";++2.12.20!;foo/publishSigned") | ||
assertEquals(dropBackPubCommand("[email protected]@foo/publishSigned"), expectedVer) | ||
|
||
assertEquals(backPubVersionToCommand("1.1.0@foo/something@bar/publishSigned"), ";foo/something;bar/publishSigned") | ||
assertEquals(dropBackPubCommand("1.1.0@foo/something@bar/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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -356,6 +356,56 @@ page will look like this: | |
|
||
Enjoy 👌 | ||
|
||
### Back-publishing support | ||
|
||
sbt-ci-release implements a mini-DSL for the Git tag for back publishing purpose, which is useful if you maintain a compiler plugin, library for Scala Native, or an sbt plugin during 2.x migration etc. | ||
|
||
``` | ||
v1.2.3[@3.x|@2.n.x|@a.b.c][@command][#comment] | ||
``` | ||
- `#` is used for comments, which is useful if you need to use the same command multiple times | ||
- `@3.x` expands to `++3.x`, and if no other commands follow, `;++3.x;publishSigned` | ||
- `@2.13.x` expands to `++2.13.x`, and if no other commands follow, `;++2.13.x;publishSigned` | ||
- Other commands such as `@foo/publishSigned` expands to `foo/publishSigned` | ||
#### Case 1: Publish all subprojects for Scala 2.13.15 | ||
`[email protected]` | ||
#### Case 2: Publish all subprojects for Scala 3.x | ||
`[email protected]`. Optionally we can add a comment: `[email protected]#comment`. | ||
We can use this to back publish sbt 2.x plugins. | ||
1. Branch off of `v1.2.3` to create `release/1.2.3` branch, and send a PR to update sbt version | ||
2. Tag the brach to `[email protected]#sbt2.0.0-Mn` | ||
#### Case 3: Publish some subprojects for Scala 2.13.15 | ||
`[email protected]@foo/publishSigned` | ||
You can create a subproject to aggregate 2 or more subprojects. | ||
#### Case 4: Publish some subprojects for supported Scala versions | ||
`v1.2.3@+foo_native/publishSigned#comment` | ||
1. Branch off of `v1.2.3` to create `release/1.2.3` branch, and send a PR to update the Scala Native version. | ||
2. Tag the branch to `v1.2.3@+foo_native/publishSigned#native0.5` | ||
#### Case 5: Minimize the use of command | ||
`v1.2.3#unique_comment`, for example `v1.2.3#native0.5_3` | ||
If you prefer to keep most of the information in a git branch instead, you can just use the comment functionality. | ||
1. Branch off of `v1.2.3` to create `release/1.2.3` branch, and send a PR to: | ||
a. Update appropriate dependency (sbt, Scala Native etc) | ||
b. Modify the `CI_RELEASE` environment variable to encode the actions you want to take, like `;++3.x;foo_native/publishSigned`. For GitHub Actions, it would be in `.github/workflows/release.yml` | ||
2. Tag the branch to `v1.2.3#unique_comment`. For record keeping, encode the version you're trying to back publishing for e.g. `v1.2.3#native0.5_3` | ||
## FAQ | ||
### How do I publish to Sonatype Central? | ||
|