Setup Publishing for KMP Libraries
Purpose
Guide users through configuring flexible publishing to multiple repository types (Maven Central, GitHub Packages, Custom Maven, JFrog, CloudSmith).
When to Use
- •User wants to publish their library
- •Asking about Maven Central, GitHub Packages, or custom repositories
- •Setting up publishing configuration
- •Troubleshooting publishing issues
- •Adding or changing publishing targets
Configuration Workflow
Step 1: Determine Publishing Strategy
Ask the user:
"Where do you want to publish your library?"
Options:
- •Maven Central - Public open-source libraries
- •GitHub Packages - Private team libraries (free)
- •Custom Maven - Self-hosted Nexus/Artifactory
- •JFrog Cloud - Managed Artifactory
- •CloudSmith - Modern package hosting
- •Multiple - Combination of above
Step 2: Update project.yml
Based on their choice, update project.yml:
Maven Central Only
publishing:
enabled: true
group_id: io.github.username # or com.company
repositories:
maven_central:
enabled: true
auto_release: false
GitHub Packages Only
publishing:
enabled: true
group_id: com.yourcompany.kmp
repositories:
github_packages:
enabled: true
owner: org-name
repository: repo-name
Maven Central + GitHub Packages (Recommended)
publishing:
enabled: true
group_id: io.github.username
repositories:
maven_central:
enabled: true
auto_release: false
github_packages:
enabled: true
owner: your-org
repository: maven-packages
strategy:
snapshots_on_main: true # Auto-publish snapshots to GitHub
releases_on_tag: true # Publish releases to both
Custom Maven (Nexus/Artifactory)
publishing:
enabled: true
group_id: com.company.kmp
repositories:
custom_maven:
enabled: true
name: Company Nexus
releases_url: https://nexus.company.com/repository/maven-releases/
snapshots_url: https://nexus.company.com/repository/maven-snapshots/
JFrog Cloud
publishing:
enabled: true
group_id: com.company.kmp
repositories:
jfrog:
enabled: true
url: https://company.jfrog.io/artifactory/maven-releases/
CloudSmith
publishing:
enabled: true
group_id: com.company.kmp
repositories:
cloudsmith:
enabled: true
owner: company
repository: maven
Step 3: Validate Configuration
Run validation:
python3 scripts/get-publishing-config.py --validate
If errors, fix them in project.yml and validate again.
Step 4: Set Up Secrets
Guide user to add required secrets to GitHub:
Get required secrets list:
python3 scripts/get-publishing-config.py --required-secrets <repository_name>
Common secrets by repository:
| Repository | Required Secrets |
|---|---|
| maven_central | MAVEN_CENTRAL_USERNAME, MAVEN_CENTRAL_PASSWORD, SIGNING_KEY, SIGNING_PASSWORD |
| github_packages | GITHUB_TOKEN (auto-provided) |
| custom_maven | CUSTOM_MAVEN_USERNAME, CUSTOM_MAVEN_PASSWORD, SIGNING_KEY, SIGNING_PASSWORD |
| jfrog | JFROG_USERNAME, JFROG_PASSWORD, SIGNING_KEY, SIGNING_PASSWORD |
| cloudsmith | CLOUDSMITH_API_KEY, SIGNING_KEY, SIGNING_PASSWORD |
Add secrets:
- •Go to GitHub repository → Settings → Secrets and variables → Actions
- •Click New repository secret
- •Add each required secret
Step 5: Repository-Specific Setup
For Maven Central
- •Create Sonatype account: https://central.sonatype.com/
- •Claim namespace: Verify group ID (io.github.username or com.company)
- •Generate GPG key:
gpg --full-generate-key gpg --armor --export-secret-keys KEY_ID | base64 > signing-key.txt gpg --keyserver keyserver.ubuntu.com --send-keys KEY_ID
- •Add secrets:
MAVEN_CENTRAL_USERNAME,MAVEN_CENTRAL_PASSWORD,SIGNING_KEY(contents of signing-key.txt),SIGNING_PASSWORD
Point to: docs/docs/development/publishing-maven-central.md
For GitHub Packages
- •Choose repository: Same as code or dedicated
maven-packages - •Update project.yml: Set
ownerandrepository - •No additional secrets needed (uses
GITHUB_TOKEN)
Point to: docs/docs/development/publishing-github-packages.md
For Custom Maven
- •Set up Nexus/Artifactory: Install and configure
- •Create repositories: maven-releases, maven-snapshots
- •Create deployment user: With deploy permissions
- •Get repository URLs: Update
project.yml - •Add secrets:
CUSTOM_MAVEN_USERNAME,CUSTOM_MAVEN_PASSWORD
Point to: docs/docs/development/publishing-custom-maven.md
For JFrog Cloud
- •Sign up: https://jfrog.com/start-free/
- •Create repositories: maven-releases, maven-snapshots
- •Generate API token: Profile → Authentication Settings
- •Add secrets:
JFROG_USERNAME,JFROG_PASSWORD(API token)
Point to: docs/docs/development/publishing-cloud-services.md#jfrog
For CloudSmith
- •Sign up: https://cloudsmith.com/
- •Create repository: Maven type
- •Generate API key: Settings → API Keys
- •Add secrets:
CLOUDSMITH_API_KEY
Point to: docs/docs/development/publishing-cloud-services.md#cloudsmith
Step 6: Test Locally
Before publishing to remote repositories:
# Build everything ./gradlew clean build # Validate API compatibility ./gradlew apiCheck # Publish to local Maven repository ./gradlew publishToMavenLocal # Check artifacts ls -la ~/.m2/repository/
Look for:
- •
.jarfiles - •
.pomfiles - •
.modulefiles - •
.ascfiles (if signing enabled)
Step 7: Publish
Via GitHub Actions (Recommended)
# Update version # gradle.properties: VERSION_NAME=1.0.0 # Commit and tag git add gradle.properties git commit -m "chore: prepare release 1.0.0" git push origin main # Create and push tag git tag -a v1.0.0 -m "Release 1.0.0" git push origin v1.0.0 # Create GitHub release (triggers publishing) gh release create v1.0.0 --title "v1.0.0" --notes "Release notes"
Manual (Local)
# Ensure credentials are set ./gradlew publish
Troubleshooting Guide
Configuration Issues
# Validate configuration python3 scripts/get-publishing-config.py --validate # Check enabled repositories python3 scripts/get-publishing-config.py --list-enabled # View specific repository config python3 scripts/get-publishing-config.py --repository maven_central
Common Errors
"Group ID cannot contain hyphens"
- •Fix: Use dots instead:
com.company.kmpnotcom.company-kmp
"No repositories enabled"
- •Fix: Set
enabled: truefor at least one repository inproject.yml
"401 Unauthorized" during publish
- •Check: Secrets are correct in GitHub
- •Verify: Account credentials haven't expired
- •Test: Log in to repository web UI manually
"Signature verification failed"
- •Check:
SIGNING_KEYis complete (including BEGIN/END markers) - •Verify:
SIGNING_PASSWORDis correct - •Try: Re-export GPG key and update secret
"Repository not found"
- •Check: Repository URLs in
project.ymlare correct - •Verify: Repository exists and you have access
- •Test: Try accessing URL in browser
Skip Signing (Local Development)
For testing without signing:
./gradlew publish -PskipSigning=true
Best Practices
Security
- •✅ Always use GitHub Actions secrets for credentials
- •✅ Never commit credentials to version control
- •✅ Use API tokens instead of passwords when possible
- •✅ Rotate credentials regularly
- •✅ Use minimum required permissions
Versioning
- •✅ Follow semantic versioning (MAJOR.MINOR.PATCH)
- •✅ Use
-SNAPSHOTfor development versions - •✅ Tag all releases in Git
- •✅ Keep CHANGELOG.md updated
Release Process
- •✅ Test locally with
publishToMavenLocalfirst - •✅ Run all tests and API validation
- •✅ Update documentation before releasing
- •✅ Let CI/CD handle publishing (don't publish from local)
- •✅ Verify publication after release
Multi-Repository Strategy
Public + Private:
# Maven Central for public releases
# GitHub Packages for private snapshots
repositories:
maven_central:
enabled: true
github_packages:
enabled: true
Benefits:
- •Public users get stable releases from Maven Central
- •Team gets fast snapshot updates from GitHub Packages
- •Keep development versions private
Quick Reference
Check Configuration
# Validate python3 scripts/get-publishing-config.py --validate # List enabled repos python3 scripts/get-publishing-config.py --list-enabled # Get repo config python3 scripts/get-publishing-config.py --repository maven_central # Check required secrets python3 scripts/get-publishing-config.py --required-secrets maven_central
Publish Commands
# Local Maven repository (testing) ./gradlew publishToMavenLocal # All configured repositories ./gradlew publish # Skip signing (local dev) ./gradlew publish -PskipSigning=true
Update Version
# gradle.properties VERSION_NAME=1.0.0 # Release VERSION_NAME=1.0.0-SNAPSHOT # Development VERSION_NAME=1.0.0-alpha.1 # Pre-release