Compare commits
13 Commits
1dc17ac487
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
aa3c3377d0
|
|||
|
96bb989ad5
|
|||
|
98950f21f3
|
|||
|
7110fdb53e
|
|||
|
d52447dc23
|
|||
| b703b09f76 | |||
|
404b2b66b7
|
|||
|
df9d8b2482
|
|||
|
7e2b93560f
|
|||
|
d0431ba1ed
|
|||
|
032f2ca7dc
|
|||
|
8bd952a4c8
|
|||
|
8bb386ebf7
|
19
.gitignore
vendored
19
.gitignore
vendored
@@ -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
|
||||
.DS_Store
|
||||
|
||||
### Go ###
|
||||
go.sum
|
||||
|
||||
### Build ###
|
||||
/hakureiUpdate.sh
|
||||
3
.idea/.gitignore
generated
vendored
3
.idea/.gitignore
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
10
.idea/codeStyles/Project.xml
generated
10
.idea/codeStyles/Project.xml
generated
@@ -1,10 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<JetCodeStyleSettings>
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</JetCodeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@@ -1,5 +0,0 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
35
.idea/gradle.xml
generated
35
.idea/gradle.xml
generated
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<compositeConfiguration>
|
||||
<compositeBuild compositeDefinitionSource="SCRIPT">
|
||||
<builds>
|
||||
<build path="$PROJECT_DIR$/buildSrc" name="buildSrc">
|
||||
<projects>
|
||||
<project path="$PROJECT_DIR$/buildSrc" />
|
||||
</projects>
|
||||
</build>
|
||||
</builds>
|
||||
</compositeBuild>
|
||||
</compositeConfiguration>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/buildSrc" />
|
||||
<option value="$PROJECT_DIR$/plt-build" />
|
||||
<option value="$PROJECT_DIR$/plt-build-wrapper" />
|
||||
<option value="$PROJECT_DIR$/plt-fetch" />
|
||||
<option value="$PROJECT_DIR$/plt-pkg" />
|
||||
<option value="$PROJECT_DIR$/plt-server" />
|
||||
<option value="$PROJECT_DIR$/plt-updated" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/kotlinc.xml
generated
6
.idea/kotlinc.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="2.2.20" />
|
||||
</component>
|
||||
</project>
|
||||
10
.idea/misc.xml
generated
10
.idea/misc.xml
generated
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" default="true" project-jdk-name="openjdk-24" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
79
api/capability.go
Normal file
79
api/capability.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Capability interface {
|
||||
ID() string
|
||||
}
|
||||
type CapabilityJSON struct {
|
||||
Capability
|
||||
}
|
||||
type BasicCapability struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
func (c BasicCapability) ID() string {
|
||||
return c.Id
|
||||
}
|
||||
|
||||
const CapabilityBasic string = "basic"
|
||||
|
||||
type DBusCapability struct {
|
||||
Id string `json:"id"`
|
||||
Own []string `json:"own,omitempty"`
|
||||
}
|
||||
|
||||
func (c DBusCapability) ID() string {
|
||||
return c.Id
|
||||
}
|
||||
|
||||
const CapabilityDBus = "dbus"
|
||||
|
||||
type capabilityType struct {
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
func (c *CapabilityJSON) MarshalJSON() ([]byte, error) {
|
||||
if c == nil || c.Capability == nil {
|
||||
return nil, fmt.Errorf("invalid capability")
|
||||
}
|
||||
var v any
|
||||
switch cv := c.Capability.(type) {
|
||||
case *BasicCapability:
|
||||
v = &struct {
|
||||
capabilityType
|
||||
*BasicCapability
|
||||
}{capabilityType{CapabilityBasic}, cv}
|
||||
|
||||
case *DBusCapability:
|
||||
v = &struct {
|
||||
capabilityType
|
||||
*DBusCapability
|
||||
}{capabilityType{CapabilityDBus}, cv}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid capability")
|
||||
}
|
||||
return json.Marshal(v)
|
||||
}
|
||||
func (c *CapabilityJSON) UnmarshalJSON(data []byte) error {
|
||||
t := new(capabilityType)
|
||||
if err := json.Unmarshal(data, &t); err != nil {
|
||||
return err
|
||||
}
|
||||
switch t.Type {
|
||||
case CapabilityBasic:
|
||||
*c = CapabilityJSON{new(BasicCapability)}
|
||||
|
||||
case CapabilityDBus:
|
||||
*c = CapabilityJSON{new(DBusCapability)}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid capability")
|
||||
}
|
||||
|
||||
return json.Unmarshal(data, c.Capability)
|
||||
}
|
||||
25
api/manifest.go
Normal file
25
api/manifest.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package api
|
||||
|
||||
import "hakurei.app/container/check"
|
||||
|
||||
type Manifest struct {
|
||||
Metadata Metadata `json:"metadata"`
|
||||
Executable Executable `json:"executable"`
|
||||
Capabilities []CapabilityJSON `json:"capabilities"`
|
||||
Permissions []CapabilityJSON `json:"permissions"`
|
||||
}
|
||||
type Metadata struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Author string `json:"author"`
|
||||
Icon *check.Absolute `json:"icon"`
|
||||
Version int `json:"version"`
|
||||
VersionName string `json:"version_name"`
|
||||
}
|
||||
type Executable struct {
|
||||
BaseImage string `json:"base_image"`
|
||||
Binary *check.Absolute `json:"binary"`
|
||||
Args []string `json:"args"`
|
||||
Env map[string]string `json:"env"`
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("goPlugin") {
|
||||
id = "goPlugin"
|
||||
implementationClass = "moe.rosa.planterette.buildsrc.GoPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Project> {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
9
cmd/plt-build-wrapper/main_test.go
Normal file
9
cmd/plt-build-wrapper/main_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPltBuildWrapper(t *testing.T) {
|
||||
|
||||
}
|
||||
52
cmd/plt-build/build.gradle.kts
Normal file
52
cmd/plt-build/build.gradle.kts
Normal file
@@ -0,0 +1,52 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "2.2.10"
|
||||
kotlin("plugin.serialization") version "2.2.20"
|
||||
}
|
||||
|
||||
group = "moe.rosa"
|
||||
version = "0.1.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
|
||||
val kotlinVersion = "2.2.10"
|
||||
val kotlinCoroutinesVersion = "1.7.0-RC"
|
||||
|
||||
dependencies {
|
||||
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
|
||||
implementation(kotlin("reflect"))
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-common:${kotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-jvm:${kotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-dependencies:${kotlinVersion}")
|
||||
implementation("org.jetbrains.kotlin:kotlin-scripting-dependencies-maven:${kotlinVersion}")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${kotlinCoroutinesVersion}")
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
resources {
|
||||
srcDirs("src/main/resources", "build/natives")
|
||||
}
|
||||
}
|
||||
kotlin {
|
||||
jvmToolchain(24)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
tasks.register<Exec>("compileNativeLib") {
|
||||
val java_home = System.getProperty("java.home")
|
||||
environment("CGO_CFLAGS" to "-I$java_home/include -I$java_home/include/linux")
|
||||
workingDir("src/main/go")
|
||||
commandLine("go", "build", "-linkshared", "-buildmode=c-shared", "-o", layout.buildDirectory.get().dir("natives").file("pltbuild.so"))
|
||||
}
|
||||
tasks.processResources {
|
||||
dependsOn += "compileNativeLib"
|
||||
}
|
||||
0
gradlew → cmd/plt-build/gradlew
vendored
0
gradlew → cmd/plt-build/gradlew
vendored
2
cmd/plt-build/main.go
Normal file
2
cmd/plt-build/main.go
Normal file
@@ -0,0 +1,2 @@
|
||||
//go:generate ./gradlew build
|
||||
package plt_build
|
||||
19
cmd/plt-build/main_test.go
Normal file
19
cmd/plt-build/main_test.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
37
cmd/plt-build/src/main/go/main.go
Normal file
37
cmd/plt-build/src/main/go/main.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
//#include "pltbuild.h"
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
//export planterette_write
|
||||
func planterette_write(
|
||||
fd C.int,
|
||||
str_p *C.char, str_sz C.size_t,
|
||||
errno_p *C.uintptr_t,
|
||||
err_str_p **C.char,
|
||||
) {
|
||||
f := os.NewFile(uintptr(fd), strconv.Itoa(int(fd)))
|
||||
defer f.Close()
|
||||
_, err := f.WriteString(C.GoStringN(str_p, C.int(str_sz)))
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
var pathError *os.PathError
|
||||
if errors.As(err, &pathError) {
|
||||
var errno syscall.Errno
|
||||
if errors.As(pathError.Err, &errno) {
|
||||
*errno_p = C.uintptr_t(errno)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
*err_str_p = C.CString(err.Error())
|
||||
return
|
||||
}
|
||||
26
cmd/plt-build/src/main/go/pltbuild.c
Normal file
26
cmd/plt-build/src/main/go/pltbuild.c
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "pltbuild.h"
|
||||
|
||||
jint throwIOException( JNIEnv *env, char *message) {
|
||||
jclass exClass;
|
||||
char *className = "java/io/IOException";
|
||||
|
||||
exClass = (*env)->FindClass(env, className);
|
||||
return (*env)->ThrowNew(env, exClass, message);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_moe_rosa_planterette_jni_GoFile_write__ILnet_java_String_2(JNIEnv *env, jobject obj, jint fd, jstring str) {
|
||||
char *s = (*env)->GetStringUTFChars(env, str, 0);
|
||||
size_t sz = (*env)->GetStringUTFLength(env, str);
|
||||
|
||||
uintptr_t *errno_p = NULL;
|
||||
char **err_str_p = NULL;
|
||||
|
||||
planterette_write(fd, s, sz, errno_p, err_str_p);
|
||||
|
||||
if(errno_p || *err_str_p ) {
|
||||
throwIOException(env, **err_str_p ? *err_str_p : strerror(*errno_p));
|
||||
}
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, str, s);
|
||||
free(err_str_p);
|
||||
}
|
||||
17
cmd/plt-build/src/main/go/pltbuild.h
Normal file
17
cmd/plt-build/src/main/go/pltbuild.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#include <jni.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef _PLTBUILD_H
|
||||
#define _PLTBUILD_H
|
||||
|
||||
/*
|
||||
* moe/rosa/planterette/jni/GoFile#write(ILnet/java/String;)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_moe_rosa_planterette_jni_GoFile_write__ILnet_java_String_2(JNIEnv *, jobject, jint, jstring);
|
||||
|
||||
extern void planterette_write(int fd, char *str_p, size_t str_sz, uintptr_t *errno_p, char **err_str_p);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
package moe.rosa.planterette.api
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed interface Capability {
|
||||
val id: String
|
||||
|
||||
@SerialName("basic")
|
||||
data class Basic(override val id: String) : Capability
|
||||
|
||||
@SerialName("dbus")
|
||||
data class DBus(override val id: String, val own: List<String>) : Capability
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package moe.rosa.planterette.api
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Manifest(
|
||||
val metadata: Metadata,
|
||||
val executable: Executable,
|
||||
val capabilities: List<Capability>,
|
||||
val permissions: List<Capability>
|
||||
) {
|
||||
@Serializable
|
||||
data class Metadata(
|
||||
var id: String,
|
||||
var name: String,
|
||||
var description: String,
|
||||
var author: String,
|
||||
var icon: String,
|
||||
var version: Int,
|
||||
@SerialName("version_name") var versionName: String
|
||||
) {
|
||||
constructor() : this("", "", "", "", "", -1, "")
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Executable(
|
||||
@SerialName("base_image") var baseImage: String,
|
||||
var binary: String,
|
||||
val args: MutableList<String>,
|
||||
val env: MutableMap<String, String>
|
||||
) {
|
||||
constructor() : this("", "", mutableListOf(), mutableMapOf())
|
||||
}
|
||||
}
|
||||
|
||||
227
cmd/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/DSL.kt
Normal file
227
cmd/plt-build/src/main/kotlin/moe/rosa/planterette/dsl/DSL.kt
Normal file
@@ -0,0 +1,227 @@
|
||||
@file:Suppress("unused")
|
||||
package moe.rosa.planterette.dsl
|
||||
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import moe.rosa.planterette.api.Capability
|
||||
import moe.rosa.planterette.api.Manifest
|
||||
import moe.rosa.planterette.jni.GoFile
|
||||
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@DslMarker
|
||||
annotation class PlanteretteBlockMarker
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@PlanteretteBlockMarker
|
||||
annotation class MetadataBlockMarker
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@PlanteretteBlockMarker
|
||||
annotation class ExecutableBlockMarker
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@PlanteretteBlockMarker
|
||||
annotation class CapabilitiesBlockMarker
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@CapabilitiesBlockMarker
|
||||
annotation class DBusBlockMarker
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@PlanteretteBlockMarker
|
||||
annotation class PermissionsBlockMarker
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@PermissionsBlockMarker
|
||||
annotation class FilesystemBlockMarker
|
||||
|
||||
@PlanteretteBlockMarker
|
||||
data class PlanteretteBlock(
|
||||
var metadata: MetadataBlock,
|
||||
var executable: ExecutableBlock,
|
||||
var capabilities: CapabilitiesBlock,
|
||||
var permissions: PermissionsBlock,
|
||||
var buildGuard: Boolean = false
|
||||
) {
|
||||
// NOTE the two underscores are because this function is only intended to be added at runtime
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Suppress("FunctionName")
|
||||
fun __build(args: Array<out String>): PlanteretteBlock {
|
||||
if(!buildGuard) {
|
||||
val manifest = Manifest(metadata.metadata, executable.executable, capabilities.capabilities, permissions.permissions)
|
||||
GoFile.write(args[0].toInt(), Json.encodeToString(manifest))
|
||||
buildGuard = true
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@MetadataBlockMarker
|
||||
data class MetadataBlock(var metadata: Manifest.Metadata)
|
||||
|
||||
@ExecutableBlockMarker
|
||||
data class ExecutableBlock(var executable: Manifest.Executable)
|
||||
|
||||
@CapabilitiesBlockMarker
|
||||
data class CapabilitiesBlock(var capabilities: MutableList<Capability>)
|
||||
|
||||
// TODO add the rest of this
|
||||
@DBusBlockMarker
|
||||
data class DBusBlock(var own: MutableList<String>)
|
||||
|
||||
@PermissionsBlockMarker
|
||||
data class PermissionsBlock(var permissions: MutableList<Capability>)
|
||||
|
||||
@FilesystemBlockMarker
|
||||
class FilesystemBlock(var filesystemPermissions: MutableList<Capability>)
|
||||
|
||||
@PlanteretteBlockMarker
|
||||
fun planterette(init: PlanteretteBlock.() -> Unit) {
|
||||
PlanteretteBlock(MetadataBlock(Manifest.Metadata()), ExecutableBlock(Manifest.Executable()), CapabilitiesBlock(mutableListOf()), PermissionsBlock(mutableListOf())).apply(init)
|
||||
}
|
||||
|
||||
@PlanteretteBlockMarker
|
||||
fun PlanteretteBlock.metadata(init: @MetadataBlockMarker MetadataBlock.() -> Unit) {
|
||||
this.metadata.apply(init)
|
||||
}
|
||||
|
||||
@MetadataBlockMarker
|
||||
fun MetadataBlock.id(id: String) {
|
||||
this.metadata.id = id
|
||||
}
|
||||
|
||||
@MetadataBlockMarker
|
||||
fun MetadataBlock.name(name: String) {
|
||||
this.metadata.name = name
|
||||
}
|
||||
|
||||
@MetadataBlockMarker
|
||||
fun MetadataBlock.description(description: String) {
|
||||
this.metadata.description = description
|
||||
}
|
||||
|
||||
@MetadataBlockMarker
|
||||
fun MetadataBlock.author(author: String) {
|
||||
this.metadata.author = author
|
||||
}
|
||||
|
||||
@MetadataBlockMarker
|
||||
fun MetadataBlock.icon(icon: String) {
|
||||
this.metadata.icon = icon
|
||||
}
|
||||
|
||||
@MetadataBlockMarker
|
||||
fun MetadataBlock.version(number: Int, name: String) {
|
||||
this.metadata.version = number
|
||||
this.metadata.versionName = name
|
||||
}
|
||||
|
||||
@PlanteretteBlockMarker
|
||||
fun PlanteretteBlock.executable(init: @ExecutableBlockMarker ExecutableBlock.() -> Unit) {
|
||||
this.executable.apply(init)
|
||||
}
|
||||
|
||||
@ExecutableBlockMarker
|
||||
fun ExecutableBlock.baseImage(baseImage: String) {
|
||||
this.executable.baseImage = baseImage
|
||||
}
|
||||
|
||||
@ExecutableBlockMarker
|
||||
fun ExecutableBlock.binary(binary: String) {
|
||||
this.executable.binary = binary
|
||||
}
|
||||
|
||||
@ExecutableBlockMarker
|
||||
fun ExecutableBlock.args(vararg args: String) {
|
||||
this.executable.args.addAll(args)
|
||||
}
|
||||
|
||||
@ExecutableBlockMarker
|
||||
fun ExecutableBlock.env(vararg env: Pair<String, String>) {
|
||||
this.executable.env.putAll(env)
|
||||
}
|
||||
|
||||
@PlanteretteBlockMarker
|
||||
fun PlanteretteBlock.capabilities(init: @CapabilitiesBlockMarker CapabilitiesBlock.() -> Unit) {
|
||||
this.capabilities.apply(init)
|
||||
}
|
||||
|
||||
internal fun CapabilitiesBlock.addBasic(id: String) {
|
||||
this.capabilities.add(Capability.Basic(id))
|
||||
}
|
||||
|
||||
@CapabilitiesBlockMarker
|
||||
fun CapabilitiesBlock.portal() {
|
||||
addBasic("moe.rosa.capabilities.Portal")
|
||||
}
|
||||
|
||||
@CapabilitiesBlockMarker
|
||||
fun CapabilitiesBlock.secrets() {
|
||||
addBasic("moe.rosa.capabilities.Secrets")
|
||||
}
|
||||
|
||||
@CapabilitiesBlockMarker
|
||||
fun CapabilitiesBlock.developer() {
|
||||
addBasic("moe.rosa.capabilities.Developer")
|
||||
}
|
||||
|
||||
@CapabilitiesBlockMarker
|
||||
fun CapabilitiesBlock.graphics() {
|
||||
addBasic("moe.rosa.capabilities.Graphics")
|
||||
}
|
||||
|
||||
@CapabilitiesBlockMarker
|
||||
fun CapabilitiesBlock.audio() {
|
||||
addBasic("moe.rosa.capabilities.Audio")
|
||||
}
|
||||
|
||||
@CapabilitiesBlockMarker
|
||||
fun CapabilitiesBlock.dbus(init: @DBusBlockMarker DBusBlock.() -> Unit) {
|
||||
val dbus = DBusBlock(mutableListOf())
|
||||
this.capabilities.add(Capability.DBus("moe.rosa.capabilities.DBus", dbus.own))
|
||||
}
|
||||
|
||||
@PlanteretteBlockMarker
|
||||
fun PlanteretteBlock.permissions(init: @PermissionsBlockMarker PermissionsBlock.() -> Unit) {
|
||||
this.permissions.apply(init)
|
||||
}
|
||||
|
||||
internal fun PermissionsBlock.addBasic(id: String) {
|
||||
this.permissions.add(Capability.Basic(id))
|
||||
}
|
||||
|
||||
@PermissionsBlockMarker
|
||||
fun PermissionsBlock.notifications() {
|
||||
addBasic("moe.rosa.permissions.Notifications")
|
||||
}
|
||||
|
||||
@PermissionsBlockMarker
|
||||
fun PermissionsBlock.filesystem(init: @FilesystemBlockMarker FilesystemBlock.() -> Unit) {
|
||||
this.permissions.addAll(FilesystemBlock(mutableListOf()).apply(init).filesystemPermissions)
|
||||
}
|
||||
|
||||
@FilesystemBlockMarker
|
||||
fun FilesystemBlock.fileManager() {
|
||||
this.filesystemPermissions.add(Capability.Basic("moe.rosa.permissions.filesystem.FileManager"))
|
||||
}
|
||||
|
||||
@FilesystemBlockMarker
|
||||
fun FilesystemBlock.etc() {
|
||||
this.filesystemPermissions.add(Capability.Basic("moe.rosa.permissions.filesystem.Etc"))
|
||||
}
|
||||
|
||||
@FilesystemBlockMarker
|
||||
fun FilesystemBlock.tmp() {
|
||||
this.filesystemPermissions.add(Capability.Basic("moe.rosa.permissions.filesystem.Tmp"))
|
||||
}
|
||||
|
||||
@PermissionsBlockMarker
|
||||
fun PermissionsBlock.network() {
|
||||
addBasic("moe.rosa.permissions.Network")
|
||||
}
|
||||
|
||||
@PermissionsBlockMarker
|
||||
fun PermissionsBlock.system() {
|
||||
addBasic("moe.rosa.permissions.System")
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package moe.rosa.planterette.dsl
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import moe.rosa.planterette.api.Capability
|
||||
import moe.rosa.planterette.api.Manifest
|
||||
import kotlin.script.experimental.annotations.KotlinScript
|
||||
import kotlin.script.experimental.api.*
|
||||
import kotlin.script.experimental.dependencies.*
|
||||
import kotlin.script.experimental.dependencies.maven.MavenDependenciesResolver
|
||||
import kotlin.script.experimental.jvm.JvmDependency
|
||||
import kotlin.script.experimental.jvm.dependenciesFromCurrentContext
|
||||
import kotlin.script.experimental.jvm.jvm
|
||||
|
||||
@KotlinScript(
|
||||
fileExtension = "plt.kts",
|
||||
compilationConfiguration = ScriptWithMavenDepsConfiguration::class
|
||||
)
|
||||
open class PltRecipe
|
||||
|
||||
object ScriptWithMavenDepsConfiguration : ScriptCompilationConfiguration(
|
||||
{
|
||||
defaultImports(DependsOn::class, Repository::class)
|
||||
defaultImports.append("moe.rosa.planterette.dsl.*")
|
||||
jvm {
|
||||
dependenciesFromCurrentContext(
|
||||
"script",
|
||||
"kotlin-scripting-dependencies"
|
||||
)
|
||||
}
|
||||
refineConfiguration {
|
||||
onAnnotations(DependsOn::class, Repository::class, handler = ::configureMavenDepsOnAnnotations)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
private val resolver = CompoundDependenciesResolver(FileSystemDependenciesResolver(), MavenDependenciesResolver())
|
||||
|
||||
fun configureMavenDepsOnAnnotations(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics<ScriptCompilationConfiguration> {
|
||||
val annotations = context.collectedData?.get(ScriptCollectedData.collectedAnnotations)?.takeIf { it.isNotEmpty() }
|
||||
?: return context.compilationConfiguration.asSuccess()
|
||||
return runBlocking {
|
||||
resolver.resolveFromScriptSourceAnnotations(annotations)
|
||||
}.onSuccess {
|
||||
context.compilationConfiguration.with {
|
||||
dependencies.append(JvmDependency(it))
|
||||
}.asSuccess()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package moe.rosa.planterette.jni
|
||||
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
|
||||
object GoFile {
|
||||
external fun write(fd: Int, str: String)
|
||||
init {
|
||||
val libname = "pltbuild.so"
|
||||
val url = GoFile::class.java.getResource("/$libname")
|
||||
val tmpDir = Files.createTempDirectory("pltbuild").toFile()
|
||||
tmpDir.deleteOnExit()
|
||||
val nativeLibTmpFile = File(tmpDir, libname)
|
||||
url?.openStream().use {
|
||||
Files.copy(it!!, nativeLibTmpFile.toPath())
|
||||
}
|
||||
System.load(nativeLibTmpFile.absolutePath)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHelloWorld(t *testing.T) {
|
||||
func TestPltFetch(t *testing.T) {
|
||||
|
||||
}
|
||||
@@ -4,6 +4,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHelloWorld(t *testing.T) {
|
||||
func TestPltPkg(t *testing.T) {
|
||||
|
||||
}
|
||||
@@ -4,6 +4,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHelloWorld(t *testing.T) {
|
||||
func TestPltServer(t *testing.T) {
|
||||
|
||||
}
|
||||
@@ -4,6 +4,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHelloWorld(t *testing.T) {
|
||||
func TestPltUpdated(t *testing.T) {
|
||||
|
||||
}
|
||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module rosa.moe/planterette
|
||||
|
||||
go 1.24.9
|
||||
|
||||
require hakurei.app v0.3.1 // indirect
|
||||
89
gradlew.bat
vendored
89
gradlew.bat
vendored
@@ -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
|
||||
@@ -1,3 +0,0 @@
|
||||
plugins {
|
||||
id("goPlugin")
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
module plt-build-wrapper
|
||||
|
||||
go 1.24
|
||||
@@ -1,30 +0,0 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "2.2.10"
|
||||
kotlin("plugin.serialization") version "2.2.20"
|
||||
}
|
||||
|
||||
group = "moe.rosa"
|
||||
version = "0.1.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(platform("org.junit:junit-bom:5.10.0"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
|
||||
implementation(kotlin("reflect"))
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(24)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
project.layout.buildDirectory.set(file("../build"))
|
||||
@@ -1,2 +0,0 @@
|
||||
package moe.rosa.planterette
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package moe.rosa.planterette
|
||||
|
||||
import moe.rosa.planterette.hakurei.HakureiConfig
|
||||
|
||||
data class PlanteretteConfig(var hakurei: HakureiConfig?)
|
||||
@@ -1,12 +0,0 @@
|
||||
package moe.rosa.planterette.dsl
|
||||
|
||||
import moe.rosa.planterette.PlanteretteConfig
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@DslMarker
|
||||
annotation class PlanteretteDSL
|
||||
|
||||
@PlanteretteDSL
|
||||
fun planterette(init: PlanteretteConfig.() -> Unit): PlanteretteConfig {
|
||||
return PlanteretteConfig(hakurei = null).apply(init)
|
||||
}
|
||||
@@ -1,392 +0,0 @@
|
||||
package moe.rosa.planterette.dsl
|
||||
|
||||
import moe.rosa.planterette.PlanteretteConfig
|
||||
import moe.rosa.planterette.dsl.DSLEnablements.*
|
||||
import moe.rosa.planterette.hakurei.*
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@PlanteretteDSL
|
||||
annotation class HakureiDSL
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@HakureiDSL
|
||||
annotation class DBusDSL
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@HakureiDSL
|
||||
annotation class ExtraPermsDSL
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@HakureiDSL
|
||||
annotation class ContainerDSL
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@ContainerDSL
|
||||
annotation class FilesystemDSL
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@FilesystemDSL
|
||||
annotation class FSBindDSL
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@FilesystemDSL
|
||||
annotation class FSEphemeralDSL
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@FilesystemDSL
|
||||
annotation class FSLinkDSL
|
||||
|
||||
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
|
||||
@FilesystemDSL
|
||||
annotation class FSOverlayDSL
|
||||
|
||||
@PlanteretteDSL
|
||||
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,
|
||||
X11,
|
||||
DBus,
|
||||
Pulse
|
||||
}
|
||||
@HakureiDSL
|
||||
fun HakureiConfig.enable(vararg enablements: DSLEnablements) {
|
||||
val enable = Enablements(wayland = null, x11 = null, dbus = null, pulse = null)
|
||||
enablements.map {
|
||||
when(it) {
|
||||
Wayland -> enable.wayland = true
|
||||
X11 -> enable.x11 = true
|
||||
DBus -> enable.dbus = true
|
||||
Pulse -> enable.pulse = true
|
||||
}
|
||||
}
|
||||
this.enablements = enable
|
||||
}
|
||||
@HakureiDSL
|
||||
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) {
|
||||
this.identity = identity
|
||||
}
|
||||
@HakureiDSL
|
||||
fun HakureiConfig.groups(vararg groups: String) {
|
||||
this.groups = groups.toList()
|
||||
}
|
||||
data class DBusConfigs(var session: DBusConfig? = null, var system: DBusConfig? = null)
|
||||
|
||||
@HakureiDSL
|
||||
fun HakureiConfig.dbus(init: @DBusDSL DBusConfigs.() -> Unit) {
|
||||
val dbus = DBusConfigs().apply(init)
|
||||
this.sessionBus = dbus.session
|
||||
this.systemBus = dbus.system
|
||||
}
|
||||
@DBusDSL
|
||||
fun DBusConfigs.session(init: @DBusDSL DBusConfig.() -> Unit) {
|
||||
this.session = DBusConfig().apply(init)
|
||||
}
|
||||
@DBusDSL
|
||||
fun DBusConfigs.system(init: @DBusDSL DBusConfig.() -> Unit) {
|
||||
this.system = DBusConfig().apply(init)
|
||||
}
|
||||
@DBusDSL
|
||||
fun DBusConfig.see(vararg see: String) {
|
||||
this.see = see.toList()
|
||||
}
|
||||
@DBusDSL
|
||||
fun DBusConfig.talk(vararg talk: String) {
|
||||
this.talk = talk.toList()
|
||||
}
|
||||
@DBusDSL
|
||||
fun DBusConfig.own(vararg own: String) {
|
||||
this.own = own.toList()
|
||||
}
|
||||
@DBusDSL
|
||||
fun DBusConfig.call(vararg call: Pair<String, String>) {
|
||||
this.call = call.toMap()
|
||||
}
|
||||
@DBusDSL
|
||||
fun DBusConfig.broadcast(vararg broadcast: Pair<String, String>) {
|
||||
this.broadcast = broadcast.toMap()
|
||||
}
|
||||
@DBusDSL
|
||||
fun DBusConfig.log(log: Boolean = true) {
|
||||
this.log = log
|
||||
}
|
||||
@DBusDSL
|
||||
fun DBusConfig.filter(filter: Boolean = true) {
|
||||
this.filter = filter
|
||||
}
|
||||
@HakureiDSL
|
||||
fun HakureiConfig.extraPerms(vararg extraPerms: ExtraPermsConfig) {
|
||||
this.extraPerms = extraPerms.toList()
|
||||
}
|
||||
@ExtraPermsDSL
|
||||
fun perm(path: String, init: ExtraPermsConfig.() -> Unit): ExtraPermsConfig {
|
||||
return ExtraPermsConfig(path = AbsolutePath(path)).apply(init)
|
||||
}
|
||||
@ExtraPermsDSL
|
||||
fun perm(path: String, ensure: Boolean? = null, rwx: String): ExtraPermsConfig {
|
||||
if(rwx.length != 3) throw IllegalArgumentException()
|
||||
// TODO(mae): is there a difference between null and false in this case?
|
||||
val read: Boolean? = when(rwx[0]) {
|
||||
'r', 'R' -> true
|
||||
else -> null
|
||||
}
|
||||
val write: Boolean? = when(rwx[1]) {
|
||||
'w', 'W' -> true
|
||||
else -> null
|
||||
}
|
||||
val execute: Boolean? = when(rwx[2]) {
|
||||
'x', 'X' -> true
|
||||
else -> null
|
||||
}
|
||||
return ExtraPermsConfig(ensure, path = AbsolutePath(path), read, write, execute)
|
||||
}
|
||||
@ExtraPermsDSL
|
||||
fun ExtraPermsConfig.ensure(ensure: Boolean = true) {
|
||||
this.ensure = ensure
|
||||
}
|
||||
@ExtraPermsDSL
|
||||
fun ExtraPermsConfig.read(read: Boolean = true) {
|
||||
this.read = read
|
||||
}
|
||||
@ExtraPermsDSL
|
||||
fun ExtraPermsConfig.write(write: Boolean = true) {
|
||||
this.write = write
|
||||
}
|
||||
@ExtraPermsDSL
|
||||
fun ExtraPermsConfig.execute(execute: Boolean = true) {
|
||||
this.execute = execute
|
||||
}
|
||||
|
||||
@HakureiDSL
|
||||
fun HakureiConfig.container(init: @ContainerDSL ContainerConfig.() -> Unit) {
|
||||
this.container = ContainerConfig().apply(init)
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.hostname(hostname: String) {
|
||||
this.hostname = hostname
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.waitDelay(waitDelay: Long) {
|
||||
this.waitDelay = waitDelay
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.noTimeout() {
|
||||
this.waitDelay = -1
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.seccompCompat(seccompCompat: Boolean = true) {
|
||||
this.seccompCompat = seccompCompat
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.devel(devel: Boolean = true) {
|
||||
this.devel = devel
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.userns(userns: Boolean = true) {
|
||||
this.userns = userns
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.hostNet(hostNet: Boolean = true) {
|
||||
this.hostNet = hostNet
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.hostAbstract(hostAbstract: Boolean = true) {
|
||||
this.hostAbstract = hostAbstract
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.tty(tty: Boolean = true) {
|
||||
this.tty = tty
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.multiarch(multiarch: Boolean = true) {
|
||||
this.multiarch = multiarch
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.env(vararg env: Pair<String, String>) {
|
||||
this.env = env.toMap()
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.mapRealUid(mapRealUid: Boolean = true) {
|
||||
this.mapRealUid = mapRealUid
|
||||
}
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.device(device: Boolean = true) {
|
||||
this.device = device
|
||||
}
|
||||
|
||||
@FilesystemDSL
|
||||
data class FilesystemConfigs(val configs: MutableList<FilesystemConfig> = mutableListOf())
|
||||
|
||||
@ContainerDSL
|
||||
fun ContainerConfig.filesystem(init: @FilesystemDSL FilesystemConfigs.() -> Unit) {
|
||||
val config = FilesystemConfigs().apply(init)
|
||||
this.filesystem = config.configs
|
||||
}
|
||||
@FilesystemDSL
|
||||
data class DummyFSBind(var target: String? = null,
|
||||
var source: String? = null,
|
||||
var write: Boolean? = null,
|
||||
var device: Boolean? = null,
|
||||
var ensure: Boolean? = null,
|
||||
var optional: Boolean? = null,
|
||||
var special: Boolean? = null) {
|
||||
fun build(): FSBind {
|
||||
return FSBind(
|
||||
target = if(target != null) { AbsolutePath(target!!) } else null,
|
||||
source = AbsolutePath(source!!),
|
||||
write = write,
|
||||
device = device,
|
||||
ensure = ensure,
|
||||
optional = optional,
|
||||
special = special
|
||||
)
|
||||
}
|
||||
}
|
||||
@FilesystemDSL
|
||||
fun FilesystemConfigs.bind(src2dst: Pair<String, String>, init: @FSBindDSL DummyFSBind.() -> Unit = {}) {
|
||||
val fs = DummyFSBind(target = src2dst.second, source = src2dst.first)
|
||||
fs.apply(init)
|
||||
this.configs.add(fs.build())
|
||||
}
|
||||
@FilesystemDSL
|
||||
fun FilesystemConfigs.bind(source: String, init: @FSBindDSL DummyFSBind.() -> Unit = {}) {
|
||||
val fs = DummyFSBind(source = source)
|
||||
fs.apply(init)
|
||||
this.configs.add(fs.build())
|
||||
}
|
||||
@FSBindDSL
|
||||
fun DummyFSBind.write(write: Boolean? = true) {
|
||||
this.write = write
|
||||
}
|
||||
@FSBindDSL
|
||||
fun DummyFSBind.device(device: Boolean? = true) {
|
||||
this.device = device
|
||||
}
|
||||
@FSBindDSL
|
||||
fun DummyFSBind.ensure(ensure: Boolean? = true) {
|
||||
this.ensure = ensure
|
||||
}
|
||||
@FSBindDSL
|
||||
fun DummyFSBind.optional(optional: Boolean? = true) {
|
||||
this.optional = optional
|
||||
}
|
||||
@FSBindDSL
|
||||
fun DummyFSBind.special(special: Boolean? = true) {
|
||||
this.special = special
|
||||
}
|
||||
@FilesystemDSL
|
||||
data class DummyFSEphemeral(val target: String? = null,
|
||||
var write: Boolean? = null,
|
||||
var size: Int? = null,
|
||||
var perm: Int? = null) {
|
||||
fun build(): FSEphemeral {
|
||||
return FSEphemeral(
|
||||
target = AbsolutePath(target!!),
|
||||
write = write!!,
|
||||
size = size,
|
||||
perm = perm!!
|
||||
)
|
||||
}
|
||||
}
|
||||
@FSEphemeralDSL
|
||||
fun DummyFSEphemeral.write(write: Boolean = true) {
|
||||
this.write = write
|
||||
}
|
||||
@FSEphemeralDSL
|
||||
fun DummyFSEphemeral.size(size: Int) {
|
||||
this.size = size
|
||||
}
|
||||
@FSEphemeralDSL
|
||||
fun DummyFSEphemeral.perm(perm: Int) {
|
||||
this.perm = perm
|
||||
}
|
||||
@FilesystemDSL
|
||||
fun FilesystemConfigs.ephemeral(target: String, init: @FSEphemeralDSL DummyFSEphemeral.() -> Unit = {}) {
|
||||
val fs = DummyFSEphemeral(target = target)
|
||||
fs.apply(init)
|
||||
this.configs.add(fs.build())
|
||||
}
|
||||
@FilesystemDSL
|
||||
data class DummyFSLink(val target: String? = null,
|
||||
val linkname: String? = null,
|
||||
var dereference: Boolean? = null) {
|
||||
fun build(): FSLink {
|
||||
return FSLink(
|
||||
target = AbsolutePath(target!!),
|
||||
linkname = linkname!!,
|
||||
dereference = dereference!!
|
||||
)
|
||||
}
|
||||
}
|
||||
@FSLinkDSL
|
||||
fun DummyFSLink.dereference(dereference: Boolean = true) {
|
||||
this.dereference = dereference
|
||||
}
|
||||
@FilesystemDSL
|
||||
fun FilesystemConfigs.link(lnk2dst: Pair<String, String>, init: @FSLinkDSL DummyFSLink.() -> Unit = {}) {
|
||||
val fs = DummyFSLink(target = lnk2dst.second, linkname = lnk2dst.first)
|
||||
fs.apply(init)
|
||||
this.configs.add(fs.build())
|
||||
}
|
||||
@FilesystemDSL
|
||||
fun FilesystemConfigs.link(target: String, init: @FSLinkDSL DummyFSLink.() -> Unit = {}) {
|
||||
val fs = DummyFSLink(target = target, linkname = target)
|
||||
fs.apply(init)
|
||||
this.configs.add(fs.build())
|
||||
}
|
||||
@FilesystemDSL
|
||||
data class DummyFSOverlay(val target: String? = null,
|
||||
var lower: MutableList<String>? = mutableListOf(),
|
||||
var upper: String? = null,
|
||||
var work: String? = null) {
|
||||
fun build(): FSOverlay {
|
||||
return FSOverlay(
|
||||
target = AbsolutePath(target!!),
|
||||
lower = lower!!.map { AbsolutePath(it)},
|
||||
upper = AbsolutePath(upper!!),
|
||||
work = AbsolutePath(work!!)
|
||||
)
|
||||
}
|
||||
}
|
||||
@FilesystemDSL
|
||||
fun FilesystemConfigs.overlay(target: String, init: @FSOverlayDSL DummyFSOverlay.() -> Unit = {}) {
|
||||
val fs = DummyFSOverlay(target = target)
|
||||
fs.apply(init)
|
||||
this.configs.add(fs.build())
|
||||
}
|
||||
@FSOverlayDSL
|
||||
fun DummyFSOverlay.lower(vararg lower: String) {
|
||||
this.lower!!.addAll(lower.toList())
|
||||
}
|
||||
@FSOverlayDSL
|
||||
fun DummyFSOverlay.upper(upper: String) {
|
||||
this.upper = upper
|
||||
}
|
||||
@FSOverlayDSL
|
||||
fun DummyFSOverlay.work(work: String) {
|
||||
this.work = work
|
||||
}
|
||||
@@ -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<AbsolutePath> {
|
||||
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<AbsolutePath>,
|
||||
val upper: AbsolutePath,
|
||||
val work: AbsolutePath,
|
||||
) : FilesystemConfig
|
||||
@@ -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<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,
|
||||
var username: String? = null,
|
||||
var shell: AbsolutePath? = null,
|
||||
var home: AbsolutePath? = null,
|
||||
|
||||
@SerialName("extra_perms") var extraPerms: List<ExtraPermsConfig>? = null,
|
||||
var identity: Int? = null,
|
||||
var groups: List<String>? = 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<String, String>? = null,
|
||||
|
||||
@SerialName("map_real_uid") var mapRealUid: Boolean? = null,
|
||||
var device: Boolean? = null,
|
||||
|
||||
var filesystem: List<FilesystemConfig>? = 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<String>? = null,
|
||||
var talk: List<String>? = null,
|
||||
var own: List<String>? = null,
|
||||
var call: Map<String, String>? = null,
|
||||
var broadcast: Map<String, String>? = 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,
|
||||
)
|
||||
@@ -1,111 +0,0 @@
|
||||
import moe.rosa.planterette.dsl.*
|
||||
import moe.rosa.planterette.dsl.DSLEnablements.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class DSLTest {
|
||||
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 {
|
||||
talk("org.freedesktop.Notifications",
|
||||
"org.freedesktop.FileManager1",
|
||||
"org.freedesktop.ScreenSaver",
|
||||
"org.freedesktop.secrets",
|
||||
"org.kde.kwalletd5",
|
||||
"org.kde.kwalletd6",
|
||||
"org.gnome.SessionManager")
|
||||
own("org.chromium.Chromium.*",
|
||||
"org.mpris.MediaPlayer2.org.chromium.Chromium.*",
|
||||
"org.mpris.MediaPlayer2.chromium.*")
|
||||
call("org.freedesktop.portal.*" to "*")
|
||||
broadcast("org.freedesktop.portal.*" to "@/org/freedesktop/portal/*")
|
||||
filter()
|
||||
}
|
||||
system {
|
||||
talk("org.bluez",
|
||||
"org.freedesktop.Avahi",
|
||||
"org.freedesktop.UPower")
|
||||
filter()
|
||||
}
|
||||
}
|
||||
username("chronos")
|
||||
shell("/run/current-system/sw/bin/zsh")
|
||||
home("/data/data/org.chromium.Chromium")
|
||||
extraPerms(
|
||||
perm("/var/lib/hakurei/u0") {
|
||||
ensure()
|
||||
execute()
|
||||
},
|
||||
perm("/var/lib/hakurei/u0/org.chromium.Chromium", rwx = "rwx")
|
||||
)
|
||||
identity(9)
|
||||
groups("video",
|
||||
"dialout",
|
||||
"plugdev")
|
||||
container {
|
||||
hostname("localhost")
|
||||
noTimeout()
|
||||
seccompCompat()
|
||||
devel()
|
||||
userns()
|
||||
hostNet()
|
||||
hostAbstract()
|
||||
tty()
|
||||
multiarch()
|
||||
env("GOOGLE_API_KEY" to "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
|
||||
"GOOGLE_DEFAULT_CLIENT_ID" to "77185425430.apps.googleusercontent.com",
|
||||
"GOOGLE_DEFAULT_CLIENT_SECRET" to "OTJgUOQcT7lO7GsGZq2G4IlT")
|
||||
mapRealUid()
|
||||
device()
|
||||
filesystem {
|
||||
bind("/var/lib/hakurei/base/org.debian" to "/") {
|
||||
write()
|
||||
special()
|
||||
}
|
||||
bind("/etc/" to "/etc/") {
|
||||
special()
|
||||
}
|
||||
ephemeral("/tmp/") {
|
||||
write()
|
||||
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")
|
||||
}
|
||||
bind("/nix/store")
|
||||
link("/run/current-system") {
|
||||
dereference()
|
||||
}
|
||||
link("/run/opengl-driver") {
|
||||
dereference()
|
||||
}
|
||||
bind("/var/lib/hakurei/u0/org.chromium.Chromium" to "/data/data/org.chromium.Chromium") {
|
||||
write()
|
||||
ensure()
|
||||
}
|
||||
bind("/dev/dri") {
|
||||
device()
|
||||
optional()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@Test
|
||||
fun hakureiDSLTest() {
|
||||
assertEquals(HakureiTest.TEMPLATE_DATA, HAKUREI_DSL_TEST.hakurei)
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
import moe.rosa.planterette.hakurei.*
|
||||
import org.junit.jupiter.api.assertDoesNotThrow
|
||||
import kotlin.test.*
|
||||
|
||||
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,
|
||||
pulse = true
|
||||
),
|
||||
sessionBus = DBusConfig(
|
||||
see = null,
|
||||
talk = listOf(
|
||||
"org.freedesktop.Notifications",
|
||||
"org.freedesktop.FileManager1",
|
||||
"org.freedesktop.ScreenSaver",
|
||||
"org.freedesktop.secrets",
|
||||
"org.kde.kwalletd5",
|
||||
"org.kde.kwalletd6",
|
||||
"org.gnome.SessionManager"
|
||||
),
|
||||
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/*"
|
||||
),
|
||||
filter = true
|
||||
),
|
||||
systemBus = DBusConfig(
|
||||
see = null,
|
||||
talk = listOf(
|
||||
"org.bluez",
|
||||
"org.freedesktop.Avahi",
|
||||
"org.freedesktop.UPower"
|
||||
),
|
||||
own = null,
|
||||
call = null,
|
||||
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,
|
||||
path = AbsolutePath("/var/lib/hakurei/u0"),
|
||||
read = null,
|
||||
write = null,
|
||||
execute = true,
|
||||
),
|
||||
ExtraPermsConfig(
|
||||
ensure = null,
|
||||
path = AbsolutePath("/var/lib/hakurei/u0/org.chromium.Chromium"),
|
||||
read = true,
|
||||
write = true,
|
||||
execute = true,
|
||||
),
|
||||
),
|
||||
identity = 9,
|
||||
groups = listOf(
|
||||
"video",
|
||||
"dialout",
|
||||
"plugdev"
|
||||
),
|
||||
container = ContainerConfig(
|
||||
hostname = "localhost",
|
||||
waitDelay = -1,
|
||||
seccompCompat = true,
|
||||
devel = true,
|
||||
userns = true,
|
||||
hostNet = true,
|
||||
hostAbstract = true,
|
||||
tty = true,
|
||||
multiarch = true,
|
||||
env = mapOf(
|
||||
"GOOGLE_API_KEY" to "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY",
|
||||
"GOOGLE_DEFAULT_CLIENT_ID" to "77185425430.apps.googleusercontent.com",
|
||||
"GOOGLE_DEFAULT_CLIENT_SECRET" to "OTJgUOQcT7lO7GsGZq2G4IlT"
|
||||
),
|
||||
mapRealUid = true,
|
||||
device = true,
|
||||
filesystem = listOf(
|
||||
FSBind(
|
||||
target = AbsolutePath("/"),
|
||||
source = AbsolutePath("/var/lib/hakurei/base/org.debian"),
|
||||
write = true,
|
||||
special = true,
|
||||
),
|
||||
FSBind(
|
||||
target = AbsolutePath("/etc/"),
|
||||
source = AbsolutePath("/etc/"),
|
||||
special = true,
|
||||
),
|
||||
FSEphemeral(
|
||||
target = AbsolutePath("/tmp/"),
|
||||
write = true,
|
||||
perm = 493
|
||||
),
|
||||
FSOverlay(
|
||||
target = AbsolutePath("/nix/store"),
|
||||
lower = listOf(
|
||||
AbsolutePath("/mnt-root/nix/.ro-store")
|
||||
),
|
||||
upper = AbsolutePath("/mnt-root/nix/.rw-store/upper"),
|
||||
work = AbsolutePath("/mnt-root/nix/.rw-store/work")
|
||||
),
|
||||
FSBind(
|
||||
source = AbsolutePath("/nix/store")
|
||||
),
|
||||
FSLink(
|
||||
target = AbsolutePath("/run/current-system"),
|
||||
linkname = "/run/current-system",
|
||||
dereference = true
|
||||
),
|
||||
FSLink(
|
||||
target = AbsolutePath("/run/opengl-driver"),
|
||||
linkname = "/run/opengl-driver",
|
||||
dereference = true
|
||||
),
|
||||
FSBind(
|
||||
target = AbsolutePath("/data/data/org.chromium.Chromium"),
|
||||
source = AbsolutePath("/var/lib/hakurei/u0/org.chromium.Chromium"),
|
||||
write = true,
|
||||
ensure = true,
|
||||
),
|
||||
FSBind(
|
||||
source = AbsolutePath("/dev/dri"),
|
||||
device = true,
|
||||
optional = true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
val TEMPLATE_JSON = ProcessBuilder("hakurei", "template")
|
||||
.start()
|
||||
.inputStream
|
||||
.readAllBytes()
|
||||
.toString(Charsets.UTF_8)
|
||||
val format = Json {
|
||||
prettyPrint = true
|
||||
ignoreUnknownKeys = true
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Test
|
||||
fun deserializeTest() {
|
||||
|
||||
println(TEMPLATE_JSON)
|
||||
val want = format.decodeFromString<HakureiConfig>(TEMPLATE_JSON)
|
||||
assertEquals(TEMPLATE_DATA, want)
|
||||
}
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
@Test
|
||||
fun serializeTest() {
|
||||
val encoded = format.encodeToString(TEMPLATE_DATA)
|
||||
val decoded = format.decodeFromString<HakureiConfig>(encoded)
|
||||
assertEquals(TEMPLATE_DATA, decoded)
|
||||
}
|
||||
@Test
|
||||
fun absolutePathTest() {
|
||||
assertDoesNotThrow {
|
||||
AbsolutePath("/test/absolutepath")
|
||||
}
|
||||
assertFailsWith(AbsolutePathException::class) {
|
||||
AbsolutePath("./../../../../")
|
||||
}
|
||||
assertEquals(AbsolutePath("/test/absolutepath"), AbsolutePath("/test/") + "absolutepath")
|
||||
}
|
||||
@Test
|
||||
fun extraPermsTest() {
|
||||
assertIs<String>(TEMPLATE_DATA.extraPerms.toString())
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
plugins {
|
||||
id("goPlugin")
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
module plt-fetch
|
||||
|
||||
go 1.24
|
||||
@@ -1,3 +0,0 @@
|
||||
plugins {
|
||||
id("goPlugin")
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
module plt-pkg
|
||||
|
||||
go 1.24
|
||||
@@ -1,3 +0,0 @@
|
||||
plugins {
|
||||
id("goPlugin")
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
module plt-server
|
||||
|
||||
go 1.24
|
||||
@@ -1,3 +0,0 @@
|
||||
plugins {
|
||||
id("goPlugin")
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
module plt-updated
|
||||
|
||||
go 1.24
|
||||
@@ -1,9 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHelloWorld(t *testing.T) {
|
||||
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
include("plt-build")
|
||||
include("plt-build-wrapper")
|
||||
include("plt-fetch")
|
||||
include("plt-pkg")
|
||||
include("plt-server")
|
||||
include("plt-updated")
|
||||
Reference in New Issue
Block a user