{"id":1655,"date":"2025-09-07T23:19:53","date_gmt":"2025-09-08T03:19:53","guid":{"rendered":"https:\/\/jausoft.com\/blog\/?p=1655"},"modified":"2025-09-07T23:25:12","modified_gmt":"2025-09-08T03:25:12","slug":"publishing-on-sonatypes-central-maven-repo","status":"publish","type":"post","link":"https:\/\/jausoft.com\/blog\/2025\/09\/07\/publishing-on-sonatypes-central-maven-repo\/","title":{"rendered":"Publishing on Sonatype&#8217;s Central Maven Repo"},"content":{"rendered":"<p>Besides deploying on your own maven mirror repository, one usually also likes to deploy on Maven Central for stability and acceptance <a href=\"http:\/\/search.maven.org\">http:\/\/search.maven.org<\/a> .<\/p>\n<p><a href=\"https:\/\/jogamp.org\">Jogamp<\/a> lacked this feature since version 2.4.0, so I had to dive back into this matter &#8211; especially since Sonatype changed their procedures due to <a href=\"https:\/\/central.sonatype.org\/news\/20250326_ossrh_sunset\/\">End-of-Life Sunset Date for OSSRH<\/a> coinciding with <a href=\"https:\/\/help.sonatype.com\/en\/sonatype-nexus-repository-2-sunsetting-information.html\">Sonatype Nexus Repository 2 Sunsetting Information<\/a> &#8211; both occurred on 2025-06-30. Since then, the <a href=\"https:\/\/help.sonatype.com\/en\/sonatype-nexus-repository-3-versions-status.html\">Nexus Repository 3<\/a> and its (web) API is utilized under the hood.<!--more--><\/p>\n<p>Today, we have to follow <a href=\"https:\/\/central.sonatype.org\/publish\/publish-portal-guide\/\">Sonatype&#8217;s Central Publisher Portal Guide<\/a> and the new <a href=\"https:\/\/central.sonatype.org\/publish\/publish-portal-ossrh-staging-api\/\">Portal OSSRH Staging API<\/a>, to allow still utilizing the regular Maven plugin deployment via e.g. <a href=\"https:\/\/maven.apache.org\/plugins\/maven-gpg-plugin\/sign-and-deploy-file-mojo.html\">gpg:sign-and-deploy-file<\/a> deployment method.<\/p>\n<ul>\n<li>Create an account on <a href=\"https:\/\/central.sonatype.com\/account\">https:\/\/central.sonatype.com\/account<\/a>.<\/li>\n<li>Secure your namespace<\/li>\n<li>Create an API token -&gt; API token username + password to be used with the <a href=\"https:\/\/central.sonatype.org\/publish\/publish-portal-ossrh-staging-api\/\">Portal OSSRH Staging API<\/a><\/li>\n<li>Change your Maven central staging upload URL to <em>https:\/\/ossrh-staging-api.central.sonatype.com\/service\/local\/staging\/deploy\/maven2\/<\/em><\/li>\n<li>Drop any package on staging, see <a href=\"https:\/\/central.sonatype.org\/publish\/publish-portal-ossrh-staging-api\/\">Portal OSSRH Staging API<\/a> script function <em>sonatype_drop_staging<\/em> below.<\/li>\n<li>Upload your artifacts for one package, covered by your pom.xml, to staging, via e.g. <a href=\"https:\/\/maven.apache.org\/plugins\/maven-gpg-plugin\/sign-and-deploy-file-mojo.html\">gpg:sign-and-deploy-file<\/a><\/li>\n<li>Upload your package from staging to the central repository, see <a href=\"https:\/\/central.sonatype.org\/publish\/publish-portal-ossrh-staging-api\/\">Portal OSSRH Staging API<\/a> script function<em> sonatype_upload_staging <\/em>below. This must be done for each package after uploading its artifacts to staging.<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>To use the <a href=\"https:\/\/central.sonatype.org\/publish\/publish-portal-ossrh-staging-api\/\">Portal OSSRH Staging API<\/a>, we are supported by <a href=\"https:\/\/ossrh-staging-api.central.sonatype.com\/swagger-ui\/\">OSSRH Staging API (swagger)<\/a> to formulate our curl commands below.<\/p>\n<p>The whole staging API is covered by our <a href=\"https:\/\/jogamp.org\/cgit\/jogamp-scripting.git\/tree\/maven\/README.md\">JogAmp maven scripting<\/a> shell functions inside <a href=\"https:\/\/jogamp.org\/cgit\/jogamp-scripting.git\/tree\/maven\/sonatype_api.sh\">sonatype_api.sh<\/a> We will discuss the scripts here<\/p>\n<p>The script uses the variables <em>api_user<\/em>, <em>api_password<\/em>, <em>repository_key<\/em>. <em>api_user\u00a0<\/em>and <em>api_password<\/em> are the secrets earlier produced with the API token on Sonatype&#8217;s central.<\/p>\n<p>The <em>repository_key<\/em> must be retrieved by<em> <span style=\"text-decoration: underline;\">sonatype_search_repos<\/span><\/em> below and it is a unique triple of <em>api_user<\/em> + upload-IP + <em>namespace<\/em> and gladly does not change.<\/p>\n<p>Let&#8217;s start with <em>sonatype_search_repos<\/em> to see if any package exist in your own namespace on staging and to retrieve your namespace and API token<\/p>\n<pre><code>sonatype_search_repos<span class=\"hl opt\">() {<\/span>\r\n    res<span class=\"hl opt\">=<\/span><span class=\"hl str\">`curl<\/span> <span class=\"hl ipl\">${CURL_OPTS} -u ${api_user}:${api_password}<\/span> <span class=\"hl str\">--anyauth --request GET \\<\/span>\r\n<span class=\"hl str\">        --header \"accept: application\/json\" \\<\/span>\r\n<span class=\"hl str\">        \"https:\/\/ossrh-staging-api.central.sonatype.com\/manual\/search\/repositories?ip=any\"`<\/span>\r\n    handle_result $? <span class=\"hl str\">\"<\/span><span class=\"hl ipl\">${res}<\/span><span class=\"hl str\">\"<\/span>\r\n    <span class=\"hl kwb\">return<\/span> $?\r\n<span class=\"hl opt\">}<\/span><\/code><\/pre>\n<p>If the resulting query contains package information, it contains the required <em>repository_key<\/em> for our next steps.<\/p>\n<p>The first mutable act is to drop any package <em>if existing<\/em> in your own namespace on staging via <em>sonatype_drop_staging.<\/em> This involves using <em>sonatype_search_repos<\/em> to query the <em>repository_key<\/em> of an existing package as described above.<\/p>\n<pre><code>sonatype_drop_staging<span class=\"hl opt\">() {<\/span>\r\n    repository_key<span class=\"hl opt\">=<\/span><span class=\"hl kwd\">$1<\/span>\r\n    <span class=\"hl kwa\">if<\/span> <span class=\"hl opt\">[<\/span> <span class=\"hl kwb\">-z<\/span> <span class=\"hl str\">\"<\/span><span class=\"hl ipl\">${repository_key}<\/span><span class=\"hl str\">\"<\/span> <span class=\"hl opt\">] ;<\/span> <span class=\"hl kwa\">then<\/span>\r\n        <span class=\"hl kwb\">echo<\/span> <span class=\"hl str\">\"repository_key is empty\"<\/span>\r\n        <span class=\"hl kwb\">exit<\/span> <span class=\"hl num\">1<\/span>\r\n    <span class=\"hl kwa\">fi<\/span>\r\n    res<span class=\"hl opt\">=<\/span><span class=\"hl str\">`curl<\/span> <span class=\"hl ipl\">${CURL_OPTS} -u ${api_user}:${api_password}<\/span> <span class=\"hl str\">--anyauth --request DELETE \\<\/span>\r\n<span class=\"hl str\">        --header 'accept: *\/*' \\<\/span>\r\n<span class=\"hl str\">        \"https:\/\/ossrh-staging-api.central.sonatype.com\/manual\/drop\/repository\/<\/span><span class=\"hl ipl\">${repository_key}<\/span><span class=\"hl str\">\"`<\/span>\r\n    handle_result $? <span class=\"hl str\">\"<\/span><span class=\"hl ipl\">${res}<\/span><span class=\"hl str\">\"<\/span>\r\n    <span class=\"hl kwb\">return<\/span> $?\r\n<span class=\"hl opt\">}<\/span><\/code><\/pre>\n<p>The final mutable act is to upload your package in your namespace on staging via <em>sonatype_upload_staging<\/em>. This involves using <em>sonatype_search_repos<\/em> to query the <em>repository_key<\/em> of an existing package as described above.<\/p>\n<pre><code>sonatype_upload_staging<span class=\"hl opt\">() {<\/span>\r\n    repository_key<span class=\"hl opt\">=<\/span><span class=\"hl kwd\">$1<\/span>\r\n    <span class=\"hl kwa\">if<\/span> <span class=\"hl opt\">[<\/span> <span class=\"hl kwb\">-z<\/span> <span class=\"hl str\">\"<\/span><span class=\"hl ipl\">${repository_key}<\/span><span class=\"hl str\">\"<\/span> <span class=\"hl opt\">] ;<\/span> <span class=\"hl kwa\">then<\/span>\r\n        <span class=\"hl kwb\">echo<\/span> <span class=\"hl str\">\"repository_key is empty\"<\/span>\r\n        <span class=\"hl kwb\">exit<\/span> <span class=\"hl num\">1<\/span>\r\n    <span class=\"hl kwa\">fi<\/span>\r\n    res<span class=\"hl opt\">=<\/span><span class=\"hl str\">`curl<\/span> <span class=\"hl ipl\">${CURL_OPTS} -u ${api_user}:${api_password}<\/span> <span class=\"hl str\">--anyauth --request POST \\<\/span>\r\n<span class=\"hl str\">        --header 'accept: *\/*' \\<\/span>\r\n<span class=\"hl str\">        -d '' \\<\/span>\r\n<span class=\"hl str\">        \"https:\/\/ossrh-staging-api.central.sonatype.com\/manual\/upload\/repository\/<\/span><span class=\"hl ipl\">${repository_key}<\/span><span class=\"hl str\">?publishing_type=automatic\"`<\/span>\r\n    handle_result $? <span class=\"hl str\">\"<\/span><span class=\"hl ipl\">${res}<\/span><span class=\"hl str\">\"<\/span>\r\n    <span class=\"hl kwb\">return<\/span> $?\r\n<span class=\"hl opt\">}<\/span><\/code><\/pre>\n<p>This procedure shall release to central namespace and also bring the validated packages into <code>publishing<\/code> state. The latter may take time, but eventually move into state <code>published<\/code>.<\/p>\n<p>To manage the Sonatype repository, e.g. see the <code>publishing<\/code>, <code>published<\/code> or <code>failed<\/code> packages we shall log-in to <a href=\"https:\/\/central.sonatype.com\/account\">https:\/\/central.sonatype.com\/account<\/a>.<\/p>\n<p>Click &#8216;Publish&#8217; in the top navigation bar, now you should see the successfully<\/p>\n<p>For details, please read <a href=\"https:\/\/jogamp.org\/cgit\/jogamp-scripting.git\/tree\/maven\/README.md\">JogAmp maven scripting<\/a>.<\/p>\n<p style=\"text-align: center;\">***<\/p>\n<p><a href=\"https:\/\/forum.jogamp.org\/Maven-Repo-tp4043909p4043914.html\">One issue I have observed<\/a> is that uploaded artifacts to staging above around 22MB via <a href=\"https:\/\/maven.apache.org\/plugins\/maven-gpg-plugin\/sign-and-deploy-file-mojo.html\">gpg:sign-and-deploy-file<\/a> often caused the file to be truncated and hence the web API command <em>sonatype_upload_staging<\/em> fails due to a mismatch of gpg signature and all checksums. This is observed on a GNU\/Linux workstation w\/ OpenJDK 17, Maven 3.8 and 3.9. Workaround for the JogAmp project was to compress mentioned files, as gladly their size was due to being an uncompressed jar file. If I receive a solution or learn the culprit, I will post it here.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Besides deploying on your own maven mirror repository, one usually also likes to deploy on Maven Central for stability and acceptance http:\/\/search.maven.org . Jogamp lacked this feature since version 2.4.0, so I had to dive back into this matter &#8211; especially since Sonatype changed their procedures due to End-of-Life Sunset Date for OSSRH coinciding with&hellip; <a class=\"more-link\" href=\"https:\/\/jausoft.com\/blog\/2025\/09\/07\/publishing-on-sonatypes-central-maven-repo\/\">Continue reading <span class=\"screen-reader-text\">Publishing on Sonatype&#8217;s Central Maven Repo<\/span> <span class=\"meta-nav\" aria-hidden=\"true\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[7,3,8],"tags":[9,27,80,16,44,33,31,17,46,29,34],"class_list":["post-1655","post","type-post","status-publish","format-standard","hentry","category-3d-opengl","category-computer-stuff","category-jogamp","tag-3d","tag-android","tag-freebsd","tag-java","tag-jogamp","tag-linux","tag-mobile","tag-opengl","tag-openjdk","tag-os-x","tag-windows"],"_links":{"self":[{"href":"https:\/\/jausoft.com\/blog\/wp-json\/wp\/v2\/posts\/1655","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jausoft.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jausoft.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jausoft.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/jausoft.com\/blog\/wp-json\/wp\/v2\/comments?post=1655"}],"version-history":[{"count":6,"href":"https:\/\/jausoft.com\/blog\/wp-json\/wp\/v2\/posts\/1655\/revisions"}],"predecessor-version":[{"id":1661,"href":"https:\/\/jausoft.com\/blog\/wp-json\/wp\/v2\/posts\/1655\/revisions\/1661"}],"wp:attachment":[{"href":"https:\/\/jausoft.com\/blog\/wp-json\/wp\/v2\/media?parent=1655"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jausoft.com\/blog\/wp-json\/wp\/v2\/categories?post=1655"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jausoft.com\/blog\/wp-json\/wp\/v2\/tags?post=1655"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}