Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement back publishing support #322

Merged
merged 1 commit into from
Oct 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ jobs:
java-version: 8
cache: sbt
- uses: sbt/setup-sbt@v1
- run: sbt +compile
- run: sbt +test
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
69 changes: 63 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:git@github.com:$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,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
}
}
47 changes: 47 additions & 0 deletions plugin/src/test/scala/com/geirsson/CiReleaseTest.scala
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("1.1.0@2.12.20"), ";++2.12.20!;publishSigned")
assertEquals(dropBackPubCommand("1.1.0@2.12.20"), expectedVer)

assertEquals(backPubVersionToCommand("1.1.0@3.x"), ";++3.x;publishSigned")
assertEquals(dropBackPubCommand("1.1.0@3.x"), 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("1.1.0@2.12.20@foo/publishSigned"), ";++2.12.20!;foo/publishSigned")
assertEquals(dropBackPubCommand("1.1.0@2.12.20@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("1.1.0@2.12.20#comment"), ";++2.12.20!;publishSigned")
assertEquals(dropBackPubCommand("1.1.0@2.12.20#comment"), expectedVer)

assertEquals(backPubVersionToCommand("1.1.0@3.x#comment"), ";++3.x;publishSigned")
assertEquals(dropBackPubCommand("1.1.0@3.x#comment"), expectedVer)
}
}
50 changes: 50 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

`v1.2.3@2.13.15`

#### Case 2: Publish all subprojects for Scala 3.x

`v1.2.3@3.x`. Optionally we can add a comment: `v1.2.3@3.x#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 `v1.2.3@3.x#sbt2.0.0-Mn`

#### Case 3: Publish some subprojects for Scala 2.13.15

`v1.2.3@2.13.15@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?
Expand Down