Publishing Kotlin Projects
This page will discuss common topics around publishing your Kotlin projects for others to use.
Basic Publishing Configuration
This example demonstrates how to publish a Mill project to Maven Central. To
do so, your module must extend mill.scalalib.PublishModule, and configure the
publishing metadata shown below:
extends: [KotlinModule, PublishModule]
kotlinVersion: 2.0.20
publishVersion: 0.0.1
artifactName: example
pomSettings:
description: Example
organization: com.lihaoyi
url: https://github.com/com.lihaoyi/example
licenses: [MIT]
versionControl: https://github.com/com.lihaoyi/example
developers: [{name: Li Haoyi, email: example@example.com}]
You can publish this locally to your ~/.ivy2 filesystem artifact repository via:
> ./mill publishLocal
Publishing Artifact(com.lihaoyi,example...,0.0.1) to ivy repo ...
Apart from publishing locally, you can also publish this project to Sonatype Maven Central via:
> ./mill mill.javalib.SonatypeCentralPublishModule/
For more details on packaging and publishing in Mill, see Kotlin Packaging & Publishing
publishLocal also accepts options like --doc=false and --sources=false,
to disable publishing javadoc JARs and source JARs, which are generated and
published by default. This can be helpful if you’re not interested in javadoc JARs,
as compiling these docjars can slow down the local publishing workflow
publishLocal also accepts --transitive=true, to also publish locally the
transitive dependencies of the module being published. This ensures the module
can be resolved from the local repository, with no missing dependencies.
Versioning Based on Git Tags
extends: [KotlinModule, PublishModule, mill.util.VcsVersionModule]
kotlinVersion: 2.0.20
artifactName: example
pomSettings:
description: Example
organization: com.lihaoyi
url: https://github.com/com.lihaoyi/example
licenses: [MIT]
versionControl: https://github.com/com.lihaoyi/example
developers: [{name: Li Haoyi, email: example@example.com}]
This example demonstrates how to automatically set your publishVersion based on the
latest git tag. This is useful for workflows such as automatic publishing tags on push,
such as that discussed in Publishing using Github Actions
> git init .
Initialized empty Git repository in...
> git add -A
> git commit -m "initial commit"
...
> git tag v0.1.0
> ./mill publishLocal
Publishing Artifact(com.lihaoyi,example...,0.1.0...) to ivy repo...
Programmable Publishing Configuration
package build
import mill.*, kotlinlib.*, publish.*
object foo extends KotlinModule, PublishModule {
def kotlinVersion = "1.9.24"
def publishVersion = "0.0.1"
def pomSettings = PomSettings(
description = "Hello",
organization = "com.lihaoyi",
url = "https://github.com/lihaoyi/example",
licenses = Seq(License.MIT),
versionControl = VersionControl.github("lihaoyi", "example"),
developers = Seq(Developer("lihaoyi", "Li Haoyi", "https://github.com/lihaoyi"))
)
}
> ./mill foo.publishLocal
Publishing Artifact(com.lihaoyi,foo,0.0.1) to ivy repo...
This is an example KotlinModule with added publishing capabilities via
PublishModule using Mill’s
programmable build.mill syntax.
It provides publishLocal for local publishing and publishSonatypeCentral
(or mill.javalib.SonatypeCentralPublishModule/) for publishing to Maven Central.
Publishing to Maven Central
Once you’ve mixed in PublishModule, apart from publishing locally, you can also publish
your project’s modules to maven central
Verifying your Maven Central Namespace
Everyone who publishes to Maven Central needs to register their organization’s namespace
(e.g. com.lihaoyi) and verify that they own it as a security measure. To do so, please
create an account at:
And register and verify your namespace by following the instructions at:
Setting up GPG
If you’ve never created a keypair before that can be used to sign your artifacts you’ll need to do this. Mill can do this for you via
$ ./mill mill.javalib.SonatypeCentralPublishModule/initGpgKeys
This command requests some basic metadata necessary for generating the GPG keys, generates a key-pair, registers it, and prints out instructions on how to use it from the terminal or in CI.
For most users who have never touched GPG before, using initGpgKeys is the easiest
way to get started. But if for some reason initGpgKeys doesn’t work, or you want to
fiddle under the hood, you can also set up your GPG keys manually:
Publishing
Once you have the various secrets exported as above, you can publish all
eligible modules in your Mill project using mill.javalib.SonatypeCentralPublishModule/:
> mill mill.javalib.SonatypeCentralPublishModule/
> mill mill.javalib.SonatypeCentralPublishModule/ --publishArtifacts foo.publishArtifacts
Publishing Using Github Actions
To publish on Github Actions, you can use something like this:
# .github/workflows/publish-artifacts.yml
name: Publish Artifacts
on:
push:
tags:
- '**'
workflow_dispatch:
jobs:
publish-artifacts:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: ./mill mill.javalib.SonatypeCentralPublishModule/
env:
MILL_PGP_PASSPHRASE: ${{ secrets.MILL_PGP_PASSPHRASE }}
MILL_PGP_SECRET_BASE64: ${{ secrets.MILL_PGP_SECRET_BASE64 }}
MILL_SONATYPE_PASSWORD: ${{ secrets.MILL_SONATYPE_PASSWORD }}
MILL_SONATYPE_USERNAME: ${{ secrets.MILL_SONATYPE_USERNAME }}
Where MILL_PGP_PASSPHRASE, MILL_PGP_SECRET_BASE64, MILL_SONATYPE_PASSWORD, and
MILL_SONATYPE_USERNAME configured for the repository’s or organization’s Github Actions
workflows. See
Using Secrets in Github Actions
for more details.
This will set up a Github Actions workflow that will be run automatically when any tag is pushed,
and can also be run manually. If you set up Versioning Based
on Git Tags, then the publishVersion will be configured to the git tag version.
SNAPSHOT versions
To publish SNAPSHOT versions (that is any version that ends with -SNAPSHOT), you need to enable support for them in
your Sonatype Central namespace. To do that, go to the
Sonatype Central portal, click on the "three dots" next to the
namespace name, and click on "Enable SNAPSHOTs".
SNAPSHOT versions do not have same semantics as regular versions: they are not signed and are not kept forever in the repository.
See Sonatype Central documentation for more information.
Your build file should look like:
extends: JavaModule
publishVersion: 0.0.1-SNAPSHOT
Running any of the publishing tasks will now publish SNAPSHOT versions. When running them you should see a note from Mill that it has detected a SNAPSHOT version.
To consume SNAPSHOT versions in your project, make sure to add the snapshot repository to your build file:
extends: JavaModule
repositories: ["https://central.sonatype.com/repository/maven-snapshots"]
mvnDeps:
- com.example:mymodule:1.0.0-SNAPSHOT
SonatypeCentralPublishModule Argument Reference
The publishAll task can be called from the CLI. If a required value is not provided via the CLI option,
it will fall back to an environment variable (if available) or raise an error if missing.
The ./mill mill.javalib.SonatypeCentralPublishModule/publishAll takes the following options:
-
username: The username for calling the Sonatype Central publishing api. Defaults to theMILL_SONATYPE_USERNAMEenvironment variable if unset. If neither the parameter nor the environment variable are set, an error will be thrown. -
password: The password for calling the Sonatype Central publishing api. Defaults to theMILL_SONATYPE_PASSWORDenvironment variable if unset. If neither the parameter nor the environment variable are set, an error will be thrown. -
gpgArgs: Arguments to pass to the gpg package for signing artifacts. Uses theMILL_PGP_PASSPHRASEenvironment variable if set. Default:[--passphrase=$MILL_PGP_PASSPHRASE], --no-tty, --pinentry-mode, loopback, --batch, --yes, --armor, --detach-sign. -
publishArtifacts: The command for generating all publishable artifacts (ex.__.publishArtifacts). Required. -
readTimeout: The timeout for receiving a response from Sonatype Central after the initial connection has occurred. Default: 60000. -
awaitTimeout: The overall timeout for all retries (including exponential backoff) of the bundle upload. Default: 120 * 1000. -
connectTimeout: The timeout for the initial connection to Sonatype Central if there is no response. Default: 5000. -
shouldRelease: Whether the bundle should be automatically released when uploaded to Sonatype Central. Iffalse, the bundle will still be uploaded, but users will need to manually log in to Sonatype Central and publish the bundle from the portal. Default: true -
bundleName: If set, all packages will be uploaded in a single bundle with the given name. If unset, packages will be uploaded separately. Recommended bundle name syntax: groupName-artifactId-versionNumber. As an example, if publishing thecom.lihaoyirequestspackage, without the bundle name, four different bundles will be uploaded, one for each scala version supported. With a bundle name ofcom.lihaoyi-requests-<new_version>, a single bundle will be uploaded that contains all packages across scala versions. It is recommended to set the bundle name, so that packages can be verified and deployed together. Default: No bundle name is set and packages will be uploaded separately
For example, you may call mill.javalib.SonatypeCentralPublishModule/ via:
$ mill \
mill.javalib.SonatypeCentralPublishModule/publishAll \
--username myusername \
--password mypassword \
--gpgArgs --passphrase=$MILL_PGP_PASSPHRASE,--no-tty,--pinentry-mode,loopback,--batch,--yes,--armor,--detach-sign \
--publishArtifacts __.publishArtifacts \
--readTimeout 36000 \
--awaitTimeout 36000 \
--connectTimeout 36000 \
--shouldRelease false \
--bundleName com.lihaoyi-requests:1.0.0
Manual GPG Setup
If you have never used GPG before, initGpgKeys is probably the easiest way to get started, but you can also follow Sonatype’s GPG Documentation which has manual instructions on how to do this.
Mill uses the following environment variables as a way to pass the necessary secrets for publishing to Maven Central:
# Sonatype Central Portal needs your public key to be uploaded so it can use for verification of artifacts from their end.
#
# Send your public key to ubuntu server so Sonatype Maven Central can use for verification of the artifacts
gpg --keyserver keyserver.ubuntu.com --send-keys $LONG_ID
#
# Check the server for information about the public key. information will be displayed if found
gpg --keyserver keyserver.ubuntu.com --recv-keys $LONG_ID
#
# The LHS and RHS of the User Token, accessible through the sonatype
# website `Profile` / `User Token` / `Access User Token`
export MILL_SONATYPE_USERNAME=...
export MILL_SONATYPE_PASSWORD=...
# The base-64 encoded PGP key, which can be encoded in the following way
# for each OS:
#
# MacOS or FreeBSD
# gpg --export-secret-key -a $LONG_ID | base64
#
# Ubuntu (assuming GNU base64)
# gpg --export-secret-key -a $LONG_ID | base64 -w0
#
# Arch
# gpg --export-secret-key -a $LONG_ID | base64 | sed -z 's;\n;;g'
#
# Windows
# gpg --export-secret-key -a %LONG_ID% | openssl base64
export MILL_PGP_SECRET_BASE64=...
# The passphrase associated with your PGP key
export MILL_PGP_PASSPHRASE=...
Publishing to Generic Maven Repositories
Apart from publishing your project’s modules to Maven Central, you may also want to publish them to a generic Maven repository (such as a privately hosted one).
Publishing Secrets
Mill uses the following environment variables as a way to pass the necessary secrets for publishing:
# Credentials for publishing to the repository
export MILL_MAVEN_USERNAME=...
export MILL_MAVEN_PASSWORD=...
Publishing
You can publish all eligible modules in your Mill project using the
Default Task of the
External Module mill.javalib.MavenPublishModule:
> mill mill.javalib.MavenPublishModule/ \
--username myusername \
--password mypassword \
--releaseUri https://example.company.com/release \
--snapshotUri https://example.company.com/snapshot
When you do not provide the username and password parameters, Mill will instead look for the MILL_MAVEN_USERNAME and
MILL_MAVEN_PASSWORD environment variables. Providing the credentials via environment variables is the recommended
approach due to security considerations.
Non-Staging Releases (classic Maven uploads)
If the site does not support staging releases as oss.sonatype.org and s01.oss.sonatype.org do (for
example, a self-hosted OSS nexus site), you can pass in the
--stagingRelease false option to simply upload release artifacts to corresponding
maven path under sonatypeUri instead of staging path.
> mill mill.scalalib.PublishModule/ \
--publishArtifacts foo.publishArtifacts \
--sonatypeCreds lihaoyi:$SONATYPE_PASSWORD \
--sonatypeUri http://example.company.com/release \
--stagingRelease false