diff --git a/.github/workflows/release-sqlite-db.yml b/.github/workflows/release-sqlite-db.yml index 7b77bae..0484ddc 100644 --- a/.github/workflows/release-sqlite-db.yml +++ b/.github/workflows/release-sqlite-db.yml @@ -35,19 +35,12 @@ jobs: - name: Build project and export SQLite DBs run: | - # Build and export policies database - ./gradlew :generator:build --quiet - ./gradlew :generator:jvmRun \ - -DmainClass=io.github.kdroidfilter.database.generator.SqlLiteExtractorKt \ - --quiet - ls -l generator/build/policies.db + # Build and run both generators using the new task + ./gradlew runGenerators --quiet - # Build and export store database - ./gradlew :storegenerator:build --quiet - ./gradlew :storegenerator:jvmRun \ - -DmainClass=io.github.kdroidfilter.database.storegenerator.SqliteStoreBuilderKt \ - --quiet - ls -l storegenerator/build/store-database.db + # Verify the generated database files + ls -l generators/policies/build/policies-database.db + ls -l generators/store/build/store-database.db - name: Define release name id: relname @@ -68,5 +61,5 @@ jobs: tag_name: ${{ env.RELEASE_NAME }} name: ${{ env.RELEASE_NAME }} files: | - generator/build/policies.db - storegenerator/build/store-database.db + generators/policies/build/policies-database.db + generators/store/build/store-database.db diff --git a/build.gradle.kts b/build.gradle.kts index ae5e8ef..240da24 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,3 +5,11 @@ plugins { alias(libs.plugins.android.application).apply(false) alias(libs.plugins.vannitktech.maven.publish).apply(false) } + +// Task to run both generators +tasks.register("runGenerators") { + group = "extraction" + description = "Runs both the SQLite policy extractor and the SQLite store extractor" + + dependsOn(":generators:policies:runPolicyExtractor", ":generators:store:runStoreExtractor") +} diff --git a/generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/SQLitePolicyExporter.kt b/generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/SQLitePolicyExporter.kt deleted file mode 100644 index 0767eec..0000000 --- a/generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/SQLitePolicyExporter.kt +++ /dev/null @@ -1,88 +0,0 @@ -package io.github.kdroidfilter.database.generator - -import io.github.kdroidfilter.database.core.policies.AppPolicy -import kotlinx.serialization.json.Json -import java.nio.file.Path -import java.sql.Connection -import java.sql.DriverManager -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter - -object SQLitePolicyExporter { - // Reuse the same JSON configuration as PolicyRepository - private val json = Json { - classDiscriminator = "type" - ignoreUnknownKeys = true - encodeDefaults = false - prettyPrint = false - serializersModule = PolicyRepository.json.serializersModule - } - - fun exportAll(root: Path, outputDb: Path) { - // Load the SQLite driver - Class.forName("org.sqlite.JDBC") - - // Get release name from environment variable or generate timestamp - val releaseName = System.getenv("RELEASE_NAME") ?: LocalDateTime.now() - .format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")) - println("🏷️ Using release name: $releaseName") - - // Creation / opening of the database - val url = "jdbc:sqlite:${outputDb.toAbsolutePath()}" - DriverManager.getConnection(url).use { conn -> - conn.autoCommit = false - createTable(conn) - insertVersion(conn, releaseName) - - val insertSql = """ - INSERT OR REPLACE INTO policies(package_name, data) - VALUES(?, ?) - """.trimIndent() - - conn.prepareStatement(insertSql).use { ps -> - PolicyRepository.loadAll(root).forEach { policy -> - val jsonStr = json.encodeToString(AppPolicy.serializer(), policy) - ps.setString(1, policy.packageName) - ps.setString(2, jsonStr) - ps.addBatch() - } - ps.executeBatch() - } - - conn.commit() - println("✅ Export ${PolicyRepository.loadAll(root).size} policies in $outputDb") - } - } - - private fun insertVersion(conn: Connection, releaseName: String) { - // Clear existing entries - conn.createStatement().use { stmt -> - stmt.executeUpdate("DELETE FROM version") - } - - // Insert new release name - conn.prepareStatement("INSERT INTO version (release_name) VALUES (?)").use { ps -> - ps.setString(1, releaseName) - ps.executeUpdate() - } - println("✅ Inserted version info: $releaseName") - } - - private fun createTable(conn: Connection) { - conn.createStatement().use { stmt -> - stmt.executeUpdate(""" - CREATE TABLE IF NOT EXISTS policies ( - package_name TEXT PRIMARY KEY, - data TEXT NOT NULL - ) - """.trimIndent()) - - stmt.executeUpdate(""" - CREATE TABLE IF NOT EXISTS version ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - release_name TEXT NOT NULL - ) - """.trimIndent()) - } - } -} diff --git a/generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/SqlLiteExtractor.kt b/generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/SqlLiteExtractor.kt deleted file mode 100644 index d868316..0000000 --- a/generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/SqlLiteExtractor.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.kdroidfilter.database.generator - -import java.nio.file.Paths - -fun main() { - val projectDir = Paths.get("").toAbsolutePath() - val root = projectDir.parent.resolve("app-policies") - val outputDb = Paths.get("build", "policies.db") - - SQLitePolicyExporter.exportAll(root, outputDb) -} diff --git a/generator/build.gradle.kts b/generators/policies/build.gradle.kts similarity index 71% rename from generator/build.gradle.kts rename to generators/policies/build.gradle.kts index 9901061..f004269 100644 --- a/generator/build.gradle.kts +++ b/generators/policies/build.gradle.kts @@ -10,6 +10,8 @@ kotlin { jvmToolchain(17) jvm() + + sourceSets { commonMain.dependencies { implementation(project(":core")) @@ -17,6 +19,7 @@ kotlin { implementation(libs.kotlinx.coroutines.test) implementation(libs.kotlinx.serialization.json) implementation(libs.kermit) + implementation(libs.platform.tools.release.fetcher) } commonTest.dependencies { @@ -42,3 +45,16 @@ kotlin { } } + +tasks.register("runPolicyExtractor") { + group = "extraction" + description = "Runs the SQLite policy extractor" + + dependsOn(kotlin.jvm().compilations["main"].compileTaskProvider) + classpath = files( + kotlin.jvm().compilations["main"].output.allOutputs, + kotlin.jvm().compilations["main"].runtimeDependencyFiles + ) + mainClass.set("SqlitePolicyExtractorKt") + +} diff --git a/generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/JsonExtractor.kt b/generators/policies/src/jvmMain/kotlin/JsonExtractor.kt similarity index 60% rename from generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/JsonExtractor.kt rename to generators/policies/src/jvmMain/kotlin/JsonExtractor.kt index 3b3bac9..dc64809 100644 --- a/generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/JsonExtractor.kt +++ b/generators/policies/src/jvmMain/kotlin/JsonExtractor.kt @@ -1,9 +1,8 @@ -import io.github.kdroidfilter.database.generator.PolicyRepository import java.nio.file.Paths fun main() { val projectDir = Paths.get("").toAbsolutePath() - val root = projectDir.parent.resolve("app-policies") + val root = projectDir.parent.resolve("../app-policies") val output = Paths.get("build","all-policies.json") PolicyRepository.exportAll(root, output) } \ No newline at end of file diff --git a/generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/PolicyRepository.kt b/generators/policies/src/jvmMain/kotlin/PolicyRepository.kt similarity index 98% rename from generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/PolicyRepository.kt rename to generators/policies/src/jvmMain/kotlin/PolicyRepository.kt index 4feeddf..7181ee3 100644 --- a/generator/src/commonMain/kotlin/io/github/kdroidfilter/database/generator/PolicyRepository.kt +++ b/generators/policies/src/jvmMain/kotlin/PolicyRepository.kt @@ -1,5 +1,3 @@ -package io.github.kdroidfilter.database.generator - import io.github.kdroidfilter.database.core.ModeSpec import io.github.kdroidfilter.database.core.policies.AppPolicy import io.github.kdroidfilter.database.core.policies.FixedPolicy diff --git a/generators/policies/src/jvmMain/kotlin/SqlitePolicyBuilder.kt b/generators/policies/src/jvmMain/kotlin/SqlitePolicyBuilder.kt new file mode 100644 index 0000000..0a46318 --- /dev/null +++ b/generators/policies/src/jvmMain/kotlin/SqlitePolicyBuilder.kt @@ -0,0 +1,98 @@ +import co.touchlab.kermit.Logger +import io.github.kdroidfilter.database.core.policies.AppPolicy +import kotlinx.serialization.json.Json +import java.nio.file.Files +import java.nio.file.Path +import java.sql.Connection +import java.sql.DriverManager +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +object SqlitePolicyBuilder { + private val logger = Logger.withTag("SqlitePolicyBuilder") + private val json = Json { + classDiscriminator = "type" + ignoreUnknownKeys = true + encodeDefaults = false + prettyPrint = false + serializersModule = PolicyRepository.json.serializersModule + } + + fun buildDatabase(policiesDir: Path, outputDbPath: Path) { + // Always create the database from scratch + logger.i { "🔄 Creating database from scratch..." } + + // Delete existing database file if it exists + if (Files.exists(outputDbPath)) { + logger.i { "🗑️ Deleting existing database file..." } + Files.delete(outputDbPath) + } + + // Get release name from environment variable or generate timestamp + val releaseName = System.getenv("RELEASE_NAME") ?: LocalDateTime.now() + .format(DateTimeFormatter.ofPattern("yyyyMMddHHmm")) + logger.i { "🏷️ Using release name: $releaseName" } + + Class.forName("org.sqlite.JDBC") + Files.createDirectories(outputDbPath.parent) + val url = "jdbc:sqlite:${outputDbPath.toAbsolutePath()}" + DriverManager.getConnection(url).use { conn -> + conn.autoCommit = false + createTables(conn) + insertVersion(conn, releaseName) + insertPolicies(conn, policiesDir) + conn.commit() + } + logger.i { "✅ SQLite database created at $outputDbPath" } + } + + private fun createTables(conn: Connection) = with(conn.createStatement()) { + executeUpdate(""" + CREATE TABLE IF NOT EXISTS policies ( + package_name TEXT PRIMARY KEY, + data TEXT NOT NULL + ) + """.trimIndent()) + + executeUpdate(""" + CREATE TABLE IF NOT EXISTS version ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + release_name TEXT NOT NULL + ) + """.trimIndent()) + close() + } + + private fun insertVersion(conn: Connection, releaseName: String) { + // Clear existing entries + conn.createStatement().use { stmt -> + stmt.executeUpdate("DELETE FROM version") + } + + // Insert new release name + conn.prepareStatement("INSERT INTO version (release_name) VALUES (?)").use { ps -> + ps.setString(1, releaseName) + ps.executeUpdate() + } + logger.i { "✅ Inserted version info: $releaseName" } + } + + private fun insertPolicies(conn: Connection, policiesDir: Path) { + val insertSql = """ + INSERT OR REPLACE INTO policies(package_name, data) + VALUES(?, ?) + """.trimIndent() + + conn.prepareStatement(insertSql).use { ps -> + val policies = PolicyRepository.loadAll(policiesDir) + policies.forEach { policy -> + val jsonStr = json.encodeToString(AppPolicy.serializer(), policy) + ps.setString(1, policy.packageName) + ps.setString(2, jsonStr) + ps.addBatch() + } + ps.executeBatch() + logger.i { "✅ Inserted ${policies.size} policies" } + } + } +} diff --git a/generators/policies/src/jvmMain/kotlin/SqlitePolicyExtractor.kt b/generators/policies/src/jvmMain/kotlin/SqlitePolicyExtractor.kt new file mode 100644 index 0000000..10aff9d --- /dev/null +++ b/generators/policies/src/jvmMain/kotlin/SqlitePolicyExtractor.kt @@ -0,0 +1,9 @@ +import java.nio.file.Path + +fun main() { + val projectDir = Path.of("").toAbsolutePath() + val policiesDir = projectDir.parent.resolve("../app-policies") + val outputDb = projectDir.resolve("build/policies-database.db") + + SqlitePolicyBuilder.buildDatabase(policiesDir, outputDb) +} \ No newline at end of file diff --git a/generator/src/commonTest/kotlin/io/github/kdroidfilter/database/generator/JsonExtractorTest.kt b/generators/policies/src/jvmTest/kotlin/JsonExtractorTest.kt similarity index 96% rename from generator/src/commonTest/kotlin/io/github/kdroidfilter/database/generator/JsonExtractorTest.kt rename to generators/policies/src/jvmTest/kotlin/JsonExtractorTest.kt index cdab63b..34038fe 100644 --- a/generator/src/commonTest/kotlin/io/github/kdroidfilter/database/generator/JsonExtractorTest.kt +++ b/generators/policies/src/jvmTest/kotlin/JsonExtractorTest.kt @@ -1,19 +1,17 @@ -package io.github.kdroidfilter.database.generator - import io.github.kdroidfilter.database.core.AppCategory import io.github.kdroidfilter.database.core.ModeSpec -import io.github.kdroidfilter.database.core.UserMode import io.github.kdroidfilter.database.core.NetworkMode import io.github.kdroidfilter.database.core.NetworkPolicy +import io.github.kdroidfilter.database.core.UserMode import io.github.kdroidfilter.database.core.policies.AppPolicy import io.github.kdroidfilter.database.core.policies.FixedPolicy import io.github.kdroidfilter.database.core.policies.ModeBasedPolicy import kotlinx.serialization.builtins.ListSerializer +import java.nio.file.Files +import java.nio.file.Path import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -import java.nio.file.Files -import java.nio.file.Path class JsonExtractorTest { @@ -21,7 +19,7 @@ class JsonExtractorTest { fun `exportAll should serialize all existing policies without error`() { // Arrange: point to the real app-policies folder in the project val projectDir = Path.of("").toAbsolutePath() - val root = projectDir.parent.resolve("app-policies") + val root = projectDir.parent.resolve("../app-policies") val output = Files.createTempFile("all-policies", ".json") // Act: generate the single JSON array @@ -75,4 +73,4 @@ class JsonExtractorTest { val names = policies.map { it.packageName }.toSet() assertTrue(names.containsAll(setOf("com.example.foo", "com.example.bar")), "Both package names must be present") } -} +} \ No newline at end of file diff --git a/storegenerator/build.gradle.kts b/generators/store/build.gradle.kts similarity index 77% rename from storegenerator/build.gradle.kts rename to generators/store/build.gradle.kts index bc77d7a..6fcaf04 100644 --- a/storegenerator/build.gradle.kts +++ b/generators/store/build.gradle.kts @@ -47,3 +47,16 @@ kotlin { } } + +tasks.register("runStoreExtractor") { + group = "extraction" + description = "Runs the SQLite store extractor" + + dependsOn(kotlin.jvm().compilations["main"].compileTaskProvider) + classpath = files( + kotlin.jvm().compilations["main"].output.allOutputs, + kotlin.jvm().compilations["main"].runtimeDependencyFiles + ) + mainClass.set("SqliteStoreExtractorKt") + +} diff --git a/storegenerator/src/commonMain/kotlin/io/github/kdroidfilter/database/storegenerator/SqliteStoreBuilder.kt b/generators/store/src/jvmMain/kotlin/SqliteStoreBuilder.kt similarity index 97% rename from storegenerator/src/commonMain/kotlin/io/github/kdroidfilter/database/storegenerator/SqliteStoreBuilder.kt rename to generators/store/src/jvmMain/kotlin/SqliteStoreBuilder.kt index c7cbb7b..3d6b1a3 100644 --- a/storegenerator/src/commonMain/kotlin/io/github/kdroidfilter/database/storegenerator/SqliteStoreBuilder.kt +++ b/generators/store/src/jvmMain/kotlin/SqliteStoreBuilder.kt @@ -1,5 +1,3 @@ -package io.github.kdroidfilter.database.storegenerator - import co.touchlab.kermit.Logger import com.kdroid.gplayscrapper.services.getGooglePlayApplicationInfo import io.github.kdroidfilter.database.core.AppCategory @@ -224,10 +222,3 @@ object SqliteStoreBuilder { } } } - -fun main() { - val root = Path.of("").toAbsolutePath() - val policies = root.resolve("../app-policies") - val output = root.resolve("build/store-database.db") - SqliteStoreBuilder.buildDatabase(policies, output) -} diff --git a/generators/store/src/jvmMain/kotlin/SqliteStoreExtractor.kt b/generators/store/src/jvmMain/kotlin/SqliteStoreExtractor.kt new file mode 100644 index 0000000..2117f12 --- /dev/null +++ b/generators/store/src/jvmMain/kotlin/SqliteStoreExtractor.kt @@ -0,0 +1,9 @@ +import java.nio.file.Path + +fun main() { + val projectDir = Path.of("").toAbsolutePath() + val policiesDir = projectDir.resolve("../../app-policies") + val outputDb = projectDir.resolve("build/store-database.db") + + SqliteStoreBuilder.buildDatabase(policiesDir, outputDb) +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 97aeb6e..f7566ab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,3 +12,5 @@ kotlin.daemon.jvmargs=-Xmx4G #Android android.useAndroidX=true android.nonTransitiveRClass=true + +kotlin.native.ignoreDisabledTargets=true \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d78a0b0..3ad7dc4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] -coilNetworkOkhttp = "3.1.0" +coilNetworkOkhttp = "3.2.0" kotlin = "2.1.21" -agp = "8.9.2" +agp = "8.9.3" kotlinx-coroutines = "1.10.2" kotlinx-serialization = "1.8.1" kermit = "2.0.5" diff --git a/settings.gradle.kts b/settings.gradle.kts index dd01576..3c71d47 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,8 +28,9 @@ dependencyResolutionManagement { mavenCentral() } } + include(":core") -include(":generator") -include(":storegenerator") +include(":generators:policies") +include(":generators:store") include(":localization") include(":sample:composeApp")