diff options
author | Nikolay Tatarinov <rockjam@actor.im> | 2016-10-03 20:19:27 +0300 |
---|---|---|
committer | Jan Christopher Vogt <oss.nsp@cvogt.org> | 2016-10-03 13:19:27 -0400 |
commit | 669ef3dfc3201fffa451b47d2b629a856afc0b25 (patch) | |
tree | 7bcb64c58266a4fe2dd3965dda47dc6f8fa71b04 /plugins/sonatype-release/src/sonatype/SonatypeLib.scala | |
parent | 174c52b2c24b8491eef687ee5eb3c3b77c34a61c (diff) | |
download | cbt-669ef3dfc3201fffa451b47d2b629a856afc0b25.tar.gz cbt-669ef3dfc3201fffa451b47d2b629a856afc0b25.tar.bz2 cbt-669ef3dfc3201fffa451b47d2b629a856afc0b25.zip |
Sonatype release plugin (#247)
Diffstat (limited to 'plugins/sonatype-release/src/sonatype/SonatypeLib.scala')
-rw-r--r-- | plugins/sonatype-release/src/sonatype/SonatypeLib.scala | 148 |
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", _) + +} |