aboutsummaryrefslogtreecommitdiff
path: root/plugins/sonatype-release/src/sonatype/SonatypeLib.scala
diff options
context:
space:
mode:
authorChristopher Vogt <oss.nsp@cvogt.org>2016-11-07 02:21:50 -0500
committerChristopher Vogt <oss.nsp@cvogt.org>2016-11-07 02:21:50 -0500
commitc6b9a480879c101028b20b9cc8716b8ffa773630 (patch)
treefff15c1d6595455f6994f5793b63b08c5bf24d4a /plugins/sonatype-release/src/sonatype/SonatypeLib.scala
parentc89f87c9c9a0c7b256f225e37c55cb34f060aa6c (diff)
parentfd849d293448d55c6bcb6f8440f44838b51fc860 (diff)
downloadcbt-c6b9a480879c101028b20b9cc8716b8ffa773630.tar.gz
cbt-c6b9a480879c101028b20b9cc8716b8ffa773630.tar.bz2
cbt-c6b9a480879c101028b20b9cc8716b8ffa773630.zip
Merge remote-tracking branch 'origin/master' into integrate-eval
Diffstat (limited to 'plugins/sonatype-release/src/sonatype/SonatypeLib.scala')
-rw-r--r--plugins/sonatype-release/src/sonatype/SonatypeLib.scala148
1 files changed, 148 insertions, 0 deletions
diff --git a/plugins/sonatype-release/src/sonatype/SonatypeLib.scala b/plugins/sonatype-release/src/sonatype/SonatypeLib.scala
new file mode 100644
index 0000000..9aab9f5
--- /dev/null
+++ b/plugins/sonatype-release/src/sonatype/SonatypeLib.scala
@@ -0,0 +1,148 @@
+package cbt.sonatype
+
+import java.io.File
+import java.net.URL
+import java.nio.file.Files._
+import java.nio.file.Paths
+
+import cbt.{ ExitCode, Lib }
+
+/**
+ * Sonatype release process is:
+ * • get your profile info to publish artifacts
+ * • open staging repository to publish artifacts
+ * • publish signed artifacts and signatures to staging repository
+ * • close staging repository
+ * • promote staging repository
+ * • drop staging repository
+ */
+
+object SonatypeLib {
+
+ val sonatypeServiceURI: String = "https://oss.sonatype.org/service/local"
+
+ val sonatypeSnapshotsURI: String = "https://oss.sonatype.org/content/repositories/snapshots"
+
+ /**
+ * login:password for Sonatype access.
+ * Order of credentials lookup:
+ * • environment variables SONATYPE_USERNAME and SONATYPE_PASSWORD
+ * • ~/.cbt/sonatype-credentials
+ */
+ def sonatypeCredentials: String = {
+ def fromEnv = for {
+ username <- Option(System.getenv("SONATYPE_USERNAME"))
+ password <- Option(System.getenv("SONATYPE_PASSWORD"))
+ } yield s"$username:$password"
+
+ def fromFile = {
+ for {
+ home <- Option(System.getProperty("user.home"))
+ credsPath = Paths.get(home, ".cbt", "sonatype-credentials")
+ } yield new String(readAllBytes(credsPath)).trim
+ }
+
+ fromEnv
+ .orElse(fromFile)
+ .getOrElse(throw new Exception(
+ "No Sonatype credentials found! You can provide them via SONATYPE_USERNAME, SONATYPE_PASSWORD env variables, " +
+ "or in ~/.cbt/sonatype-credentials file as login:password"
+ ))
+ }
+}
+
+final class SonatypeLib(
+ sonatypeServiceURI: String,
+ sonatypeSnapshotsURI: String,
+ sonatypeCredentials: String,
+ profileName: String)(lib: Lib) {
+
+ private val sonatypeApi = new SonatypeHttpApi(sonatypeServiceURI, sonatypeCredentials, profileName)(sonatypeLogger)
+
+ /*
+ * Signed publish steps:
+ * • create new staging repo
+ * • create artifacts and sign them
+ * • publish jars to created repo
+ */
+ def sonatypePublishSigned(
+ sourceFiles: Seq[File],
+ artifacts: Seq[File],
+ groupId: String,
+ artifactId: String,
+ version: String,
+ isSnapshot: Boolean,
+ scalaMajorVersion: String
+ ): ExitCode = {
+ if(sourceFiles.nonEmpty) {
+ System.err.println(lib.blue("Staring publishing to Sonatype."))
+
+ val profile = getStagingProfile()
+
+ val deployURI = (if (isSnapshot) {
+ sonatypeSnapshotsURI
+ } else {
+ val repoId = sonatypeApi.createStagingRepo(profile)
+ s"${sonatypeServiceURI}/staging/deployByRepositoryId/${repoId.repositoryId}"
+ }) + s"/${groupId.replace(".", "/")}/${artifactId}_${scalaMajorVersion}/${version}"
+
+ lib.publishSigned(
+ artifacts = artifacts,
+ url = new URL(deployURI),
+ credentials = Some(sonatypeCredentials)
+ )
+ System.err.println(lib.green("Successfully published artifacts to Sonatype."))
+ ExitCode.Success
+ } else {
+ System.err.println(lib.red("Sources are empty, won't publish empty jar."))
+ ExitCode.Failure
+ }
+ }
+
+ /**
+ * Release is:
+ * • find staging repo related to current profile;
+ * • close this staging repo;
+ * • wait until this repo is released;
+ * • drop this repo.
+ */
+ def sonatypeRelease(
+ groupId: String,
+ artifactId: String,
+ version: String
+ ): ExitCode = {
+ val profile = getStagingProfile()
+
+ sonatypeApi.getStagingRepos(profile).toList match {
+ case Nil =>
+ System.err.println(lib.red("No staging repositories found, you need to publish artifacts first."))
+ ExitCode.Failure
+ case repo :: Nil =>
+ sonatypeApi.finishRelease(repo, profile)
+ System.err.println(lib.green(s"Successfully released ${groupId}/${artifactId} v:${version}"))
+ ExitCode.Success
+ case repos =>
+ val showRepo = { r: StagingRepository => s"${r.repositoryId} in state: ${r.state}" }
+ val toRelease = lib.pickOne(lib.blue(s"More than one staging repo found. Select one of them:"), repos)(showRepo)
+
+ toRelease map { repo =>
+ sonatypeApi.finishRelease(repo, profile)
+ System.err.println(lib.green(s"Successfully released ${groupId}/${artifactId} v:${version}"))
+ ExitCode.Success
+ } getOrElse {
+ System.err.println(lib.red("Wrong repository number, try again please."))
+ ExitCode.Failure
+ }
+ }
+ }
+
+ private def getStagingProfile() =
+ try {
+ sonatypeApi.getStagingProfile
+ } catch {
+ case e: Exception => throw new Exception(s"Failed to get info for profile: $profileName", e)
+ }
+
+ private def sonatypeLogger: String => Unit = lib.logger.log("Sonatype", _)
+
+}