Upgrade to Pro — share decks privately, control downloads, hide ads and more …

What I Learned from Publishing Libraries to Mav...

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

What I Learned from Publishing Libraries to Maven Central for 10 Years

Publishing to Maven Central sounds simple… until it isn’t.

In this talk, I’ll share a decade’s worth of lessons learned from publishing open source libraries to Maven Central and other repositories. From GPG key nightmares and Gradle plugin magic to simplify your publishing workflow: those are the tips wish you knew earlier.

If you’ve ever fighted with .pom files, staging repositories, or just want to understand how to go from a gradlew publish to GitHub download badge, this talk is for you.

Expect incident stories, practical tips, and a series of do’s and don’ts to don’t be miserable when publishing to Central and making your next release smooth and drama-free!

Avatar for Nicola Corti

Nicola Corti

February 26, 2026
Tweet

More Decks by Nicola Corti

Other Decks in Technology

Transcript

  1. M AV E N C E N T R AL

    W H AT I L E A R N E D F RO M P U B L I S H I N G L I B R A R I E S F O R 1 0 Y E A R S TO @ C O RT I N I C O NICOLA CORTI
  2. “ I T ’ S N OT RO C K

    E T S C I E N C E ” ✅
  3. L AU N C H P L A N B

    U I L D U P LOA D V E R I F Y R E L E A S E
  4. B U I L D M I S S I

    O N : M AV E N C E N T R AL
  5. B U I L D M I S S I

    O N : M AV E N C E N T R AL • ✅ Have a good CI in place • With Unit tests & E2E tests • ✅ Have good KDoc coverage • Dokka setup is nice to have
  6. B U I L D M I S S I

    O N : M AV E N C E N T R AL • ✅ Decide your API strategy • Single library • Debug/Release variants • Multiple libraries • Do you need a BOM?
  7. B U I L D M I S S I

    O N : M AV E N C E N T R AL • ✅ Decide your Versioning strategy • Semantic versioning • 0ver.org • CalVer • Odd/Even numbers • TeX versioning 😅 <major>.<minor>.<patch> 0.<minor>.<patch> <yyyy>.<mm>.<dd> 3.141592653 <odd>.x.y <even>.x.y F R E E
  8. U P LOA D M I S S I O

    N : M AV E N C E N T R AL
  9. U P LOA D M I S S I O

    N : M AV E N C E N T R AL • GPG Signing • ✅ Make sure you create a GPG key only for the library plugins { id("signing") }
  10. U P LOA D M I S S I O

    N : M AV E N C E N T R AL • GPG Signing • ✅ Make sure you create a GPG key only for the library plugins { id("signing") } signing { useGpgCmd() sign(publishing.publications["release"]) }
  11. U P LOA D M I S S I O

    N : M AV E N C E N T R AL • GPG Signing • ✅ Make sure you create a GPG key only for the library • ✅ Use the in-memory GPG key plugins { id("signing") } signing { useGpgCmd() sign(publishing.publications["release"]) }
  12. U P LOA D M I S S I O

    N : M AV E N C E N T R AL • GPG Signing • ✅ Make sure you create a GPG key only for the library • ✅ Use the in-memory GPG key plugins { id("signing") } val signingKey = findProperty("SIGNING_KEY") as? String val signingPwd = findProperty("SIGNING_PWD") as? String signing { useGpgCmd() sign(publishing.publications["release"]) }
  13. U P LOA D M I S S I O

    N : M AV E N C E N T R AL • GPG Signing • ✅ Make sure you create a GPG key only for the library • ✅ Use the in-memory GPG key plugins { id("signing") } val signingKey = findProperty("SIGNING_KEY") as? String val signingPwd = findProperty("SIGNING_PWD") as? String signing { useInMemoryPgpKeys(signingKey, signingPwd) sign(publishing.publications["release"]) }
  14. U P LOA D M I S S I O

    N : M AV E N C E N T R AL • GPG Signing • ✅ Make sure you create a GPG key only for the library • ✅ Use the in-memory GPG key • Distribute your public key plugins { id("signing") } val signingKey = findProperty("SIGNING_KEY") as? String val signingPwd = findProperty("SIGNING_PWD") as? String signing { useInMemoryPgpKeys(signingKey, signingPwd) sign(publishing.publications["release"]) }
  15. U P LOA D M I S S I O

    N : M AV E N C E N T R AL • GPG Signing • ✅ Make sure you create a GPG key only for the library • ✅ Use the in-memory GPG key • Distribute your public key • Using GPG is a crucial security feature of Maven. plugins { id("signing") } val signingKey = findProperty("SIGNING_KEY") as? String val signingPwd = findProperty("SIGNING_PWD") as? String signing { useInMemoryPgpKeys(signingKey, signingPwd) sign(publishing.publications["release"]) }
  16. U P LOA D M I S S I O

    N : M AV E N C E N T R AL • Create an account on Sonatype • Register your namespace • Verify by DNS • Verify by GitHub project creation • Create your access token • ✅ Rotate your tokens periodically
  17. U P LOA D M I S S I O

    N : M AV E N C E N T R AL • What are the requirements for a good release: • -sources.jar • -javadoc.jar • POM file • Signatures • Hashes https://central.sonatype.org/publish/requirements/
  18. U P LOA D M I S S I O

    N : M AV E N C E N T R AL • ✅ Use Maven Local before going public • ./gradlew publishToMavenLocal • tree ~/.m2 $ tree ~/.m2 /Users/nicola/.m2 └── repository └── dev └── detekt ├── detekt-api │ ├── 2.0.0-alpha.0 │ │ ├── detekt-api-2.0.0-alpha.0-javadoc.jar │ │ ├── detekt-api-2.0.0-alpha.0-sources.jar │ │ ├── detekt-api-2.0.0-alpha.0.jar │ │ ├── detekt-api-2.0.0-alpha.0.module │ │ └── detekt-api-2.0.0-alpha.0.pom │ └── maven-metadata-local.xml ├── detekt-cli │ ├── 2.0.0-alpha.0 │ │ ├── detekt-cli-2.0.0-alpha.0-all.jar │ │ ├── detekt-cli-2.0.0-alpha.0-javadoc.jar │ │ ├── detekt-cli-2.0.0-alpha.0-sources.jar │ │ ├── detekt-cli-2.0.0-alpha.0.jar │ │ ├── detekt-cli-2.0.0-alpha.0.module │ │ └── detekt-cli-2.0.0-alpha.0.pom │ └── maven-metadata-local.xml ├── detekt-compiler-plugin │ ├── 2.0.0-alpha.0 │ │ ├── detekt-compiler-plugin-2.0.0-alpha.0-all.j │ │ ├── detekt-compiler-plugin-2.0.0-alpha.0-javad │ │ ├── detekt-compiler-plugin-2.0.0-alpha.0-sourc │ │ ├── detekt-compiler-plugin-2.0.0-alpha.0.jar │ │ ├── detekt-compiler-plugin-2.0.0-alpha.0.modul │ │ └── detekt-compiler-plugin-2.0.0-alpha.0.pom │ └── maven-metadata-local.xml ├── detekt-core │ ├── 2.0.0-alpha.0 │ │ ├── detekt-core-2.0.0-alpha.0-javadoc.jar │ │ ├── detekt-core-2.0.0-alpha.0-sources.jar │ │ ├── detekt-core-2.0.0-alpha.0.jar │ │ ├── detekt-core-2.0.0-alpha.0.module
  19. M I S S I O N : M AV

    E N C E N T R AL U P LOA D “Automate all the things” ✅
  20. M I S S I O N : M AV

    E N C E N T R AL U P LOA D maven-publish gh/gradle-nexus/publish-plugin gh/vanniktech/gradle-maven-publish-plugin
  21. V E R I F Y M I S S

    I O N : M AV E N C E N T R AL S NA P S H OT STAG I N G
  22. V E R I F Y M I S S

    I O N : M AV E N C E N T R AL • ✅ They are key to stress test your publishing pipeline • Versioned as <version>-SNAPSHOT • Less strict requirements (e.g. no GPG signature) • Same version can be republished • Hosted in a different repository S NA P S H OT central.sonatype.com/repository/maven-snapshots/ 😢
  23. V E R I F Y M I S S

    I O N : M AV E N C E N T R AL • Once you upload your release it will end up in a staging repository. • OPEN: Ready to receive artifacts • CLOSED: Verifications passed • RELEASED: Shipped to Maven Central STAG I N G ./gradlew closeAndReleaseSonatypeStagingRepository
  24. R E L E A S E M I S

    S I O N : M AV E N C E N T R AL
  25. R E L E A S E M I S

    S I O N : M AV E N C E N T R AL • Releases are immutable • If you made a mistake, you need to publish another release • ✅ Timing is key • Releases take hours to appear on Maven Central
  26. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL S O U R C E : S PA C E X - H O W N O T T O L A N D A N O R B I TA L R O C K E T B O O S T E R
  27. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL CircleCI is pwnd
  28. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL • CI secrets compromised • Rotate all the tokens • Revoke the GPG keys • ✅ Make sure new tokens are scoped
  29. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL A confusing dependency
  30. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL
  31. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL
  32. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL
  33. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL
  34. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL
  35. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL
  36. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL
  37. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL • Particular supply chain attack called typosquatting • JCenter was less strict on repo verification • ✅ Pay attention when using multiple public repositories
  38. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL M aven Gate
  39. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL allprojects { repositories { google() } }
  40. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL allprojects { repositories { google() mavenCentral() } }
  41. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL allprojects { repositories { google() mavenCentral() maven { url = uri("https://jitpack.io") } } }
  42. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL allprojects { repositories { google() mavenCentral() maven { url = uri("https://jitpack.io") } } } implementation(“io.apollo:lib:1.0”) 🪦
  43. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL allprojects { repositories { google() mavenCentral() maven { url = uri("https://jitpack.io") } } } implementation(“io.apollo:lib:1.1”) 😵
  44. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL • ✅ Use GPG signature verification • ✅ Use a Maven Proxy • ⚠ Do not let your domain expire • ⚠ Do not abandon your GitHub user • ⚠ Do not use JitPack or combination of other public repository ./gradlew --write-verification-metadata pgp,sha256 --export-keys
  45. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL Shai-Hulud
  46. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL backslash (0.26m downloads per week) chalk-template (3.9m downloads per week) supports-hyperlinks (19.2m downloads per week) has-ansi (12.1m downloads per week) simple-swizzle (26.26m downloads per week) color-string (27.48m downloads per week) error-ex (47.17m downloads per week) color-name (191.71m downloads per week) is-arrayish (73.8m downloads per week) slice-ansi (59.8m downloads per week) color-convert (193.5m downloads per week) wrap-ansi (197.99m downloads per week) ansi-regex (243.64m downloads per week) supports-color (287.1m downloads per week) strip-ansi (261.17m downloads per week) chalk (299.99m downloads per week) debug (357.6m downloads per week) ansi-styles (371.41m downloads per week)
  47. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL
  48. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL
  49. FA I LU R E S M I S S

    I O N : M AV E N C E N T R AL • Another supply chain attack with social engineering aspect • GPG would have saved NPM here • ✅ Attempting to investigate could do more harm than good
  50. S U CC E S S M I S S

    I O N : M AV E N C E N T R AL
  51. Q U E ST I O N S ? M

    I S S I O N : M AV E N C E N T R AL @ C O RT I N I C O