From f8aee95d38ad7f2f5187a2c4c508e692f589e4ec Mon Sep 17 00:00:00 2001 From: vyshniak Date: Fri, 24 May 2024 08:57:15 +0300 Subject: [PATCH] SDKJAVA-136 Github CI for Gradle Resolver Signed-off-by: Oleksandr Vyshniak --- .github/scripts/deploy.sh | 14 ++++ .github/scripts/push-tag.sh | 19 +++++ .github/workflows/release.yml | 32 +++++++++ .github/workflows/test.yml | 27 +++++++ CONTRIBUTING.md | 32 ++++++++- README.md | 4 ++ build.gradle | 72 ++++++++++++++++++- .../gradle/ArtifactPropertiesResolver.java | 6 ++ .../artifact/gradle/CredentialsResolver.java | 6 ++ .../platform/artifact/gradle/HereAuth.java | 18 +++++ 10 files changed, 226 insertions(+), 4 deletions(-) create mode 100755 .github/scripts/deploy.sh create mode 100755 .github/scripts/push-tag.sh create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/scripts/deploy.sh b/.github/scripts/deploy.sh new file mode 100755 index 0000000..19a6b29 --- /dev/null +++ b/.github/scripts/deploy.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -ev + +export GPG_TTY=$(tty) + +gpg --version + +export ORG_GRADLE_PROJECT_asciiArmoredSigningKey=$(echo $GPG_PRIVATE_KEY | base64 -d) +export ORG_GRADLE_PROJECT_signingPassword=$GPG_PASSPHRASE +export ORG_GRADLE_PROJECT_ossrhUsername=$OSSRH_USERNAME +export ORG_GRADLE_PROJECT_ossrhPassword=$OSSRH_PASSWORD + +RELEASE_TAG=$(git describe --abbrev=0) +./gradlew clean publish closeAndReleaseStagingRepositories -PprojVersion=$RELEASE_TAG --info diff --git a/.github/scripts/push-tag.sh b/.github/scripts/push-tag.sh new file mode 100755 index 0000000..c7c9d4b --- /dev/null +++ b/.github/scripts/push-tag.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -ev + +# Prepare release +PREVIOUS_RELEASE_TAG=$(git describe --abbrev=0) +if [[ $PREVIOUS_RELEASE_TAG =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # Set current released version + RELEASE_TAG=$(echo ${PREVIOUS_RELEASE_TAG} | awk -F. -v OFS=. '{$NF += 1 ; print}') +else + echo "Cannot parse the latest release tag: ${PREVIOUS_RELEASE_TAG}" + exit 1 +fi + +git config user.name "GitHub CI" +git config user.email "ARTIFACT_SERVICE_SUPPORT@here.com" + +git tag -a "${RELEASE_TAG}" -m "Release ${RELEASE_TAG} from build ${GITHUB_JOB}" + +git push origin --tags \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b527ae4 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,32 @@ +name: Release + +on: + push: + branches: [ master ] + +jobs: + release: + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip release]')" + steps: + - uses: actions/checkout@v2 + with: + ref: master + fetch-depth: 0 + - name: Set up JDK 8 + uses: actions/setup-java@v2 + with: + java-version: '8' + distribution: 'adopt' + - name: Test + run: ./gradlew test + - name: Push git tag + run: .github/scripts/push-tag.sh + - name: Deploy Release + env: + GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + GPG_PRIVATE_KEY: ${{ secrets.GPG_KEY }} + run: .github/scripts/deploy.sh \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..40717d8 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,27 @@ +name: Test + +# Controls when the workflow will run +on: + # Triggers the workflow on pull request events but only for the master branch + pull_request: + branches: [ master ] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + - name: Set up JDK 8 + uses: actions/setup-java@v2 + with: + java-version: '8' + distribution: 'adopt' + - name: Validate + run: ./gradlew spotlessCheck + - name: Test + run: ./gradlew test \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 302b47d..1c6d756 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,37 @@ To publish the plugin into the local Maven repository run the command `./gradlew The project has unit tests based on `junit-jupiter` and `mockito`. To run the tests run the command: `./gradlew test`. ## Continuous Integration -todo describe CI/CD workflow + +The CI is run on GitHub, meaning that files in the `.github` folder are used to define the workflows. + +### Presubmit Verification +The purpose of this verification is to do a check that the code is not broken. + +The presubmit verification does the following: + +#### `Test` workflow + +##### `Validate` step +This step checks the code style in all `.groovy` and `.java` files in the project. This job fails if the code is not formatted properly. + +##### `Test` step +This step runs unit tests + +### Submit Verification +The purpose of this workflow is to verify that the `master` branch is always in the `ready for a deploy` +state and release the plugin into the [Maven Central repository](https://repo.maven.apache.org/maven2/com/here/platform/artifact/gradle/gradle-resolver/). + +The submit verification runs all the [Presubmit Verification](#presubmit-verification) workflows with the following additional steps: +#### `Release` workflow + +##### `Test` step +This step runs unit tests + +##### `Push git tag` step +The step increments the current version and pushes a new git tag + +##### `Deploy Release` step +This step releases the plugin to the [Maven Central repository](https://repo.maven.apache.org/maven2/com/here/platform/artifact/gradle/gradle-resolver/). ## Coding Standards diff --git a/README.md b/README.md index aad1752..a06dde3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ +[![Build Status](https://github.com/heremaps/here-artifact-gradle-resolver/actions/workflows/release.yml/badge.svg)](https://github.com/heremaps/here-artifact-gradle-resolver/actions?query=workflow%3ARelease+branch%3Amaster) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.here.platform.artifact.gradle/gradle-resolver/badge.svg)](https://search.maven.org/artifact/com.here.platform.artifact.gradle/gradle-resolver) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) + # HERE Gradle Resolver for Workspace and Marketplace ## Introduction diff --git a/build.gradle b/build.gradle index 66a530c..1eef5b9 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,9 @@ plugins { id 'maven-publish' id 'groovy' id 'groovy-gradle-plugin' - id "com.diffplug.spotless" version "6.13.0" + id 'com.diffplug.spotless' version '6.13.0' + id 'signing' + id 'io.github.gradle-nexus.publish-plugin' version '2.0.0' } repositories { @@ -11,13 +13,21 @@ repositories { mavenCentral() } -group = "com.here.platform.artifact" +group = "com.here.platform.artifact.gradle" -version = "0.0.1-SNAPSHOT" +if (project.hasProperty('projVersion')) { + project.version = project.projVersion +} else { + project.version = '0.0.1-SNAPSHOT' +} + +ext.isReleaseVersion = !version.endsWith("SNAPSHOT") java { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 + withJavadocJar() + withSourcesJar() } dependencies { @@ -36,10 +46,14 @@ test { } gradlePlugin { + vcsUrl = "https://github.com/heremaps/here-artifact-gradle-resolver" + website = "https://github.com/heremaps/here-artifact-gradle-resolver" plugins { create("hereArtifactResolver") { id = "com.here.platform.artifact.gradle" implementationClass = "com.here.platform.artifact.gradle.ArtifactResolver" + displayName = "HERE platform Gradle Resolver plugin" + description = "The HERE platform Gradle resolver plugin provides Java and Scala developers with access to HERE platform artifacts via Gradle" } } } @@ -49,4 +63,56 @@ spotless { target '**/*.java' eclipse().configFile(rootProject.file('codestyle-formatter-settings.xml')) } +} + +publishing { + afterEvaluate { + publications { + withType(MavenPublication) { + pom { + name = "HERE platform Gradle Resolver plugin" + url = "https://github.com/heremaps/here-artifact-gradle-resolver" + description = "The HERE platform Gradle resolver plugin provides Java and Scala developers with access to HERE platform artifacts via Gradle" + licenses { + license { + name = "The Apache License, Version 2.0" + url = "http://www.apache.org/licenses/LICENSE-2.0.txt" + } + } + scm { + connection = "scm:https://github.com/heremaps/here-artifact-gradle-resolver.git" + developerConnection = "scm:git@github.com:heremaps/here-artifact-gradle-resolver.git" + url = "https://github.com/heremaps/here-artifact-gradle-resolver" + } + developers { + developer { + name = "HERE Artifact Service Team" + email = "ARTIFACT_SERVICE_SUPPORT@here.com" + organization = "HERE Europe B.V." + organizationUrl = "https://github.com/heremaps" + } + } + } + } + } + } +} + +signing { + required { isReleaseVersion } + def signingKey = findProperty("asciiArmoredSigningKey") + def signingPassword = findProperty("signingPassword") + useInMemoryPgpKeys(signingKey, signingPassword) + sign publishing.publications +} + +nexusPublishing { + repositories { + sonatype { + nexusUrl.set(uri("https://oss.sonatype.org/service/local/")) + snapshotRepositoryUrl.set(uri("https://oss.sonatype.org/content/repositories/snapshots/")) + username.set(project.properties["ossrhUsername"].toString()) + password.set(project.properties["ossrhPassword"].toString()) + } + } } \ No newline at end of file diff --git a/src/main/java/com/here/platform/artifact/gradle/ArtifactPropertiesResolver.java b/src/main/java/com/here/platform/artifact/gradle/ArtifactPropertiesResolver.java index 3f3b476..0db4c41 100644 --- a/src/main/java/com/here/platform/artifact/gradle/ArtifactPropertiesResolver.java +++ b/src/main/java/com/here/platform/artifact/gradle/ArtifactPropertiesResolver.java @@ -84,6 +84,11 @@ public class ArtifactPropertiesResolver { ArtifactPropertiesResolver() { } + /** + * Get or create singleton instance + * + * @return ArtifactPropertiesResolver instance + */ public static ArtifactPropertiesResolver getInstance() { if (INSTANCE == null) { INSTANCE = new ArtifactPropertiesResolver(); @@ -96,6 +101,7 @@ public static ArtifactPropertiesResolver getInstance() { * * @param tokenUrl here token url * @return resolved default artifact service url + * @throws java.io.IOException in case of a problem with connection */ public String resolveArtifactServiceUrl(String tokenUrl) throws IOException { String artifactApiLookupUrl = getApiLookupUrl(tokenUrl) + "/platform/apis/artifact/v1"; diff --git a/src/main/java/com/here/platform/artifact/gradle/CredentialsResolver.java b/src/main/java/com/here/platform/artifact/gradle/CredentialsResolver.java index 12226c2..6b569e7 100644 --- a/src/main/java/com/here/platform/artifact/gradle/CredentialsResolver.java +++ b/src/main/java/com/here/platform/artifact/gradle/CredentialsResolver.java @@ -35,6 +35,12 @@ public class CredentialsResolver { private static final String HERE_CREDENTIALS_PATH = ".here/credentials.properties"; private static final String HERE_CREDENTIALS_ENV = "HERE_CREDENTIALS_FILE"; + /** + * Resolve credentials based on a precedence: 1) -DhereCredentialsFile system property * 2) HERE_CREDENTIALS_FILE + * environment variable 3) HERE_CREDENTIALS_STRING environment variable 4) * ~/.here/credentials.properties file + * + * @return resolved credentials + */ public Properties resolveCredentials() { Properties properties = new Properties(); File file = resolveFile(); diff --git a/src/main/java/com/here/platform/artifact/gradle/HereAuth.java b/src/main/java/com/here/platform/artifact/gradle/HereAuth.java index bac8161..41d3b2f 100644 --- a/src/main/java/com/here/platform/artifact/gradle/HereAuth.java +++ b/src/main/java/com/here/platform/artifact/gradle/HereAuth.java @@ -30,6 +30,9 @@ import java.util.Properties; +/** + * Responsible for a handling of HERE authentication + */ public class HereAuth { private static final int OAUTH_CONNECTION_TIMEOUT_IN_MS = 20000; @@ -46,6 +49,11 @@ public class HereAuth { this.hereCredentials = credentialsResolver.resolveCredentials(); } + /** + * Get or create singleton instance + * + * @return HereAuth instance + */ public static HereAuth getInstance() { if (INSTANCE == null) { INSTANCE = new HereAuth(new CredentialsResolver()); @@ -53,10 +61,20 @@ public static HereAuth getInstance() { return INSTANCE; } + /** + * Return token endpoint URL based on resolved credentials + * + * @return token endpoint URL + */ public String getTokenEndpointUrl() { return hereCredentials.getProperty(HERE_ENDPOINT_URL_KEY); } + /** + * Request new bearer token + * + * @return token + */ public String getToken() { TokenEndpoint tokenEndpoint = HereAccount .getTokenEndpoint(createHttpProvider(), new FromProperties(new SettableSystemClock(), hereCredentials));