diff --git a/.github/workflows/deploy_snapshots.yml b/.github/workflows/deploy_snapshots.yml deleted file mode 100644 index 74b909475..000000000 --- a/.github/workflows/deploy_snapshots.yml +++ /dev/null @@ -1,40 +0,0 @@ -# This workflow builds the project on every commit to the 'master' branch and deploys the artifacts - -name: Build and Deploy - -on: - push: - branches: [ master ] - -jobs: - build-and-deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v1 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Set up JDK - uses: actions/setup-java@v2 - with: - distribution: 'adopt' - java-version: 11 - - name: Set up environment variables - env: - BINTRAY_USER: ${{ secrets.bintray_user }} - BINTRAY_KEY: ${{ secrets.bintray_key }} - ARTIFACTORY_PASS: ${{ secrets.artifactory_password }} - run: | - mkdir -p $HOME/.gradle - touch $HOME/.gradle/gradle.properties - echo $'\n'bintrayuser=$BINTRAY_USER >> $HOME/.gradle/gradle.properties - echo $'\n'bintraykey=$BINTRAY_KEY >> $HOME/.gradle/gradle.properties - echo $'\n'artifactoryPassword=$ARTIFACTORY_PASS >> $HOME/.gradle/gradle.properties - - name: Build - run: ./gradlew assemble --full-stacktrace -# Deployment doesn't work yet -# - name: Deploy snapshots to artifactory -# run: ./gradlew artifactoryPublish diff --git a/.github/workflows/publication_maven.yml b/.github/workflows/publication_maven.yml index 747962b87..008b562d7 100644 --- a/.github/workflows/publication_maven.yml +++ b/.github/workflows/publication_maven.yml @@ -1,11 +1,23 @@ name: Publication to MavenCentral -on: workflow_dispatch +on: + workflow_dispatch: + inputs: + releaseType: + description: 'Release type' + required: true + type: choice + options: + - SonatypeReleases + - SonatypeSnapshots + version: + description: 'Version (for example 1.4.2-SNAPSHOT)' + required: true + type: string jobs: publication: runs-on: ubuntu-latest - if: github.ref == 'refs/heads/master' steps: - uses: actions/checkout@v2 - uses: actions/cache@v1 @@ -27,14 +39,14 @@ jobs: env: OSSRH_USER: ${{ secrets.ossrh_user }} OSSRH_PASSWORD: ${{ secrets.ossrh_password }} - OSSRH_STAGING_PROFILE_ID: ${{ secrets.ossrh_staging_profile_id }} PGP_KEYID: ${{ secrets.pgp_keyid }} PGP_KEY: ${{ secrets.pgp_key }} PGP_PASSWORD: ${{ secrets.pgp_password }} run: > - ./gradlew publishRelease -Pkaspresso.ossrh.user=${OSSRH_USER} + ./gradlew publishAllPublicationsTo${{ inputs.releaseType }}Repository + -Pkaspresso.version=${{ inputs.version }} + -Pkaspresso.ossrh.user=${OSSRH_USER} -Pkaspresso.ossrh.password=${OSSRH_PASSWORD} - -Pkaspresso.ossrh.stagingProfileId=${OSSRH_STAGING_PROFILE_ID} -Pkaspresso.pgp.keyid=${PGP_KEYID} -Pkaspresso.pgp.key=${PGP_KEY} -Pkaspresso.pgp.password=${PGP_PASSWORD} diff --git a/build-logic/publication/src/main/kotlin/com/kaspersky/kaspresso/publication/CreateStagingRepositoryTask.kt b/build-logic/publication/src/main/kotlin/com/kaspersky/kaspresso/publication/CreateStagingRepositoryTask.kt deleted file mode 100644 index 92579342f..000000000 --- a/build-logic/publication/src/main/kotlin/com/kaspersky/kaspresso/publication/CreateStagingRepositoryTask.kt +++ /dev/null @@ -1,105 +0,0 @@ -package com.kaspersky.kaspresso.publication - -import com.github.salomonbrys.kotson.fromJson -import com.google.gson.Gson -import com.google.gson.JsonParseException -import okhttp3.Authenticator -import okhttp3.Credentials -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import org.gradle.api.DefaultTask -import org.gradle.api.file.RegularFile -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import java.io.IOException -import java.util.concurrent.TimeUnit - -abstract class CreateStagingRepositoryTask : DefaultTask() { - - @get:Input - abstract val stagingProfileId: Property - - @get:Input - abstract val user: Property - - @get:Input - abstract val password: Property - - @get:Input - abstract val repositoryDescription: Property - - @Suppress("UnstableApiUsage") - @get:OutputFile - val repositoryIdFile: Property = project.objects.fileProperty() - - @TaskAction - fun doWork() { - val authenticator = Authenticator { _, response -> - response.request - .newBuilder() - .addHeader("Authorization", Credentials.basic(user.get(), password.get())) - .build() - } - - val okHttpClient = OkHttpClient.Builder() - .authenticator(authenticator) - .readTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) - .writeTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) - .connectTimeout(TIMEOUT_SEC, TimeUnit.SECONDS) - .build() - - val url = "$SONATYPE_BASE_URL/staging/profiles/${stagingProfileId.get()}/start" - - val gson = Gson() - - val request = Request.Builder() - .url(url) - .post( - """{ "data": { "description": "${repositoryDescription.get()}" } }""" - .toRequestBody(contentType = "application/json".toMediaType()) - ) - .build() - - val response = try { - okHttpClient.newCall(request).execute() - } catch (e: IOException) { - failTask("network error", e) - } - - val body = response.body?.use { it.string() } - - if (response.isSuccessful && !body.isNullOrBlank()) { - try { - val responseBody = gson.fromJson(body) - val createdRepoId = responseBody.data.stagedRepositoryId - logger.info("Repository $createdRepoId created") - val repositoryUrl = "$SONATYPE_BASE_URL/staging/deployByRepositoryId/$createdRepoId" - repositoryIdFile.get().asFile.writeText(repositoryUrl) - } catch (e: JsonParseException) { - failTask("can't parse sonatype response", e) - } - } else { - failTask( - "network error (responseCode=${response.code}; " + - "responseBody=${body?.take(RESPONSE_BODY_STRING_LIMIT)})" - ) - } - } - - private fun failTask(reason: String, cause: Throwable? = null): Nothing { - throw IllegalStateException("Can't create staging repository; $reason", cause) - } - - private data class ResponseData(val stagedRepositoryId: String) - private data class ResponseBody(val data: ResponseData) - - private companion object { - const val RESPONSE_BODY_STRING_LIMIT = 128 - const val TIMEOUT_SEC = 300L - const val SONATYPE_BASE_URL = "https://s01.oss.sonatype.org/service/local" - } -} diff --git a/build-logic/publication/src/main/kotlin/convention.publication-android-lib.gradle.kts b/build-logic/publication/src/main/kotlin/convention.publication-android-lib.gradle.kts index b8035b85b..f096194bb 100644 --- a/build-logic/publication/src/main/kotlin/convention.publication-android-lib.gradle.kts +++ b/build-logic/publication/src/main/kotlin/convention.publication-android-lib.gradle.kts @@ -2,7 +2,7 @@ import com.android.build.gradle.LibraryExtension import com.kaspersky.kaspresso.publication.KotlinLibraryPublishExtension plugins { - id("convention.publication-release") + id("convention.publication-base") } val publishExtension = extensions.create("publish") diff --git a/build-logic/publication/src/main/kotlin/convention.publication-base.gradle.kts b/build-logic/publication/src/main/kotlin/convention.publication-base.gradle.kts index 80ef85132..c52a56063 100644 --- a/build-logic/publication/src/main/kotlin/convention.publication-base.gradle.kts +++ b/build-logic/publication/src/main/kotlin/convention.publication-base.gradle.kts @@ -1,10 +1,11 @@ plugins { `maven-publish` + id("convention.sonatype") } group = "com.kaspersky.android-components" -version = property("stableVersion") ?: error("stableVersion property not set") +version = property("kaspresso.version") ?: error("kaspresso.version property not set") publishing.publications.withType().configureEach { pom { diff --git a/build-logic/publication/src/main/kotlin/convention.publication-kotlin-lib.gradle.kts b/build-logic/publication/src/main/kotlin/convention.publication-kotlin-lib.gradle.kts index 46548e5e7..4116a366c 100644 --- a/build-logic/publication/src/main/kotlin/convention.publication-kotlin-lib.gradle.kts +++ b/build-logic/publication/src/main/kotlin/convention.publication-kotlin-lib.gradle.kts @@ -1,7 +1,7 @@ import com.kaspersky.kaspresso.publication.KotlinLibraryPublishExtension plugins { - id("convention.publication-release") + id("convention.publication-base") } plugins.withId("kotlin") { diff --git a/build-logic/publication/src/main/kotlin/convention.publication-release.gradle.kts b/build-logic/publication/src/main/kotlin/convention.publication-release.gradle.kts deleted file mode 100644 index 6cabdcb45..000000000 --- a/build-logic/publication/src/main/kotlin/convention.publication-release.gradle.kts +++ /dev/null @@ -1,10 +0,0 @@ -plugins { - id("convention.publication-base") - id("convention.sonatype") -} - -tasks.register("publishRelease") { - group = "publication" - - dependsOn(tasks.named("publishToSonatype")) -} diff --git a/build-logic/publication/src/main/kotlin/convention.sonatype.gradle.kts b/build-logic/publication/src/main/kotlin/convention.sonatype.gradle.kts index c73de6ba1..60b661f80 100644 --- a/build-logic/publication/src/main/kotlin/convention.sonatype.gradle.kts +++ b/build-logic/publication/src/main/kotlin/convention.sonatype.gradle.kts @@ -1,69 +1,31 @@ -import com.kaspersky.kaspresso.publication.CreateStagingRepositoryTask - plugins { id("convention.publication-base") signing } -val ossrhUsername: Provider = providers.gradleProperty("kaspresso.ossrh.user") - .forUseAtConfigurationTime() - -val ossrhPassword: Provider = providers.gradleProperty("kaspresso.ossrh.password") - .forUseAtConfigurationTime() - -val ossrhStagingProfileId: Provider = providers.gradleProperty("kaspresso.ossrh.stagingProfileId") - .forUseAtConfigurationTime() - -val sonatypeRepoName = "SonatypeReleases" +val ossrhUsername = findProperty("kaspresso.ossrh.user")?.toString() +val ossrhPassword = findProperty("kaspresso.ossrh.password")?.toString() -val repositoryUrlOutputFilePath: Provider = rootProject.layout.buildDirectory.file("sonatype-repo.id") - -val createStagingRepositoryTask: TaskProvider = with(rootProject.tasks) { - val createStagingTaskName = "createSonatypeStagingRepository" - - try { - @Suppress("UNCHECKED_CAST") - named(createStagingTaskName) as TaskProvider - } catch (e: UnknownTaskException) { - register(createStagingTaskName) { - group = "publication" - - stagingProfileId.set(ossrhStagingProfileId) - user.set(ossrhUsername) - password.set(ossrhPassword) - repositoryDescription.set("Release v.$version") - repositoryIdFile.set(repositoryUrlOutputFilePath) - } - } -} - -tasks.withType().configureEach { - - // https://docs.gradle.org/current/userguide/publishing_customization.html#sec:configuring_publishing_tasks - if (name.contains(sonatypeRepoName)) { - doFirst { - - // no direct task access, because "cannot be cast to class CreateStagingRepositoryTask" for some reason - val repositoryUrl = repositoryUrlOutputFilePath.get().asFile.readText() - repository = repository.apply { setUrl(repositoryUrl) } - } - dependsOn(createStagingRepositoryTask) - } -} - -val publishTask = tasks.register("publishToSonatype") { - group = "publication" - - dependsOn(tasks.named("publishAllPublicationsTo${sonatypeRepoName}Repository")) -} +val sonatypeReleasesRepoName = "SonatypeReleases" +val sonatypeSnapshotsRepoName = "SonatypeSnapshots" publishing { repositories { maven { - name = sonatypeRepoName + name = sonatypeReleasesRepoName + setUrl("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") credentials { - username = ossrhUsername.orNull - password = ossrhPassword.orNull + username = ossrhUsername + password = ossrhPassword + } + } + + maven { + name = sonatypeSnapshotsRepoName + setUrl("https://s01.oss.sonatype.org/content/repositories/snapshots/") + credentials { + username = ossrhUsername + password = ossrhPassword } } } @@ -72,23 +34,17 @@ publishing { signing { sign(publishing.publications) - val signingKeyId = providers.gradleProperty("kaspresso.pgp.keyid") - .forUseAtConfigurationTime() - .orNull - val signingKey = providers.gradleProperty("kaspresso.pgp.key") - .forUseAtConfigurationTime() - .orNull - val signingPassword = providers.gradleProperty("kaspresso.pgp.password") - .forUseAtConfigurationTime() - .orNull + val signingKeyId = findProperty("kaspresso.pgp.keyid")?.toString() + val signingKey = findProperty("kaspresso.pgp.key")?.toString() + val signingPassword = findProperty("kaspresso.pgp.password")?.toString() useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) } tasks.withType().configureEach { onlyIf { - gradle.taskGraph.hasTask(publishTask.get()) + val isReleaseQueued = gradle.taskGraph.hasTask("publishAllPublicationsTo${sonatypeReleasesRepoName}Repository") + val isSnapshotQueued = gradle.taskGraph.hasTask("publishAllPublicationsTo${sonatypeSnapshotsRepoName}Repository") + isReleaseQueued || isSnapshotQueued } } - -fun String?.toFile() = if (this.isNullOrBlank()) null else File(this) diff --git a/gradle.properties b/gradle.properties index 0f9d86a9c..87a03e14c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,5 +6,4 @@ kotlin.code.style=official android.useAndroidX = true -stableVersion=1.4.2 -snapshotVersion=1.4.3-SNAPSHOT +kaspresso.version=1.4.2