{"id":21206229,"url":"https://github.com/rtmigo/mavence","last_synced_at":"2026-04-14T04:02:00.709Z","repository":{"id":61780959,"uuid":"552581689","full_name":"rtmigo/mavence","owner":"rtmigo","description":"CLI for publishing Gradle projects (Kotlin, Java, etc.) to Maven Central","archived":false,"fork":false,"pushed_at":"2022-10-23T23:05:19.000Z","size":263,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-29T08:02:53.172Z","etag":null,"topics":["central","gpg","gradle","java","jvm","kotlin","maven","ossrh","publish","reasons-to-hate-java","signing","sonatype"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rtmigo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-10-16T22:33:31.000Z","updated_at":"2025-02-17T18:02:48.000Z","dependencies_parsed_at":"2023-01-20T09:26:04.367Z","dependency_job_id":null,"html_url":"https://github.com/rtmigo/mavence","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/rtmigo/mavence","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fmavence","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fmavence/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fmavence/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fmavence/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rtmigo","download_url":"https://codeload.github.com/rtmigo/mavence/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rtmigo%2Fmavence/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31781292,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T02:24:21.117Z","status":"ssl_error","status_checked_at":"2026-04-14T02:24:20.627Z","response_time":153,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["central","gpg","gradle","java","jvm","kotlin","maven","ossrh","publish","reasons-to-hate-java","signing","sonatype"],"created_at":"2024-11-20T20:54:55.442Z","updated_at":"2026-04-14T04:02:00.692Z","avatar_url":"https://github.com/rtmigo.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# [mavence](https://github.com/rtmigo/mavence) #experimental\n\nCLI utility for publishing Gradle projects (Kotlin, Java, etc.) to Maven\nCentral.\n\nThis essentially does the same thing as\nthe [Signing](https://docs.gradle.org/current/userguide/signing_plugin.html) and\n[Nexus](https://github.com/gradle-nexus/publish-plugin) plugins.\n\n\u003cdetails\u003e\u003csummary\u003eWhy not publish with plugins?\u003c/summary\u003e\n\n- Building locally\n- Publishing somewhere\n\nThese tasks are almost unrelated.\n\nBy placing publishing logic in a build script, you make the foundation of the\nproject complex and ugly.\n\n\u003c/details\u003e\n\n# Install and run\n\n## Command line\n\n```\nwget https://github.com/rtmigo/mavence/releases/latest/download/mavence.jar\n```\n\nRun:\n\n```\njava -jar mavence.jar\n```\n\n## Manually\n\nJust get the\nlatest [mavence.jar](https://github.com/rtmigo/mavence/releases/latest/download/mavence.jar)\nfrom the [releases page](https://github.com/rtmigo/mavence/releases).\n\nRun:\n\n```\njava -jar ~/Downloads/mavence.jar\n```\n\n# Setting the environment\n\nBefore publishing, you will need to set the following four environment\nvariables:\n\n| variable             | wtf                                                       |\n|----------------------|-----------------------------------------------------------|\n| `SONATYPE_USERNAME`  | Username for Sonatype JIRA (optionally replaced by token) |\n| `SONATYPE_PASSWORD`  | Password for Sonatype JIRA (optionally replaced by token) |\n| `MAVEN_GPG_KEY`      | Locally generated private key in ASCII armor              |  \n| `MAVEN_GPG_PASSWORD` | Password protecting the private key                       |\n\n\u003cdetails\u003e\u003csummary\u003eWhere to get Sonatype variables\u003c/summary\u003e\n\n[Register](https://getstream.io/blog/publishing-libraries-to-mavencentral-2021/#registering-a-sonatype-account)\non the [Sonatype Jira](https://issues.sonatype.org/secure/Dashboard.jspa)\nand chat with bots, until they **verify** that you can publish a package.\nThat gives you `SONATYPE_USERNAME` and `SONATYPE_PASSWORD` you can use for\npublishing.\n\nAdditionally, you\ncan [generate tokens](https://central.sonatype.org/publish/manage-user/#generate-token-on-nxrm-servers)\nto use them *instead* of the username and password. The\ntokens can be placed in the same `SONATYPE_USERNAME` and `SONATYPE_PASSWORD` and\ndo not require other changes.\n\n**May the Google be with you.**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eWhere to get GPG variables\u003c/summary\u003e\n\n## Generate key\n\nIt gives you `MAVEN_GPG_PASSWORD`.\n\n```bash\n$ gpg --gen-key\n```\n\n`gpg` will interactively prompt you to choose a password for the new key. It is\nthis password that should later be placed in the variable `MAVEN_GPG_PASSWORD`.\n\n## See your private key\n\nIt gives you `MAVEN_GPG_KEY`.\n\n```bash\n$ gpg --list-keys\n```\n\n```\npub   rsa3072 2022-10-18 [SC]\n      1292EC426424C9BA0A581EE060C994FDCD3CADBD       \u003c\u003c this is the ID\nuid           [ultimate] John Doe \u003cdoe@example.com\u003e\nsub   rsa3072 2022-10-18 [E]\n```\n\n```bash\n$ gpg --export-secret-keys --armor 1292EC426424C9BA0A581EE060C994FDCD3CADBD\n```\n\n```\n-----BEGIN PGP PRIVATE KEY BLOCK-----\n\nlQWGBGNOko0BDACzxxMh4EwjlOBRuV94reQglPp5Chzdw4yJHKBYffGGCy27nmde\nQ05nuVbGJvHqv6jF1+zRNMIEKS/Ioa1C4jenEe0j3boGM2IgjHtPq7WuOeSR2ErX\n...\n\n-----END PGP PRIVATE KEY BLOCK-----\n```\n\nOr put it directly to `MAVEN_GPG_KEY` environment variable (Bash):\n\n```bash\n$ MAVEN_GPG_KEY=$(gpg --export-secret-keys --armor 1292EC426424C9BA0A581EE060C994FDCD3CADBD)\n\n$ export MAVEN_GPG_KEY \n```\n\n## Send the public key to [a keyserver](https://unix.stackexchange.com/a/692097)\n\nYou won't come back to this again, but it will be important for the servers when\npublishing the package.\n\n```bash\n$ gpg --list-keys\n```\n\n```\npub   rsa3072 2022-10-18 [SC]\n      1292EC426424C9BA0A581EE060C994FDCD3CADBD       \u003c\u003c this is the ID\nuid           [ultimate] John Doe \u003cdoe@example.com\u003e\nsub   rsa3072 2022-10-18 [E]\n```\n\n```bash\n$ gpg --keyserver hkps://keys.openpgp.org --send-keys 1292EC426424C9BA0A581EE060C994FDCD3CADBD\n```\n\nSome servers will just store the key. Some may require prior email verification.\nSome servers disappear. You have to choose the right one for the moment.\n\n\u003c/details\u003e\n\n# Minimal configuration\n\nWe're using Gradle configuration to build a Maven package, but not push\nit Central. Creating in this way seems like a reasonable compromise.\n\n### build.gradle.kts\n\n```kotlin\nplugins {\n    id(\"java-library\")\n    id(\"maven-publish\")\n}\n\njava {\n    withSourcesJar()\n    withJavadocJar()\n}\n\ngroup = \"my.domain\"\nversion = \"0.1.2\"\n\npublishing {\n    publications {\n        create\u003cMavenPublication\u003e(\"thelib\") {\n            from(components[\"java\"])\n            pom {\n                val github = \"https://github.com/doe/thelib\"\n\n                name.set(\"The Lib\")\n                description.set(\"There are dumber things than copy-pasting\")\n                url.set(github)\n\n                developers {\n                    developer {\n                        name.set(\"John Doe\")\n                        email.set(\"doe@sample.com\")\n                    }\n                }\n                scm {\n                    url.set(github)\n                    connection.set(github.replace(\"https:\", \"scm:git:\"))\n                }\n                licenses {\n                    license {\n                        name.set(\"Apache 2.0 License\")\n                        url.set(\"$github/blob/HEAD/LICENSE\")\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\n### settings.gradle.kts\n\n```kotlin\nrootProject.name = \"thelib\"\n```\n\n## Package name\n\nThe published package will have a version like `my.domain:thelib:0.1.2`.\n\n\u003cdetails\u003e\u003csummary\u003eGroup and Version\u003c/summary\u003e\n\nIt is the first and third part of `my.domain:thelib:0.1.2`,\ni.e. `my.domain`\nand `0.1.2`.\n\nThey can be defined in `build.gradle.kts` like that:\n\n```kotlin\ngroup = \"my.domain\"\nversion = \"0.1.2\"\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eArtifact\u003c/summary\u003e\n\nIt is the second part of `my.domain:thelib:0.1.2`, i.e. `thelib`.\n\n`mavence` takes it\nfrom [archivesBaseName](https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:archivesBaseName)\nGradle property.\n\n### If we release the root project:\n\n```\nthelib/                   \u003c\u003c\u003c dir name will be the artifact name \n    src/\n    build.gradle.kts\n    settings.gradle.kts   \u003c\u003c\u003c unless redefined here\n```\n\nThe redefine the root project name, add the following:\n\n```kotlin\n// settings.gradle.kts\n\nrootProject.name = \"newname\"\n```     \n\n### If we release a subproject:\n\n```\nmyrootproject/ \n    thelib/               \u003c\u003c\u003c dir name will be the artifact name\n        src/\n        build.gradle.kts\n    settings.gradle.kts    \n```\n\n\u003c/details\u003e\n\n# Publishing\n\n\u003cdetails\u003e\u003csummary\u003eKeep in mind\u003c/summary\u003e\n\nWhen publishing, the servers may not return meaningful error responses.\n\nThey often return a generic \"500 Internal Server Error\" code, spontaneous\n\"403 Forbidden\" or accept the file, but never publish it as a maven package.\n\nIf publishing a package fails for any reason, sometimes this can be fixed by:\n\n* retrying \n* retrying later\n* editing your package's metadata\n\n\u003c/details\u003e\n\n## Publish to Maven Central\n\nSet environment variables `MAVEN_GPG_KEY`, `MAVEN_GPG_PASSWORD`\n, `SONATYPE_USERNAME`, `SONATYPE_PASSWORD` and run:\n\n```\ncd /path/to/thelib\njava -jar mavence.jar central\n```\n\nThis single command will do all the necessary work: build, signing, staging\nand release.\n\n### Publish to Maven Local\n\nThis will place the package file inside the local `~/.m2` directory. This way\nyou can\ntest the package without sending it anywhere.\n\n```\ncd /path/to/thelib\njava -jar mavence.jar local\n```\n\n\u003cdetails\u003e\u003csummary\u003estdout\u003c/summary\u003e\n\n```\n{\n  \"group\": \"my.domain\",\n  \"artifact\": \"thelib\",\n  \"version\": \"0.1.2\",\n  \"notation\": \"my.domain:thelib:0.1.2\",\n  \"mavenRepo\": \"file:///home/doe/.m2/repository\"\n}\n```\n\n\u003c/details\u003e\n\n### Publish to Staging\n\nSet environment variables `MAVEN_GPG_KEY`, `MAVEN_GPG_PASSWORD`\n, `SONATYPE_USERNAME`, `SONATYPE_PASSWORD` and run:\n\n```\ncd /path/to/thelib\njava -jar mavence.jar stage\n```\n\nThis will push the package to\na [temporary remote repository](https://s01.oss.sonatype.org/content/repositories/)\n.\nThis way you can test the package without sending it to Central.\n\n## Testing before publishing\n\nAlthough the utility prints quite a lot, `stdout` remains clean and only\nreceives the result as JSON.\n\nBash:\n\n```\nJSON=$(java -jar mavence.jar local)\n\necho $JSON\n```\n\nOutput:\n\n```json\n{\n  \"group\": \"my.domain\",\n  \"artifact\": \"thelib\",\n  \"version\": \"0.1.2\",\n  \"notation\": \"my.domain:thelib:0.1.2\",\n  \"mavenRepo\": \"file:///home/doe/.m2/repository\"\n}\n```\n\nUsing this data, you can test the package before it is sent.\n\nI usually use Python and [tempground](https://pypi.org/project/tempground/) for\nsuch testing.\n\n# License\n\nCopyright © 2022 [Artsiom iG](https://github.com/rtmigo).\nReleased under the [ISC License](LICENSE).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frtmigo%2Fmavence","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frtmigo%2Fmavence","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frtmigo%2Fmavence/lists"}