Skip to content

Commit 6d9f383

Browse files
Add PublishCredentialsModule, MavenPublish and MavenPublishModule (#6023)
### Summary Implements #5592 and builds upon #5601. ### High Level Overview of Changes This PR implements three traits: - `MavenPublishModule` is meant to mirror `SonatypeCentralPublishModule` for plain (!?) Maven repositories - `MavenModule` contains the actual publishing logic. This has been taken from parts of `SonatypeCentralPublishModule` and is now extended by both `MavenPublishModule` as well ass `SonatypeCentralPublishModule` to share code - `MavenCredentialsModule` contains tasks for obtaining Maven/Sonatype credentials. These tasks have been extracted from the `SonatypeCentralPublishModule` to be shared by both it and the new `MavenPublishModule` ### Differences between this PR and #5601 #### Code Structure #5601 essentially copied parts of `SonatypeCentralPublishModule` to create `MavenPublishModule`. Apart from duplicating code, this also created code you may find confusing in places (e.g. by containing a function named [publishSnapshot](https://github.com/com-lihaoyi/mill/pull/5601/files#diff-7ffbad225d6fa5d1de00e7ea2066f029c9fe4b90c09785ac3cd51ea0d12b3638R126) which is used to publish both release and snapshot artifacts). This PR tries to improve upon that by instead factoring out all code which can be factored out. #### Removal of Unused Settings #5601 defines the tasks `mavenConnectTimeout`, `mavenReadTimeout` and `mavenAwaitTimeout` as part of the `MavenPublishModule`. These are however not actually used anywhere. They are used in `SonatypeCentralPublishModule` and are specific to publishing releases to Sonatype Central. #### Bugfix for Choosing the Repo URI The below was changed after being identified during testing with a private build. ```diff - val uri = if (isSnapshot) releaseUri else snapshotUri + val uri = if (isSnapshot) snapshotUri else releaseUri ``` ### Open Question: Build Compilation Error When Extending `MavenPublishModule` When running `./mill dist.installLocalCache` and then using the local `SNAPSHOT` version of mill including the changes made in this PR in a build and extending `MavenPublishModule` in a local build module, I currently get the following exception when running `./mill resolve _` on that build: ``` [build.mill-59] [error] ## Exception when compiling 23 sources to /home/max/Repositories/github.com/ttmzero/rtp-backend/out/mill-build/compile.dest/classes [build.mill-59] [error] dotty.tools.dotc.core.MissingType: Cannot resolve reference to type com.lumidion.sonatype.central.client.core.type.SonatypeCredentials. [build.mill-59] [error] The classfile defining the type might be missing from the classpath. ``` This error can be removed by defining `Deps.sonatypeCentralClient` as a `mvnDeps` instead of both a `compileMvnDeps` and a `runMvnDeps` in `libs/scalalib/package.mill` and `libs/javalib/package.mill`. I do not know why this is the case and would require some help here. --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent d0f7bdd commit 6d9f383

File tree

7 files changed

+241
-85
lines changed

7 files changed

+241
-85
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package mill.javalib
2+
3+
import com.lumidion.sonatype.central.client.core.SonatypeCredentials
4+
import mill.api.daemon.Logger
5+
import mill.javalib.PublishModule.PublishData
6+
import mill.javalib.internal.MavenWorkerSupport as InternalMavenWorkerSupport
7+
8+
private[mill] trait MavenPublish {
9+
10+
def mavenPublishDatas(
11+
publishDatas: Seq[PublishData],
12+
credentials: SonatypeCredentials,
13+
releaseUri: String,
14+
snapshotUri: String,
15+
taskDest: os.Path,
16+
log: Logger,
17+
env: Map[String, String],
18+
worker: InternalMavenWorkerSupport.Api
19+
): Unit = {
20+
val dryRun = env.get("MILL_TESTS_PUBLISH_DRY_RUN").contains("1")
21+
22+
val (snapshots, releases) = publishDatas.partition(_.meta.isSnapshot)
23+
24+
releases.map(_ -> false).appendedAll(snapshots.map(_ -> true)).foreach { (data, isSnapshot) =>
25+
mavenPublishData(
26+
dryRun = dryRun,
27+
publishData = data,
28+
isSnapshot = isSnapshot,
29+
credentials = credentials,
30+
releaseUri = releaseUri,
31+
snapshotUri = snapshotUri,
32+
taskDest = taskDest,
33+
log = log,
34+
worker = worker
35+
)
36+
}
37+
}
38+
39+
def mavenPublishData(
40+
dryRun: Boolean,
41+
publishData: PublishData,
42+
isSnapshot: Boolean,
43+
credentials: SonatypeCredentials,
44+
releaseUri: String,
45+
snapshotUri: String,
46+
taskDest: os.Path,
47+
log: Logger,
48+
worker: InternalMavenWorkerSupport.Api
49+
): Unit = {
50+
val uri = if (isSnapshot) snapshotUri else releaseUri
51+
val artifacts = MavenWorkerSupport.RemoteM2Publisher.asM2ArtifactsFromPublishDatas(
52+
publishData.meta,
53+
publishData.payloadAsMap
54+
)
55+
56+
if (isSnapshot) {
57+
log.info(
58+
s"Detected a 'SNAPSHOT' version for ${publishData.meta}, publishing to Maven Repository at '$uri'"
59+
)
60+
}
61+
62+
/** Maven uses this as a workspace for file manipulation. */
63+
val mavenWorkspace = taskDest / "maven"
64+
65+
if (dryRun) {
66+
val publishTo = taskDest / "repository"
67+
val result = worker.publishToLocal(
68+
publishTo = publishTo,
69+
workspace = mavenWorkspace,
70+
artifacts
71+
)
72+
log.info(s"Dry-run publishing to '$publishTo' finished with result: $result")
73+
} else {
74+
val result = worker.publishToRemote(
75+
uri = uri,
76+
workspace = mavenWorkspace,
77+
username = credentials.username,
78+
password = credentials.password,
79+
artifacts
80+
)
81+
log.info(s"Publishing to '$uri' finished with result: $result")
82+
}
83+
}
84+
85+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package mill.javalib
2+
3+
import mill.*
4+
import mill.api.*
5+
import mill.util.Tasks
6+
7+
/**
8+
* External module to publish artifactes to Maven repositories other than `central.sonatype.org`
9+
* (e.g. a private Maven repository).
10+
*/
11+
object MavenPublishModule extends ExternalModule, DefaultTaskModule, MavenWorkerSupport,
12+
PublishCredentialsModule, MavenPublish {
13+
14+
def defaultTask(): String = "publishAll"
15+
16+
def publishAll(
17+
publishArtifacts: mill.util.Tasks[PublishModule.PublishData] =
18+
Tasks.resolveMainDefault("__:PublishModule.publishArtifacts"),
19+
username: String = "",
20+
password: String = "",
21+
releaseUri: String,
22+
snapshotUri: String
23+
): Command[Unit] = Task.Command {
24+
val artifacts = Task.sequence(publishArtifacts.value)()
25+
26+
val credentials = getPublishCredentials("MILL_MAVEN", username, password)()
27+
28+
mavenPublishDatas(
29+
artifacts,
30+
credentials,
31+
releaseUri = releaseUri,
32+
snapshotUri = snapshotUri,
33+
taskDest = Task.dest,
34+
log = Task.log,
35+
env = Task.env,
36+
worker = mavenWorker()
37+
)
38+
}
39+
40+
lazy val millDiscover: Discover = Discover[this.type]
41+
42+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package mill.javalib
2+
3+
import com.lumidion.sonatype.central.client.core.SonatypeCredentials
4+
import mill.api.*
5+
6+
/**
7+
* Internal module to Retrieve credentials for publishing to Maven repositories
8+
* (e.g. `central.sonatype.org` or a private Maven repository).
9+
*/
10+
private[mill] trait PublishCredentialsModule extends Module {
11+
def getPublishCredentials(
12+
envVariablePrefix: String,
13+
usernameParameterValue: String,
14+
passwordParameterValue: String
15+
): Task[SonatypeCredentials] = Task.Anon {
16+
val username =
17+
getPublishCredential(usernameParameterValue, "username", s"${envVariablePrefix}_USERNAME")()
18+
val password =
19+
getPublishCredential(passwordParameterValue, "password", s"${envVariablePrefix}_PASSWORD")()
20+
Result.Success(SonatypeCredentials(username, password))
21+
}
22+
23+
private def getPublishCredential(
24+
credentialParameterValue: String,
25+
credentialName: String,
26+
envVariableName: String
27+
): Task[String] = Task.Anon {
28+
if (credentialParameterValue.nonEmpty) {
29+
Result.Success(credentialParameterValue)
30+
} else {
31+
(for {
32+
credential <- Task.env.get(envVariableName)
33+
} yield {
34+
Result.Success(credential)
35+
}).getOrElse(
36+
Result.Failure(
37+
s"No $credentialName set. Consider using the $envVariableName environment variable or passing `$credentialName` argument"
38+
)
39+
)
40+
}
41+
}
42+
}

libs/javalib/src/mill/javalib/SonatypeCentralPublishModule.scala

Lines changed: 31 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,30 @@
11
package mill.javalib
22

33
import com.lihaoyi.unroll
4-
import com.lumidion.sonatype.central.client.core.{PublishingType, SonatypeCredentials}
4+
import com.lumidion.sonatype.central.client.core.PublishingType
5+
import com.lumidion.sonatype.central.client.core.SonatypeCredentials
56
import mill.*
6-
import javalib.*
7-
import mill.api.{ExternalModule, Task}
8-
import mill.util.Tasks
7+
import mill.api.BuildCtx
98
import mill.api.DefaultTaskModule
9+
import mill.api.ExternalModule
1010
import mill.api.Result
11-
import mill.javalib.SonatypeCentralPublishModule.{
12-
defaultAwaitTimeout,
13-
defaultConnectTimeout,
14-
defaultCredentials,
15-
defaultReadTimeout,
16-
getPublishingTypeFromReleaseFlag,
17-
getSonatypeCredentials
18-
}
19-
import mill.javalib.publish.Artifact
20-
import mill.javalib.publish.SonatypeHelpers.{PASSWORD_ENV_VARIABLE_NAME, USERNAME_ENV_VARIABLE_NAME}
21-
import mill.api.BuildCtx
11+
import mill.api.Task
2212
import mill.api.daemon.Logger
2313
import mill.javalib.PublishModule.PublishData
14+
import mill.javalib.SonatypeCentralPublishModule.defaultAwaitTimeout
15+
import mill.javalib.SonatypeCentralPublishModule.defaultConnectTimeout
16+
import mill.javalib.SonatypeCentralPublishModule.defaultCredentials
17+
import mill.javalib.SonatypeCentralPublishModule.defaultReadTimeout
18+
import mill.javalib.SonatypeCentralPublishModule.getPublishingTypeFromReleaseFlag
2419
import mill.javalib.internal.PublishModule.GpgArgs
20+
import mill.javalib.publish.Artifact
21+
import mill.javalib.publish.SonatypeHelpers.CREDENTIALS_ENV_VARIABLE_PREFIX
22+
import mill.util.Tasks
2523

26-
trait SonatypeCentralPublishModule extends PublishModule, MavenWorkerSupport {
24+
import javalib.*
25+
26+
trait SonatypeCentralPublishModule extends PublishModule, MavenWorkerSupport,
27+
PublishCredentialsModule {
2728

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

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

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

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

171172
def publishSnapshot(publishData: PublishData): Unit = {
172-
val uri = sonatypeCentralSnapshotUri
173-
val artifacts = MavenWorkerSupport.RemoteM2Publisher.asM2ArtifactsFromPublishDatas(
174-
publishData.meta,
175-
publishData.payloadAsMap
176-
)
177-
178-
log.info(
179-
s"Detected a 'SNAPSHOT' version for ${publishData.meta}, publishing to Sonatype Central Snapshots at '$uri'"
173+
mavenPublishData(
174+
dryRun = dryRun,
175+
publishData = publishData,
176+
isSnapshot = true,
177+
credentials = credentials,
178+
releaseUri = sonatypeCentralSnapshotUri,
179+
snapshotUri = sonatypeCentralSnapshotUri,
180+
taskDest = taskDest,
181+
log = log,
182+
worker = worker
180183
)
181-
182-
/** Maven uses this as a workspace for file manipulation. */
183-
val mavenWorkspace = taskDest / "maven"
184-
185-
if (dryRun) {
186-
val publishTo = taskDest / "repository"
187-
val result = worker.publishToLocal(
188-
publishTo = publishTo,
189-
workspace = mavenWorkspace,
190-
artifacts
191-
)
192-
log.info(s"Dry-run publishing to '$publishTo' finished with result: $result")
193-
} else {
194-
val result = worker.publishToRemote(
195-
uri = uri,
196-
workspace = mavenWorkspace,
197-
username = credentials.username,
198-
password = credentials.password,
199-
artifacts
200-
)
201-
log.info(s"Publishing to '$uri' finished with result: $result")
202-
}
203184
}
204185

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

261-
private def getSonatypeCredential(
262-
credentialParameterValue: String,
263-
credentialName: String,
264-
envVariableName: String
265-
): Task[String] = Task.Anon {
266-
if (credentialParameterValue.nonEmpty) {
267-
Result.Success(credentialParameterValue)
268-
} else {
269-
(for {
270-
credential <- Task.env.get(envVariableName)
271-
} yield {
272-
Result.Success(credential)
273-
}).getOrElse(
274-
Result.Failure(
275-
s"No $credentialName set. Consider using the $envVariableName environment variable or passing `$credentialName` argument"
276-
)
277-
)
278-
}
279-
}
280-
281-
private def getSonatypeCredentials(
282-
usernameParameterValue: String,
283-
passwordParameterValue: String
284-
): Task[SonatypeCredentials] = Task.Anon {
285-
val username =
286-
getSonatypeCredential(usernameParameterValue, "username", USERNAME_ENV_VARIABLE_NAME)()
287-
val password =
288-
getSonatypeCredential(passwordParameterValue, "password", PASSWORD_ENV_VARIABLE_NAME)()
289-
Result.Success(SonatypeCredentials(username, password))
290-
}
291-
292242
lazy val millDiscover: mill.api.Discover = mill.api.Discover[this.type]
293243
}

libs/javalib/src/mill/javalib/publish/SonatypeHelpers.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import java.security.MessageDigest
88
object SonatypeHelpers {
99
// http://central.sonatype.org/pages/working-with-pgp-signatures.html#signing-a-file
1010

11-
val USERNAME_ENV_VARIABLE_NAME = "MILL_SONATYPE_USERNAME"
12-
val PASSWORD_ENV_VARIABLE_NAME = "MILL_SONATYPE_PASSWORD"
11+
val CREDENTIALS_ENV_VARIABLE_PREFIX = "MILL_SONATYPE"
12+
val USERNAME_ENV_VARIABLE_NAME = s"${CREDENTIALS_ENV_VARIABLE_PREFIX}_USERNAME"
13+
val PASSWORD_ENV_VARIABLE_NAME = s"${CREDENTIALS_ENV_VARIABLE_PREFIX}_PASSWORD"
1314

1415
private[mill] def getArtifactMappings(
1516
isSigned: Boolean,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package mill.scalalib
22

33
object Dependency extends mill.api.ExternalModule.Alias(mill.javalib.Dependency)
4+
object MavenPublishModule
5+
extends mill.api.ExternalModule.Alias(mill.javalib.MavenPublishModule)
46
object SonatypeCentralPublishModule
57
extends mill.api.ExternalModule.Alias(mill.javalib.SonatypeCentralPublishModule)

0 commit comments

Comments
 (0)