diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 65ca9d63..744606dd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: matrix: include: - os: macos-latest - targets: iosSimulatorArm64Test jvmTest + targets: iosSimulatorArm64Test jvmTest lintKotlin - os: ubuntu-latest targets: testDebugUnitTest testReleaseUnitTest jvmTest - os: windows-latest diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 2dfe928e..4e6fe394 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -1,12 +1,13 @@ -import app.cash.sqldelight.core.capitalize +import com.android.build.gradle.internal.tasks.factory.dependsOn import com.powersync.plugins.sonatype.setupGithubRepository import de.undercouch.gradle.tasks.download.Download import org.gradle.internal.os.OperatingSystem import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.TestExecutable import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest -import java.util.* +import org.jetbrains.kotlin.konan.target.Family plugins { alias(libs.plugins.kotlinMultiplatform) @@ -76,6 +77,56 @@ val buildCInteropDef by tasks.registering { outputs.files(defFile) } +val binariesFolder = project.layout.buildDirectory.dir("binaries/desktop") +val downloadPowersyncDesktopBinaries by tasks.registering(Download::class) { + val coreVersion = libs.versions.powersync.core.get() + val linux_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.so" + val linux_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.so" + val macos_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.dylib" + val macos_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.dylib" + val windows_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/powersync_x64.dll" + + if (binariesAreProvided) { + src(listOf(linux_aarch64, linux_x64, macos_aarch64, macos_x64, windows_x64)) + } else { + val (aarch64, x64) = when { + os.isLinux -> linux_aarch64 to linux_x64 + os.isMacOsX -> macos_aarch64 to macos_x64 + os.isWindows -> null to windows_x64 + else -> error("Unknown operating system: $os") + } + val arch = System.getProperty("os.arch") + src(when { + crossArch -> listOfNotNull(aarch64, x64) + arch == "aarch64" -> listOfNotNull(aarch64) + arch == "amd64" || arch == "x86_64" -> listOfNotNull(x64) + else -> error("Unsupported architecture: $arch") + }) + } + dest(binariesFolder.map { it.dir("powersync") }) + onlyIfModified(true) +} + +val downloadPowersyncFramework by tasks.registering(Download::class) { + val coreVersion = libs.versions.powersync.core.get() + val framework = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/powersync-sqlite-core.xcframework.zip" + + src(framework) + dest(binariesFolder.map { it.file("framework/powersync-sqlite-core.xcframework.zip") }) + onlyIfModified(true) +} + +val unzipPowersyncFramework by tasks.registering(Copy::class) { + dependsOn(downloadPowersyncFramework) + + from( + zipTree(downloadPowersyncFramework.get().dest).matching { + include("powersync-sqlite-core.xcframework/**") + }, + ) + into(binariesFolder.map { it.dir("framework") }) +} + kotlin { androidTarget { publishLibraryVariants("release", "debug") @@ -114,10 +165,32 @@ kotlin { } cinterops.create("powersync-sqlite-core") } + + if (konanTarget.family == Family.IOS && konanTarget.name.contains("simulator")) { + binaries.withType().configureEach { + linkTaskProvider.dependsOn(unzipPowersyncFramework) + linkerOpts("-framework", "powersync-sqlite-core") + val frameworkRoot = binariesFolder.map { it.dir("framework/powersync-sqlite-core.xcframework/ios-arm64_x86_64-simulator") }.get().asFile.path + + linkerOpts("-F", frameworkRoot) + linkerOpts("-rpath", frameworkRoot) + } + } + /* + If we ever need macOS support: + { + binaries.withType().configureEach { + linkTaskProvider.dependsOn(downloadPowersyncDesktopBinaries) + linkerOpts("-lpowersync") + linkerOpts("-L", binariesFolder.map { it.dir("powersync") }.get().asFile.path) + } + } + */ } explicitApi() + applyDefaultHierarchyTemplate() sourceSets { all { languageSettings { @@ -125,6 +198,10 @@ kotlin { } } + val commonIntegrationTest by creating { + dependsOn(commonTest.get()) + } + commonMain.dependencies { implementation(libs.uuid) implementation(libs.kotlin.stdlib) @@ -156,8 +233,16 @@ kotlin { commonTest.dependencies { implementation(libs.kotlin.test) implementation(libs.test.coroutines) + implementation(libs.test.turbine) implementation(libs.kermit.test) } + + // We're putting the native libraries into our JAR, so integration tests for the JVM can run as part of the unit + // tests. + jvmTest.get().dependsOn(commonIntegrationTest) + + // We're linking the xcframework for the simulator tests, so they can use integration tests too + iosSimulatorArm64Test.orNull?.dependsOn(commonIntegrationTest) } } @@ -212,146 +297,13 @@ android { val os = OperatingSystem.current() val binariesAreProvided = project.findProperty("powersync.binaries.provided") == "true" val crossArch = project.findProperty("powersync.binaries.cross-arch") == "true" -val binariesFolder = project.layout.buildDirectory.dir("binaries/desktop") if (binariesAreProvided && crossArch) { error("powersync.binaries.provided and powersync.binaries.cross-arch must not be both defined.") } -val getBinaries = if (binariesAreProvided) { - // Binaries for all OS must be provided (manually or by the CI) in binaries/desktop - - val verifyPowersyncBinaries = tasks.register("verifyPowersyncBinaries") { - val directory = projectDir.resolve("binaries/desktop") - val binaries = listOf( - directory.resolve("libpowersync-sqlite_aarch64.so"), - directory.resolve("libpowersync-sqlite_x64.so"), - directory.resolve("libpowersync-sqlite_aarch64.dylib"), - directory.resolve("libpowersync-sqlite_x64.dylib"), - directory.resolve("powersync-sqlite_x64.dll"), - ) - doLast { - binaries.forEach { - if (!it.exists()) error("File $it does not exist") - if (!it.isFile) error("File $it is not a regular file") - } - } - outputs.files(*binaries.toTypedArray()) - } - verifyPowersyncBinaries -} else { - // Building locally for the current OS - - val localProperties = Properties() - val localPropertiesFile = rootProject.file("local.properties") - if (localPropertiesFile.exists()) { - localPropertiesFile.inputStream().use { localProperties.load(it) } - } - val cmakeExecutable = localProperties.getProperty("cmake.path") ?: "cmake" - - fun registerCMakeTasks( - suffix: String, - vararg defines: String, - ): TaskProvider { - val cmakeConfigure = tasks.register("cmakeJvmConfigure${suffix.capitalize()}") { - dependsOn(unzipSQLiteSources) - group = "cmake" - workingDir = layout.buildDirectory.dir("cmake/$suffix").get().asFile - inputs.files( - "src/jvmMain/cpp", - "src/jvmNative/cpp", - sqliteSrcFolder, - ) - outputs.dir(workingDir) - executable = cmakeExecutable - args(listOf(file("src/jvmMain/cpp/CMakeLists.txt").absolutePath, "-DSUFFIX=$suffix", "-DCMAKE_BUILD_TYPE=Release") + defines.map { "-D$it" }) - doFirst { - workingDir.mkdirs() - } - } - - val cmakeBuild = tasks.register("cmakeJvmBuild${suffix.capitalize()}") { - dependsOn(cmakeConfigure) - group = "cmake" - workingDir = layout.buildDirectory.dir("cmake/$suffix").get().asFile - inputs.files( - "src/jvmMain/cpp", - "src/jvmNative/cpp", - sqliteSrcFolder, - workingDir, - ) - outputs.dir(workingDir.resolve(if (os.isWindows) "output/Release" else "output")) - executable = cmakeExecutable - args("--build", ".", "--config", "Release") - } - - return cmakeBuild - } - - val (aarch64, x64) = when { - os.isMacOsX -> { - val aarch64 = registerCMakeTasks("aarch64", "CMAKE_OSX_ARCHITECTURES=arm64") - val x64 = registerCMakeTasks("x64", "CMAKE_OSX_ARCHITECTURES=x86_64") - aarch64 to x64 - } - os.isLinux -> { - val aarch64 = registerCMakeTasks("aarch64", "CMAKE_C_COMPILER=aarch64-linux-gnu-gcc", "CMAKE_CXX_COMPILER=aarch64-linux-gnu-g++") - val x64 = registerCMakeTasks("x64", "CMAKE_C_COMPILER=x86_64-linux-gnu-gcc", "CMAKE_CXX_COMPILER=x86_64-linux-gnu-g++") - aarch64 to x64 - } - os.isWindows -> { - val x64 = registerCMakeTasks("x64") - null to x64 - } - else -> error("Unknown operating system: $os") - } - - val arch = System.getProperty("os.arch") - val cmakeJvmBuilds = when { - crossArch -> listOfNotNull(aarch64, x64) - arch == "aarch64" -> listOfNotNull(aarch64) - arch == "amd64" || arch == "x86_64" -> listOfNotNull(x64) - else -> error("Unsupported architecture: $arch") - } - - tasks.register("cmakeJvmBuild") { - dependsOn(cmakeJvmBuilds) - group = "cmake" - from(cmakeJvmBuilds) - into(binariesFolder.map { it.dir("sqlite") }) - } -} - -val downloadPowersyncDesktopBinaries = tasks.register("downloadPowersyncDesktopBinaries") { - val coreVersion = libs.versions.powersync.core.get() - val linux_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.so" - val linux_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.so" - val macos_aarch64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_aarch64.dylib" - val macos_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/libpowersync_x64.dylib" - val windows_x64 = "https://github.com/powersync-ja/powersync-sqlite-core/releases/download/v$coreVersion/powersync_x64.dll" - if (binariesAreProvided) { - src(listOf(linux_aarch64, linux_x64, macos_aarch64, macos_x64, windows_x64)) - } else { - val (aarch64, x64) = when { - os.isLinux -> linux_aarch64 to linux_x64 - os.isMacOsX -> macos_aarch64 to macos_x64 - os.isWindows -> null to windows_x64 - else -> error("Unknown operating system: $os") - } - val arch = System.getProperty("os.arch") - src(when { - crossArch -> listOfNotNull(aarch64, x64) - arch == "aarch64" -> listOfNotNull(aarch64) - arch == "amd64" || arch == "x86_64" -> listOfNotNull(x64) - else -> error("Unsupported architecture: $arch") - }) - } - dest(binariesFolder.map { it.dir("powersync") }) - onlyIfModified(true) -} - tasks.named(kotlin.jvm().compilations["main"].processResourcesTaskName) { - from(getBinaries, downloadPowersyncDesktopBinaries) + from(downloadPowersyncDesktopBinaries) } // We want to build with recent JDKs, but need to make sure we support Java 8. https://jakewharton.com/build-on-latest-java-test-through-lowest-java/ diff --git a/core/src/commonIntegrationTest/kotlin/com/powersync/DatabaseTest.kt b/core/src/commonIntegrationTest/kotlin/com/powersync/DatabaseTest.kt new file mode 100644 index 00000000..33435263 --- /dev/null +++ b/core/src/commonIntegrationTest/kotlin/com/powersync/DatabaseTest.kt @@ -0,0 +1,110 @@ +package com.powersync + +import app.cash.turbine.turbineScope +import com.powersync.db.SqlCursor +import com.powersync.db.getString +import com.powersync.db.schema.Column +import com.powersync.db.schema.Schema +import com.powersync.db.schema.Table +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class DatabaseTest { + private lateinit var database: PowerSyncDatabase + + @BeforeTest + fun setupDatabase() { + database = + PowerSyncDatabase( + factory = com.powersync.testutils.factory, + schema = + Schema( + Table(name = "users", columns = listOf(Column.text("name"), Column.text("email"))), + ), + dbFilename = "testdb", + ) + + runBlocking { + database.disconnectAndClear(true) + } + } + + @AfterTest + fun tearDown() { + runBlocking { database.disconnectAndClear(true) } + com.powersync.testutils.cleanup("testdb") + } + + @Test + fun testLinksPowerSync() = + runTest { + database.get("SELECT powersync_rs_version() AS r;") { it.getString(0)!! } + } + + @Test + fun testTableUpdates() = + runTest { + turbineScope { + val query = database.watch("SELECT * FROM users") { User.from(it) }.testIn(this) + + // Wait for initial query + assertEquals(0, query.awaitItem().size) + + database.execute( + "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", + listOf("Test", "test@example.org"), + ) + assertEquals(1, query.awaitItem().size) + + database.writeTransaction { + it.execute( + "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", + listOf("Test2", "test2@example.org"), + ) + it.execute( + "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", + listOf("Test3", "test3@example.org"), + ) + } + + assertEquals(3, query.awaitItem().size) + + try { + database.writeTransaction { + it.execute("DELETE FROM users;") + it.execute("syntax error, revert please") + } + } catch (e: Exception) { + // Ignore + } + + database.execute( + "INSERT INTO users (id, name, email) VALUES (uuid(), ?, ?)", + listOf("Test4", "test4@example.org"), + ) + assertEquals(4, query.awaitItem().size) + + query.expectNoEvents() + query.cancel() + } + } + + private data class User( + val id: String, + val name: String, + val email: String, + ) { + companion object { + fun from(cursor: SqlCursor): User = + User( + id = cursor.getString("id"), + name = cursor.getString("name"), + email = cursor.getString("email"), + ) + } + } +} diff --git a/core/src/commonIntegrationTest/kotlin/com/powersync/testutils/TestUtils.kt b/core/src/commonIntegrationTest/kotlin/com/powersync/testutils/TestUtils.kt new file mode 100644 index 00000000..249e7bd8 --- /dev/null +++ b/core/src/commonIntegrationTest/kotlin/com/powersync/testutils/TestUtils.kt @@ -0,0 +1,7 @@ +package com.powersync.testutils + +import com.powersync.DatabaseDriverFactory + +expect val factory: DatabaseDriverFactory + +expect fun cleanup(path: String) diff --git a/core/src/iosSimulatorArm64Test/kotlin/com/powersync/testutils/TestUtils.iosSimulatorArm64.kt b/core/src/iosSimulatorArm64Test/kotlin/com/powersync/testutils/TestUtils.iosSimulatorArm64.kt new file mode 100644 index 00000000..10f976bc --- /dev/null +++ b/core/src/iosSimulatorArm64Test/kotlin/com/powersync/testutils/TestUtils.iosSimulatorArm64.kt @@ -0,0 +1,15 @@ +package com.powersync.testutils + +import com.powersync.DatabaseDriverFactory +import kotlinx.io.files.Path +import kotlinx.io.files.SystemFileSystem + +actual val factory: DatabaseDriverFactory + get() = DatabaseDriverFactory() + +actual fun cleanup(path: String) { + val resolved = Path(path) + if (SystemFileSystem.exists(resolved)) { + SystemFileSystem.delete(resolved) + } +} diff --git a/core/src/jvmMain/cpp/CMakeLists.txt b/core/src/jvmMain/cpp/CMakeLists.txt deleted file mode 100644 index 1e4b24c5..00000000 --- a/core/src/jvmMain/cpp/CMakeLists.txt +++ /dev/null @@ -1,71 +0,0 @@ -cmake_minimum_required(VERSION 3.18.1) - -project(powersync-sqlite) - -if(NOT SQLITE3_SRC_DIR) - set(SQLITE3_SRC_DIR "../../../build/native/sqlite") -endif() - -if(SUFFIX) - set(PACKAGE_NAME "powersync-sqlite_${SUFFIX}") -else() - message(FATAL_ERROR "SUFFIX not set") -endif() - -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY output) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY output) - -set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -find_package(JNI REQUIRED) -find_package(Threads REQUIRED) - -include_directories(${SQLITE3_SRC_DIR}) -include_directories(${JNI_INCLUDE_DIRS}) - -add_library(sqlite3 STATIC - "${SQLITE3_SRC_DIR}/sqlite3.c" -) - -# Flags used by the Xerial SQlite JDBC driver. Using the same flags ensures maximum compatibility. -# See https://github.com/xerial/sqlite-jdbc/blob/533efaec2b1558d67e23ed496e6c32d505acd812/Makefile#L68 -target_compile_definitions( - sqlite3 - PUBLIC SQLITE_ENABLE_LOAD_EXTENSION=1 - PUBLIC SQLITE_HAVE_ISNAN - PUBLIC HAVE_USLEEP=1 - PUBLIC SQLITE_ENABLE_COLUMN_METADATA - PUBLIC SQLITE_CORE - PUBLIC SQLITE_ENABLE_FTS3 - PUBLIC SQLITE_ENABLE_FTS3_PARENTHESIS - PUBLIC SQLITE_ENABLE_FTS5 - PUBLIC SQLITE_ENABLE_RTREE - PUBLIC SQLITE_ENABLE_STAT4 - PUBLIC SQLITE_ENABLE_DBSTAT_VTAB - PUBLIC SQLITE_ENABLE_MATH_FUNCTIONS - PUBLIC SQLITE_THREADSAFE=1 - PUBLIC SQLITE_DEFAULT_MEMSTATUS=0 - PUBLIC SQLITE_DEFAULT_FILE_PERMISSIONS=0666 - PUBLIC SQLITE_MAX_VARIABLE_NUMBER=250000 - PUBLIC SQLITE_MAX_MMAP_SIZE=1099511627776 - PUBLIC SQLITE_MAX_LENGTH=2147483647 - PUBLIC SQLITE_MAX_COLUMN=32767 - PUBLIC SQLITE_MAX_SQL_LENGTH=1073741824 - PUBLIC SQLITE_MAX_FUNCTION_ARG=127 - PUBLIC SQLITE_MAX_ATTACHED=125 - PUBLIC SQLITE_MAX_PAGE_COUNT=4294967294 - PUBLIC SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS -) - -add_library( - ${PACKAGE_NAME} - SHARED - ../../jvmNative/cpp/sqlite_bindings.cpp -) - -target_link_libraries( - ${PACKAGE_NAME} - PRIVATE - sqlite3 - ${CMAKE_THREAD_LIBS_INIT} -) \ No newline at end of file diff --git a/core/src/jvmMain/kotlin/com/powersync/DatabaseDriverFactory.jvm.kt b/core/src/jvmMain/kotlin/com/powersync/DatabaseDriverFactory.jvm.kt index 6b0a09ba..d99a0756 100644 --- a/core/src/jvmMain/kotlin/com/powersync/DatabaseDriverFactory.jvm.kt +++ b/core/src/jvmMain/kotlin/com/powersync/DatabaseDriverFactory.jvm.kt @@ -2,33 +2,11 @@ package com.powersync import com.powersync.db.internal.InternalSchema import kotlinx.coroutines.CoroutineScope +import org.sqlite.SQLiteCommitListener import java.nio.file.Path -import kotlin.io.path.absolutePathString @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING", "SqlNoDataSourceInspection") public actual class DatabaseDriverFactory { - private var driver: PsSqlDriver? = null - - private external fun setupSqliteBinding() - - // Used in native - @Suppress("unused") - private fun onTableUpdate(tableName: String) { - driver?.updateTable(tableName) - } - - // Used in native - @Suppress("unused") - private fun onTransactionCommit(success: Boolean) { - driver?.also { driver -> - // Only clear updates on rollback - // We manually fire updates when a transaction ended - if (!success) { - driver.clearTableUpdates() - } - } - } - internal actual fun createDriver( scope: CoroutineScope, dbFilename: String, @@ -44,31 +22,29 @@ public actual class DatabaseDriverFactory { // driver.enableWriteAheadLogging() driver.loadExtensions( powersyncExtension to "sqlite3_powersync_init", - jniExtension to "powersync_init", ) - setupSqliteBinding() + val mappedDriver = PsSqlDriver(scope = scope, driver = driver) - this.driver = - PsSqlDriver( - scope = scope, - driver = driver, - ) + driver.connection.database.addUpdateListener { _, _, table, _ -> + mappedDriver.updateTable(table) + } + driver.connection.database.addCommitListener( + object : SQLiteCommitListener { + override fun onCommit() { + // We track transactions manually + } + + override fun onRollback() { + mappedDriver.clearTableUpdates() + } + }, + ) - return this.driver!! + return mappedDriver } public companion object { - private val jniExtension: Path - private val powersyncExtension: Path - - init { - val nativeLib = extractLib("powersync-sqlite") - @Suppress("UnsafeDynamicallyLoadedCode") - System.load(nativeLib.absolutePathString()) - jniExtension = nativeLib - - powersyncExtension = extractLib("powersync") - } + private val powersyncExtension: Path = extractLib("powersync") } } diff --git a/core/src/jvmMain/kotlin/com/powersync/PSJdbcSqliteDriver.kt b/core/src/jvmMain/kotlin/com/powersync/PSJdbcSqliteDriver.kt index 518299a5..ab3ec258 100644 --- a/core/src/jvmMain/kotlin/com/powersync/PSJdbcSqliteDriver.kt +++ b/core/src/jvmMain/kotlin/com/powersync/PSJdbcSqliteDriver.kt @@ -53,7 +53,7 @@ internal class PSJdbcSqliteDriver( listenersToNotify.forEach(Query.Listener::queryResultsChanged) } - private val connection: SQLiteConnection = DriverManager.getConnection(url, properties) as SQLiteConnection + val connection: SQLiteConnection = DriverManager.getConnection(url, properties) as SQLiteConnection private var transaction: Transaction? = null diff --git a/core/src/jvmTest/kotlin/com/powersync/testutils/TestUtils.jvm.kt b/core/src/jvmTest/kotlin/com/powersync/testutils/TestUtils.jvm.kt new file mode 100644 index 00000000..ec793bd1 --- /dev/null +++ b/core/src/jvmTest/kotlin/com/powersync/testutils/TestUtils.jvm.kt @@ -0,0 +1,11 @@ +package com.powersync.testutils + +import com.powersync.DatabaseDriverFactory +import java.io.File + +actual val factory: DatabaseDriverFactory + get() = DatabaseDriverFactory() + +actual fun cleanup(path: String) { + File(path).delete() +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1c8dd30b..d95c5432 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,6 +18,7 @@ uuid = "0.8.2" powersync-core = "0.3.8" sqlite-android = "3.45.0" sqlite-jdbc = "3.45.2.0" +turbine = "1.2.0" sqlDelight = "2.0.2" stately = "2.1.0" @@ -57,6 +58,7 @@ mavenPublishPlugin = { module = "com.vanniktech:gradle-maven-publish-plugin", ve test-junit = { group = "junit", name = "junit", version.ref = "junit" } test-junitKtx = { module = "androidx.test.ext:junit-ktx", version.ref = "androidx-test-junit" } test-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } +test-turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } diff --git a/plugins/sonatype/src/main/kotlin/com/powersync/plugins/sonatype/SonatypeCentralExtension.kt b/plugins/sonatype/src/main/kotlin/com/powersync/plugins/sonatype/SonatypeCentralExtension.kt index ff14bcfe..cf9f340b 100644 --- a/plugins/sonatype/src/main/kotlin/com/powersync/plugins/sonatype/SonatypeCentralExtension.kt +++ b/plugins/sonatype/src/main/kotlin/com/powersync/plugins/sonatype/SonatypeCentralExtension.kt @@ -34,7 +34,7 @@ public abstract class SonatypeCentralExtension( log.info("Setting up the `:${PUBLISH_LOCAL_TASK_NAME}` task") project.gradlePublishing.repositories.maven { repo -> repo.name = REPO_DIR - repo.url = project.uri(project.layout.buildDirectory.dir(REPO_DIR)) + repo.url = project.uri(project.rootProject.layout.buildDirectory.dir(REPO_DIR)) } log.info("Setting up the `:${COMPONENT_BUNDLE_TASK_NAME}` task")