diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4a5f4470..3e567577 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: @@ -114,14 +121,25 @@ jobs: fetch-depth: 0 - uses: actions/download-artifact@v4 with: - merge-multiple: true - - run: "ls -al" + name: XCFramework + - 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/ + - 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: 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/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..d066c4eb 100644 --- a/compose/build.gradle.kts +++ b/compose/build.gradle.kts @@ -23,7 +23,7 @@ kotlin { sourceSets { commonMain.dependencies { - api(project(":core")) + api(projects.core) implementation(compose.runtime) } androidMain.dependencies { 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/EncryptedDatabaseTest.kt b/core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt index 2a2d6bdb..edd2a7ed 100644 --- a/core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt +++ b/core-tests-android/src/androidTest/java/com/powersync/EncryptedDatabaseTest.kt @@ -1,57 +1,18 @@ package com.powersync +import com.powersync.testutils.IntegrationTestHelpers 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 { - + private val helpers = IntegrationTestHelpers( + InstrumentationRegistry.getInstrumentation().targetContext + ) @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() - } + fun testEncryptedDatabase() = helpers.testEncryptedDatabase() } 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() + } } 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/ 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..fd2abbd5 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) } @@ -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 9004bbcb..d00f78ab 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) @@ -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)