diff --git a/src/main/scala/xerial/sbt/Sonatype.scala b/src/main/scala/xerial/sbt/Sonatype.scala index 357819c4..66a8e3b7 100755 --- a/src/main/scala/xerial/sbt/Sonatype.scala +++ b/src/main/scala/xerial/sbt/Sonatype.scala @@ -12,8 +12,8 @@ import sbt._ import sbt.librarymanagement.MavenRepository import wvlet.log.{LogLevel, LogSupport} import xerial.sbt.sonatype.SonatypeClient.StagingRepositoryProfile -import xerial.sbt.sonatype.SonatypeService._ -import xerial.sbt.sonatype.{SonatypeClient, SonatypeException, SonatypeService} +import xerial.sbt.sonatype.SonatypeService.* +import xerial.sbt.sonatype.{EnvironmentCredentials, SonatypeClient, SonatypeException, SonatypeService} import scala.concurrent.duration.Duration import scala.concurrent.{Await, ExecutionContext, Future} @@ -77,15 +77,13 @@ object Sonatype extends AutoPlugin with LogSupport { case _ => false } if (!alreadyContainsSonatypeCredentials) { - val env = sys.env.get(_) (for { - username <- env("SONATYPE_USERNAME") - password <- env("SONATYPE_PASSWORD") + usernameAndPassword <- EnvironmentCredentials.getUsernameAndPassword() } yield Credentials( "Sonatype Nexus Repository Manager", sonatypeCredentialHost.value, - username, - password + usernameAndPassword.username, + usernameAndPassword.password )).toSeq } else Seq.empty }, diff --git a/src/main/scala/xerial/sbt/sonatype/EnvironmentCredentials.scala b/src/main/scala/xerial/sbt/sonatype/EnvironmentCredentials.scala new file mode 100644 index 00000000..54520562 --- /dev/null +++ b/src/main/scala/xerial/sbt/sonatype/EnvironmentCredentials.scala @@ -0,0 +1,23 @@ +package xerial.sbt.sonatype + +final class UsernameAndPassword(val username: String, val password: String) + +object UsernameAndPassword { + def fromToken(token: String): UsernameAndPassword = token.split(':') match { + case Array(user, pass) => new UsernameAndPassword(user, pass) + } +} + +object EnvironmentCredentials { + val Username = "SONATYPE_USERNAME" + val Password = "SONATYPE_PASSWORD" + val Token = "SONATYPE_TOKEN" + + def getUsernameAndPassword(envVars: Map[String, String] = sys.env): Option[UsernameAndPassword] = + envVars.get(Token).map(UsernameAndPassword.fromToken).orElse { + for { + u <- envVars.get(Username) + p <- envVars.get(Password) + } yield new UsernameAndPassword(u, p) + } +} diff --git a/src/test/scala/xerial/sbt/sonatype/EnvironmentCredentialsTest.scala b/src/test/scala/xerial/sbt/sonatype/EnvironmentCredentialsTest.scala new file mode 100644 index 00000000..c9294d7a --- /dev/null +++ b/src/test/scala/xerial/sbt/sonatype/EnvironmentCredentialsTest.scala @@ -0,0 +1,44 @@ +package xerial.sbt.sonatype + +import wvlet.airspec.AirSpec + +class EnvironmentCredentialsTest extends AirSpec { + test("Extract a username & password from a SONATYPE_TOKEN environment variable") { + val usernameAndPassword = EnvironmentCredentials + .getUsernameAndPassword( + Map( + "SONATYPE_TOKEN" -> "user:pass" + ) + ).get + + usernameAndPassword.username shouldBe "user" + usernameAndPassword.password shouldBe "pass" + } + + test("Extract a legacy Sonatype Username & Password from environment variables") { + val usernameAndPassword = EnvironmentCredentials + .getUsernameAndPassword( + Map( + "SONATYPE_USERNAME" -> "user", + "SONATYPE_PASSWORD" -> "pass" + ) + ).get + + usernameAndPassword.username shouldBe "user" + usernameAndPassword.password shouldBe "pass" + } + + test("Prefer a Sonatype token over a legacy Sonatype Username & Password") { + val usernameAndPassword = EnvironmentCredentials + .getUsernameAndPassword( + Map( + "SONATYPE_TOKEN" -> "newUser:newPass", + "SONATYPE_USERNAME" -> "oldUser", + "SONATYPE_PASSWORD" -> "oldPass" + ) + ).get + + usernameAndPassword.username shouldBe "newUser" + usernameAndPassword.password shouldBe "newPass" + } +}