From f59ac808e60f2e90f923882309350f59f91a742b Mon Sep 17 00:00:00 2001 From: Philipp Schmelter Date: Tue, 8 Apr 2025 23:33:34 +0200 Subject: [PATCH 1/5] added mavenCentralPublish.gradle --- CHANGELOG.md | 1 + gradle/scripts/mavenCentralPublish.gradle | 109 ++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 gradle/scripts/mavenCentralPublish.gradle diff --git a/CHANGELOG.md b/CHANGELOG.md index 83cf10c4..0fd1a3bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added Marius Staudt to list of reviewers [#516](https://github.com/ie3-institute/OSMoGrid/issues/501) - Create `CITATION.cff` [#531](https://github.com/ie3-institute/OSMoGrid/issues/531) - Implemented GitHub Actions Pipeline [#545](https://github.com/ie3-institute/OSMoGrid/issues/545) +- Added `mavenCentralPublish.gradle` to enable deployment to MavenCentral [#568](https://github.com/ie3-institute/OSMoGrid/issues/568) ### Changed - Rely on Java 17 diff --git a/gradle/scripts/mavenCentralPublish.gradle b/gradle/scripts/mavenCentralPublish.gradle new file mode 100644 index 00000000..e56723bc --- /dev/null +++ b/gradle/scripts/mavenCentralPublish.gradle @@ -0,0 +1,109 @@ +/* Maven publish - start */ + +tasks.register("sourcesJar", Jar) { + archiveClassifier.set("sources") + from sourceSets.main.allJava +} + +tasks.register("javadocJar", Jar) { + dependsOn tasks.named("javadoc", Javadoc) + archiveClassifier.set("javadoc") + from { tasks.named("javadoc", Javadoc).get().destinationDir } +} + +if (project.hasProperty('user') && project.hasProperty('password') && project.hasProperty('deployVersion')) { + + // snapshot version differs from normal version + String versionString = project.getProperty('deployVersion') + + + publishing { + publications { + create("mavenJava", MavenPublication) { + + versionMapping { + // resolves dynamic versioning to current version number + usage('java-api') { + fromResolutionOf('runtimeClasspath') + } + usage('java-runtime') { + fromResolutionResult() + } + } + pom { + description = 'OSMoGrid - a tool to generate life like electrical grid models based on publicly available data, mainly OpenStreetMap.' + name = 'OSMoGrid' + url = 'https://github.com/ie3-institute/OSMoGrid' + organization { + name = 'Institute of Energy Systems, Energy Efficiency and Energy Economics (ie3)/TU Dortmund University' + url = 'https:www.ie3.tu-dortmund.de/' + } + issueManagement { + system = 'GitHub' + url = 'https:github.com/ie3-institute/OSMoGrid/issues' + } + licenses { + license { + name = 'BSD 3-Clause License' + url = 'https:github.com/ie3-institute/OSMoGrid/blob/master/LICENSE' + } + } + developers { + developer { + organization = "Institute of Energy Systems, Energy Efficiency and Energy Economics (ie3)/TU Dortmund University" + organizationUrl = "https:ie3.etit.tu-dortmund.de" + } + } + scm { + connection = 'scm:git:git:github.com/ie3-institute/OSMoGrid.git' + developerConnection = 'scm:git:ssh:github.com:ie3-institute/OSMoGrid.git' + url = 'https:github.com/ie3-institute/OSMoGrid' + } + } + + removeTestDependenciesFromPom(pom) + groupId = group + artifactId = 'OSMoGrid' + version = versionString + + from components.java + artifact tasks.named("sourcesJar") + artifact tasks.named("javadocJar") + } + } + repositories { + maven { + def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" + def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + url = versionString.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + credentials { + username project.getProperty('user') + password project.getProperty('password') + } + } + } + signing { + useInMemoryPgpKeys( + findProperty('signingKey') as String, + findProperty('signingPassword') as String + ) + sign publications.mavenJava + } + } + + tasks.named("generatePomFileForMavenJavaPublication") { + destination = layout.buildDirectory.file("generated-pom.xml").get().asFile + } +} + +def removeTestDependenciesFromPom(pom) { + pom.withXml { + def root = asNode() + // eliminate test-scoped dependencies (no need in maven central POMs) + root.dependencies.removeAll { dep -> + dep.scope == "test" + } + } +} + +/* Maven publish - end */ From 57c53e39428c68caf7c53ee5705463094807cc13 Mon Sep 17 00:00:00 2001 From: Philipp Schmelter Date: Thu, 10 Apr 2025 00:01:26 +0200 Subject: [PATCH 2/5] added mavenCentralPublish.gradle to build.gradle --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 0102867a..51d09255 100644 --- a/build.gradle +++ b/build.gradle @@ -46,6 +46,7 @@ apply from: scriptsLocation + 'scoverage.gradle' // scoverage scala code coverag apply from: scriptsLocation + 'vcs.gradle' apply from: scriptsLocation + 'semVer.gradle' apply from: scriptsLocation + 'tscfg.gradle' +apply from: scriptsLocation + 'mavenCentralPublish.gradle' apply from: scriptsLocation + 'branchName.gradle' // checks naming scheme of branches configurations { From 4f4978c2311c93dec74267e9ee07e850d00ad467 Mon Sep 17 00:00:00 2001 From: Philipp Schmelter Date: Thu, 10 Apr 2025 00:03:20 +0200 Subject: [PATCH 3/5] tasks.register --- build.gradle | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 51d09255..4b01867b 100644 --- a/build.gradle +++ b/build.gradle @@ -149,9 +149,8 @@ tasks.withType(Javadoc){ options.encoding = 'UTF-8' } -task printVersion { +tasks.register("printVersion") { doLast { println project.version } } - From fbb492f83236e8a2d958ffba10fad27f4383e8fa Mon Sep 17 00:00:00 2001 From: Philipp Schmelter Date: Thu, 15 May 2025 14:16:43 +0200 Subject: [PATCH 4/5] remove comments --- gradle/scripts/mavenCentralPublish.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gradle/scripts/mavenCentralPublish.gradle b/gradle/scripts/mavenCentralPublish.gradle index e56723bc..2e06503c 100644 --- a/gradle/scripts/mavenCentralPublish.gradle +++ b/gradle/scripts/mavenCentralPublish.gradle @@ -1,5 +1,3 @@ -/* Maven publish - start */ - tasks.register("sourcesJar", Jar) { archiveClassifier.set("sources") from sourceSets.main.allJava @@ -105,5 +103,3 @@ def removeTestDependenciesFromPom(pom) { } } } - -/* Maven publish - end */ From 4993b4756f1cfd9bc11d965a4625996f802f19c2 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 29 Sep 2025 15:30:46 +0200 Subject: [PATCH 5/5] adapted to new mavenCentral usage --- .github/workflows/ci.yml | 25 ++++++++++-- build.gradle | 3 +- .../stagingAtMavenCentralPortal.gradle | 39 +++++++++++++++++++ ...adle => uploadToMavenCentralPortal.gradle} | 28 ++++++------- 4 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 gradle/scripts/stagingAtMavenCentralPortal.gradle rename gradle/scripts/{mavenCentralPublish.gradle => uploadToMavenCentralPortal.gradle} (78%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a6f769c4..8436c0a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,13 +84,17 @@ jobs: #Deployment - name: Deploy - if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' + if: github.ref == 'refs/heads/main' env: ORG_GRADLE_PROJECT_signingKey: ${{ secrets.MAVENCENTRAL_SIGNINGKEY }} ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.MAVENCENTRAL_SIGNINGPASS }} - ORG_GRADLE_PROJECT_user: ${{ secrets.MAVENCENTRAL_USER }} - ORG_GRADLE_PROJECT_password: ${{ secrets.MAVENCENTRAL_PASS }} + ORG_GRADLE_PROJECT_mavenCentralUser: ${{ github.actor == 'sebastian-peter' && secrets.MAVENCENTRAL_USER || + github.actor == 'danielfeismann' && secrets.MAVENCENTRAL_DANIEL_USER }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ github.actor == 'sebastian-peter' && secrets.MAVENCENTRAL_PASS || + github.actor == 'danielfeismann' && secrets.MAVENCENTRAL_DANIEL_PASS }} + run: | + echo "Using MavenCentral Token of GitHub Actor: ${{ github.actor }}" if [ "${GITHUB_REF}" == "refs/heads/main" ]; then currentVersion=$(./gradlew -q currentVersion) else @@ -100,3 +104,18 @@ jobs: echo "currentVersion=$currentVersion" ./gradlew publish -PdeployVersion=$currentVersion + + + #MavenCentral Staging + - name: MavenCentral Staging + if: github.ref == 'refs/heads/main' + env: + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.MAVENCENTRAL_SIGNINGKEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.MAVENCENTRAL_SIGNINGPASS }} + ORG_GRADLE_PROJECT_mavenCentralUser: ${{ github.actor == 'sebastian-peter' && secrets.MAVENCENTRAL_USER || + github.actor == 'danielfeismann' && secrets.MAVENCENTRAL_DANIEL_USER }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ github.actor == 'sebastian-peter' && secrets.MAVENCENTRAL_PASS || + github.actor == 'danielfeismann' && secrets.MAVENCENTRAL_DANIEL_PASS }} + + run: | + ./gradlew stagingAtMavenCentralPortal diff --git a/build.gradle b/build.gradle index 3ef3249a..3652d75f 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,8 @@ apply from: scriptsLocation + 'scoverage.gradle' // scoverage scala code coverag apply from: scriptsLocation + 'vcs.gradle' apply from: scriptsLocation + 'semVer.gradle' apply from: scriptsLocation + 'tscfg.gradle' -apply from: scriptsLocation + 'mavenCentralPublish.gradle' +apply from: scriptsLocation + 'uploadToMavenCentralPortal.gradle' // upload for deploy +apply from: scriptsLocation + 'stagingAtMavenCentralPortal.gradle' // stage for deploy apply from: scriptsLocation + 'branchName.gradle' // checks naming scheme of branches configurations { diff --git a/gradle/scripts/stagingAtMavenCentralPortal.gradle b/gradle/scripts/stagingAtMavenCentralPortal.gradle new file mode 100644 index 00000000..3ed6a673 --- /dev/null +++ b/gradle/scripts/stagingAtMavenCentralPortal.gradle @@ -0,0 +1,39 @@ +tasks.register('stagingAtMavenCentralPortal') { + group = 'publishing' + description = 'Stages uploaded artifacts to Maven Central Portal for manual approval' + + doLast { + def username = project.getProperty('mavenCentralUser') + def password = project.getProperty('mavenCentralPassword') + def deployVersion = project.findProperty('deployVersion') ?: project.version + + if (!username || !password) { + throw new GradleException("Sonatype credentials not found. Set sonatypeUser and sonatypePassword properties or environment variables.") + } + + // Request API for repo key + def repositoryString = providers.exec { + commandLine 'curl', + '-u', "${username}:${password}", + 'https://ossrh-staging-api.central.sonatype.com/manual/search/repositories' + }.getStandardOutput().getAsText().get() + + def repositoryGroovy = new groovy.json.JsonSlurper().parseText(repositoryString) + def key = repositoryGroovy.repositories[0].key + + // Stage via curl + def stageResult = providers.exec { + ignoreExitValue true + commandLine 'curl', + '-u', "${username}:${password}", + '-i', '-X', 'POST', "https://ossrh-staging-api.central.sonatype.com/manual/upload/repository/$key" + } + + if (stageResult.result.get().exitValue == 0) { + println "✓ Staging successful!" + println "Check status at: https://central.sonatype.com/publishing/deployments" + } else { + throw new GradleException("Staging failed") + } + } +} \ No newline at end of file diff --git a/gradle/scripts/mavenCentralPublish.gradle b/gradle/scripts/uploadToMavenCentralPortal.gradle similarity index 78% rename from gradle/scripts/mavenCentralPublish.gradle rename to gradle/scripts/uploadToMavenCentralPortal.gradle index 2e06503c..eeb68e2f 100644 --- a/gradle/scripts/mavenCentralPublish.gradle +++ b/gradle/scripts/uploadToMavenCentralPortal.gradle @@ -1,3 +1,5 @@ +/* Maven publish - start */ + tasks.register("sourcesJar", Jar) { archiveClassifier.set("sources") from sourceSets.main.allJava @@ -9,12 +11,11 @@ tasks.register("javadocJar", Jar) { from { tasks.named("javadoc", Javadoc).get().destinationDir } } -if (project.hasProperty('user') && project.hasProperty('password') && project.hasProperty('deployVersion')) { +if (project.hasProperty('mavenCentralUser') && project.hasProperty('mavenCentralPassword') && project.hasProperty('deployVersion')) { // snapshot version differs from normal version String versionString = project.getProperty('deployVersion') - publishing { publications { create("mavenJava", MavenPublication) { @@ -31,7 +32,7 @@ if (project.hasProperty('user') && project.hasProperty('password') && project.ha pom { description = 'OSMoGrid - a tool to generate life like electrical grid models based on publicly available data, mainly OpenStreetMap.' name = 'OSMoGrid' - url = 'https://github.com/ie3-institute/OSMoGrid' + url = 'https:github.com/ie3-institute/OSMoGrid' organization { name = 'Institute of Energy Systems, Energy Efficiency and Energy Economics (ie3)/TU Dortmund University' url = 'https:www.ie3.tu-dortmund.de/' @@ -65,26 +66,25 @@ if (project.hasProperty('user') && project.hasProperty('password') && project.ha version = versionString from components.java - artifact tasks.named("sourcesJar") - artifact tasks.named("javadocJar") + artifact sourcesJar + artifact javadocJar } } repositories { maven { - def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" - def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" - url = versionString.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + name = "MavenCentral" + url = "https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/" credentials { - username project.getProperty('user') - password project.getProperty('password') + username = project.findProperty('mavenCentralUser') + password = project.findProperty('mavenCentralPassword') } } } signing { useInMemoryPgpKeys( - findProperty('signingKey') as String, - findProperty('signingPassword') as String - ) + findProperty('signingKey') as String, + findProperty('signingPassword') as String + ) sign publications.mavenJava } } @@ -103,3 +103,5 @@ def removeTestDependenciesFromPom(pom) { } } } + +/* Maven publish - end */