From 22d84dd5dd4afa46d59e9e8aa77d6c0590e131ca Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 3 Dec 2025 14:07:27 +0100 Subject: [PATCH 1/7] Attach prebuilt libraries to GitHub releases --- .github/workflows/deploy.yml | 32 ++++++++++++++++++++++-- build.gradle.kts | 12 ++++----- compose/build.gradle.kts | 2 +- integrations/room/build.gradle.kts | 2 +- integrations/sqldelight/build.gradle.kts | 2 +- integrations/supabase/build.gradle.kts | 2 +- 6 files changed, 40 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4a5f4470..b5044014 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -73,6 +73,8 @@ jobs: build_xcframeworks: name: Build XCFrameworks + needs: + - fetch_prebuilts runs-on: macos-latest steps: - uses: actions/checkout@v4 @@ -91,8 +93,13 @@ jobs: uses: gradle/actions/setup-gradle@v4 with: cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} + - name: Download prebuilts + uses: actions/download-artifact@v5 + with: + artifact-ids: ${{ needs.fetch_prebuilts.outputs.artifact_id }} + path: internal/prebuild-binaries/build/output/ - name: Build frameworks - run: "./gradlew internal:PowerSyncKotlin:buildRelease" + run: "./gradlew -PhasPrebuiltAssets=true internal:PowerSyncKotlin:buildRelease" - uses: actions/upload-artifact@v4 with: @@ -105,7 +112,7 @@ jobs: add_assets: permissions: contents: write - needs: [draft_release, build_xcframeworks] + needs: [draft_release, build_xcframeworks, fetch_prebuilts] name: Add assets to pending release runs-on: ubuntu-latest steps: @@ -123,6 +130,27 @@ jobs: run: | gh release upload "${{ needs.draft_release.outputs.tag }}" PowersyncKotlinRelease.zip + - name: Download prebuilts + uses: actions/download-artifact@v5 + with: + artifact-ids: ${{ needs.fetch_prebuilts.outputs.artifact_id }} + path: internal/prebuild-binaries/build/output/ + + - name: Archive prebuilts + run: | + find internal/prebuild-binaries/build/output + zip -r prebuilt_libraries.zip internal/prebuild-binaries/build/output/ + + - run: "ls -al" + - name: Upload XCFramework + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + run: | + gh release upload "${{ needs.draft_release.outputs.tag }}" PowersyncKotlinRelease.zip + gh release upload "${{ needs.draft_release.outputs.tag }}" prebuilt_libraries.zip + + - name: "Update release description" env: GH_TOKEN: ${{ github.token }} diff --git a/build.gradle.kts b/build.gradle.kts index a7bdb6bf..08f33e14 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,12 +30,12 @@ tasks.getByName("clean") { // Merges individual module docs into a single HTML output dependencies { - dokka(project(":common:")) - dokka(project(":core:")) - dokka(project(":compose:")) - dokka(project(":integrations:room")) - dokka(project(":integrations:sqldelight")) - dokka(project(":integrations:supabase")) + dokka(projects.common) + dokka(projects.core) + dokka(projects.compose) + dokka(projects.integrations.room) + dokka(projects.integrations.sqldelight) + dokka(projects.integrations.supabase) dokka(projects.sqlite3multipleciphers) } diff --git a/compose/build.gradle.kts b/compose/build.gradle.kts index 50ff8c39..50fe95ba 100644 --- a/compose/build.gradle.kts +++ b/compose/build.gradle.kts @@ -23,7 +23,7 @@ kotlin { sourceSets { commonMain.dependencies { - api(project(":core")) + api(projects.common) implementation(compose.runtime) } androidMain.dependencies { diff --git a/integrations/room/build.gradle.kts b/integrations/room/build.gradle.kts index 07977edc..0cd5f272 100644 --- a/integrations/room/build.gradle.kts +++ b/integrations/room/build.gradle.kts @@ -26,7 +26,7 @@ kotlin { } commonMain.dependencies { - api(project(":core")) + api(projects.common) api(libs.androidx.room.runtime) api(libs.androidx.sqlite.bundled) diff --git a/integrations/sqldelight/build.gradle.kts b/integrations/sqldelight/build.gradle.kts index 2400f68f..c8ca4b7c 100644 --- a/integrations/sqldelight/build.gradle.kts +++ b/integrations/sqldelight/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { sourceSets { commonMain.dependencies { - api(projects.core) + api(projects.common) api(libs.sqldelight.runtime) implementation(libs.kotlinx.coroutines.core) } diff --git a/integrations/supabase/build.gradle.kts b/integrations/supabase/build.gradle.kts index 9004bbcb..0db77b62 100644 --- a/integrations/supabase/build.gradle.kts +++ b/integrations/supabase/build.gradle.kts @@ -27,7 +27,7 @@ kotlin { sourceSets { commonMain.dependencies { - api(projects.core) + api(projects.common) implementation(libs.kotlinx.coroutines.core) implementation(libs.supabase.client) api(libs.supabase.auth) From 3ca6d9c70b69c75c617f8bd7b9b32f595048f8ae Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 3 Dec 2025 14:15:55 +0100 Subject: [PATCH 2/7] Add testutils dependency since core is gone --- integrations/sqldelight/build.gradle.kts | 1 + integrations/supabase/build.gradle.kts | 2 ++ 2 files changed, 3 insertions(+) diff --git a/integrations/sqldelight/build.gradle.kts b/integrations/sqldelight/build.gradle.kts index c8ca4b7c..fd2abbd5 100644 --- a/integrations/sqldelight/build.gradle.kts +++ b/integrations/sqldelight/build.gradle.kts @@ -28,6 +28,7 @@ kotlin { dependencies { // Separate project because SQLDelight can't generate code in test source sets. implementation(projects.integrations.sqldelightTestDatabase) + implementation(projects.internal.testutils) implementation(libs.kotlin.test) implementation(libs.kotlinx.io) diff --git a/integrations/supabase/build.gradle.kts b/integrations/supabase/build.gradle.kts index 0db77b62..d00f78ab 100644 --- a/integrations/supabase/build.gradle.kts +++ b/integrations/supabase/build.gradle.kts @@ -38,6 +38,8 @@ kotlin { dependsOn(commonTest.get()) dependencies { + implementation(projects.internal.testutils) + implementation(libs.kotlin.test) implementation(libs.kotlinx.io) implementation(libs.test.turbine) From a229eac22112269fcf6fd6646ecf792b511a2851 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 3 Dec 2025 14:29:02 +0100 Subject: [PATCH 3/7] Revert compose changes --- compose/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compose/build.gradle.kts b/compose/build.gradle.kts index 50fe95ba..d066c4eb 100644 --- a/compose/build.gradle.kts +++ b/compose/build.gradle.kts @@ -23,7 +23,7 @@ kotlin { sourceSets { commonMain.dependencies { - api(projects.common) + api(projects.core) implementation(compose.runtime) } androidMain.dependencies { From 53d0c18cce91ace9580368262e19c3ce65112ff2 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Wed, 3 Dec 2025 17:17:16 +0100 Subject: [PATCH 4/7] Review feedback --- .github/workflows/deploy.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b5044014..3e567577 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -121,15 +121,7 @@ jobs: fetch-depth: 0 - uses: actions/download-artifact@v4 with: - merge-multiple: true - - run: "ls -al" - - name: Upload XCFramework - env: - GH_TOKEN: ${{ github.token }} - GH_REPO: ${{ github.repository }} - run: | - gh release upload "${{ needs.draft_release.outputs.tag }}" PowersyncKotlinRelease.zip - + name: XCFramework - name: Download prebuilts uses: actions/download-artifact@v5 with: @@ -141,7 +133,6 @@ jobs: find internal/prebuild-binaries/build/output zip -r prebuilt_libraries.zip internal/prebuild-binaries/build/output/ - - run: "ls -al" - name: Upload XCFramework env: GH_TOKEN: ${{ github.token }} @@ -150,7 +141,6 @@ jobs: gh release upload "${{ needs.draft_release.outputs.tag }}" PowersyncKotlinRelease.zip gh release upload "${{ needs.draft_release.outputs.tag }}" prebuilt_libraries.zip - - name: "Update release description" env: GH_TOKEN: ${{ github.token }} From b12b834acde64afc0b09db82752a73ee1c277a68 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Thu, 4 Dec 2025 09:43:54 +0100 Subject: [PATCH 5/7] Prepare release --- .github/workflows/docs-deploy.yml | 5 +---- CHANGELOG.md | 2 +- gradle.properties | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index c75c8366..d77825d6 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -31,10 +31,7 @@ jobs: validate-wrappers: true - name: Build Docs run: | - ./gradlew --scan \ - --no-configuration-cache \ - -PGITHUB_PUBLISH_TOKEN=${{ secrets.GITHUB_TOKEN }} \ - dokkaGenerate + ./gradlew --scan dokkaGenerate shell: bash - name: Upload static files as artifact id: deployment diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f186968..19d1b945 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 1.9.0 (unreleased) +## 1.9.0 - Updated user agent string formats to allow viewing version distributions in the new PowerSync dashboard. - Sync options: `newClientImplementation` is now the default. diff --git a/gradle.properties b/gradle.properties index e38adef3..cd92fe66 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ development=true RELEASE_SIGNING_ENABLED=true # Library config GROUP=com.powersync -LIBRARY_VERSION=1.8.1 +LIBRARY_VERSION=1.9.0 GITHUB_REPO=https://github.com/powersync-ja/powersync-kotlin.git # POM POM_URL=https://github.com/powersync-ja/powersync-kotlin/ From 81b2dfba2e19666b2fbf30e7ff43220864d9b945 Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Thu, 4 Dec 2025 09:53:35 +0100 Subject: [PATCH 6/7] Fix device tests --- core-tests-android/proguard-rules.pro | 4 ++ .../java/com/powersync/AndroidDatabaseTest.kt | 3 + .../com/powersync/EncryptedDatabaseTest.kt | 57 ------------------- .../testutils/IntegrationTestHelpers.kt | 36 ++++++++++++ 4 files changed, 43 insertions(+), 57 deletions(-) delete mode 100644 core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt diff --git a/core-tests-android/proguard-rules.pro b/core-tests-android/proguard-rules.pro index 13d95dad..f282a14f 100644 --- a/core-tests-android/proguard-rules.pro +++ b/core-tests-android/proguard-rules.pro @@ -30,6 +30,10 @@ *; } +-keep class androidx.test.** { + *; +} + -keep class androidx.tracing.Trace { public static void beginSection(java.lang.String); public static void endSection(); diff --git a/core-tests-android/src/androidTest/java/com/powersync/AndroidDatabaseTest.kt b/core-tests-android/src/androidTest/java/com/powersync/AndroidDatabaseTest.kt index d9212a15..ecf71f38 100644 --- a/core-tests-android/src/androidTest/java/com/powersync/AndroidDatabaseTest.kt +++ b/core-tests-android/src/androidTest/java/com/powersync/AndroidDatabaseTest.kt @@ -44,4 +44,7 @@ class AndroidDatabaseTest { @Test fun canUseTempStore() = helpers.canUseTempStore() + + @Test + fun testEncryptedDatabase() = helpers.testEncryptedDatabase() } diff --git a/core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt b/core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt deleted file mode 100644 index 2a2d6bdb..00000000 --- a/core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.powersync - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import androidx.sqlite.SQLiteException -import androidx.sqlite.execSQL -import app.cash.turbine.turbineScope -import com.powersync.db.schema.Schema -import com.powersync.encryption.AndroidEncryptedDatabaseFactory -import com.powersync.encryption.Key -import com.powersync.testutils.UserRow -import kotlinx.coroutines.* -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.runTest -import org.junit.After -import org.junit.Assert.* -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class EncryptedDatabaseTest { - - @Test - fun testEncryptedDatabase() = - runTest { - val context = InstrumentationRegistry.getInstrumentation().targetContext - - val database = PowerSyncDatabase( - factory = AndroidEncryptedDatabaseFactory( - context, - Key.Passphrase("mykey") - ), - schema = Schema(UserRow.table), - dbFilename = "encrypted_test", - ) - - assertEquals("chacha20", database.get("PRAGMA cipher") { it.getString(0)!! }) - - database.execute( - "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", - listOf("Test", "test@example.org"), - ) - database.close() - - val unencryptedFactory = DatabaseDriverFactory(context) - val unencrypted = unencryptedFactory.openConnection("encrypted_test", null, false) - - try { - unencrypted.execSQL("SELECT * FROM sqlite_schema") - throw IllegalStateException("Was able to read schema from encrypted database without supplying a key") - } catch (_: SQLiteException) { - // Expected - } - unencrypted.close() - } -} diff --git a/core-tests-android/src/main/java/com/powersync/testutils/IntegrationTestHelpers.kt b/core-tests-android/src/main/java/com/powersync/testutils/IntegrationTestHelpers.kt index 9f829ded..cfb00abf 100644 --- a/core-tests-android/src/main/java/com/powersync/testutils/IntegrationTestHelpers.kt +++ b/core-tests-android/src/main/java/com/powersync/testutils/IntegrationTestHelpers.kt @@ -1,11 +1,15 @@ package com.powersync.testutils import android.content.Context +import androidx.sqlite.SQLiteException +import androidx.sqlite.execSQL import app.cash.turbine.turbineScope import com.powersync.DatabaseDriverFactory import com.powersync.PowerSyncDatabase import com.powersync.PowerSyncException import com.powersync.db.schema.Schema +import com.powersync.encryption.AndroidEncryptedDatabaseFactory +import com.powersync.encryption.Key import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking @@ -228,4 +232,36 @@ class IntegrationTestHelpers(private val context: Context) { database.execute("INSERT INTO foo VALUES (?)", parameters = listOf(data)) } } + + fun testEncryptedDatabase() = runTest { + val database = PowerSyncDatabase( + factory = AndroidEncryptedDatabaseFactory( + context, + Key.Passphrase("mykey") + ), + schema = Schema(UserRow.table), + dbFilename = "encrypted_test", + ) + + check(database.get("PRAGMA cipher") { it.getString(0)!! } == "chacha20") { + "Should be able to query PRAGMA cipher" + } + + database.execute( + "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", + listOf("Test", "test@example.org"), + ) + database.close() + + val unencryptedFactory = DatabaseDriverFactory(context) + val unencrypted = unencryptedFactory.openConnection("encrypted_test", null, false) + + try { + unencrypted.execSQL("SELECT * FROM sqlite_schema") + throw IllegalStateException("Was able to read schema from encrypted database without supplying a key") + } catch (_: SQLiteException) { + // Expected + } + unencrypted.close() + } } From bc1169f63638591b6cad64e954d5ba807dd9eeec Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Thu, 4 Dec 2025 10:19:48 +0100 Subject: [PATCH 7/7] Use separate test class for encrypted tests --- .../java/com/powersync/AndroidDatabaseTest.kt | 3 --- .../com/powersync/EncryptedDatabaseTest.kt | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt diff --git a/core-tests-android/src/androidTest/java/com/powersync/AndroidDatabaseTest.kt b/core-tests-android/src/androidTest/java/com/powersync/AndroidDatabaseTest.kt index ecf71f38..d9212a15 100644 --- a/core-tests-android/src/androidTest/java/com/powersync/AndroidDatabaseTest.kt +++ b/core-tests-android/src/androidTest/java/com/powersync/AndroidDatabaseTest.kt @@ -44,7 +44,4 @@ class AndroidDatabaseTest { @Test fun canUseTempStore() = helpers.canUseTempStore() - - @Test - fun testEncryptedDatabase() = helpers.testEncryptedDatabase() } diff --git a/core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt b/core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt new file mode 100644 index 00000000..edd2a7ed --- /dev/null +++ b/core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt @@ -0,0 +1,18 @@ +package com.powersync + +import com.powersync.testutils.IntegrationTestHelpers +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class EncryptedDatabaseTest { + private val helpers = IntegrationTestHelpers( + InstrumentationRegistry.getInstrumentation().targetContext + ) + @Test + fun testEncryptedDatabase() = helpers.testEncryptedDatabase() +}