Skip to content

Commit ae37fbf

Browse files
committed
Migrate publishing to vanniktech.maven.publish
1 parent 22b0de8 commit ae37fbf

4 files changed

Lines changed: 81 additions & 98 deletions

File tree

‎.github/workflows/build.yml‎

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,26 @@ jobs:
115115
script: ./gradlew connectedAndroidDeviceTest
116116

117117
release:
118-
name: Release ${{ matrix.module }} to Sonatype
119-
strategy:
120-
matrix:
121-
module: [decompose-router, decompose-router-wear]
122-
118+
name: ${{ matrix.registry.name }}
123119
if: ${{ github.event_name != 'pull_request' }}
124-
runs-on: macos-latest
120+
runs-on: macos-latest # required to build the Apple-target klibs
125121
needs:
126122
- build
127123
- test
128-
124+
permissions:
125+
contents: read
126+
packages: write # for GitHub Packages
127+
strategy:
128+
# Don't let one registry failing cancel the upload to the other.
129+
fail-fast: false
130+
matrix:
131+
registry:
132+
# Uploads both modules as a single Central Portal deployment. Without
133+
# automaticRelease, it waits for manual approval at central.sonatype.com.
134+
- name: Publish to Maven Central
135+
task: publishToMavenCentral
136+
- name: Publish to GitHub Packages
137+
task: publishAllPublicationsToGitHubPackagesRepository
129138
steps:
130139
- uses: actions/checkout@v4
131140

@@ -138,21 +147,17 @@ jobs:
138147
- name: Setup gradle
139148
uses: gradle/actions/setup-gradle@v4
140149

141-
- name: Write secrets to local.properties
142-
if: ${{ github.event_name != 'pull_request' }}
143-
run: |
144-
echo sonatypeUsername="${SONATYPE_USERNAME}" >> "local.properties"
145-
echo sonatypePassword="${SONATYPE_PASSWORD}" >> "local.properties"
146-
echo gpgKeyPassword="${GPG_KEY_PASSWORD}" >> "local.properties"
147-
echo gpgKeySecret="${GPG_KEY_SECRET}" >> "local.properties"
150+
- name: ${{ matrix.registry.name }}
151+
run: ./gradlew ${{ matrix.registry.task }} --no-configuration-cache
148152
env:
149-
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
150-
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
151-
GPG_KEY_PASSWORD: ${{ secrets.GPG_KEY_PASSWORD }}
152-
GPG_KEY_SECRET: ${{ secrets.GPG_KEY_SECRET }}
153-
154-
- name: Release to sonatype
155-
run: ./gradlew :${{ matrix.module }}:publishAllPublicationsToMavenRepository
153+
# Maven Central credentials (ignored by the GitHub Packages task, and vice versa).
154+
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
155+
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
156+
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY_SECRET }}
157+
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }}
158+
# GitHub Packages credentials — GITHUB_TOKEN is auto-provided, no secret needed.
159+
GITHUB_ACTOR: ${{ github.actor }}
160+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
156161

157162
documentation:
158163
name: Publish documentation

‎CLAUDE.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,6 @@ Each `Router<C>` wraps a Decompose `*Navigation<C>` (delegating navigation calls
7272
## Versioning & publishing
7373

7474
- Versions are centralized in `gradle/libs.versions.toml`. Current version is set in the root `build.gradle.kts` (`allprojects { version = ... }`).
75-
- Publishing targets Sonatype Central Portal. `SNAPSHOT` versions go to the snapshots repo; releases to the staging deploy API. Credentials (`sonatypeUsername`/`sonatypePassword`, `gpgKeySecret`/`gpgKeyPassword`) are read from `local.properties` (git-ignored; CI writes them from secrets). Artifacts are GPG-signed and ship a Dokka-generated `javadocJar`.
76-
- CI (`.github/workflows/build.yml`) runs `apiCheck` → per-target tests + Android emulator tests → (on push to `main`) release to Sonatype and publish docs to GitHub Pages.
75+
- Publishing uses the `com.vanniktech.maven.publish` plugin, applied to every published subproject from the root `build.gradle.kts` `subprojects {}` block (the `app` is skipped). Two registries: Maven Central via `publishToMavenCentral` (Sonatype Central Portal, `SNAPSHOT`s go to the snapshots repo; releases wait for manual "Publish" on the portal since there's no `automaticRelease`) and GitHub Packages via `publishAllPublicationsToGitHubPackagesRepository`. Artifacts are GPG-signed (`signAllPublications()`) and the plugin wires the Dokka javadoc jar automatically.
76+
- Credentials are Gradle properties, not `local.properties`: `mavenCentralUsername`/`mavenCentralPassword` and `signingInMemoryKey`/`signingInMemoryKeyPassword` for Central; `GITHUB_ACTOR`/`GITHUB_TOKEN` env vars for GitHub Packages. In CI these come from `ORG_GRADLE_PROJECT_*` env vars fed by repo secrets (`MAVEN_CENTRAL_USERNAME`/`MAVEN_CENTRAL_PASSWORD`, `GPG_KEY_SECRET`/`GPG_KEY_PASSWORD`).
77+
- CI (`.github/workflows/build.yml`) runs `apiCheck` → per-target tests + Android emulator tests → (on push to `main`) a `release` matrix job publishing to both registries and a docs job publishing to GitHub Pages.

‎build.gradle.kts‎

Lines changed: 50 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import java.util.Properties
2-
31
plugins {
42
alias(libs.plugins.kotlin.multiplatform) apply false
53
alias(libs.plugins.android.kotlin.multiplatform.library) apply false
64
alias(libs.plugins.kotlin.compose) apply false
75
alias(libs.plugins.compose.multiplatform) apply false
6+
alias(libs.plugins.maven.publish) apply false
87

98
alias(libs.plugins.dokka)
109
alias(libs.plugins.binary.compatibility.validator)
@@ -18,11 +17,6 @@ buildscript {
1817
}
1918
}
2019

21-
fun localProperties(): Properties = Properties().apply {
22-
val file = rootProject.file("local.properties")
23-
if (file.exists()) file.inputStream().use { load(it) }
24-
}
25-
2620
apiValidation {
2721
ignoredProjects += listOf("app")
2822
}
@@ -42,83 +36,64 @@ allprojects {
4236

4337
group = "io.github.xxfast"
4438
version = "0.10.0-SNAPSHOT"
39+
}
4540

46-
// Do not publish the app
47-
if (name.contains("app")) return@allprojects
41+
subprojects {
42+
// The demo app is not published and has no API docs.
43+
if (name == "app") return@subprojects
4844

4945
apply(plugin = "org.jetbrains.dokka")
50-
apply(plugin = "maven-publish")
51-
apply(plugin = "signing")
52-
53-
val localProperties: Properties = localProperties()
54-
55-
extensions.configure<PublishingExtension> {
56-
repositories {
57-
maven {
58-
val isSnapshot = version.toString().endsWith("SNAPSHOT")
59-
url = uri(
60-
if (!isSnapshot) "https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2"
61-
else "https://central.sonatype.com/repository/maven-snapshots/"
62-
)
63-
64-
credentials {
65-
username = localProperties.getProperty("sonatypeUsername")
66-
password = localProperties.getProperty("sonatypePassword")
46+
apply(plugin = "com.vanniktech.maven.publish")
47+
48+
// Publishes to the Sonatype Central Portal (central.sonatype.com). Credentials/signing
49+
// come from Gradle properties: mavenCentralUsername/mavenCentralPassword and
50+
// signingInMemoryKey/signingInMemoryKeyPassword (see CI env / ~/.gradle/gradle.properties).
51+
configure<com.vanniktech.maven.publish.MavenPublishBaseExtension> {
52+
publishToMavenCentral() // no automaticRelease: deployment waits for manual "Publish" on the portal
53+
signAllPublications()
54+
55+
pom {
56+
name.set("Decompose-Router")
57+
description.set("A Compose-multiplatform navigation library that leverage Decompose to create an API inspired by Conductor")
58+
url.set("https://github.com/xxfast/Decompose-Router")
59+
licenses {
60+
license {
61+
name.set("Apache-2.0")
62+
url.set("https://opensource.org/licenses/Apache-2.0")
6763
}
6864
}
69-
}
70-
71-
val javadocJar = tasks.register<Jar>("javadocJar") {
72-
dependsOn(tasks.named("dokkaGeneratePublicationHtml"))
73-
archiveClassifier.set("javadoc")
74-
from(layout.buildDirectory.dir("dokka/html"))
75-
}
76-
77-
publications {
78-
withType<MavenPublication> {
79-
artifact(javadocJar)
80-
81-
pom {
82-
name.set("Decompose-Router")
83-
description.set("A Compose-multiplatform navigation library that leverage Decompose to create an API inspired by Conductor")
84-
licenses {
85-
license {
86-
name.set("Apache-2.0")
87-
url.set("https://opensource.org/licenses/Apache-2.0")
88-
}
89-
}
90-
url.set("https://github.com/xxfast/Decompose-Router")
91-
issueManagement {
92-
system.set("Github")
93-
url.set("https://github.com/xxfast/Decompose-Router/issues")
94-
}
95-
scm {
96-
connection.set("https://github.com/xxfast/Decompose-Router.git")
97-
url.set("https://github.com/xxfast/Decompose-Router")
98-
}
99-
developers {
100-
developer {
101-
name.set("Isuru Rajapakse")
102-
email.set("isurukusumal36@gmail.com")
103-
}
104-
}
65+
issueManagement {
66+
system.set("Github")
67+
url.set("https://github.com/xxfast/Decompose-Router/issues")
68+
}
69+
scm {
70+
url.set("https://github.com/xxfast/Decompose-Router")
71+
connection.set("scm:git:git://github.com/xxfast/Decompose-Router.git")
72+
developerConnection.set("scm:git:ssh://git@github.com/xxfast/Decompose-Router.git")
73+
}
74+
developers {
75+
developer {
76+
id.set("xxfast")
77+
name.set("Isuru Rajapakse")
78+
email.set("isurukusumal36@gmail.com")
10579
}
10680
}
10781
}
10882
}
10983

110-
val publishing = extensions.getByType<PublishingExtension>()
111-
extensions.configure<SigningExtension> {
112-
useInMemoryPgpKeys(
113-
localProperties.getProperty("gpgKeySecret"),
114-
localProperties.getProperty("gpgKeyPassword"),
115-
)
116-
117-
sign(publishing.publications)
118-
}
119-
120-
// TODO: remove after https://youtrack.jetbrains.com/issue/KT-46466 is fixed
121-
project.tasks.withType(AbstractPublishToMaven::class.java).configureEach {
122-
dependsOn(project.tasks.withType(Sign::class.java))
84+
// Secondary target: GitHub Packages. Maven Central (above) stays the primary registry.
85+
// Publish with `./gradlew publishAllPublicationsToGitHubPackagesRepository`. In CI the
86+
// credentials come from the auto-provided GITHUB_ACTOR / GITHUB_TOKEN env vars.
87+
configure<PublishingExtension> {
88+
repositories {
89+
maven {
90+
name = "GitHubPackages"
91+
url = uri("https://maven.pkg.github.com/xxfast/Decompose-Router")
92+
credentials {
93+
username = System.getenv("GITHUB_ACTOR")
94+
password = System.getenv("GITHUB_TOKEN")
95+
}
96+
}
97+
}
12398
}
12499
}

‎gradle/libs.versions.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ horologist = "0.7.15"
1212
kotlin = "2.4.0"
1313
kotlinx-coroutines = "1.11.0"
1414
kotlinx-serialization = "1.11.0"
15+
maven-publish = "0.37.0"
1516
wear-compose = "1.6.2"
1617

1718
[libraries]
@@ -41,3 +42,4 @@ compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "compose-m
4142
kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
4243
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
4344
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
45+
maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "maven-publish" }

0 commit comments

Comments
 (0)