From fdd6c9b03bc447664153fac344f63adc0a57a9fe Mon Sep 17 00:00:00 2001 From: jcpitre <106176106+jcpitre@users.noreply.github.com> Date: Fri, 20 Oct 2023 13:15:36 -0400 Subject: [PATCH] feat: 1588 publish to maven central (#1596) Added steps to publish_assets.yml to push to Sonatype. Modified gradle config to sign artefacts and push. --- .github/workflows/publish_assets.yml | 37 ++++++ build.gradle | 115 +++++++++++++++++- core/build.gradle | 23 +--- docs/RELEASE.md | 35 +++++- main/build.gradle | 13 +- model/build.gradle | 15 ++- .../annotation/CurrencyAmount.java | 4 +- 7 files changed, 201 insertions(+), 41 deletions(-) diff --git a/.github/workflows/publish_assets.yml b/.github/workflows/publish_assets.yml index 887665cc6d..0137445355 100644 --- a/.github/workflows/publish_assets.yml +++ b/.github/workflows/publish_assets.yml @@ -3,6 +3,7 @@ name: Upload Release Assets on: release: types: [ prereleased, released ] + workflow_dispatch: jobs: upload-release-assets: @@ -21,9 +22,11 @@ jobs: distribution: 'zulu' - name: Generate CLI jar + if: github.event_name == 'release' run: ./gradlew shadowJar - name: Upload JAR Asset + if: github.event_name == 'release' uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -34,9 +37,11 @@ jobs: asset_content_type: application/java-archive - name: Generate rules.json + if: github.event_name == 'release' run: ./gradlew webClientRulesJSON - name: Upload rules.json asset + if: github.event_name == 'release' uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -47,12 +52,15 @@ jobs: asset_content_type: application/json - name: Generate JavaDocs + if: github.event_name == 'release' run: ./gradlew aggregateJavadoc - name: Zip JavaDocs + if: github.event_name == 'release' run: zip -r javadocs.zip build/docs/aggregateJavadoc - name: Upload zipped Javadocs + if: github.event_name == 'release' uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -62,3 +70,32 @@ jobs: asset_name: javadocs.zip asset_content_type: application/zip + # The following steps will publish artifacts to a sonatype staging repo with the aim of promoting them to maven central + # Pretty much everything is done through gradle. + # The version used will be according to the axion-release-plugin, meaning it will take a tag if present. + # The tag should follow semantic versioning, e.g. v1.2.3. There could be a suffix, e.g. v1.2.3-TEST + # gradle will build, sign then upload artifacts to a Sonatype staging repo. + # See https://s01.oss.sonatype.org for accessing these repos. + # At this point it should manually be closed, which will trigger acceptance tests for maven central (but not transfer yet) + # Once closed, the repo is available for testing. + # After testing, it can be manually promoted on the sonatype site, which will then publish to maven central. + # Note than once in maven central a release cannot be removed or altered. + + - name: Load secrets from 1Password + id: onepw_secrets + uses: 1password/load-secrets-action@v1.3.1 + with: + export-env: true # Export loaded secrets as environment variables + env: + OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }} # This is required to connect to the vault in our 1Password account. + MAVEN_GPG_PRIVATE_KEY: "op://rbiv7rvkkrsdlpcrz3bmv7nmcu/yztcx47yzp4vizjyaq7ulvkgoi/Private Key" + MAVEN_GPG_PASSPHRASE: "op://rbiv7rvkkrsdlpcrz3bmv7nmcu/yztcx47yzp4vizjyaq7ulvkgoi/password" + + - name: Build and Publish to Sonatype + run: | + # The gradle java verifying plugin does not work with java 17. + # Don't verify since it has already been done when the PR was created. + ./gradlew publish -x verifyGoogleJavaFormat + env: + SONATYPE_USERNAME: ${{secrets.SONATYPE_USERNAME}} + SONATYPE_PASSWORD: ${{secrets.SONATYPE_PASSWORD}} diff --git a/build.gradle b/build.gradle index 1c53bd7ae5..2025f48341 100644 --- a/build.gradle +++ b/build.gradle @@ -13,9 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +/* + * A note about publishing and signing. + * Maven central requires that artifacts be signed. And upload is done to Sonatype. + * To publish you will need these environment variables defined: + * SONATYPE_USERNAME + * SONATYPE_PASSWORD + * MAVEN_GPG_PRIVATE_KEY + * MAVEN_GPG_PASSPHRASE + * Suggestion is to put these in a shell script with restricted read permissions, then source it before calling + * ./gradlew publish. + */ plugins { id 'java' + id 'maven-publish' + id 'signing' id 'test-report-aggregation' id 'com.github.sherter.google-java-format' version '0.9' id "io.freefair.aggregate-javadoc" version "6.4.3" @@ -24,7 +36,7 @@ plugins { // Setup and configure properties that are consistent across all projects, including sub-modules. allprojects { - group 'org.mobilitydata' + group 'org.mobilitydata.gtfs-validator' // Per the axion-release plugin, this computes the project version based // on the most recent tag in the repo. @@ -66,6 +78,105 @@ allprojects { } } +subprojects { + apply plugin: 'java' + + // Cannot publish a SNAPSHOT. The provided sonatype url will not accept it. + tasks.withType(PublishToMavenRepository).all { task -> + task.onlyIf { + if (project.version.toString().contains('SNAPSHOT')) { + throw new GradleException("Publishing is not allowed for SNAPSHOT versions. Currently " + project.version) + } + true + } + } + + task javadocJar(type: Jar) { + archiveClassifier.set('javadoc') + from javadoc + } + + task sourcesJar(type: Jar) { + archiveClassifier.set('sources') + from sourceSets.main.allSource + } + + // These modules require the same publishing configuration, apart from the name of the module + // Also we want to limit artefact publishing to these modules. + if (project.name == 'main' || + project.name == 'core' || + project.name == 'model') { + def fullProjectName = 'gtfs-validator-' + project.name + + afterEvaluate { + publishing { + repositories { + // This is the sonatype staging repo for maven. + // Once uploaded, the repo needs to be manually closed, which will trigger acceptance tests for + // maven central (but not transfer yet). + // Once successfully closed, the repo is available for testing. + // After testing, it can be manually promoted on the sonatype site, which will then publish to maven central. + maven { + url = 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2' + credentials { + username System.getenv("SONATYPE_USERNAME") + password System.getenv("SONATYPE_PASSWORD") + } + } + } + + publications { + mavenJava(MavenPublication) { + from components.java + artifactId = fullProjectName + + artifact sourcesJar + artifact javadocJar + + // Definition of the pom that will be included with the uploaded artifacts. + pom { + name = fullProjectName + description = 'The ' + project.name + " artifacts from the gtfs validator" + url = 'https://github.com/MobilityData/gtfs-validator' + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'https://github.com/MobilityData/gtfs-validator/blob/master/LICENSE' + } + } + developers { + developer { + id = 'dev' + name = 'Dev group' + email = 'it@mobilitydata.org' + } + } + scm { + connection = 'scm:git:git://github.com/MobilityData/gtfs-validator.git' + developerConnection = 'scm:git:ssh://github.com/MobilityData/gtfs-validator.git' + url = 'https://github.com/MobilityData/gtfs-validator' + } + } + } + } + } + signing { + useInMemoryPgpKeys(System.getenv('MAVEN_GPG_PRIVATE_KEY'), System.getenv('MAVEN_GPG_PASSPHRASE')) + sign publishing.publications.mavenJava + } + } + + } + compileJava { + options.compilerArgs << '-parameters' + } + + compileTestJava { + options.compilerArgs << '-parameters' + } + +} + reporting { reports { // Necessary for unit test result aggregation. diff --git a/core/build.gradle b/core/build.gradle index ec9f5746fd..cccd38adc9 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -17,8 +17,13 @@ plugins { id 'java' id 'maven-publish' + id 'signing' } +//publishing { +// publishing and signing are done in gtfs-validator build.gradle to minimize repetition +//} + dependencies { implementation project(':model') annotationProcessor project(':processor:notices') @@ -50,21 +55,3 @@ jar { 'Implementation-Version': project.version) } } - -publishing { - publications { - mavenJava(MavenPublication) { - artifactId = 'gtfs-validator-core' - - from components.java - } - } -} - -compileJava { - options.compilerArgs << '-parameters' -} - -compileTestJava { - options.compilerArgs << '-parameters' -} diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 5ece0ed396..73c8a07c1b 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -33,12 +33,43 @@ is:pr is:closed merged:>2020-07-28 base:master sort:updated-desc 💡 For more details on versioning, see [Understanding Maven Version Numbers](https://docs.oracle.com/middleware/1212/core/MAVEN/maven_version.htm#MAVEN8855). -### 3. Remove all `sha` Docker images added since last release +### 4. Remove all `sha` Docker images added since last release 1. Find the [list of Docker images for this project.](https://github.com/orgs/MobilityData/packages/container/gtfs-validator/versions) 1. Delete all `sha`tagged Docker images added since last release. ![grhc preview](https://user-images.githubusercontent.com/35747326/100006687-e1b5d080-2d98-11eb-846d-af12fbd7ca9f.png) **⚠️ Note: this manipulation can only be done by someone whose GitHub account has `Admin` access rights over the `gtfs-validator` package.** -### 3. Update the release number in the wiki +### 5. Update the release number in the wiki By updating the version number in the project's wiki, users of the app will be advised to upgrade if their local version does not match. Update [this page](https://github.com/MobilityData/gtfs-validator/wiki/Current-Version) with the new version. + +### 6. Publishing to Maven Central +* Maven central is a repository used by developers to download libraries that can be used in their own development. +* We upload some jars (currently gtfs-validator-main, gtfs-validator-core and gtfs-validator-model) there to make them available. +Uploaded artefacts have versions. +* Publication to Maven Central requires some manual operations. + +* Typically when doing a release the publish_assets.yml Github action is automatically run. +This will upload some assets +to be available on the release page itself (see for example [Release 4.1.0 assets](https://github.com/MobilityData/gtfs-validator/releases/tag/v4.1.0#:~:text=7%20other%20contributors-,Assets,-6)) + + +* This Github action also publishes to Sonatype. This is used as a staging area before making the arftefacts available via Maven Central. +* See [Sonatype Staging Repositories](https://s01.oss.sonatype.org/#stagingRepositories) (login required) +* There should be a repository in the list with name orgmobilitydata-####. This is automatically created by Sonatype when files are uploaded. + +![image](https://github.com/MobilityData/gtfs-validator/assets/106176106/f08a24ec-addb-4d63-840d-24297c505822) + + +* You can browse the repo content to make sure everything is there. In particular there should be the jars for the code, jars for javadoc, for sources, and files for the maven pom. +* Everything should be signed, as evidenced by the presence of files with extension .sha1, .sha256, .sha512 etc. +* Also make sure the version is correct. +* You then need to manually close the repo. Doing this will trigger acceptance tests for Maven Central. + +![image](https://github.com/MobilityData/gtfs-validator/assets/106176106/1d8916c6-a640-43cf-9658-82193d127b1d) + +* Once the repository is closed it becomes available for inclusion in projects for testing. The URL to use as repository in your gradle or maven configuration files can be found in the summary for the repo. +![image](https://github.com/MobilityData/gtfs-validator/assets/106176106/c809c1ca-67d7-4c45-bfa5-47441e163d2f) + +* Once satisfied with the testing, the repo can be released to Maven Central. +* Note that once a release is deployed on Maven Central, it cannot be removed or modified. If problems are detected after this stage, a new release with a different version has to be created. \ No newline at end of file diff --git a/main/build.gradle b/main/build.gradle index b20e9cb31b..a64445d84b 100644 --- a/main/build.gradle +++ b/main/build.gradle @@ -17,17 +17,12 @@ plugins { id 'java' id 'maven-publish' + id 'signing' } -publishing { - publications { - mavenJava(MavenPublication) { - artifactId = 'gtfs-validator-main' - - from components.java - } - } -} +//publishing { +// publishing and signing are done in gtfs-validator build.gradle to minimize repetition +//} dependencies { annotationProcessor project(':processor:notices') diff --git a/model/build.gradle b/model/build.gradle index e5bc4c440a..2ca9771567 100644 --- a/model/build.gradle +++ b/model/build.gradle @@ -1,18 +1,17 @@ plugins { id 'java' + id 'maven-publish' + id 'signing' } +//publishing { +// publishing and signing are done in gtfs-validator build.gradle to minimize repetition +//} + + dependencies { annotationProcessor 'com.google.auto.value:auto-value:1.7.4' compileOnly 'com.google.auto.value:auto-value-annotations:1.7.4' implementation 'org.jetbrains:annotations:20.1.0' implementation 'com.google.code.findbugs:jsr305:3.0.2' } - -compileJava { - options.compilerArgs << '-parameters' -} - -compileTestJava { - options.compilerArgs << '-parameters' -} diff --git a/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/CurrencyAmount.java b/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/CurrencyAmount.java index edabc99703..04a1d9a0a0 100644 --- a/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/CurrencyAmount.java +++ b/model/src/main/java/org/mobilitydata/gtfsvalidator/annotation/CurrencyAmount.java @@ -17,8 +17,8 @@ package org.mobilitydata.gtfsvalidator.annotation; /** - * Generates a {@link org.mobilitydata.gtfsvalidator.notice.InvalidCurrencyAmountNotice} for a - * currency amount value that does not match the specification of its currency code. + * Generates a InvalidCurrencyAmountNotice for a currency amount value that does not match the + * specification of its currency code. * *

Example. *