diff --git a/.gitignore b/.gitignore index b1dff0d..59c9a17 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,6 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store +/app/src/test/resources/debian-images/ +/app/src/test/resources/squashfs-root/ diff --git a/.idea/artifacts/api_jvm.xml b/.idea/artifacts/api_jvm.xml new file mode 100644 index 0000000..22ad2b6 --- /dev/null +++ b/.idea/artifacts/api_jvm.xml @@ -0,0 +1,8 @@ + + + $PROJECT_DIR$/api/build/libs + + + + + \ No newline at end of file diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml new file mode 100644 index 0000000..e8aaecf --- /dev/null +++ b/.idea/dictionaries/project.xml @@ -0,0 +1,8 @@ + + + + buildsrc + planterette + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 9d62f93..6793009 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,10 +1,32 @@ + diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..5e62b5a --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 9c0ff7d..096d052 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -1,9 +1,32 @@ plugins { - id("buildsrc.convention.kotlin-jvm") + kotlin("multiplatform") alias(libs.plugins.kotlinPluginSerialization) } +kotlin { + jvm() + val nativeTarget = if(System.getProperty("os.arch") == "aarch64") { + linuxArm64("native") + } else { + linuxX64("native") + } + nativeTarget.binaries { + sharedLib { + baseName = "planterette" + } + } + sourceSets { + commonMain.dependencies { + implementation(libs.bundles.kotlinxEcosystem) + } + commonTest.dependencies { + implementation(libs.bundles.kotlinxEcosystem) + implementation(kotlin("test")) + } + jvmMain.dependencies { -dependencies { - implementation(libs.bundles.kotlinxEcosystem) - testImplementation(kotlin("test")) -} \ No newline at end of file + } + jvmTest.dependencies { + + } + } +} diff --git a/api/src/main/kotlin/app/hakurei/planterette/api/HakureiConfig.kt b/api/src/commonMain/kotlin/app/hakurei/planterette/api/HakureiConfig.kt similarity index 75% rename from api/src/main/kotlin/app/hakurei/planterette/api/HakureiConfig.kt rename to api/src/commonMain/kotlin/app/hakurei/planterette/api/HakureiConfig.kt index 167ef72..500efa0 100644 --- a/api/src/main/kotlin/app/hakurei/planterette/api/HakureiConfig.kt +++ b/api/src/commonMain/kotlin/app/hakurei/planterette/api/HakureiConfig.kt @@ -8,35 +8,37 @@ data class HakureiConfig( val id: String, val path: String? = null, - val args: Array, + val args: List, val enablements: Byte, - @SerialName("session_bus") val sessionBus: DbusConfig?, - @SerialName("system_bus") val systemBus: DbusConfig?, - @SerialName("direct_wayland") val directWayland: Boolean?, + @SerialName("session_bus") val sessionBus: DbusConfig? = null, + @SerialName("system_bus") val systemBus: DbusConfig? = null, + @SerialName("direct_wayland") val directWayland: Boolean? = null, val username: String? = null, val shell: String? = null, val data: String, val dir: String, - @SerialName("extra_perms") val extraPerms: Array?, + @SerialName("extra_perms") val extraPerms: List?, val identity: Int, - val groups: Array, + val groups: List, val container: ContainerConfig ) { @Serializable data class DbusConfig( - val see: Array? = null, - val talk: Array, - val own: Array?, - val call: Map?, - val broadcast: Map?, - val log: Boolean, - val filter: Boolean - ) + val see: List? = null, + val talk: List? = null, + val own: List? = null, + val call: Map? = null, + val broadcast: Map? = null, + val log: Boolean? = null, + val filter: Boolean? = null + ) { + } + @Serializable data class ExtraPermConfig( val ensure: Boolean? = null, @@ -48,7 +50,7 @@ data class HakureiConfig( @Serializable data class ContainerConfig( val hostname: String? = null, - + @SerialName("wait_delay") val delay: Int = -1, @SerialName("seccomp_flags") val seccompFlags: Int, @SerialName("seccomp_presets") val seccompPresets: Int, @SerialName("seccomp_compat") val seccompCompat: Boolean? = null, @@ -63,29 +65,29 @@ data class HakureiConfig( @SerialName("map_real_uid") val mapRealUid: Boolean, val device: Boolean? = null, - val filesystem: Array, - @SerialName("symlink") val link: Array, + val filesystem: List, + @SerialName("symlink") val link: List, val etc: String? = null, @SerialName("auto_etc") val autoEtc: Boolean, - val cover: Array + val cover: List ) + @Serializable data class FilesystemConfig( val dst: String? = null, val src: String, val write: Boolean? = null, - val device: Boolean? = null, + @SerialName("dev") val device: Boolean? = null, @SerialName("require") val must: Boolean? = null ) + @Serializable enum class Enablement(val value: Int) { Wayland(1 shl 0), X11(1 shl 1), DBus(1 shl 2), - Pulse(1 shl 3), - - EM(0b10000); + Pulse(1 shl 3); companion object { fun enablements(vararg enablements: Enablement): Byte { @@ -93,6 +95,7 @@ data class HakureiConfig( } } } + @Serializable enum class SeccompFilterPreset(val value: Int) { Ext(1 shl 0), @@ -107,6 +110,7 @@ data class HakureiConfig( } } } + @Serializable enum class HakureiExportFlag(val value: Int) { Multiarch(1 shl 0), diff --git a/api/src/commonMain/kotlin/app/hakurei/planterette/api/PackageManifest.kt b/api/src/commonMain/kotlin/app/hakurei/planterette/api/PackageManifest.kt new file mode 100644 index 0000000..ddf4b1b --- /dev/null +++ b/api/src/commonMain/kotlin/app/hakurei/planterette/api/PackageManifest.kt @@ -0,0 +1,33 @@ +package app.hakurei.planterette.api + +import kotlinx.serialization.Serializable + +@Serializable +data class PackageManifest( + val hakureiConfig: HakureiConfig, + val id: String, + val version: Version, + val name: String, + val description: String, + val architecture: List, + val baseImage: BaseImage +) { + @Serializable + data class Version(val version: String, val canonicalVersion: UInt) + + @Serializable + enum class Architecture { + X86_64, + ARM64, + } + @Serializable + enum class BaseImage { + DEBIAN, + CHIMERA, + + } + companion object { + val fileList = listOf("planterette.json", "icon.png", "image.tar") + val signatureFile = "package.sig" + } +} \ No newline at end of file diff --git a/api/src/commonMain/kotlin/app/hakurei/planterette/api/Util.kt b/api/src/commonMain/kotlin/app/hakurei/planterette/api/Util.kt new file mode 100644 index 0000000..cfd3ad3 --- /dev/null +++ b/api/src/commonMain/kotlin/app/hakurei/planterette/api/Util.kt @@ -0,0 +1,16 @@ +package app.hakurei.planterette.api + +import kotlinx.io.Buffer +import kotlinx.io.buffered +import kotlinx.io.files.Path +import kotlinx.io.files.SystemFileSystem +import kotlinx.io.readString + +object Util { + fun readFileToString(path: String): String { + val buffer = Buffer() + val source = SystemFileSystem.source(Path(path)) + source.buffered().transferTo(buffer) + return buffer.readString() + } +} \ No newline at end of file diff --git a/api/src/test/kotlin/app/hakurei/planterette/api/HakureiConfigTest.kt b/api/src/commonTest/kotlin/app/hakurei/planterette/api/HakureiConfigTest.kt similarity index 70% rename from api/src/test/kotlin/app/hakurei/planterette/api/HakureiConfigTest.kt rename to api/src/commonTest/kotlin/app/hakurei/planterette/api/HakureiConfigTest.kt index b09bd4d..f1158ca 100644 --- a/api/src/test/kotlin/app/hakurei/planterette/api/HakureiConfigTest.kt +++ b/api/src/commonTest/kotlin/app/hakurei/planterette/api/HakureiConfigTest.kt @@ -3,23 +3,22 @@ package app.hakurei.planterette.api import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.serializer -import org.junit.jupiter.api.Test -import java.io.FileInputStream -import kotlin.test.asserter +import kotlin.test.Test +import kotlin.test.assertEquals class HakureiConfigTest { @OptIn(ExperimentalSerializationApi::class) val format = Json { explicitNulls = false + prettyPrint = true } - val templateFile: String = javaClass.classLoader.getResource("template.json")!!.readText() @Test fun testHakureiConfigSerialization() { - val testConfig: HakureiConfig = HakureiConfig( + val testConfig = HakureiConfig( id = "org.chromium.Chromium", path = "/run/current-system/sw/bin/chromium", - args = arrayOf( + args = listOf( "chromium", "--ignore-gpu-blocklist", "--disable-smooth-scrolling", @@ -30,36 +29,29 @@ class HakureiConfigTest { HakureiConfig.Enablement.Pulse), sessionBus = HakureiConfig.DbusConfig( see = null, - talk = arrayOf("org.freedesktop.Notifications", "org.freedesktop.FileManager1", "org.freedesktop.ScreenSaver", + talk = listOf("org.freedesktop.Notifications", "org.freedesktop.FileManager1", "org.freedesktop.ScreenSaver", "org.freedesktop.secrets", "org.kde.kwalletd5", "org.kde.kwalletd6", "org.gnome.SessionManager"), - own = arrayOf("org.chromium.Chromium.*", "org.mpris.MediaPlayer2.org.chromium.Chromium.*", + own = listOf("org.chromium.Chromium.*", "org.mpris.MediaPlayer2.org.chromium.Chromium.*", "org.mpris.MediaPlayer2.chromium.*"), call = mapOf("org.freedesktop.portal.*" to "*"), broadcast = mapOf("org.freedesktop.portal.*" to "@/org/freedesktop/portal/*"), - log = false, filter = true ), systemBus = HakureiConfig.DbusConfig( - see = null, - talk = arrayOf("org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"), - own = null, - call = null, - broadcast = null, - log = false, + talk = listOf("org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"), filter = true ), - directWayland = false, username = "chronos", shell = "/run/current-system/sw/bin/zsh", data = "/var/lib/hakurei/u0/org.chromium.Chromium", dir = "/data/data/org.chromium.Chromium", - extraPerms = arrayOf( + extraPerms = listOf( HakureiConfig.ExtraPermConfig(path = "/var/lib/hakurei/u0", ensure = true, execute = true), HakureiConfig.ExtraPermConfig(path = "/var/lib/hakurei/u0/org.chromium.Chromium", read = true, write = true, execute = true) ), identity = 9, - groups = arrayOf("video", "dialout", "plugdev"), + groups = listOf("video", "dialout", "plugdev"), container = HakureiConfig.ContainerConfig( hostname = "localhost", devel = true, @@ -76,22 +68,30 @@ class HakureiConfigTest { "GOOGLE_DEFAULT_CLIENT_ID" to "77185425430.apps.googleusercontent.com", "GOOGLE_DEFAULT_CLIENT_SECRET" to "OTJgUOQcT7lO7GsGZq2G4IlT", ), - filesystem = arrayOf( + filesystem = listOf( HakureiConfig.FilesystemConfig(src = "/nix/store"), HakureiConfig.FilesystemConfig(src = "/run/current-system"), HakureiConfig.FilesystemConfig(src = "/run/opengl-driver"), HakureiConfig.FilesystemConfig(src = "/var/db/nix-channels"), - HakureiConfig.FilesystemConfig(src = "/var/lib/hakurei/u0/org.chromium.Chromium", dst = "/data/data/org.chromium.Chromium", write = true, must = true), + HakureiConfig.FilesystemConfig( + src = "/var/lib/hakurei/u0/org.chromium.Chromium", + dst = "/data/data/org.chromium.Chromium", + write = true, + must = true + ), HakureiConfig.FilesystemConfig(src = "/dev/dri", device = true) ), - link = arrayOf("/run/user/65534", "/run/user/150"), + link = listOf("/run/user/65534", "/run/user/150"), etc = "/etc", autoEtc = true, - cover = arrayOf("/var/run/nscd") + cover = listOf("/var/run/nscd"), + delay = -1, + seccompCompat = true ) ) val json = format.encodeToString(serializer(), testConfig) - println("can't compare to template generated by go right now, so printing here. add \"prettyPrint = true\" to format to add whitespace") - println(json) + val deserializeJson: HakureiConfig = format.decodeFromString(json) + val templateJson: HakureiConfig = format.decodeFromString(Util.readFileToString("/home/lilly/Documents/Projects/Rosa/planterette/api/src/commonTest/resources/template.json")) + assertEquals(templateJson, deserializeJson, "") } } \ No newline at end of file diff --git a/api/src/test/resources/template.json b/api/src/commonTest/resources/template.json similarity index 96% rename from api/src/test/resources/template.json rename to api/src/commonTest/resources/template.json index c33ced9..543b572 100644 --- a/api/src/test/resources/template.json +++ b/api/src/commonTest/resources/template.json @@ -70,8 +70,10 @@ ], "container": { "hostname": "localhost", + "wait_delay": -1, "seccomp_flags": 1, "seccomp_presets": 1, + "seccomp_compat": true, "devel": true, "userns": true, "net": true, @@ -109,10 +111,8 @@ } ], "symlink": [ - [ - "/run/user/65534", - "/run/user/150" - ] + "/run/user/65534", + "/run/user/150" ], "etc": "/etc", "auto_etc": true, @@ -120,4 +120,4 @@ "/var/run/nscd" ] } -} \ No newline at end of file +} diff --git a/api/src/jvmMain/kotlin/app/hakurei/planterette/api/dsl/HakureiDSL.kt b/api/src/jvmMain/kotlin/app/hakurei/planterette/api/dsl/HakureiDSL.kt new file mode 100644 index 0000000..8ecaee7 --- /dev/null +++ b/api/src/jvmMain/kotlin/app/hakurei/planterette/api/dsl/HakureiDSL.kt @@ -0,0 +1,2 @@ +package app.hakurei.planterette.api.dsl + diff --git a/buildSrc/src/main/kotlin/kotlin-jvm.gradle.kts b/buildSrc/src/main/kotlin/kotlin-jvm.gradle.kts index 8005336..e47f264 100644 --- a/buildSrc/src/main/kotlin/kotlin-jvm.gradle.kts +++ b/buildSrc/src/main/kotlin/kotlin-jvm.gradle.kts @@ -2,14 +2,6 @@ package buildsrc.convention import org.gradle.api.tasks.testing.logging.TestLogEvent -plugins { - kotlin("jvm") -} - -kotlin { - jvmToolchain(21) -} - tasks.withType().configureEach { useJUnitPlatform() diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts new file mode 100644 index 0000000..840fe91 --- /dev/null +++ b/cli/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":api")) + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(21) +} \ No newline at end of file diff --git a/app/build.gradle.kts b/daemon/build.gradle.kts similarity index 91% rename from app/build.gradle.kts rename to daemon/build.gradle.kts index bfba26b..8eca01e 100644 --- a/app/build.gradle.kts +++ b/daemon/build.gradle.kts @@ -1,6 +1,6 @@ plugins { id("buildsrc.convention.kotlin-jvm") - + kotlin("jvm") application } diff --git a/app/src/main/kotlin/app/hakurei/planterette/Planterette.kt b/daemon/src/main/kotlin/app/hakurei/planterette/Planterette.kt similarity index 100% rename from app/src/main/kotlin/app/hakurei/planterette/Planterette.kt rename to daemon/src/main/kotlin/app/hakurei/planterette/Planterette.kt diff --git a/daemon/src/test/resources/debian.json b/daemon/src/test/resources/debian.json new file mode 100644 index 0000000..2f7684e --- /dev/null +++ b/daemon/src/test/resources/debian.json @@ -0,0 +1,124 @@ +{ + "id": "org.debian", + "path": "/bin/bash", + "args": [ + "bash" + ], + "enablements": 13, + "session_bus": { + "see": null, + "talk": [ + "org.freedesktop.Notifications", + "org.freedesktop.FileManager1", + "org.freedesktop.ScreenSaver" + ], + "own": null, + "call": null, + "broadcast": null, + "filter": true + }, + "system_bus": { + "see": null, + "talk": null, + "own": null, + "call": null, + "broadcast": null, + "filter": true + }, + "username": "chronos", + "shell": "/bin/bash", + "data": "/tmp/chronos", + "dir": "/.hakurei/home", + "identity": 9, + "groups": null, + "container": { + "hostname": "debian", + "seccomp_flags": 0, + "seccomp_presets": 0, + "devel": true, + "userns": true, + "net": true, + "tty": true, + "multiarch": true, + "env": null, + "map_real_uid": true, + "filesystem": [ + { + "dst": "/boot", + "src": "/mnt/debian/boot" + }, + { + "dst": "/home", + "src": "/mnt/debian/home" + }, + { + "dst": "/media", + "src": "/mnt/debian/media" + }, + { + "dst": "/mnt", + "src": "/mnt/debian/mnt" + }, + { + "dst": "/opt", + "src": "/mnt/debian/opt" + }, + { + "dst": "/root", + "src": "/mnt/debian/root" + }, + { + "dst": "/srv", + "src": "/mnt/debian/srv" + }, + { + "dst": "/usr", + "src": "/mnt/debian/usr" + }, + { + "dst": "/var", + "src": "/mnt/debian/var" + }, + { + "src": "/sys/block" + }, + { + "src": "/sys/bus" + }, + { + "src": "/sys/class" + }, + { + "src": "/sys/dev" + }, + { + "src": "/sys/devices" + }, + { + "src": "/dev/dri", + "dev": true + } + ], + "symlink": [ + [ + "usr/bin", + "/bin" + ], + [ + "usr/lib", + "/lib" + ], + [ + "usr/lib64", + "/lib64" + ], + [ + "usr/sbin", + "/sbin" + ] + ], + "etc": "/mnt/debian/etc", + "auto_etc": false, + "cover": null + } +} diff --git a/gradle.properties b/gradle.properties index 6f78d3f..d02e284 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,3 @@ org.gradle.caching=true org.gradle.configuration-cache=true +kotlin.mpp.applyDefaultHierarchyTemplate=false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 48380e2..a0adfeb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,17 +1,20 @@ [versions] -kotlin = "2.1.21" +kotlin = "2.2.0" kotlinxDatetime = "0.6.1" kotlinxSerializationJSON = "1.7.3" kotlinxCoroutines = "1.9.0" +kotlinxIo = "0.8.0" [libraries] kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } kotlinxDatetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" } kotlinxSerialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJSON" } kotlinxCoroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } +kotlinxIo = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinxIo" } [bundles] -kotlinxEcosystem = ["kotlinxDatetime", "kotlinxSerialization", "kotlinxCoroutines"] +kotlinxEcosystem = ["kotlinxDatetime", "kotlinxSerialization", "kotlinxCoroutines", "kotlinxIo"] [plugins] -kotlinPluginSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } \ No newline at end of file +kotlinPluginSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +kotlinPluginMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } \ No newline at end of file diff --git a/gui/build.gradle.kts b/gui/build.gradle.kts new file mode 100644 index 0000000..ae280b1 --- /dev/null +++ b/gui/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(":api")) + implementation(project(":cli")) + testImplementation(kotlin("test")) +} + +tasks.test { + useJUnitPlatform() +} +kotlin { + jvmToolchain(21) +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 54ff296..2ba66ff 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -9,7 +9,9 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" } -include(":app") +include(":daemon") include(":api") +include(":cli") +include(":gui") rootProject.name = "planterette" \ No newline at end of file