diff --git a/.github/workflows/publish-new-version.yml b/.github/workflows/publish-new-version.yml new file mode 100644 index 0000000..d451cae --- /dev/null +++ b/.github/workflows/publish-new-version.yml @@ -0,0 +1,38 @@ +name: Publish New Version + +on: + workflow_dispatch: + inputs: + bump: + type: choice + description: "Type of version bump to perform" + options: + - patch + - minor + - major + +jobs: + pre_release_check: + name: Pre release check + runs-on: ubuntu-24.04 + environment: 'publish' + steps: + - name: Check + id: pre_release_check_step + run: echo "Pre release check" + publish: + needs: pre_release_check + uses: GetStream/android-ci-actions/.github/workflows/release-new-version.yml@main + with: + ref: "develop" + bump: ${{ inputs.bump }} + file-path: ./buildSrc/src/main/kotlin/io/getstream/core/Configuration.kt + excluded-modules: "app" + secrets: + OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} + SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }} + STREAM_PUBLIC_BOT_TOKEN: ${{ secrets.STREAM_PUBLIC_BOT_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index d852e16..7737ba7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,10 +9,12 @@ /.idea/assetWizardSettings.xml .DS_Store /build +buildSrc/build /captures .externalNativeBuild .cxx local.properties +.kotlin # Log/OS Files *.log diff --git a/build.gradle.kts b/build.gradle.kts index 3274f82..278bd2d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,9 @@ import java.io.FileNotFoundException import java.util.Calendar + +apply(plugin = "io.github.gradle-nexus.publish-plugin") +apply(plugin = "org.jetbrains.dokka") + apply(from = "${rootDir}/gradle/scripts/sonar.gradle") // Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { @@ -9,6 +13,8 @@ plugins { alias(libs.plugins.android.library) apply false alias(libs.plugins.kotlin.compose) apply false alias(libs.plugins.ksp) apply false + alias(libs.plugins.dokka) apply false + alias(libs.plugins.nexus) apply false alias(libs.plugins.arturbosch.detekt) apply true alias(libs.plugins.spotless) apply true alias(libs.plugins.sonarqube) apply true @@ -68,4 +74,6 @@ subprojects { } } } -} \ No newline at end of file +} + +apply(from = "${rootDir}/scripts/publish-root.gradle") \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..b22ed73 --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/io/getstream/core/Configuration.kt b/buildSrc/src/main/kotlin/io/getstream/core/Configuration.kt new file mode 100644 index 0000000..60a729c --- /dev/null +++ b/buildSrc/src/main/kotlin/io/getstream/core/Configuration.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2014-2025 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-feeds-android/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.getstream.core + +object Configuration { + const val compileSdk = 36 + const val targetSdk = 36 + const val sampleTargetSdk = 36 + const val minSdk = 21 + const val majorVersion = 0 + const val minorVersion = 0 + const val patchVersion = 1 + const val versionName = "$majorVersion.$minorVersion.$patchVersion" + const val snapshotVersionName = "$majorVersion.$minorVersion.${patchVersion + 1}-SNAPSHOT" + const val artifactGroup = "io.getstream" +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c0b197f..c204b8d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ junit = "4.13.2" junitVersion = "1.3.0" espressoCore = "3.7.0" appcompat = "1.7.1" -kotlinxCoroutinesTest = "1.10.2" +kotlinxCoroutines = "1.10.2" lintApi = "31.12.0" material = "1.12.0" jetbrainsKotlinJvm = "2.2.0" @@ -25,7 +25,8 @@ detekt = "1.23.8" spotless = "7.2.1" kover = "0.9.1" sonarqube = "6.0.1.5171" - +kotlinDokka = "1.9.20" +nexusPlugin = "1.3.0" [libraries] androidx-core = { module = "androidx.test:core", version.ref = "core" } @@ -35,7 +36,8 @@ junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } -kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutinesTest" } +kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } lint-api = { module = "com.android.tools.lint:lint-api", version.ref = "lintApi" } lint-checks = { module = "com.android.tools.lint:lint-checks", version.ref = "lintApi" } lint-tests = { module = "com.android.tools.lint:lint-tests", version.ref = "lintApi" } @@ -63,7 +65,6 @@ retrofit-moshi = { group = "com.squareup.retrofit2", name = "converter-moshi", v retrofit-scalars = { group = "com.squareup.retrofit2", name = "converter-scalars", version.ref = "retrofit" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } - [plugins] android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } @@ -73,6 +74,8 @@ kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "ko ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } arturbosch-detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } spotless = { id = "com.diffplug.spotless", version.ref = "spotless" } +dokka = { id = "org.jetbrains.dokka", version.ref = "kotlinDokka" } +nexus = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "nexusPlugin" } kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover"} sonarqube = { id = "org.sonarqube", version.ref = "sonarqube"} diff --git a/scripts/publish-module.gradle b/scripts/publish-module.gradle new file mode 100644 index 0000000..1428202 --- /dev/null +++ b/scripts/publish-module.gradle @@ -0,0 +1,111 @@ +apply plugin: 'maven-publish' +apply plugin: 'signing' +apply plugin: 'org.jetbrains.dokka' + +tasks.register('androidSourcesJar', Jar) { + archiveClassifier.set('sources') + if (project.plugins.findPlugin("com.android.library")) { + from android.sourceSets.main.java.srcDirs + from android.sourceSets.main.kotlin.srcDirs + } else { + from sourceSets.main.java.srcDirs + from sourceSets.main.kotlin.srcDirs + } +} + +tasks.withType(dokkaHtmlPartial.getClass()).configureEach { + pluginsMapConfiguration.set( + ["org.jetbrains.dokka.base.DokkaBase": """{ "separateInheritedMembers": true}"""] + ) +} + +task javadocJar(type: Jar, dependsOn: dokkaJavadoc) { + archiveClassifier.set('javadoc') + from dokkaJavadoc.outputDirectory +} + +artifacts { + archives androidSourcesJar + archives javadocJar +} + +group = PUBLISH_GROUP_ID +version = PUBLISH_VERSION + +afterEvaluate { + publishing { + publications { + release(MavenPublication) { + tasks.named("generateMetadataFileForReleasePublication").configure { dependsOn("androidSourcesJar") } + groupId PUBLISH_GROUP_ID + artifactId PUBLISH_ARTIFACT_ID + version PUBLISH_VERSION + if (project.plugins.findPlugin("com.android.library")) { + from components.release + } else { + from components.java + } + + artifact javadocJar + + pom { + name = PUBLISH_ARTIFACT_ID + description = 'Stream Core official Android SDK' + url = 'https://github.com/getstream/stream-android-core' + licenses { + license { + name = 'Stream License' + url = 'https://github.com/GetStream/stream-android-core/blob/main/LICENSE' + } + } + developers { + developer { + id = 'jcminarro' + name = 'Jc Miñarro' + email = 'josecarlos@getstream.io' + } + developer { + id = 'aleksandar-apostolov' + name = 'Aleksandar Apostolov' + email = 'aleksandar.apostolov@getstream.io' + } + developer { + id = 'VelikovPetar' + name = 'Petar Velikov' + email = 'petar.velikov@getstream.io' + } + developer { + id = 'andremion' + name = 'André Mion' + email = 'andre.rego@getstream.io' + } + developer { + id = 'rahul-lohra' + name = 'Rahul Kumar Lohra' + email = 'rahul.lohra@getstream.io' + } + developer { + id = 'gpunto' + name = 'Gianmarco' + email = 'gianmarco.david@getstream.io' + } + } + scm { + connection = 'scm:git:github.com/getstream/stream-android-core.git' + developerConnection = 'scm:git:ssh://github.com/getstream/stream-android-core.git' + url = 'https://github.com/getstream/stream-android-core/tree/main' + } + } + } + } + } +} + +signing { + useInMemoryPgpKeys( + rootProject.ext["signing.keyId"], + rootProject.ext["signing.key"], + rootProject.ext["signing.password"], + ) + sign publishing.publications +} diff --git a/scripts/publish-root.gradle b/scripts/publish-root.gradle new file mode 100644 index 0000000..b169ac2 --- /dev/null +++ b/scripts/publish-root.gradle @@ -0,0 +1,67 @@ +import io.getstream.core.Configuration + +// Create variables with empty default values +ext["ossrhUsername"] = '' +ext["ossrhPassword"] = '' +ext["sonatypeStagingProfileId"] = '' +ext["signing.keyId"] = '' +ext["signing.password"] = '' +ext["signing.key"] = '' +ext["snapshot"] = '' + +File secretPropsFile = project.rootProject.file('local.properties') +if (secretPropsFile.exists()) { + // Read local.properties file first if it exists + Properties p = new Properties() + new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) } + p.each { name, value -> ext[name] = value } +} else { + // Use system environment variables + ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME') + ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD') + ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID') + ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') + ext["signing.password"] = System.getenv('SIGNING_PASSWORD') + ext["signing.key"] = System.getenv('SIGNING_KEY') + ext["snapshot"] = System.getenv('SNAPSHOT') +} + +if (snapshot) { + ext["rootVersionName"] = Configuration.snapshotVersionName +} else { + ext["rootVersionName"] = Configuration.versionName +} + +// Set up Sonatype repository +nexusPublishing { + repositories { + sonatype { + nexusUrl = uri("https://ossrh-staging-api.central.sonatype.com/service/local/") + snapshotRepositoryUrl = uri("https://central.sonatype.com/repository/maven-snapshots/") + stagingProfileId = sonatypeStagingProfileId + username = ossrhUsername + password = ossrhPassword + version = rootVersionName + } + } +} + +tasks.register("printAllArtifacts") { + group = "publishing" + + doLast { + subprojects.each { subproject -> + subproject.plugins.withId("maven-publish") { + def publishingExtension = subproject.extensions.findByType(PublishingExtension) + publishingExtension?.publications?.all { publication -> + if (publication instanceof MavenPublication) { + def groupId = publication.groupId + def artifactId = publication.artifactId + def version = publication.version + println("$groupId:$artifactId:$version") + } + } + } + } + } +} \ No newline at end of file diff --git a/stream-android-core-annotations/build.gradle.kts b/stream-android-core-annotations/build.gradle.kts index f3bd531..d613e05 100644 --- a/stream-android-core-annotations/build.gradle.kts +++ b/stream-android-core-annotations/build.gradle.kts @@ -2,6 +2,15 @@ plugins { id("java-library") alias(libs.plugins.jetbrains.kotlin.jvm) } + +rootProject.extra.apply { + set("PUBLISH_GROUP_ID", io.getstream.core.Configuration.artifactGroup) + set("PUBLISH_ARTIFACT_ID", "stream-android-core-annotations") + set("PUBLISH_VERSION", rootProject.extra.get("rootVersionName")) +} + +apply(from = "${rootDir}/scripts/publish-module.gradle") + java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 diff --git a/stream-android-core-lint/build.gradle.kts b/stream-android-core-lint/build.gradle.kts index 2d48647..16f9a2f 100644 --- a/stream-android-core-lint/build.gradle.kts +++ b/stream-android-core-lint/build.gradle.kts @@ -2,6 +2,15 @@ plugins { id("java-library") alias(libs.plugins.jetbrains.kotlin.jvm) } + +rootProject.extra.apply { + set("PUBLISH_GROUP_ID", io.getstream.core.Configuration.artifactGroup) + set("PUBLISH_ARTIFACT_ID", "stream-android-core-lint") + set("PUBLISH_VERSION", rootProject.extra.get("rootVersionName")) +} + +apply(from = "${rootDir}/scripts/publish-module.gradle") + java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 diff --git a/stream-android-core/build.gradle.kts b/stream-android-core/build.gradle.kts index 4b8954e..08d945d 100644 --- a/stream-android-core/build.gradle.kts +++ b/stream-android-core/build.gradle.kts @@ -1,4 +1,5 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import io.getstream.core.Configuration plugins { alias(libs.plugins.android.library) @@ -9,6 +10,14 @@ plugins { alias(libs.plugins.kover) } +rootProject.extra.apply { + set("PUBLISH_GROUP_ID", Configuration.artifactGroup) + set("PUBLISH_ARTIFACT_ID", "stream-android-core") + set("PUBLISH_VERSION", rootProject.extra.get("rootVersionName")) +} + +apply(from = "${rootDir}/scripts/publish-module.gradle") + kotlin { compilerOptions { jvmTarget.set(JvmTarget.JVM_11) @@ -20,10 +29,10 @@ kotlin { android { namespace = "io.getstream.android.core" - compileSdk = 36 + compileSdk = Configuration.compileSdk defaultConfig { - minSdk = 21 + minSdk = Configuration.minSdk testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles("consumer-rules.pro") @@ -48,6 +57,10 @@ android { warningsAsErrors = true lintConfig = rootProject.file("lint.xml") } + + publishing { + singleVariant("release") { } + } } dependencies { @@ -58,9 +71,7 @@ dependencies { } implementation(project(":stream-android-core-annotations")) - implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.material) + implementation(libs.kotlinx.coroutines) detektPlugins(libs.detekt.formatting) @@ -83,7 +94,4 @@ dependencies { testImplementation(libs.mockk) testImplementation(libs.kotlinx.coroutines.test) testImplementation(libs.mockwebserver) - - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) } \ No newline at end of file