Skip to content

Latest commit

 

History

History
131 lines (106 loc) · 6.75 KB

File metadata and controls

131 lines (106 loc) · 6.75 KB

Publishing & release runbook

The Phase 6 release step. It is deliberately not automated: publishing a released version to Maven Central is irreversible (an accepted version can never be re-uploaded or deleted) and requires secret credentials (a signing key and a Central Portal token). This page is the checklist a human follows; the safe local half can be run any time.

What a KMP library publishes

The Kotlin Multiplatform plugin produces one Maven publication per target plus an umbrella, not a single artifact:

Publication Coordinates (example) Role
kotlinMultiplatform (root) io.spine:elastic:<v> umbrella; carries Gradle Module Metadata (.module) that redirects common consumers to the right per-target artifact
jvm io.spine:elastic-jvm:<v> JVM jar + POM
macosArm64 io.spine:elastic-macosarm64:<v> Apple-silicon klib
linuxX64, linuxArm64, mingwX64, iosArm64, iosSimulatorArm64 elastic-<target> one klib each

Each set is the primary artifact + -sources.jar + -javadoc.jar + .pom (+ the root .module), each signed (.asc) with checksums. The coordinate suffix is the lowercased target name (-macosarm64, not -macosArm64).

Host constraint: the Apple-silicon klibs can only be built on macOS. Publish the whole set from one macOS host in one shot — splitting the publication across hosts causes Central to reject the deployment. This is why a KMP release runs on a Mac (it can build everything a Linux runner can, plus the Apple klibs).

Safe, local, reversible — do this any time

publishToMavenLocal needs no credentials and no network, writes only to ~/.m2/repository, and is fully reversible (delete the directory). It is the correct smoke test of the publication wiring, POM contents, and artifact set.

# The version gate first: a stale version would clobber the Maven-Local artifact
# that consumer integration tests rely on. (This repo enforces it via a hook.)
./gradlew :elastic:publishToMavenLocal
ls -R ~/.m2/repository/io/spine/elastic*/   # inspect the full artifact set

As of this writing the elastic module applies kmp-module but not kmp-publish, so it registers no publication tasks yet. Wiring publication is the first release task below; until then, publishToMavenLocal produces only the default KMP publications (no docs/sources jars, no signing).

Wiring publication (one-time, in the build)

The repo already ships the Spine convention for KMP publishing — apply it on elastic:

// elastic/build.gradle.kts
plugins {
    `kmp-module`
    `kmp-publish`          // adds maven-publish + attaches the docs jars
}

spinePublishing {
    // The Spine convention (io.spine.gradle.publish.PublishingRepos) ships exactly two
    // remote destinations: Cloud Artifact Registry and GitHub Packages. Maven Central is
    // NOT one of them — see the section below to add it.
    destinations = PublishingRepos.run { setOf(cloudArtifactRegistry, gitHub("elastic")) }
    customPublishing = true   // required: spinePublishing delegates KMP to kmp-publish
}

kmp-publish attaches the HTML-docs jar to the kotlinMultiplatform and jvm publications; spinePublishing fills in the POM (name, description, url, licenses, developers, scm) and the destination repositories. Validate with publishToMavenLocal before configuring any remote destination. Publishing to the configured Spine remotes uses the per-repository task Gradle derives from the destination name, e.g. publishAllPublicationsToCloudArtifactRegistryRepository.

Maven Central (Central Portal) — the irreversible step

Maven Central is not a Spine-convention destination — PublishingRepos defines only Cloud Artifact Registry and GitHub Packages. Releasing to Central therefore requires wiring it explicitly first, and the exact Gradle task name follows the repository you configure (there is no built-in ...ToMavenCentralRepository task).

Prerequisites (all must hold):

  1. A configured Maven Central destination. Add a Central Portal repository to the build — either a publishing.repositories { maven { name = "..."; url = ... } } block (its name determines the task) or a Central-publishing plugin such as com.vanniktech.maven.publish (which registers its own publishToMavenCentral task). Nothing in the current build wires this.
  2. A non-SNAPSHOT version. Central rejects -SNAPSHOT. The project is currently on a 1.0.0-SNAPSHOT-NNN line; a Central release needs a real 1.0.0-class version (a separate, deliberate version decision — not this phase's snapshot bump).
  3. A claimed/verified namespace for io.spine on the Central Portal (central.sonatype.com — the legacy OSSRH / oss.sonatype.org path reached end-of-life on 2025-06-30; new deployments use the Portal).
  4. A Portal token (a generated token, not the login password): ORG_GRADLE_PROJECT_mavenCentralUsername / ...Password.
  5. A PGP signing key, published to a public keyserver, provided in memory: ORG_GRADLE_PROJECT_signingInMemoryKey (+ ...KeyPassword, + ...KeyId for a subkey). Every artifact must carry a .asc signature.
  6. A complete POM: name, description, url, licenses, developers, scm — filled by spinePublishing.

Release (once prerequisite 1 is wired — substitute your configured repository / plugin task name; do not assume a fixed one):

# Build + sign all publications and stage them to the Portal. Still requires a manual
# "release" click in the Portal UI (or an auto-release variant, which is one-way).
# Some plugin versions need --no-configuration-cache.
#   ./gradlew publishAllPublicationsTo<YourCentralRepoName>Repository
# or, with the vanniktech plugin:
#   ./gradlew publishToMavenCentral

Then verify the full artifact set (root .module, every per-target jar, -sources/-javadoc, .pom, .asc, checksums) appears before releasing.

Boundary

Step Class
publishToMavenLocal, footprint/JMH runs, POM generation safe / local / reversible
Wiring kmp-publish + spinePublishing in the build safe (a build change; validate locally)
Signing locally (needs the private key) local, but touches a secret
Claiming the namespace, generating a Portal token one-time, needs Portal login
Wiring a Maven Central destination (repository or plugin) safe (a build change; not a default Spine destination)
The configured Central-publish task (name follows your repo/plugin) needs secret credentials; released versions are permanent and immutable

The last row is the human-gated action. Everything above it is reproducible without secrets and is what this phase delivers and validates.