Skip to content
Merged
85 changes: 85 additions & 0 deletions libs/javalib/src/mill/javalib/MavenPublish.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package mill.javalib

import com.lumidion.sonatype.central.client.core.SonatypeCredentials
import mill.api.daemon.Logger
import mill.javalib.PublishModule.PublishData
import mill.javalib.internal.MavenWorkerSupport as InternalMavenWorkerSupport

private[mill] trait MavenPublish {

def mavenPublishDatas(
publishDatas: Seq[PublishData],
credentials: SonatypeCredentials,
releaseUri: String,
snapshotUri: String,
taskDest: os.Path,
log: Logger,
env: Map[String, String],
worker: InternalMavenWorkerSupport.Api
): Unit = {
val dryRun = env.get("MILL_TESTS_PUBLISH_DRY_RUN").contains("1")

val (snapshots, releases) = publishDatas.partition(_.meta.isSnapshot)

releases.map(_ -> false).appendedAll(snapshots.map(_ -> true)).foreach { (data, isSnapshot) =>
mavenPublishData(
dryRun = dryRun,
publishData = data,
isSnapshot = isSnapshot,
credentials = credentials,
releaseUri = releaseUri,
snapshotUri = snapshotUri,
taskDest = taskDest,
log = log,
worker = worker
)
}
}

def mavenPublishData(
dryRun: Boolean,
publishData: PublishData,
isSnapshot: Boolean,
credentials: SonatypeCredentials,
releaseUri: String,
snapshotUri: String,
taskDest: os.Path,
log: Logger,
worker: InternalMavenWorkerSupport.Api
): Unit = {
val uri = if (isSnapshot) snapshotUri else releaseUri
val artifacts = MavenWorkerSupport.RemoteM2Publisher.asM2ArtifactsFromPublishDatas(
publishData.meta,
publishData.payloadAsMap
)

if (isSnapshot) {
log.info(
s"Detected a 'SNAPSHOT' version for ${publishData.meta}, publishing to Maven Repository at '$uri'"
)
}

/** Maven uses this as a workspace for file manipulation. */
val mavenWorkspace = taskDest / "maven"

if (dryRun) {
val publishTo = taskDest / "repository"
val result = worker.publishToLocal(
publishTo = publishTo,
workspace = mavenWorkspace,
artifacts
)
log.info(s"Dry-run publishing to '$publishTo' finished with result: $result")
} else {
val result = worker.publishToRemote(
uri = uri,
workspace = mavenWorkspace,
username = credentials.username,
password = credentials.password,
artifacts
)
log.info(s"Publishing to '$uri' finished with result: $result")
}
}

}
42 changes: 42 additions & 0 deletions libs/javalib/src/mill/javalib/MavenPublishModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package mill.javalib

import mill.*
import mill.api.*
import mill.util.Tasks

/**
* External module to publish artifactes to Maven repositories other than `central.sonatype.org`
* (e.g. a private Maven repository).
*/
object MavenPublishModule extends ExternalModule, DefaultTaskModule, MavenWorkerSupport,
PublishCredentialsModule, MavenPublish {

def defaultTask(): String = "publishAll"

def publishAll(
publishArtifacts: mill.util.Tasks[PublishModule.PublishData] =
Tasks.resolveMainDefault("__:PublishModule.publishArtifacts"),
username: String = "",
password: String = "",
releaseUri: String,
snapshotUri: String
): Command[Unit] = Task.Command {
val artifacts = Task.sequence(publishArtifacts.value)()

val credentials = getPublishCredentials("MILL_MAVEN", username, password)()

mavenPublishDatas(
artifacts,
credentials,
releaseUri = releaseUri,
snapshotUri = snapshotUri,
taskDest = Task.dest,
log = Task.log,
env = Task.env,
worker = mavenWorker()
)
}

lazy val millDiscover: Discover = Discover[this.type]

}
42 changes: 42 additions & 0 deletions libs/javalib/src/mill/javalib/PublishCredentialsModule.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package mill.javalib

import com.lumidion.sonatype.central.client.core.SonatypeCredentials
import mill.api.*

/**
* Internal module to Retrieve credentials for publishing to Maven repositories
* (e.g. `central.sonatype.org` or a private Maven repository).
*/
private[mill] trait PublishCredentialsModule extends Module {
def getPublishCredentials(
envVariablePrefix: String,
usernameParameterValue: String,
passwordParameterValue: String
): Task[SonatypeCredentials] = Task.Anon {
val username =
getPublishCredential(usernameParameterValue, "username", s"${envVariablePrefix}_USERNAME")()
val password =
getPublishCredential(passwordParameterValue, "password", s"${envVariablePrefix}_PASSWORD")()
Result.Success(SonatypeCredentials(username, password))
}

private def getPublishCredential(
credentialParameterValue: String,
credentialName: String,
envVariableName: String
): Task[String] = Task.Anon {
if (credentialParameterValue.nonEmpty) {
Result.Success(credentialParameterValue)
} else {
(for {
credential <- Task.env.get(envVariableName)
} yield {
Result.Success(credential)
}).getOrElse(
Result.Failure(
s"No $credentialName set. Consider using the $envVariableName environment variable or passing `$credentialName` argument"
)
)
}
}
}
112 changes: 31 additions & 81 deletions libs/javalib/src/mill/javalib/SonatypeCentralPublishModule.scala
Original file line number Diff line number Diff line change
@@ -1,29 +1,30 @@
package mill.javalib

import com.lihaoyi.unroll
import com.lumidion.sonatype.central.client.core.{PublishingType, SonatypeCredentials}
import com.lumidion.sonatype.central.client.core.PublishingType
import com.lumidion.sonatype.central.client.core.SonatypeCredentials
import mill.*
import javalib.*
import mill.api.{ExternalModule, Task}
import mill.util.Tasks
import mill.api.BuildCtx
import mill.api.DefaultTaskModule
import mill.api.ExternalModule
import mill.api.Result
import mill.javalib.SonatypeCentralPublishModule.{
defaultAwaitTimeout,
defaultConnectTimeout,
defaultCredentials,
defaultReadTimeout,
getPublishingTypeFromReleaseFlag,
getSonatypeCredentials
}
import mill.javalib.publish.Artifact
import mill.javalib.publish.SonatypeHelpers.{PASSWORD_ENV_VARIABLE_NAME, USERNAME_ENV_VARIABLE_NAME}
import mill.api.BuildCtx
import mill.api.Task
import mill.api.daemon.Logger
import mill.javalib.PublishModule.PublishData
import mill.javalib.SonatypeCentralPublishModule.defaultAwaitTimeout
import mill.javalib.SonatypeCentralPublishModule.defaultConnectTimeout
import mill.javalib.SonatypeCentralPublishModule.defaultCredentials
import mill.javalib.SonatypeCentralPublishModule.defaultReadTimeout
import mill.javalib.SonatypeCentralPublishModule.getPublishingTypeFromReleaseFlag
import mill.javalib.internal.PublishModule.GpgArgs
import mill.javalib.publish.Artifact
import mill.javalib.publish.SonatypeHelpers.CREDENTIALS_ENV_VARIABLE_PREFIX
import mill.util.Tasks

trait SonatypeCentralPublishModule extends PublishModule, MavenWorkerSupport {
import javalib.*

trait SonatypeCentralPublishModule extends PublishModule, MavenWorkerSupport,
PublishCredentialsModule {

@deprecated("Use `sonatypeCentralGpgArgsForKey` instead.", "Mill 1.0.1")
def sonatypeCentralGpgArgs: T[String] =
Expand Down Expand Up @@ -63,7 +64,7 @@ trait SonatypeCentralPublishModule extends PublishModule, MavenWorkerSupport {
@unroll docs: Boolean = true
): Task.Command[Unit] = Task.Command {
val artifact = artifactMetadata()
val credentials = getSonatypeCredentials(username, password)()
val credentials = getPublishCredentials(CREDENTIALS_ENV_VARIABLE_PREFIX, username, password)()
val publishData = publishArtifactsPayload(sources = sources, docs = docs)()
val publishingType = getPublishingTypeFromReleaseFlag(sonatypeCentralShouldRelease())

Expand Down Expand Up @@ -97,8 +98,8 @@ trait SonatypeCentralPublishModule extends PublishModule, MavenWorkerSupport {
/**
* External module to publish artifacts to `central.sonatype.org`
*/
object SonatypeCentralPublishModule extends ExternalModule with DefaultTaskModule
with MavenWorkerSupport {
object SonatypeCentralPublishModule extends ExternalModule, DefaultTaskModule, MavenWorkerSupport,
PublishCredentialsModule, MavenPublish {
private final val sonatypeCentralGpgArgsSentinelValue = "<user did not override this method>"

def self = this
Expand Down Expand Up @@ -127,7 +128,7 @@ object SonatypeCentralPublishModule extends ExternalModule with DefaultTaskModul
val artifacts = Task.sequence(publishArtifacts.value)()

val finalBundleName = if (bundleName.isEmpty) None else Some(bundleName)
val credentials = getSonatypeCredentials(username, password)()
val credentials = getPublishCredentials(CREDENTIALS_ENV_VARIABLE_PREFIX, username, password)()
def makeGpgArgs() = internal.PublishModule.pgpImportSecretIfProvidedAndMakeGpgArgs(
Task.env,
GpgArgs.fromUserProvided(gpgArgs)
Expand Down Expand Up @@ -169,37 +170,17 @@ object SonatypeCentralPublishModule extends ExternalModule with DefaultTaskModul
val dryRun = env.get("MILL_TESTS_PUBLISH_DRY_RUN").contains("1")

def publishSnapshot(publishData: PublishData): Unit = {
val uri = sonatypeCentralSnapshotUri
val artifacts = MavenWorkerSupport.RemoteM2Publisher.asM2ArtifactsFromPublishDatas(
publishData.meta,
publishData.payloadAsMap
)

log.info(
s"Detected a 'SNAPSHOT' version for ${publishData.meta}, publishing to Sonatype Central Snapshots at '$uri'"
mavenPublishData(
dryRun = dryRun,
publishData = publishData,
isSnapshot = true,
credentials = credentials,
releaseUri = sonatypeCentralSnapshotUri,
snapshotUri = sonatypeCentralSnapshotUri,
taskDest = taskDest,
log = log,
worker = worker
)

/** Maven uses this as a workspace for file manipulation. */
val mavenWorkspace = taskDest / "maven"

if (dryRun) {
val publishTo = taskDest / "repository"
val result = worker.publishToLocal(
publishTo = publishTo,
workspace = mavenWorkspace,
artifacts
)
log.info(s"Dry-run publishing to '$publishTo' finished with result: $result")
} else {
val result = worker.publishToRemote(
uri = uri,
workspace = mavenWorkspace,
username = credentials.username,
password = credentials.password,
artifacts
)
log.info(s"Publishing to '$uri' finished with result: $result")
}
}

def publishReleases(artifacts: Seq[PublishData], gpgArgs: GpgArgs): Unit = {
Expand Down Expand Up @@ -258,36 +239,5 @@ object SonatypeCentralPublishModule extends ExternalModule with DefaultTaskModul
}
}

private def getSonatypeCredential(
credentialParameterValue: String,
credentialName: String,
envVariableName: String
): Task[String] = Task.Anon {
if (credentialParameterValue.nonEmpty) {
Result.Success(credentialParameterValue)
} else {
(for {
credential <- Task.env.get(envVariableName)
} yield {
Result.Success(credential)
}).getOrElse(
Result.Failure(
s"No $credentialName set. Consider using the $envVariableName environment variable or passing `$credentialName` argument"
)
)
}
}

private def getSonatypeCredentials(
usernameParameterValue: String,
passwordParameterValue: String
): Task[SonatypeCredentials] = Task.Anon {
val username =
getSonatypeCredential(usernameParameterValue, "username", USERNAME_ENV_VARIABLE_NAME)()
val password =
getSonatypeCredential(passwordParameterValue, "password", PASSWORD_ENV_VARIABLE_NAME)()
Result.Success(SonatypeCredentials(username, password))
}

lazy val millDiscover: mill.api.Discover = mill.api.Discover[this.type]
}
5 changes: 3 additions & 2 deletions libs/javalib/src/mill/javalib/publish/SonatypeHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import java.security.MessageDigest
object SonatypeHelpers {
// http://central.sonatype.org/pages/working-with-pgp-signatures.html#signing-a-file

val USERNAME_ENV_VARIABLE_NAME = "MILL_SONATYPE_USERNAME"
val PASSWORD_ENV_VARIABLE_NAME = "MILL_SONATYPE_PASSWORD"
val CREDENTIALS_ENV_VARIABLE_PREFIX = "MILL_SONATYPE"
val USERNAME_ENV_VARIABLE_NAME = s"${CREDENTIALS_ENV_VARIABLE_PREFIX}_USERNAME"
val PASSWORD_ENV_VARIABLE_NAME = s"${CREDENTIALS_ENV_VARIABLE_PREFIX}_PASSWORD"

private[mill] def getArtifactMappings(
isSigned: Boolean,
Expand Down
2 changes: 2 additions & 0 deletions libs/scalalib/src/mill/scalalib/aliases.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package mill.scalalib

object Dependency extends mill.api.ExternalModule.Alias(mill.javalib.Dependency)
object MavenPublishModule
extends mill.api.ExternalModule.Alias(mill.javalib.MavenPublishModule)
object SonatypeCentralPublishModule
extends mill.api.ExternalModule.Alias(mill.javalib.SonatypeCentralPublishModule)
Loading
Loading