diff --git a/.github/actions/publish-android-sdk/action.yml b/.github/actions/publish-android-sdk/action.yml new file mode 100644 index 000000000..1787f3264 --- /dev/null +++ b/.github/actions/publish-android-sdk/action.yml @@ -0,0 +1,71 @@ +name: 'Publish Android SDK' +description: 'Build and publish Android SDK packages to Maven Central' + +inputs: + workspace-path: + description: 'Path to the Android SDK workspace' + required: true + default: 'sdk/@launchdarkly/observability-android' + java_version: + description: 'The Java version to use.' + required: false + default: '17' + java_distribution: + description: 'The Java distribution to use.' + required: false + default: 'temurin' + aws-role-arn: + description: 'AWS role ARN for accessing secrets' + required: true + dry-run: + description: 'Whether to run the publish in dry-run mode' + required: false + default: 'false' + prerelease: + description: 'Whether to publish a prerelease version' + required: false + default: 'false' + +runs: + using: 'composite' + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: ${{ inputs.java_distribution }} + java-version: ${{ inputs.java_version }} + + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + + - uses: launchdarkly/gh-actions/actions/release-secrets@release-secrets-v1.2.0 + name: Get secrets + with: + aws_assume_role: ${{ inputs.aws-role-arn }} + ssm_parameter_pairs: | + /production/common/releasing/sonatype/central/username = SONATYPE_USER_NAME, + /production/common/releasing/sonatype/central/password = SONATYPE_PASSWORD, + /production/common/releasing/android_code_signing/private_key_id = SIGNING_KEY_ID, + /production/common/releasing/android_code_signing/private_key_passphrase = SIGNING_KEY_PASSPHRASE + s3_path_pairs: 'launchdarkly-releaser/android/code-signing-keyring.gpg = code-signing-keyring.gpg' + + - name: Publish Library + shell: bash + if: ${{ inputs.dry-run != 'true' }} + working-directory: ${{ inputs.workspace-path }} + env: + LD_RELEASE_IS_PRERELEASE: ${{ inputs.prerelease }} + SIGNING_KEY_ID: ${{ env.SIGNING_KEY_ID }} + SIGNING_KEY_PASSPHRASE: ${{ env.SIGNING_KEY_PASSPHRASE }} + SIGNING_SECRET_KEY_RING_FILE: ${{ github.workspace }}/code-signing-keyring.gpg + SONATYPE_USER_NAME: ${{ env.SONATYPE_USER_NAME }} + SONATYPE_PASSWORD: ${{ env.SONATYPE_PASSWORD }} + run: source $GITHUB_ACTION_PATH/publish.sh + + - name: Dry Run Publish Library + shell: bash + if: ${{ inputs.dry-run == 'true' }} + run: echo "Dry run. Not publishing." diff --git a/.github/actions/publish-android-sdk/publish.sh b/.github/actions/publish-android-sdk/publish.sh new file mode 100755 index 000000000..837aaa0c2 --- /dev/null +++ b/.github/actions/publish-android-sdk/publish.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -ue + +echo "Publishing to Sonatype" +if [ "${LD_RELEASE_IS_PRERELEASE}" == "true" ]; then + echo "PRERELEASE" + ./gradlew publishToSonatype -Psigning.keyId="${SIGNING_KEY_ID}" -Psigning.password="${SIGNING_KEY_PASSPHRASE}" -Psigning.secretKeyRingFile="${SIGNING_SECRET_KEY_RING_FILE}" -PsonatypeUsername="${SONATYPE_USER_NAME}" -PsonatypePassword="${SONATYPE_PASSWORD}" || { + echo "Gradle publish/release failed" >&2 + exit 1 + } +else + echo "RELEASE" + ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -Psigning.keyId="${SIGNING_KEY_ID}" -Psigning.password="${SIGNING_KEY_PASSPHRASE}" -Psigning.secretKeyRingFile="${SIGNING_SECRET_KEY_RING_FILE}" -PsonatypeUsername="${SONATYPE_USER_NAME}" -PsonatypePassword="${SONATYPE_PASSWORD}" || { + echo "Gradle publish/release failed" >&2 + exit 1 + } +fi diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index 92163b1eb..ec1f15de3 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -22,6 +22,10 @@ on: description: 'Should release @launchdarkly/observability-python package.' type: boolean required: true + release_launchdarkly_android: + description: 'Should release @launchdarkly/observability-android package.' + type: boolean + required: true python_tag_name: description: 'The tag name for the python package. Should be set when publishing a python package.' type: string @@ -124,3 +128,18 @@ jobs: base64-subjects: '${{ needs.publish-python-sdk.outputs.package-hashes }}' upload-assets: true upload-tag-name: ${{ inputs.python_tag_name }} + + publish-android-sdk: + runs-on: ubuntu-latest + if: ${{ inputs.release_launchdarkly_android == true }} + permissions: + id-token: write + steps: + - name: Publish Android SDK + id: publish + uses: ./.github/actions/publish-android-sdk + with: + workspace-path: sdk/@launchdarkly/observability-android + aws-role-arn: ${{ vars.AWS_ROLE_ARN }} + dry-run: ${{ inputs.dry-run }} + prerelease: ${{ inputs.prerelease }} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 905437cbb..3c1cf243b 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -14,6 +14,8 @@ jobs: outputs: python-plugin-released: ${{ steps.release.outputs['sdk/@launchdarkly/observability-python--release_created'] }} python-plugin-tag-name: ${{ steps.release.outputs['sdk/@launchdarkly/observability-python--tag_name'] }} + android-plugin-released: ${{ steps.release.outputs['sdk/@launchdarkly/observability-android--release_created'] }} + android-plugin-tag-name: ${{ steps.release.outputs['sdk/@launchdarkly/observability-android--tag_name'] }} steps: - uses: googleapis/release-please-action@a02a34c4d625f9be7cb89156071d8567266a2445 id: release @@ -65,3 +67,21 @@ jobs: base64-subjects: '${{ needs.release-python-plugin.outputs.package-hashes }}' upload-assets: true upload-tag-name: ${{ needs.release-package.outputs.python-plugin-tag-name }} + + release-android-plugin: + runs-on: ubuntu-latest + permissions: + id-token: write # Used for publishing secrets and documentation. + contents: write + needs: ['release-package'] + if: ${{ needs.release-package.outputs.android-plugin-released == 'true' }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Publish Android SDK + id: publish + uses: ./.github/actions/publish-android-sdk + with: + workspace-path: sdk/@launchdarkly/observability-android + aws-role-arn: ${{ vars.AWS_ROLE_ARN }} diff --git a/e2e/android/app/src/main/java/com/example/androidobservability/ViewModel.kt b/e2e/android/app/src/main/java/com/example/androidobservability/ViewModel.kt index ad9531bc0..de60cdd44 100644 --- a/e2e/android/app/src/main/java/com/example/androidobservability/ViewModel.kt +++ b/e2e/android/app/src/main/java/com/example/androidobservability/ViewModel.kt @@ -1,8 +1,8 @@ package com.example.androidobservability -import LDObserve import androidx.lifecycle.ViewModel import com.launchdarkly.observability.interfaces.Metric +import com.launchdarkly.observability.sdk.LDObserve import com.launchdarkly.sdk.android.LDClient import io.opentelemetry.api.common.AttributeKey import io.opentelemetry.api.common.Attributes diff --git a/release-please-config.json b/release-please-config.json index e390fe42c..276bd451d 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -7,6 +7,14 @@ "include-v-in-tag": false, "extra-files": ["PROVENANCE.md"], "include-component-in-tag": true + }, + "sdk/@launchdarkly/observability-android": { + "package-name": "launchdarkly-observability-android", + "release-type": "simple", + "versioning": "default", + "include-v-in-tag": false, + "include-component-in-tag": true, + "extra-files": ["gradle.properties"] } } } diff --git a/sdk/@launchdarkly/observability-android/build.gradle.kts b/sdk/@launchdarkly/observability-android/build.gradle.kts new file mode 100644 index 000000000..72ce309ba --- /dev/null +++ b/sdk/@launchdarkly/observability-android/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("io.github.gradle-nexus.publish-plugin").version("2.0.0").apply(true) +} + +nexusPublishing { + this.repositories { + sonatype { + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + } + } +} diff --git a/sdk/@launchdarkly/observability-android/gradle.properties b/sdk/@launchdarkly/observability-android/gradle.properties index ecccc8ee1..40c2bf67d 100644 --- a/sdk/@launchdarkly/observability-android/gradle.properties +++ b/sdk/@launchdarkly/observability-android/gradle.properties @@ -5,3 +5,6 @@ org.gradle.configuration-cache=true android.useAndroidX=true +#x-release-please-start-version +version=0.1.0 +#x-release-please-end diff --git a/sdk/@launchdarkly/observability-android/lib/build.gradle.kts b/sdk/@launchdarkly/observability-android/lib/build.gradle.kts index 3a91b24e3..47ca23487 100644 --- a/sdk/@launchdarkly/observability-android/lib/build.gradle.kts +++ b/sdk/@launchdarkly/observability-android/lib/build.gradle.kts @@ -1,6 +1,8 @@ plugins { // Apply the Android library plugin id("com.android.library") + id("maven-publish") + id("signing") // Apply the Kotlin Android plugin for Android-compatible Kotlin support. alias(libs.plugins.kotlin.android) @@ -34,13 +36,15 @@ dependencies { testRuntimeOnly("org.junit.platform:junit-platform-launcher") } +val releaseVersion = version.toString() + android { namespace = "com.launchdarkly.observability" compileSdk = 30 defaultConfig { minSdk = 24 - + version = releaseVersion testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } @@ -56,4 +60,63 @@ android { kotlinOptions { jvmTarget = "1.8" } + + publishing { + singleVariant("release") { + withJavadocJar() + withSourcesJar() + } + } +} + +publishing { + publications { + create("release") { + groupId = "com.launchdarkly" + artifactId = "launchdarkly-observability-android" + version = releaseVersion + + pom { + name.set("LaunchDarkly Observability Android SDK") + description.set( + "Official LaunchDarkly Observability Android SDK for use with the LaunchDarkly Android SDK." + ) + url.set("https://github.com/launchdarkly/observability-sdk/") + organization { + name.set("LaunchDarkly") + url.set("https://launchdarkly.com/") + } + developers { + developer { + id.set("sdks") + name.set("LaunchDarkly SDK Team") + email.set("sdks@launchdarkly.com") + } + } + licenses { + license { + name.set("The Apache License, Version 2.0") + url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + scm { + connection.set( + "scm:git:https://github.com/launchdarkly/observability-sdk.git" + ) + developerConnection.set( + "scm:git:ssh:github.com/launchdarkly/observability-sdk.git" + ) + url.set("https://github.com/launchdarkly/observability-sdk/") + } + } + + afterEvaluate { + from(components["release"]) + } + } + } +} + +signing { + sign(publishing.publications["release"]) } diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/client/ObservabilityClient.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/client/ObservabilityClient.kt index 30e80c451..2e93986da 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/client/ObservabilityClient.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/client/ObservabilityClient.kt @@ -1,3 +1,5 @@ +package com.launchdarkly.observability.client + import android.app.Application import com.launchdarkly.observability.client.InstrumentationManager import com.launchdarkly.observability.interfaces.Metric diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/plugin/Observability.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/plugin/Observability.kt index 6e6109e07..516ed2cd0 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/plugin/Observability.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/plugin/Observability.kt @@ -1,7 +1,8 @@ package com.launchdarkly.observability.plugin -import ObservabilityClient import android.app.Application +import com.launchdarkly.observability.client.ObservabilityClient +import com.launchdarkly.observability.sdk.LDObserve import com.launchdarkly.sdk.android.LDClient import com.launchdarkly.sdk.android.integrations.EnvironmentMetadata import com.launchdarkly.sdk.android.integrations.Hook diff --git a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/sdk/LDObserve.kt b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/sdk/LDObserve.kt index f4f924f13..8be42aa81 100644 --- a/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/sdk/LDObserve.kt +++ b/sdk/@launchdarkly/observability-android/lib/src/main/kotlin/com/launchdarkly/observability/sdk/LDObserve.kt @@ -1,3 +1,6 @@ +package com.launchdarkly.observability.sdk + +import com.launchdarkly.observability.client.ObservabilityClient import com.launchdarkly.observability.interfaces.Metric import com.launchdarkly.observability.interfaces.Observe import io.opentelemetry.api.common.Attributes