diff --git a/.gitignore b/.gitignore index b1dff0d..9a4bdaa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,11 @@ -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar +### Gradle/Java ### +**/.gradle +**/build/ !**/src/main/**/build/ !**/src/test/**/build/ ### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ +**/.idea *.iws *.iml *.ipr @@ -42,4 +39,10 @@ bin/ .vscode/ ### Mac OS ### -.DS_Store \ No newline at end of file +.DS_Store + +### Go ### +go.sum + +### Build ### +/hakureiUpdate.sh \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 26d3352..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 1bec35e..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index 295bdcf..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml deleted file mode 100644 index 3efb2d8..0000000 --- a/.idea/kotlinc.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index b861657..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts deleted file mode 100644 index f005f4a..0000000 --- a/build.gradle.kts +++ /dev/null @@ -1,19 +0,0 @@ -tasks.register("build").configure { - group = "build" - dependsOn("test") - dependsOn(":plt-build:build") - dependsOn(":plt-build-wrapper:compileGo") - dependsOn(":plt-fetch:compileGo") - dependsOn(":plt-pkg:compileGo") - dependsOn(":plt-server:compileGo") - dependsOn(":plt-updated:compileGo") -} -tasks.register("test").configure { - group = "verification" - dependsOn(":plt-build:test") - dependsOn(":plt-build-wrapper:testGo") - dependsOn(":plt-fetch:testGo") - dependsOn(":plt-pkg:testGo") - dependsOn(":plt-server:testGo") - dependsOn(":plt-updated:testGo") -} \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts deleted file mode 100644 index 33aba19..0000000 --- a/buildSrc/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - `kotlin-dsl` -} -repositories { - gradlePluginPortal() -} - -gradlePlugin { - plugins { - create("goPlugin") { - id = "goPlugin" - implementationClass = "moe.rosa.planterette.buildsrc.GoPlugin" - } - } -} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/moe/rosa/planterette/buildsrc/GoPlugin.kt b/buildSrc/src/main/kotlin/moe/rosa/planterette/buildsrc/GoPlugin.kt deleted file mode 100644 index c3c2bde..0000000 --- a/buildSrc/src/main/kotlin/moe/rosa/planterette/buildsrc/GoPlugin.kt +++ /dev/null @@ -1,29 +0,0 @@ -package moe.rosa.planterette.buildsrc - -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.tasks.Exec - -@Suppress("unused") // FIXME(mae) i have literally no clue why idea thinks GoPlugin is unused -class GoPlugin : Plugin { - override fun apply(project: Project) { - project.tasks.register("compileGo", Exec::class.java) { - group = "go" - description = "compile all go source files and output into build directory" - workingDir(project.layout.projectDirectory) - commandLine("go", "build", "-o", "../build/go/${project.name}") - } - project.tasks.register("runGo", Exec::class.java) { - group = "go" - description = "run go application" - workingDir(project.layout.projectDirectory) - commandLine("go", "run", "main.go") - } - project.tasks.register("testGo", Exec::class.java) { - group = "go" - description = "run go test" - workingDir(project.layout.projectDirectory) - commandLine("go", "test") - } - } -} \ No newline at end of file diff --git a/plt-build-wrapper/main.go b/cmd/plt-build-wrapper/main.go similarity index 100% rename from plt-build-wrapper/main.go rename to cmd/plt-build-wrapper/main.go diff --git a/cmd/plt-build-wrapper/main_test.go b/cmd/plt-build-wrapper/main_test.go new file mode 100644 index 0000000..06c0efd --- /dev/null +++ b/cmd/plt-build-wrapper/main_test.go @@ -0,0 +1,9 @@ +package main + +import ( + "testing" +) + +func TestPltBuildWrapper(t *testing.T) { + +} diff --git a/plt-build/build.gradle.kts b/cmd/plt-build/build.gradle.kts similarity index 100% rename from plt-build/build.gradle.kts rename to cmd/plt-build/build.gradle.kts diff --git a/gradle.properties b/cmd/plt-build/gradle.properties similarity index 100% rename from gradle.properties rename to cmd/plt-build/gradle.properties diff --git a/gradle/wrapper/gradle-wrapper.jar b/cmd/plt-build/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from gradle/wrapper/gradle-wrapper.jar rename to cmd/plt-build/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/cmd/plt-build/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from gradle/wrapper/gradle-wrapper.properties rename to cmd/plt-build/gradle/wrapper/gradle-wrapper.properties diff --git a/gradlew b/cmd/plt-build/gradlew similarity index 100% rename from gradlew rename to cmd/plt-build/gradlew diff --git a/cmd/plt-build/main.go b/cmd/plt-build/main.go new file mode 100644 index 0000000..c6f661d --- /dev/null +++ b/cmd/plt-build/main.go @@ -0,0 +1,2 @@ +//go:generate ./gradlew build +package plt_build diff --git a/cmd/plt-build/main_test.go b/cmd/plt-build/main_test.go new file mode 100644 index 0000000..a8ce81e --- /dev/null +++ b/cmd/plt-build/main_test.go @@ -0,0 +1,19 @@ +package plt_build + +import ( + "os" + "os/exec" + "testing" + "time" +) + +func TestPltBuild(t *testing.T) { + cmd := exec.CommandContext(t.Context(), "./gradlew", "test") + cmd.WaitDelay = 100 * time.Millisecond + cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr + err := cmd.Run() + + if err != nil { + t.Error(err) + } +} diff --git a/plt-build/settings.gradle.kts b/cmd/plt-build/settings.gradle.kts similarity index 100% rename from plt-build/settings.gradle.kts rename to cmd/plt-build/settings.gradle.kts diff --git a/plt-build/src/main/kotlin/moe/rosa/planterette/PlanteretteConfig.kt b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/PlanteretteConfig.kt similarity index 51% rename from plt-build/src/main/kotlin/moe/rosa/planterette/PlanteretteConfig.kt rename to cmd/plt-build/src/main/kotlin/moe/rosa/planterette/PlanteretteConfig.kt index 709c92b..7f8ebb6 100644 --- a/plt-build/src/main/kotlin/moe/rosa/planterette/PlanteretteConfig.kt +++ b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/PlanteretteConfig.kt @@ -2,4 +2,8 @@ package moe.rosa.planterette import moe.rosa.planterette.hakurei.HakureiConfig +/** + * Represents a Planterette build configuration. + * @param hakurei Hakurei container configuration for the application. + */ data class PlanteretteConfig(var hakurei: HakureiConfig?) \ No newline at end of file diff --git a/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/DSL.kt b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/DSL.kt similarity index 100% rename from plt-build/src/main/kotlin/moe/rosa/planterette/dsl/DSL.kt rename to cmd/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/DSL.kt diff --git a/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/HakureiDSL.kt b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/HakureiDSL.kt similarity index 97% rename from plt-build/src/main/kotlin/moe/rosa/planterette/dsl/HakureiDSL.kt rename to cmd/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/HakureiDSL.kt index 01f50ff..af7d2cc 100644 --- a/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/HakureiDSL.kt +++ b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/HakureiDSL.kt @@ -44,11 +44,7 @@ annotation class FSOverlayDSL fun PlanteretteConfig.hakurei(id: String, init: @HakureiDSL HakureiConfig.() -> Unit) { this.hakurei = HakureiConfig(id).apply(init) } -@HakureiDSL -fun HakureiConfig.executable(path: String, vararg args: String) { - this.path = AbsolutePath(path) - this.args = args.toList() -} + @HakureiDSL enum class DSLEnablements { Wayland, @@ -73,18 +69,7 @@ fun HakureiConfig.enable(vararg enablements: DSLEnablements) { fun HakureiConfig.directWayland(directWayland: Boolean = true) { this.directWayland = directWayland } -@HakureiDSL -fun HakureiConfig.username(username: String) { - this.username = username -} -@HakureiDSL -fun HakureiConfig.shell(shell: String) { - this.shell = AbsolutePath(shell) -} -@HakureiDSL -fun HakureiConfig.home(home: String) { - this.home = AbsolutePath(home) -} + //TODO(mae) automatic identity? @HakureiDSL fun HakureiConfig.identity(identity: Int? = null) { @@ -237,7 +222,23 @@ fun ContainerConfig.mapRealUid(mapRealUid: Boolean = true) { fun ContainerConfig.device(device: Boolean = true) { this.device = device } - +@ContainerDSL +fun ContainerConfig.username(username: String) { + this.username = username +} +@ContainerDSL +fun ContainerConfig.shell(shell: String) { + this.shell = AbsolutePath(shell) +} +@ContainerDSL +fun ContainerConfig.home(home: String) { + this.home = AbsolutePath(home) +} +@ContainerDSL +fun ContainerConfig.executable(path: String, vararg args: String) { + this.path = AbsolutePath(path) + this.args = args.toList() +} @FilesystemDSL data class FilesystemConfigs(val configs: MutableList = mutableListOf()) diff --git a/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/MetadataDSL.kt b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/MetadataDSL.kt new file mode 100644 index 0000000..c2329c7 --- /dev/null +++ b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/MetadataDSL.kt @@ -0,0 +1,8 @@ +package moe.rosa.planterette.dsl + +import moe.rosa.planterette.PlanteretteConfig + +@PlanteretteDSL +fun PlanteretteConfig.metadata() { + +} \ No newline at end of file diff --git a/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Filesystem.kt b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Filesystem.kt new file mode 100644 index 0000000..5a486e8 --- /dev/null +++ b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Filesystem.kt @@ -0,0 +1,181 @@ +package moe.rosa.planterette.hakurei + +import kotlinx.serialization.* +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* + +import java.nio.file.Path + + +/** + * Points to the file system root. + */ +val ROOT = AbsolutePath("/") + +/** + * Points to the directory for system-specific configuration. + */ +val ETC = AbsolutePath("/etc") + +/** + * Points to the place for small temporary files. + */ +val TMP = AbsolutePath("/tmp") + +/** + * Points to a "tmpfs" file system for system packages to place runtime data, socket files, and similar. + */ +val RUN = AbsolutePath("/run") + +/** + * Points to a directory containing per-user runtime directories, + * each usually individually mounted "tmpfs" instances. + */ +val RUN_USER: AbsolutePath = RUN + "user/" + +/** + * Points to persistent, variable system data. Writable during normal system operation. + */ +val VAR = AbsolutePath("/var/") + +/** + * Points to persistent system data. + */ +val VAR_LIB: AbsolutePath = VAR + "lib/" + +/** + * Points to a nonstandard directory that is usually empty. + */ +val VAR_EMPTY: AbsolutePath = VAR + "empty/" + +/** + * Points to the root directory for device nodes. + */ +val DEV = AbsolutePath("/dev/") + +/** + * Points to a virtual kernel file system exposing the process list and other functionality. + */ +val PROC = AbsolutePath("/proc/") + +/** + * Points to a hierarchy below `/proc/` that exposes a number of kernel tunables. + */ +val PROC_SYS: AbsolutePath = PROC + "sys/" + +/** + * Points to a virtual kernel file system exposing discovered devices and other functionality. + */ +val SYS = AbsolutePath("/sys") +/** + * Holds a pathname checked to be absolute. + * @constructor checks pathname and returns a new [AbsolutePath] if pathname is absolute. + */ +@Serializable(with = AbsolutePathSerializer::class) +data class AbsolutePath(val pathname: String, @Transient val path: Path = Path.of(pathname)) { + init { + if(!isAbsolute(pathname)) { + throw AbsolutePathException(pathname) + } + } + //TODO discuss if we should keep this operator overloading around, i think it makes things cleaner but ik ozy doesn't like operator overloading + operator fun plus(other: String): AbsolutePath { + return AbsolutePath(pathname + other) + } + operator fun plus(other: AbsolutePath): AbsolutePath { + return AbsolutePath(pathname + other.pathname) + } + companion object { + fun isAbsolute(pathname: String): Boolean { + return Path.of(pathname).isAbsolute + } + } +} +object AbsolutePathSerializer : KSerializer { + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(this::class.qualifiedName!!, PrimitiveKind.STRING) + override fun serialize(encoder: Encoder, value: AbsolutePath) { + encoder.encodeString(value.pathname) + } + + override fun deserialize(decoder: Decoder): AbsolutePath { + val path = decoder.decodeString() + return AbsolutePath(path) + } +} + +/** + * Returned by [AbsolutePath()] and holds the invalid pathname. + */ +data class AbsolutePathException(val pathname: String) : IllegalArgumentException("Path $pathname is not absolute") + +@Serializable sealed interface FilesystemConfig + +/** + * Represents a host to container bind mount. + * @param target mount point in container, same as source if empty + * @param source host filesystem path to make available to the container + * @param write do not mount target read only + * @param device do not disable device files on target, implies write + * @param ensure create source as a directory if it does not exist + * @param optional skip this mount point if source does not exist + * @param special enable special behavior: + * for autoroot, target must be set to [Filesystem.ROOT]; + * for autoetc, target must be set to [Filesystem.ETC] + */ +@Serializable +@SerialName("bind") +data class FSBind( + @SerialName("dst") val target: AbsolutePath? = null, + @SerialName("src") val source: AbsolutePath, + val write: Boolean? = null, + @SerialName("dev") val device: Boolean? = null, + val ensure: Boolean? = null, + val optional: Boolean? = null, + val special: Boolean? = null, +) : FilesystemConfig + +/** + * Represents an ephemeral (temporary) container mount point. + * @param target mount point in container + * @param write do not mount filesystem read-only + * @param size upper limit on the size of the filesystem + * @param perm initial permission bits of the new filesystem + */ +@Serializable +@SerialName("ephemeral") +data class FSEphemeral( + @SerialName("dst") val target: AbsolutePath, + val write: Boolean, + val size: Int? = null, + val perm: Int, +) : FilesystemConfig + +/** + * Represents a symlink in the container filesystem. + * @param target link path in container + * @param linkname linkname the symlink points to + * @param dereference whether to dereference linkname before creating the link + */ +@Serializable +@SerialName("link") +data class FSLink( + @SerialName("dst") val target: AbsolutePath, + val linkname: String, + val dereference: Boolean, +) : FilesystemConfig + +/** + * Represents an overlay mount point. + * @param target mount point in container + * @param lower any filesystem, does not need to be on a writable filesystem + * @param upper the upperdir is normally on a writable filesystem, leave as null to mount Lower readonly + * @param work the workdir needs to be an empty directory on the same filesystem as `upper`, must not be null if `upper` is populated + */ +@Serializable +@SerialName("overlay") +data class FSOverlay( + @SerialName("dst") val target: AbsolutePath, + val lower: List, + val upper: AbsolutePath? = null, + val work: AbsolutePath? = null, +) : FilesystemConfig \ No newline at end of file diff --git a/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Hakurei.kt b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Hakurei.kt new file mode 100644 index 0000000..f46a574 --- /dev/null +++ b/cmd/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Hakurei.kt @@ -0,0 +1,162 @@ +package moe.rosa.planterette.hakurei + +import kotlinx.serialization.* +import java.time.Duration + +val WAIT_DELAY_DEFAULT = Duration.ofSeconds(1)!! +val WAIT_DELAY_MAX = Duration.ofSeconds(30)!! + +const val IDENTITY_MIN = 0 +const val IDENTITY_MAX = 9999 + +/** + * [HakureiConfig] configures an application container. + * @param id Reverse-DNS style configured arbitrary identifier string. + * Passed to wayland security-context-v1 and used as part of defaults in dbus session proxy. + * @param enablements System services to make available in the container. + * @param sessionBus Session D-Bus proxy configuration. + * If set to null, session bus proxy assume built-in defaults. + * @param systemBus System D-Bus proxy configuration. + * If set to nil, system bus proxy is disabled. + * @param directWayland Direct access to wayland socket, no attempt is made to attach security-context-v1 + * and the bare socket is made available to the container. + * @param extraPerms Extra acl update ops to perform before setuid. + * @param identity Numerical application id, passed to hsu, used to derive init user namespace credentials. + * @param groups Init user namespace supplementary groups inherited by all container processes. + * @param container High level container configuration. + */ +@Serializable +data class HakureiConfig( + var id: String? = null, + + var enablements: Enablements? = null, + @SerialName("session_bus") var sessionBus: DBusConfig? = null, + @SerialName("system_bus") var systemBus: DBusConfig? = null, + @SerialName("direct_wayland") var directWayland: Boolean? = null, + + @SerialName("extra_perms") var extraPerms: List? = null, + var identity: Int? = null, + var groups: List? = null, + + var container: ContainerConfig? = null, +) + +/** + * Describes the container configuration to be applied to the container. + * @param hostname Container UTS namespace hostname. + * @param waitDelay Duration in nanoseconds to wait for after interrupting the initial process. + * Defaults to [WAIT_DELAY_DEFAULT] if less than or equals to zero, + * or [WAIT_DELAY_MAX] if greater than [WAIT_DELAY_MAX]. + * + * @param seccompCompat Emit Flatpak-compatible seccomp filter programs. + * @param devel Allow ptrace and friends. + * @param userns Allow userns creation and container setup syscalls. + * @param hostNet Share host net namespace. + * @param hostAbstract Share abstract unix socket scope. + * @param tty Allow dangerous terminal I/O (faking input). + * @param multiarch Allow multiarch. + * + * @param env Initial process environment variables. + * + * @param mapRealUid Map target user uid to privileged user uid in the container user namespace. + * Some programs fail to connect to dbus session running as a different uid, + * this option works around it by mapping priv-side caller uid in container. + * + * @param device Mount `/dev/` from the init mount namespace as-is in the container mount namespace. + * @param filesystem Container mount points. + * If the first element targets /, it is inserted early and excluded from path hiding. + * @param username String used as the username of the emulated user, validated against the default `NAME_REGEX` from adduser. + * @param shell Pathname of shell in the container filesystem to use for the emulated user. + * @param home Directory in the container filesystem to enter and use as the home directory of the emulated user. + * @param path Pathname to executable file in the container filesystem. + * @param args Final args passed to the initial program. + */ +@Serializable +data class ContainerConfig( + var hostname: String? = null, + @SerialName("wait_delay") var waitDelay: Long? = null, + @SerialName("seccomp_compat") var seccompCompat: Boolean? = null, + var devel: Boolean? = null, + var userns: Boolean? = null, + @SerialName("host_net") var hostNet: Boolean? = null, + @SerialName("host_abstract") var hostAbstract: Boolean? = null, + var tty: Boolean? = null, + var multiarch: Boolean? = null, + + var env: Map? = null, + + @SerialName("map_real_uid") var mapRealUid: Boolean? = null, + var device: Boolean? = null, + + var filesystem: List? = null, + + var username: String? = "chronos", + var shell: AbsolutePath? = null, + var home: AbsolutePath? = null, + var path: AbsolutePath? = null, + var args: List? = null, +) + +/** + * Describes an acl update op. + */ +@Serializable +data class ExtraPermsConfig( + var ensure: Boolean? = null, + var path: AbsolutePath, + @SerialName("r") var read: Boolean? = null, + @SerialName("w") var write: Boolean? = null, + @SerialName("x") var execute: Boolean? = null, +) { + override fun toString(): String { + val buffer = StringBuffer(5 + path.toString().length) + buffer.append("---") + if(ensure == true) { + buffer.append("+") + } + buffer.append(":") + buffer.append(path.toString()) + if(read == true) { + buffer.setCharAt(0, 'r') + } + if(write == true) { + buffer.setCharAt(1, 'w') + } + if(execute == true) { + buffer.setCharAt(2, 'x') + } + return buffer.toString() + } +} + +/** + * Configures the `xdg-dbus-proxy` process. + * @param see Set `see` policy for `NAME` (`--see=NAME`) + * @param talk Set `talk` policy for `NAME` (`--talk=NAME`) + * @param own Set `own` policy for `NAME` (`--own=NAME) + * @param call Set `RULE` for calls on `NAME` (`--call=NAME=RULE`) + * @param broadcast Set `RULE` for broadcasts from `NAME` (`--broadcast=NAME=RULE`) + * @param log Turn on logging (`--log`) + * @param filter Enable filtering (`--filter`) + */ +@Serializable +data class DBusConfig( + var see: List? = null, + var talk: List? = null, + var own: List? = null, + var call: Map? = null, + var broadcast: Map? = null, + var log: Boolean? = null, + var filter: Boolean? = null, +) + +/** + * Represents an optional host service to export to the target user. + */ +@Serializable +data class Enablements( + var wayland: Boolean? = null, + var x11: Boolean? = null, + var dbus: Boolean? = null, + var pulse: Boolean? = null, +) diff --git a/plt-build/src/test/kotlin/DSLTest.kt b/cmd/plt-build/src/test/kotlin/HakureiDSLTest.kt similarity index 87% rename from plt-build/src/test/kotlin/DSLTest.kt rename to cmd/plt-build/src/test/kotlin/HakureiDSLTest.kt index 0044dab..7d4a5f2 100644 --- a/plt-build/src/test/kotlin/DSLTest.kt +++ b/cmd/plt-build/src/test/kotlin/HakureiDSLTest.kt @@ -1,19 +1,12 @@ import moe.rosa.planterette.dsl.* import moe.rosa.planterette.dsl.DSLEnablements.* -import kotlin.test.Test -import kotlin.test.assertEquals +import kotlin.test.* -class DSLTest { +class HakureiDSLTest { companion object { val HAKUREI_DSL_TEST = planterette { hakurei("org.chromium.Chromium") { - executable("/run/current-system/sw/bin/chromium", - "chromium", - "--ignore-gpu-blocklist", - "--disable-smooth-scrolling", - "--enable-features=UseOzonePlatform", - "--ozone-platform=wayland" - ) + enable(Wayland, DBus, Pulse) dbus { session { @@ -38,9 +31,7 @@ class DSLTest { filter() } } - username("chronos") - shell("/run/current-system/sw/bin/zsh") - home("/data/data/org.chromium.Chromium") + extraPerms( perm("/var/lib/hakurei/u0") { ensure() @@ -67,6 +58,16 @@ class DSLTest { "GOOGLE_DEFAULT_CLIENT_SECRET" to "OTJgUOQcT7lO7GsGZq2G4IlT") mapRealUid() device() + executable("/run/current-system/sw/bin/chromium", + "chromium", + "--ignore-gpu-blocklist", + "--disable-smooth-scrolling", + "--enable-features=UseOzonePlatform", + "--ozone-platform=wayland" + ) + username("chronos") + shell("/run/current-system/sw/bin/zsh") + home("/data/data/org.chromium.Chromium") filesystem { bind("/var/lib/hakurei/base/org.debian" to "/") { write() @@ -80,11 +81,10 @@ class DSLTest { perm(493) } overlay("/nix/store") { - lower("/mnt-root/nix/.ro-store") - upper("/mnt-root/nix/.rw-store/upper") - work("/mnt-root/nix/.rw-store/work") + lower("/var/lib/hakurei/base/org.nixos/ro-store") + upper("/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/upper") + work("/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/work") } - bind("/nix/store") link("/run/current-system") { dereference() } diff --git a/plt-build/src/test/kotlin/HakureiTest.kt b/cmd/plt-build/src/test/kotlin/HakureiTest.kt similarity index 87% rename from plt-build/src/test/kotlin/HakureiTest.kt rename to cmd/plt-build/src/test/kotlin/HakureiTest.kt index ad5537f..e3347d0 100644 --- a/plt-build/src/test/kotlin/HakureiTest.kt +++ b/cmd/plt-build/src/test/kotlin/HakureiTest.kt @@ -8,14 +8,7 @@ class HakureiTest { companion object { val TEMPLATE_DATA = HakureiConfig( id = "org.chromium.Chromium", - path = AbsolutePath("/run/current-system/sw/bin/chromium"), - args = listOf( - "chromium", - "--ignore-gpu-blocklist", - "--disable-smooth-scrolling", - "--enable-features=UseOzonePlatform", - "--ozone-platform=wayland" - ), + enablements = Enablements( wayland = true, dbus = true, @@ -57,9 +50,7 @@ class HakureiTest { broadcast = null, filter = true ), - username = "chronos", - shell = AbsolutePath("/run/current-system/sw/bin/zsh"), - home = AbsolutePath("/data/data/org.chromium.Chromium"), + extraPerms = listOf( ExtraPermsConfig( ensure = true, @@ -119,13 +110,10 @@ class HakureiTest { FSOverlay( target = AbsolutePath("/nix/store"), lower = listOf( - AbsolutePath("/mnt-root/nix/.ro-store") + AbsolutePath("/var/lib/hakurei/base/org.nixos/ro-store") ), - upper = AbsolutePath("/mnt-root/nix/.rw-store/upper"), - work = AbsolutePath("/mnt-root/nix/.rw-store/work") - ), - FSBind( - source = AbsolutePath("/nix/store") + upper = AbsolutePath("/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/upper"), + work = AbsolutePath("/var/lib/hakurei/nix/u0/org.chromium.Chromium/rw-store/work") ), FSLink( target = AbsolutePath("/run/current-system"), @@ -148,7 +136,19 @@ class HakureiTest { device = true, optional = true ) - ) + ), + + username = "chronos", + shell = AbsolutePath("/run/current-system/sw/bin/zsh"), + home = AbsolutePath("/data/data/org.chromium.Chromium"), + path = AbsolutePath("/run/current-system/sw/bin/chromium"), + args = listOf( + "chromium", + "--ignore-gpu-blocklist", + "--disable-smooth-scrolling", + "--enable-features=UseOzonePlatform", + "--ozone-platform=wayland" + ), ) ) val TEMPLATE_JSON = ProcessBuilder("hakurei", "template") diff --git a/plt-fetch/main.go b/cmd/plt-fetch/main.go similarity index 100% rename from plt-fetch/main.go rename to cmd/plt-fetch/main.go diff --git a/plt-build-wrapper/main_test.go b/cmd/plt-fetch/main_test.go similarity index 52% rename from plt-build-wrapper/main_test.go rename to cmd/plt-fetch/main_test.go index 76b32fb..8967edc 100644 --- a/plt-build-wrapper/main_test.go +++ b/cmd/plt-fetch/main_test.go @@ -4,6 +4,6 @@ import ( "testing" ) -func TestHelloWorld(t *testing.T) { +func TestPltFetch(t *testing.T) { } diff --git a/plt-pkg/main.go b/cmd/plt-pkg/main.go similarity index 100% rename from plt-pkg/main.go rename to cmd/plt-pkg/main.go diff --git a/plt-server/main_test.go b/cmd/plt-pkg/main_test.go similarity index 52% rename from plt-server/main_test.go rename to cmd/plt-pkg/main_test.go index 76b32fb..acc383a 100644 --- a/plt-server/main_test.go +++ b/cmd/plt-pkg/main_test.go @@ -4,6 +4,6 @@ import ( "testing" ) -func TestHelloWorld(t *testing.T) { +func TestPltPkg(t *testing.T) { } diff --git a/plt-server/main.go b/cmd/plt-server/main.go similarity index 100% rename from plt-server/main.go rename to cmd/plt-server/main.go diff --git a/plt-fetch/main_test.go b/cmd/plt-server/main_test.go similarity index 52% rename from plt-fetch/main_test.go rename to cmd/plt-server/main_test.go index 76b32fb..2162747 100644 --- a/plt-fetch/main_test.go +++ b/cmd/plt-server/main_test.go @@ -4,6 +4,6 @@ import ( "testing" ) -func TestHelloWorld(t *testing.T) { +func TestPltServer(t *testing.T) { } diff --git a/plt-updated/main.go b/cmd/plt-updated/main.go similarity index 100% rename from plt-updated/main.go rename to cmd/plt-updated/main.go diff --git a/plt-pkg/main_test.go b/cmd/plt-updated/main_test.go similarity index 52% rename from plt-pkg/main_test.go rename to cmd/plt-updated/main_test.go index 76b32fb..be7ebc2 100644 --- a/plt-pkg/main_test.go +++ b/cmd/plt-updated/main_test.go @@ -4,6 +4,6 @@ import ( "testing" ) -func TestHelloWorld(t *testing.T) { +func TestPltUpdated(t *testing.T) { } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6f68a5f --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module rosa.moe/planterette + +go 1.24.9 + +require hakurei.app v0.3.1 // indirect diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index ac1b06f..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,89 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/plt-build-wrapper/build.gradle.kts b/plt-build-wrapper/build.gradle.kts deleted file mode 100644 index f942929..0000000 --- a/plt-build-wrapper/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("goPlugin") -} \ No newline at end of file diff --git a/plt-build-wrapper/go.mod b/plt-build-wrapper/go.mod deleted file mode 100644 index 598c3f6..0000000 --- a/plt-build-wrapper/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module plt-build-wrapper - -go 1.24 diff --git a/plt-build/src/main/kotlin/moe/rosa/planterette/Main.kt b/plt-build/src/main/kotlin/moe/rosa/planterette/Main.kt deleted file mode 100644 index aba9c1a..0000000 --- a/plt-build/src/main/kotlin/moe/rosa/planterette/Main.kt +++ /dev/null @@ -1,2 +0,0 @@ -package moe.rosa.planterette - diff --git a/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Filesystem.kt b/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Filesystem.kt deleted file mode 100644 index 1aaa5c3..0000000 --- a/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Filesystem.kt +++ /dev/null @@ -1,85 +0,0 @@ -package moe.rosa.planterette.hakurei - -import kotlinx.serialization.* -import kotlinx.serialization.descriptors.* -import kotlinx.serialization.encoding.* - -import java.nio.file.Path - -/** - * AbsolutePath holds a pathname checked to be absolute. - * @constructor checks pathname and returns a new AbsolutePath if pathname is absolute. - */ -@Serializable(with = AbsolutePathSerializer::class) -data class AbsolutePath(val pathname: String, @Transient val path: Path = Path.of(pathname)) { - init { - if(!isAbsolute(pathname)) { - throw AbsolutePathException(pathname) - } - } - operator fun plus(other: String): AbsolutePath { - return AbsolutePath(pathname + other) - } - companion object { - fun isAbsolute(pathname: String): Boolean { - return Path.of(pathname).isAbsolute - } - } -} - -object AbsolutePathSerializer : KSerializer { - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor(this::class.qualifiedName!!, PrimitiveKind.STRING) - override fun serialize(encoder: Encoder, value: AbsolutePath) { - encoder.encodeString(value.pathname) - } - - override fun deserialize(decoder: Decoder): AbsolutePath { - val path = decoder.decodeString() - return AbsolutePath(path) - } -} - -/** - * AbsolutePathException is returned by @see AbsolutePath() and holds the invalid pathname. - */ -data class AbsolutePathException(val pathname: String) : IllegalArgumentException("Path $pathname is not absolute") - -@Serializable sealed interface FilesystemConfig - -@Serializable -@SerialName("bind") -data class FSBind( - @SerialName("dst") val target: AbsolutePath? = null, - @SerialName("src") val source: AbsolutePath, - val write: Boolean? = null, - @SerialName("dev") val device: Boolean? = null, - val ensure: Boolean? = null, - val optional: Boolean? = null, - val special: Boolean? = null, -) : FilesystemConfig - -@Serializable -@SerialName("ephemeral") -data class FSEphemeral( - @SerialName("dst") val target: AbsolutePath, - val write: Boolean, - val size: Int? = null, - val perm: Int, -) : FilesystemConfig - -@Serializable -@SerialName("link") -data class FSLink( - @SerialName("dst") val target: AbsolutePath, - val linkname: String, - val dereference: Boolean, -) : FilesystemConfig - -@Serializable -@SerialName("overlay") -data class FSOverlay( - @SerialName("dst") val target: AbsolutePath, - val lower: List, - val upper: AbsolutePath, - val work: AbsolutePath, -) : FilesystemConfig \ No newline at end of file diff --git a/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Hakurei.kt b/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Hakurei.kt deleted file mode 100644 index b3528d1..0000000 --- a/plt-build/src/main/kotlin/moe/rosa/planterette/hakurei/Hakurei.kt +++ /dev/null @@ -1,91 +0,0 @@ -package moe.rosa.planterette.hakurei - -import kotlinx.serialization.* - -@Serializable -data class HakureiConfig( - var id: String? = null, - var path: AbsolutePath? = null, - var args: List? = null, - var enablements: Enablements? = null, - @SerialName("session_bus") var sessionBus: DBusConfig? = null, - @SerialName("system_bus") var systemBus: DBusConfig? = null, - @SerialName("direct_wayland") var directWayland: Boolean? = null, - var username: String? = null, - var shell: AbsolutePath? = null, - var home: AbsolutePath? = null, - - @SerialName("extra_perms") var extraPerms: List? = null, - var identity: Int? = null, - var groups: List? = null, - - var container: ContainerConfig? = null, -) - -@Serializable -data class ContainerConfig( - var hostname: String? = null, - @SerialName("wait_delay") var waitDelay: Long? = null, - @SerialName("seccomp_compat") var seccompCompat: Boolean? = null, - var devel: Boolean? = null, - var userns: Boolean? = null, - @SerialName("host_net") var hostNet: Boolean? = null, - @SerialName("host_abstract") var hostAbstract: Boolean? = null, - var tty: Boolean? = null, - var multiarch: Boolean? = null, - - var env: Map? = null, - - @SerialName("map_real_uid") var mapRealUid: Boolean? = null, - var device: Boolean? = null, - - var filesystem: List? = null, -) - -@Serializable -data class ExtraPermsConfig( - var ensure: Boolean? = null, - var path: AbsolutePath, - @SerialName("r") var read: Boolean? = null, - @SerialName("w") var write: Boolean? = null, - @SerialName("x") var execute: Boolean? = null, -) { - override fun toString(): String { - val buffer = StringBuffer(5 + path.toString().length) - buffer.append("---") - if(ensure == true) { - buffer.append("+") - } - buffer.append(":") - buffer.append(path.toString()) - if(read == true) { - buffer.setCharAt(0, 'r') - } - if(write == true) { - buffer.setCharAt(1, 'w') - } - if(execute == true) { - buffer.setCharAt(2, 'x') - } - return buffer.toString() - } -} - -@Serializable -data class DBusConfig( - var see: List? = null, - var talk: List? = null, - var own: List? = null, - var call: Map? = null, - var broadcast: Map? = null, - var log: Boolean? = null, - var filter: Boolean? = null, -) - -@Serializable -data class Enablements( - var wayland: Boolean? = null, - var x11: Boolean? = null, - var dbus: Boolean? = null, - var pulse: Boolean? = null, -) diff --git a/plt-build/src/test/resources/ChromiumExample.kts b/plt-build/src/test/resources/ChromiumExample.kts deleted file mode 100644 index e69de29..0000000 diff --git a/plt-fetch/build.gradle.kts b/plt-fetch/build.gradle.kts deleted file mode 100644 index f942929..0000000 --- a/plt-fetch/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("goPlugin") -} \ No newline at end of file diff --git a/plt-fetch/go.mod b/plt-fetch/go.mod deleted file mode 100644 index cce4de5..0000000 --- a/plt-fetch/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module plt-fetch - -go 1.24 diff --git a/plt-pkg/build.gradle.kts b/plt-pkg/build.gradle.kts deleted file mode 100644 index f942929..0000000 --- a/plt-pkg/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("goPlugin") -} \ No newline at end of file diff --git a/plt-pkg/go.mod b/plt-pkg/go.mod deleted file mode 100644 index 1845e72..0000000 --- a/plt-pkg/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module plt-pkg - -go 1.24 diff --git a/plt-server/build.gradle.kts b/plt-server/build.gradle.kts deleted file mode 100644 index f942929..0000000 --- a/plt-server/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("goPlugin") -} \ No newline at end of file diff --git a/plt-server/go.mod b/plt-server/go.mod deleted file mode 100644 index 71d8b4e..0000000 --- a/plt-server/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module plt-server - -go 1.24 diff --git a/plt-updated/build.gradle.kts b/plt-updated/build.gradle.kts deleted file mode 100644 index f942929..0000000 --- a/plt-updated/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("goPlugin") -} \ No newline at end of file diff --git a/plt-updated/go.mod b/plt-updated/go.mod deleted file mode 100644 index 4d28f79..0000000 --- a/plt-updated/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module plt-updated - -go 1.24 diff --git a/plt-updated/main_test.go b/plt-updated/main_test.go deleted file mode 100644 index 76b32fb..0000000 --- a/plt-updated/main_test.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "testing" -) - -func TestHelloWorld(t *testing.T) { - -} diff --git a/settings.gradle.kts b/settings.gradle.kts deleted file mode 100644 index 4878082..0000000 --- a/settings.gradle.kts +++ /dev/null @@ -1,6 +0,0 @@ -include("plt-build") -include("plt-build-wrapper") -include("plt-fetch") -include("plt-pkg") -include("plt-server") -include("plt-updated")