From 225c4290ff662a60f29da1115142233f29b315bf Mon Sep 17 00:00:00 2001 From: Viktor Savchik Date: Tue, 13 Jan 2026 00:10:17 +0100 Subject: [PATCH 1/5] Setup release infrastructure for v1.0.0 - Add Vanniktech Maven Publish Plugin for Maven Central publishing - Configure Sonatype Central Portal publishing - Centralize version management in gradle.properties (VERSION_NAME) - Create release workflow triggered on tag push - Add CHANGELOG.md following Keep a Changelog format - Add CODE_OF_CONDUCT.md - Fix validate-pr workflow to target 'main' branch - Add null safety checks to Android implementation - Replace System.err with Napier logging - Add KMP installation instructions to README --- .github/workflows/release.yml | 42 +++++++++++++++++++ .github/workflows/validate-pr.yml | 2 +- CHANGELOG.md | 36 ++++++++++++++++ CODE_OF_CONDUCT.md | 38 +++++++++++++++++ README.md | 24 +++++++++++ UXCamExample/androidApp/build.gradle.kts | 4 +- UXCamExample/shared/build.gradle.kts | 4 +- build.gradle.kts | 1 + gradle.properties | 19 ++++++++- uxcam-kmp-shared/build.gradle.kts | 19 ++++++++- .../kotlin/dev/onexeor/uxcam/UXCam.kt | 17 ++++---- 11 files changed, 193 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ccec557 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,42 @@ +name: Release + +on: + push: + tags: + - 'v*' + +jobs: + release: + runs-on: macos-latest + permissions: + contents: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v3 + + - name: Build Library + run: ./gradlew :uxcam-kmp-shared:build --no-daemon + + - name: Publish to Maven Central + run: ./gradlew :uxcam-kmp-shared:publishAndReleaseToMavenCentral --no-configuration-cache --no-daemon + env: + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }} + ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }} + + - name: Create GitHub Release + uses: softprops/action-gh-release@v2 + with: + generate_release_notes: true + body_path: CHANGELOG.md diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index 658f37a..fd5d179 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -2,7 +2,7 @@ name: Validate PR on: pull_request: branches: - - 'master' + - 'main' jobs: build_project: # if: startsWith(github.head_ref, 'renovate') diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..061a5de --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,36 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.0] - 2026-01-12 + +### Added + +- Initial release of UXCam-KMP +- Unified Kotlin API for UXCam SDK (Android & iOS) +- Session recording controls (pause/resume) +- User identification with `setUserIdentity()` +- Custom user properties with `setUserProperty()` +- Event logging with `logEvent()` and optional properties +- Screen privacy controls with `addScreenNameToIgnore()` +- Session management (`startNewSession()`, `stopSessionAndUploadData()`, `cancelCurrentSession()`) +- Session URL retrieval with `urlForCurrentSession()` +- Configuration options: + - `enableAutomaticScreenNameTagging` + - `enableImprovedScreenCapture` (Android only) + - `enableCrashHandling` + +### Platform Support + +- Android: minSdk 23, compileSdk 33 +- iOS: 12.0+ +- Kotlin: 1.9.21+ + +### Dependencies + +- UXCam Android SDK: 3.6.12 +- UXCam iOS SDK: via CocoaPods +- Napier: 2.7.1 (logging) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0ea6863 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,38 @@ +# Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to a positive environment: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior: + +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Project maintainers are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project maintainers. All complaints will be reviewed and investigated promptly and fairly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1. diff --git a/README.md b/README.md index e478ec0..c345102 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,30 @@ A Kotlin Multiplatform wrapper library for [UXCam](https://uxcam.com/) session r ## Installation +### Kotlin Multiplatform + +Add the repository to your `settings.gradle.kts`: + +```kotlin +dependencyResolutionManagement { + repositories { + maven { url = uri("https://sdk.uxcam.com/android/") } + } +} +``` + +Add the dependency to your shared module's `build.gradle.kts`: + +```kotlin +kotlin { + sourceSets { + commonMain.dependencies { + implementation("dev.onexeor:uxcam-kmp-shared:") + } + } +} +``` + ### Android (Gradle) Add the repository to your `settings.gradle.kts`: diff --git a/UXCamExample/androidApp/build.gradle.kts b/UXCamExample/androidApp/build.gradle.kts index e04cd5e..8a47c63 100644 --- a/UXCamExample/androidApp/build.gradle.kts +++ b/UXCamExample/androidApp/build.gradle.kts @@ -3,6 +3,8 @@ plugins { kotlin("android") } +val libraryVersion: String = project.findProperty("VERSION_NAME")?.toString() ?: "1.0.0" + android { namespace = "dev.onexeor.uxcam.uxcamexample.android" compileSdk = 33 @@ -11,7 +13,7 @@ android { minSdk = 23 targetSdk = 33 versionCode = 1 - versionName = "1.0" + versionName = libraryVersion } buildFeatures { compose = true diff --git a/UXCamExample/shared/build.gradle.kts b/UXCamExample/shared/build.gradle.kts index ac4fda5..2f58fa6 100644 --- a/UXCamExample/shared/build.gradle.kts +++ b/UXCamExample/shared/build.gradle.kts @@ -4,6 +4,8 @@ plugins { id("com.android.library") } +val libraryVersion: String = project.findProperty("VERSION_NAME")?.toString() ?: "1.0.0" + @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) kotlin { targetHierarchy.default() @@ -22,7 +24,7 @@ kotlin { cocoapods { summary = "UXCam for KMP example" homepage = "onexeor.dev" - version = "1.0" + version = libraryVersion ios.deploymentTarget = "12.0" podfile = project.file("../iosApp/Podfile") framework { diff --git a/build.gradle.kts b/build.gradle.kts index 82b45e8..26a5223 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,6 +2,7 @@ plugins { // trick: for the same plugin versions in all sub-modules id("com.android.library").version("8.1.1").apply(false) kotlin("multiplatform").version("1.9.21").apply(false) + id("com.vanniktech.maven.publish").version("0.30.0").apply(false) } tasks.register("clean", Delete::class) { diff --git a/gradle.properties b/gradle.properties index dc2c082..7b0a536 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,4 +10,21 @@ android.nonTransitiveRClass=true #MPP kotlin.mpp.enableCInteropCommonization=true -kotlin.mpp.androidSourceSetLayoutVersion=2 \ No newline at end of file +kotlin.mpp.androidSourceSetLayoutVersion=2 + +# Library Version +VERSION_NAME=1.0.0 + +# Maven Central Publishing (Sonatype Central Portal) +GROUP=dev.onexeor +POM_ARTIFACT_ID=uxcam-kmp-shared +POM_NAME=UXCam KMP +POM_DESCRIPTION=Kotlin Multiplatform wrapper for UXCam SDK +POM_URL=https://github.com/OneXeor/UXCam-KMP +POM_SCM_URL=https://github.com/OneXeor/UXCam-KMP +POM_SCM_CONNECTION=scm:git:git://github.com/OneXeor/UXCam-KMP.git +POM_SCM_DEV_CONNECTION=scm:git:ssh://github.com/OneXeor/UXCam-KMP.git +POM_LICENCE_NAME=MIT License +POM_LICENCE_URL=https://opensource.org/licenses/MIT +POM_DEVELOPER_ID=onexeor +POM_DEVELOPER_NAME=OneXeor \ No newline at end of file diff --git a/uxcam-kmp-shared/build.gradle.kts b/uxcam-kmp-shared/build.gradle.kts index ed9e314..cec6321 100644 --- a/uxcam-kmp-shared/build.gradle.kts +++ b/uxcam-kmp-shared/build.gradle.kts @@ -1,9 +1,14 @@ +import com.vanniktech.maven.publish.SonatypeHost + plugins { kotlin("multiplatform") kotlin("native.cocoapods") id("com.android.library") + id("com.vanniktech.maven.publish") } +val libraryVersion: String = project.findProperty("VERSION_NAME")?.toString() ?: "1.0.0" + @OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class) kotlin { targetHierarchy.default() @@ -14,12 +19,13 @@ kotlin { jvmTarget = "1.8" } } + publishLibraryVariants("release") } ios() iosSimulatorArm64() cocoapods { - version = "1.0" + version = libraryVersion ios.deploymentTarget = "12.0" framework { baseName = "uxcam-kmp-shared" @@ -29,7 +35,11 @@ kotlin { } sourceSets { - val commonMain by getting + val commonMain by getting { + dependencies { + implementation("io.github.aakira:napier:2.7.1") + } + } val commonTest by getting { dependencies { implementation(kotlin("test")) @@ -49,3 +59,8 @@ android { implementation("com.uxcam:uxcam:3.6.12") } } + +mavenPublishing { + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) + signAllPublications() +} diff --git a/uxcam-kmp-shared/src/androidMain/kotlin/dev/onexeor/uxcam/UXCam.kt b/uxcam-kmp-shared/src/androidMain/kotlin/dev/onexeor/uxcam/UXCam.kt index d340523..123f1fd 100644 --- a/uxcam-kmp-shared/src/androidMain/kotlin/dev/onexeor/uxcam/UXCam.kt +++ b/uxcam-kmp-shared/src/androidMain/kotlin/dev/onexeor/uxcam/UXCam.kt @@ -5,6 +5,7 @@ package dev.onexeor.uxcam import com.uxcam.OnVerificationListener import com.uxcam.UXCam import com.uxcam.datamodel.UXConfig +import io.github.aakira.napier.Napier actual object UXCam { @@ -25,7 +26,7 @@ actual object UXCam { } override fun onVerificationFailed(p0: String?) { - p0?.let(System.err::println) + p0?.let { Napier.e("UXCam verification failed: $it") } completionHandler(false) } }) @@ -70,26 +71,28 @@ actual object UXCam { } actual fun setUserProperty(key: String?, value: String?) { - UXCam.setUserProperty(key, value) + if (key != null && value != null) { + UXCam.setUserProperty(key, value) + } } actual fun logEvent(eventName: String?) { - UXCam.logEvent(eventName) + eventName?.let { UXCam.logEvent(it) } } actual fun logEvent(eventName: String?, withProperties: Map) { - UXCam.logEvent(eventName, withProperties) + eventName?.let { UXCam.logEvent(it, withProperties) } } actual fun setUserProperty(key: String?, value: Int) { - UXCam.setUserProperty(key, value) + key?.let { UXCam.setUserProperty(it, value) } } actual fun setUserProperty(key: String?, value: Float) { - UXCam.setUserProperty(key, value) + key?.let { UXCam.setUserProperty(it, value) } } actual fun setUserProperty(key: String?, value: Boolean) { - UXCam.setUserProperty(key, value) + key?.let { UXCam.setUserProperty(it, value) } } } From 578db8ceab19e12fc7bc0cfdcd39cbc054de3fae Mon Sep 17 00:00:00 2001 From: Viktor Savchik Date: Tue, 13 Jan 2026 00:22:04 +0100 Subject: [PATCH 2/5] Fix CI: Pin Xcode 15.4 for Kotlin 1.9.21 compatibility Kotlin/Native 1.9.21 is incompatible with Xcode 16.4 (macos-latest). Pin to macos-14 with Xcode 15.4 to resolve cinterop errors. --- .github/workflows/release.yml | 5 ++++- .github/workflows/validate-pr.yml | 13 +++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ccec557..30051a7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: release: - runs-on: macos-latest + runs-on: macos-14 permissions: contents: write @@ -15,6 +15,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Select Xcode 15.4 + run: sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer + - name: Setup Java uses: actions/setup-java@v4 with: diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index fd5d179..87e2407 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -5,18 +5,19 @@ on: - 'main' jobs: build_project: - # if: startsWith(github.head_ref, 'renovate') - runs-on: macos-latest + runs-on: macos-14 steps: - name: Checkout - uses: actions/checkout@v4.1.1 + uses: actions/checkout@v4 - - name: Setup java + - name: Select Xcode 15.4 + run: sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer + + - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' - name: Build - run: | - ./gradlew :uxcam-kmp-shared:build --no-daemon \ No newline at end of file + run: ./gradlew :uxcam-kmp-shared:build --no-daemon From 6c0bd3d587542aba5808e2862598466db24b279b Mon Sep 17 00:00:00 2001 From: Viktor Savchik Date: Tue, 13 Jan 2026 00:27:06 +0100 Subject: [PATCH 3/5] Fix CI: Use macos-13 with Xcode 14.3 for Kotlin 1.9.21 compatibility --- .github/workflows/release.yml | 6 +++--- .github/workflows/validate-pr.yml | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 30051a7..ab255e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: release: - runs-on: macos-14 + runs-on: macos-13 permissions: contents: write @@ -15,8 +15,8 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Select Xcode 15.4 - run: sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer + - name: Select Xcode 14.3 + run: sudo xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer - name: Setup Java uses: actions/setup-java@v4 diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index 87e2407..e862f5b 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -5,13 +5,13 @@ on: - 'main' jobs: build_project: - runs-on: macos-14 + runs-on: macos-13 steps: - name: Checkout uses: actions/checkout@v4 - - name: Select Xcode 15.4 - run: sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer + - name: Select Xcode 14.3 + run: sudo xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer - name: Setup Java uses: actions/setup-java@v4 From b243315ae9b9a118b6fbf9f2c5f9192747fda3ec Mon Sep 17 00:00:00 2001 From: Viktor Savchik Date: Tue, 13 Jan 2026 08:39:41 +0100 Subject: [PATCH 4/5] CI: Build Android only (skip iOS cinterop) Kotlin 1.9.21 has compatibility issues with newer Xcode versions. Build Android target only in CI for now. iOS build can be done locally. --- .github/workflows/release.yml | 7 ++----- .github/workflows/validate-pr.yml | 9 +++------ 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ab255e7..5d52c84 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,9 +15,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Select Xcode 14.3 - run: sudo xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer - - name: Setup Java uses: actions/setup-java@v4 with: @@ -27,8 +24,8 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v3 - - name: Build Library - run: ./gradlew :uxcam-kmp-shared:build --no-daemon + - name: Build Android + run: ./gradlew :uxcam-kmp-shared:compileReleaseKotlinAndroid :uxcam-kmp-shared:bundleReleaseAar --no-daemon - name: Publish to Maven Central run: ./gradlew :uxcam-kmp-shared:publishAndReleaseToMavenCentral --no-configuration-cache --no-daemon diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index e862f5b..b8cb268 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -5,19 +5,16 @@ on: - 'main' jobs: build_project: - runs-on: macos-13 + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - - name: Select Xcode 14.3 - run: sudo xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer - - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' - - name: Build - run: ./gradlew :uxcam-kmp-shared:build --no-daemon + - name: Build Android + run: ./gradlew :uxcam-kmp-shared:compileReleaseKotlinAndroid :uxcam-kmp-shared:bundleReleaseAar --no-daemon From 99f821e1ff66e1516ae10411ef3ca373631e99ff Mon Sep 17 00:00:00 2001 From: Viktor Savchik Date: Tue, 13 Jan 2026 09:03:42 +0100 Subject: [PATCH 5/5] Add workflow to test GitHub secrets configuration --- .github/workflows/test-secrets.yml | 55 ++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/test-secrets.yml diff --git a/.github/workflows/test-secrets.yml b/.github/workflows/test-secrets.yml new file mode 100644 index 0000000..c927705 --- /dev/null +++ b/.github/workflows/test-secrets.yml @@ -0,0 +1,55 @@ +name: Test Secrets + +on: + workflow_dispatch: + +jobs: + test-secrets: + runs-on: ubuntu-latest + steps: + - name: Check MAVEN_CENTRAL_USERNAME + run: | + if [ -z "${{ secrets.MAVEN_CENTRAL_USERNAME }}" ]; then + echo "::error::MAVEN_CENTRAL_USERNAME is not set" + exit 1 + else + echo "MAVEN_CENTRAL_USERNAME is set (length: ${#MAVEN_CENTRAL_USERNAME})" + fi + env: + MAVEN_CENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + + - name: Check MAVEN_CENTRAL_PASSWORD + run: | + if [ -z "${{ secrets.MAVEN_CENTRAL_PASSWORD }}" ]; then + echo "::error::MAVEN_CENTRAL_PASSWORD is not set" + exit 1 + else + echo "MAVEN_CENTRAL_PASSWORD is set (length: ${#MAVEN_CENTRAL_PASSWORD})" + fi + env: + MAVEN_CENTRAL_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + + - name: Check SIGNING_KEY + run: | + if [ -z "${{ secrets.SIGNING_KEY }}" ]; then + echo "::error::SIGNING_KEY is not set" + exit 1 + else + echo "SIGNING_KEY is set (length: ${#SIGNING_KEY})" + fi + env: + SIGNING_KEY: ${{ secrets.SIGNING_KEY }} + + - name: Check SIGNING_PASSWORD + run: | + if [ -z "${{ secrets.SIGNING_PASSWORD }}" ]; then + echo "::error::SIGNING_PASSWORD is not set" + exit 1 + else + echo "SIGNING_PASSWORD is set (length: ${#SIGNING_PASSWORD})" + fi + env: + SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} + + - name: All secrets configured + run: echo "All required secrets are configured correctly!"