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