Merge pull request 'kt-go-split' (#1) from kt-go-split into main
Reviewed-on: #1
This commit is contained in:
		
						commit
						3efcca8651
					
				
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,12 +1,14 @@ | |||||||
| .gradle | .gradle | ||||||
| gradlew.bat |  | ||||||
| build/ | build/ | ||||||
| !gradle/wrapper/gradle-wrapper.jar | !gradle/wrapper/gradle-wrapper.jar | ||||||
| !**/src/main/**/build/ | !**/src/main/**/build/ | ||||||
| !**/src/test/**/build/ | !**/src/test/**/build/ | ||||||
| 
 | 
 | ||||||
| ### IntelliJ IDEA ### | ### IntelliJ IDEA ### | ||||||
| /.idea/ | .idea/modules.xml | ||||||
|  | .idea/jarRepositories.xml | ||||||
|  | .idea/compiler.xml | ||||||
|  | .idea/libraries/ | ||||||
| *.iws | *.iws | ||||||
| *.iml | *.iml | ||||||
| *.ipr | *.ipr | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,5 +1,3 @@ | |||||||
| # Default ignored files | # Default ignored files | ||||||
| /shelf/ | /shelf/ | ||||||
| /workspace.xml | /workspace.xml | ||||||
| # Environment-dependent path to Maven home directory |  | ||||||
| /mavenHomeManager.xml |  | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								.idea/codeStyles/Project.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | <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
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								.idea/codeStyles/codeStyleConfig.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | <component name="ProjectCodeStyleConfiguration"> | ||||||
|  |   <state> | ||||||
|  |     <option name="USE_PER_PROJECT_SETTINGS" value="true" /> | ||||||
|  |   </state> | ||||||
|  | </component> | ||||||
							
								
								
									
										8
									
								
								.idea/dictionaries/project.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								.idea/dictionaries/project.xml
									
									
									
										generated
									
									
									
								
							| @ -1,8 +0,0 @@ | |||||||
| <component name="ProjectDictionaryState"> |  | ||||||
|   <dictionary name="project"> |  | ||||||
|     <words> |  | ||||||
|       <w>buildsrc</w> |  | ||||||
|       <w>planterette</w> |  | ||||||
|     </words> |  | ||||||
|   </dictionary> |  | ||||||
| </component> |  | ||||||
							
								
								
									
										17
									
								
								.idea/gradle.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										17
									
								
								.idea/gradle.xml
									
									
									
										generated
									
									
									
								
							| @ -4,28 +4,11 @@ | |||||||
|   <component name="GradleSettings"> |   <component name="GradleSettings"> | ||||||
|     <option name="linkedExternalProjectsSettings"> |     <option name="linkedExternalProjectsSettings"> | ||||||
|       <GradleProjectSettings> |       <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="externalProjectPath" value="$PROJECT_DIR$" /> | ||||||
|         <option name="gradleHome" value="" /> |         <option name="gradleHome" value="" /> | ||||||
|         <option name="modules"> |         <option name="modules"> | ||||||
|           <set> |           <set> | ||||||
|             <option value="$PROJECT_DIR$" /> |             <option value="$PROJECT_DIR$" /> | ||||||
|             <option value="$PROJECT_DIR$/buildSrc" /> |  | ||||||
|             <option value="$PROJECT_DIR$/libplt" /> |  | ||||||
|             <option value="$PROJECT_DIR$/plt" /> |  | ||||||
|             <option value="$PROJECT_DIR$/plt-build" /> |  | ||||||
|             <option value="$PROJECT_DIR$/plt-fetch" /> |  | ||||||
|             <option value="$PROJECT_DIR$/plt-pkg" /> |  | ||||||
|           </set> |           </set> | ||||||
|         </option> |         </option> | ||||||
|       </GradleProjectSettings> |       </GradleProjectSettings> | ||||||
|  | |||||||
							
								
								
									
										6
									
								
								.idea/kotlinc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/kotlinc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="KotlinJpsPluginSettings"> | ||||||
|  |     <option name="version" value="2.2.20" /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
							
								
								
									
										5
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										5
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							| @ -1,7 +1,10 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <project version="4"> | <project version="4"> | ||||||
|   <component name="ExternalStorageConfigurationManager" enabled="true" /> |   <component name="ExternalStorageConfigurationManager" enabled="true" /> | ||||||
|   <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK"> |   <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" /> |     <output url="file://$PROJECT_DIR$/out" /> | ||||||
|   </component> |   </component> | ||||||
| </project> | </project> | ||||||
							
								
								
									
										21
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								README.md
									
									
									
									
									
								
							| @ -1,23 +1,2 @@ | |||||||
| # planterette | # planterette | ||||||
| 
 | 
 | ||||||
| This project uses [Gradle](https://gradle.org/). |  | ||||||
| To build and run the application, use the *Gradle* tool window by clicking the Gradle icon in the right-hand toolbar, |  | ||||||
| or run it directly from the terminal: |  | ||||||
| 
 |  | ||||||
| * Run `./gradlew run` to build and run the application. |  | ||||||
| * Run `./gradlew build` to only build the application. |  | ||||||
| * Run `./gradlew check` to run all checks, including tests. |  | ||||||
| * Run `./gradlew clean` to clean all build outputs. |  | ||||||
| 
 |  | ||||||
| Note the usage of the Gradle Wrapper (`./gradlew`). |  | ||||||
| This is the suggested way to use Gradle in production projects. |  | ||||||
| 
 |  | ||||||
| [Learn more about the Gradle Wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html). |  | ||||||
| 
 |  | ||||||
| [Learn more about Gradle tasks](https://docs.gradle.org/current/userguide/command_line_interface.html#common_tasks). |  | ||||||
| 
 |  | ||||||
| This project follows the suggested multi-module setup and consists of the `app` and `api` subprojects. |  | ||||||
| The shared build logic was extracted to a convention plugin located in `buildSrc`. |  | ||||||
| 
 |  | ||||||
| This project uses a version catalog (see `gradle/libs.versions.toml`) to declare and version dependencies |  | ||||||
| and both a build cache and a configuration cache (see `gradle.properties`). |  | ||||||
							
								
								
									
										46
									
								
								build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | |||||||
|  | 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(kotlin("test")) | ||||||
|  |     implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0") | ||||||
|  |     implementation(kotlin("reflect")) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | tasks.test { | ||||||
|  |     useJUnitPlatform() | ||||||
|  |     dependsOn(testGo) | ||||||
|  | } | ||||||
|  | tasks.classes { | ||||||
|  |     dependsOn(compileGo) | ||||||
|  | } | ||||||
|  | kotlin { | ||||||
|  |     jvmToolchain(24) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | val compileGo = tasks.register("compileGo", Exec::class) { | ||||||
|  |     group = "go" | ||||||
|  |     workingDir(layout.projectDirectory) | ||||||
|  |     commandLine("/usr/bin/go", "build", "-o", "build/go/planterette", "./src/main/go") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | val testGo = tasks.register("testGo", Exec::class) { | ||||||
|  |     group = "go" | ||||||
|  |     workingDir(layout.projectDirectory) | ||||||
|  |     commandLine("/usr/bin/go", "test", "./src/main/go") | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | val runGo = tasks.register("runGo", Exec::class) { | ||||||
|  |     group = "go" | ||||||
|  |     workingDir(layout.projectDirectory) | ||||||
|  |     commandLine("/usr/bin/go", "run", "./src/main/go") | ||||||
|  | } | ||||||
| @ -1,11 +0,0 @@ | |||||||
| plugins { |  | ||||||
|     `kotlin-dsl` |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| kotlin { |  | ||||||
|     jvmToolchain(24) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| dependencies { |  | ||||||
|     implementation(libs.kotlinGradlePlugin) |  | ||||||
| } |  | ||||||
| @ -1,15 +0,0 @@ | |||||||
| dependencyResolutionManagement { |  | ||||||
| 
 |  | ||||||
|     @Suppress("UnstableApiUsage") |  | ||||||
|     repositories { |  | ||||||
|         mavenCentral() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     versionCatalogs { |  | ||||||
|         create("libs") { |  | ||||||
|             from(files("../gradle/libs.versions.toml")) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| rootProject.name = "buildSrc" |  | ||||||
| @ -1,15 +0,0 @@ | |||||||
| package buildsrc.convention |  | ||||||
| 
 |  | ||||||
| import org.gradle.api.tasks.testing.logging.TestLogEvent |  | ||||||
| 
 |  | ||||||
| tasks.withType<Test>().configureEach { |  | ||||||
|     useJUnitPlatform() |  | ||||||
| 
 |  | ||||||
|     testLogging { |  | ||||||
|         events( |  | ||||||
|             TestLogEvent.FAILED, |  | ||||||
|             TestLogEvent.PASSED, |  | ||||||
|             TestLogEvent.SKIPPED |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,3 +1 @@ | |||||||
| org.gradle.caching=true | kotlin.code.style=official | ||||||
| org.gradle.configuration-cache=true |  | ||||||
| kotlin.mpp.applyDefaultHierarchyTemplate=false |  | ||||||
|  | |||||||
| @ -1,20 +0,0 @@ | |||||||
| [versions] |  | ||||||
| kotlin = "2.2.0" |  | ||||||
| kotlinxDatetime = "0.6.1" |  | ||||||
| kotlinxSerializationJSON = "1.7.3" |  | ||||||
| kotlinxCoroutines = "1.9.0" |  | ||||||
| kotlinxIo = "0.8.0" |  | ||||||
| 
 |  | ||||||
| [libraries] |  | ||||||
| kotlinGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } |  | ||||||
| kotlinxDatetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" } |  | ||||||
| kotlinxSerialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJSON" } |  | ||||||
| kotlinxCoroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } |  | ||||||
| kotlinxIo = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinxIo" } |  | ||||||
| 
 |  | ||||||
| [bundles] |  | ||||||
| kotlinxEcosystem = ["kotlinxDatetime", "kotlinxSerialization", "kotlinxCoroutines", "kotlinxIo"] |  | ||||||
| 
 |  | ||||||
| [plugins] |  | ||||||
| kotlinPluginSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } |  | ||||||
| kotlinPluginMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } |  | ||||||
							
								
								
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gradle/wrapper/gradle-wrapper.jar
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							| @ -1,4 +1,4 @@ | |||||||
| #Tue Aug 26 23:15:20 CDT 2025 | #Fri Sep 26 23:47:58 CDT 2025 | ||||||
| distributionBase=GRADLE_USER_HOME | distributionBase=GRADLE_USER_HOME | ||||||
| distributionPath=wrapper/dists | distributionPath=wrapper/dists | ||||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip | ||||||
|  | |||||||
							
								
								
									
										43
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										43
									
								
								gradlew
									
									
									
									
										vendored
									
									
								
							| @ -15,8 +15,6 @@ | |||||||
| # See the License for the specific language governing permissions and | # See the License for the specific language governing permissions and | ||||||
| # limitations under the License. | # limitations under the License. | ||||||
| # | # | ||||||
| # SPDX-License-Identifier: Apache-2.0 |  | ||||||
| # |  | ||||||
| 
 | 
 | ||||||
| ############################################################################## | ############################################################################## | ||||||
| # | # | ||||||
| @ -57,7 +55,7 @@ | |||||||
| #       Darwin, MinGW, and NonStop. | #       Darwin, MinGW, and NonStop. | ||||||
| # | # | ||||||
| #   (3) This script is generated from the Groovy template | #   (3) This script is generated from the Groovy template | ||||||
| #       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt | #       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt | ||||||
| #       within the Gradle project. | #       within the Gradle project. | ||||||
| # | # | ||||||
| #       You can find Gradle at https://github.com/gradle/gradle/. | #       You can find Gradle at https://github.com/gradle/gradle/. | ||||||
| @ -82,11 +80,13 @@ do | |||||||
|     esac |     esac | ||||||
| done | done | ||||||
| 
 | 
 | ||||||
| # This is normally unused | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit | ||||||
| # shellcheck disable=SC2034 | 
 | ||||||
|  | APP_NAME="Gradle" | ||||||
| APP_BASE_NAME=${0##*/} | APP_BASE_NAME=${0##*/} | ||||||
| # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) | 
 | ||||||
| APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | ||||||
|  | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | ||||||
| 
 | 
 | ||||||
| # Use the maximum available, or set MAX_FD != -1 to use that value. | # Use the maximum available, or set MAX_FD != -1 to use that value. | ||||||
| MAX_FD=maximum | MAX_FD=maximum | ||||||
| @ -133,29 +133,22 @@ location of your Java installation." | |||||||
|     fi |     fi | ||||||
| else | else | ||||||
|     JAVACMD=java |     JAVACMD=java | ||||||
|     if ! command -v java >/dev/null 2>&1 |     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | ||||||
|     then |  | ||||||
|         die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |  | ||||||
| 
 | 
 | ||||||
| Please set the JAVA_HOME variable in your environment to match the | Please set the JAVA_HOME variable in your environment to match the | ||||||
| location of your Java installation." | location of your Java installation." | ||||||
|     fi |  | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| # Increase the maximum file descriptors if we can. | # Increase the maximum file descriptors if we can. | ||||||
| if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then | ||||||
|     case $MAX_FD in #( |     case $MAX_FD in #( | ||||||
|       max*) |       max*) | ||||||
|         # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. |  | ||||||
|         # shellcheck disable=SC2039,SC3045 |  | ||||||
|         MAX_FD=$( ulimit -H -n ) || |         MAX_FD=$( ulimit -H -n ) || | ||||||
|             warn "Could not query maximum file descriptor limit" |             warn "Could not query maximum file descriptor limit" | ||||||
|     esac |     esac | ||||||
|     case $MAX_FD in  #( |     case $MAX_FD in  #( | ||||||
|       '' | soft) :;; #( |       '' | soft) :;; #( | ||||||
|       *) |       *) | ||||||
|         # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. |  | ||||||
|         # shellcheck disable=SC2039,SC3045 |  | ||||||
|         ulimit -n "$MAX_FD" || |         ulimit -n "$MAX_FD" || | ||||||
|             warn "Could not set maximum file descriptor limit to $MAX_FD" |             warn "Could not set maximum file descriptor limit to $MAX_FD" | ||||||
|     esac |     esac | ||||||
| @ -200,15 +193,11 @@ if "$cygwin" || "$msys" ; then | |||||||
|     done |     done | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| 
 | # Collect all arguments for the java command; | ||||||
| # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | #   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of | ||||||
| DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | #     shell script including quotes and variable substitutions, so put them in | ||||||
| 
 | #     double quotes to make sure that they get re-expanded; and | ||||||
| # Collect all arguments for the java command: | #   * put everything else in single quotes, so that it's not re-expanded. | ||||||
| #   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, |  | ||||||
| #     and any embedded shellness will be escaped. |  | ||||||
| #   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be |  | ||||||
| #     treated as '${Hostname}' itself on the command line. |  | ||||||
| 
 | 
 | ||||||
| set -- \ | set -- \ | ||||||
|         "-Dorg.gradle.appname=$APP_BASE_NAME" \ |         "-Dorg.gradle.appname=$APP_BASE_NAME" \ | ||||||
| @ -216,12 +205,6 @@ set -- \ | |||||||
|         org.gradle.wrapper.GradleWrapperMain \ |         org.gradle.wrapper.GradleWrapperMain \ | ||||||
|         "$@" |         "$@" | ||||||
| 
 | 
 | ||||||
| # Stop when "xargs" is not available. |  | ||||||
| if ! command -v xargs >/dev/null 2>&1 |  | ||||||
| then |  | ||||||
|     die "xargs is not available" |  | ||||||
| fi |  | ||||||
| 
 |  | ||||||
| # Use "xargs" to parse quoted args. | # Use "xargs" to parse quoted args. | ||||||
| # | # | ||||||
| # With -n1 it outputs one arg per line, with the quotes and backslashes removed. | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. | ||||||
|  | |||||||
							
								
								
									
										89
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								gradlew.bat
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,89 @@ | |||||||
|  | @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,26 +0,0 @@ | |||||||
| plugins { |  | ||||||
|     kotlin("multiplatform") |  | ||||||
|     alias(libs.plugins.kotlinPluginSerialization) |  | ||||||
| } |  | ||||||
| kotlin { |  | ||||||
|     jvm() |  | ||||||
|     val nativeTarget = if(System.getProperty("os.arch") == "aarch64") { |  | ||||||
|         linuxArm64("native") |  | ||||||
|     } else { |  | ||||||
|         linuxX64("native") |  | ||||||
|     } |  | ||||||
|     nativeTarget.binaries { |  | ||||||
|         sharedLib { |  | ||||||
|             baseName = "libplt" |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     sourceSets { |  | ||||||
|         commonMain.dependencies { |  | ||||||
|             implementation(libs.bundles.kotlinxEcosystem) |  | ||||||
|         } |  | ||||||
|         commonTest.dependencies { |  | ||||||
|             implementation(libs.bundles.kotlinxEcosystem) |  | ||||||
|             implementation(kotlin("test")) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,51 +0,0 @@ | |||||||
| package app.hakurei.planterette.api |  | ||||||
| 
 |  | ||||||
| import app.hakurei.planterette.api.AbsolutePath.Companion.isAbsolute |  | ||||||
| import kotlinx.io.files.Path |  | ||||||
| import kotlinx.serialization.KSerializer |  | ||||||
| import kotlinx.serialization.Serializable |  | ||||||
| import kotlinx.serialization.Serializer |  | ||||||
| import kotlinx.serialization.Transient |  | ||||||
| import kotlinx.serialization.descriptors.PrimitiveKind |  | ||||||
| import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor |  | ||||||
| import kotlinx.serialization.descriptors.SerialDescriptor |  | ||||||
| import kotlinx.serialization.encoding.Decoder |  | ||||||
| import kotlinx.serialization.encoding.Encoder |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * 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(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(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") |  | ||||||
| @ -1,209 +0,0 @@ | |||||||
| package app.hakurei.planterette.api |  | ||||||
| 
 |  | ||||||
| import kotlinx.serialization.SerialName |  | ||||||
| import kotlinx.serialization.Serializable |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * HakureiConfig is used to seal an app implementation. |  | ||||||
|  * @param id reverse-DNS style arbitrary identifier string from config; Passed to wayland security-context-v1 as application ID and used as part of defaults in dbus session proxy |  | ||||||
|  * |  | ||||||
|  * @param path absolute path to executable file |  | ||||||
|  * @param args final args passed to container init |  | ||||||
|  * |  | ||||||
|  * @param enablements system services to make available in the container |  | ||||||
|  * |  | ||||||
|  * @param sessionBus session D-Bus proxy configuration; null makes session bus proxy assume built-in defaults |  | ||||||
|  * @param systemBus system D-Bus proxy configuration; null disables system bus proxy |  | ||||||
|  * @param directWayland direct access to the wayland socket; when this gets set no attempt is made to attach security-context-v1 and the bare socket is mounted to the sandbox |  | ||||||
|  * |  | ||||||
|  * @param username passwd username in container, defaults to passwd name of target uid or chronos |  | ||||||
|  * @param shell absolute path to shell |  | ||||||
|  * @param data absolute path to home directory in the init mount namespace |  | ||||||
|  * @param dir directory to ender and use as home in the container mount namespace, null for Data |  | ||||||
|  * @param extraPerms extra ACL ops, dispatches before container init |  | ||||||
|  * |  | ||||||
|  * @param identity numerical application id, used for init user namespace credentials |  | ||||||
|  * @param groups list of supplementary groups inherited by container processes |  | ||||||
|  * |  | ||||||
|  * @param container abstract container configuration baseline |  | ||||||
|  */ |  | ||||||
| @Serializable |  | ||||||
| data class HakureiConfig( |  | ||||||
|     val id: String, |  | ||||||
| 
 |  | ||||||
|     val path: AbsolutePath? = null, |  | ||||||
|     val args: List<String>, |  | ||||||
| 
 |  | ||||||
|     val enablements: Byte, |  | ||||||
| 
 |  | ||||||
|     @SerialName("session_bus") val sessionBus: DbusConfig? = null, |  | ||||||
|     @SerialName("system_bus") val systemBus: DbusConfig? = null, |  | ||||||
|     @SerialName("direct_wayland") val directWayland: Boolean? = null, |  | ||||||
| 
 |  | ||||||
|     val username: String? = null, |  | ||||||
|     val shell: AbsolutePath, |  | ||||||
|     val data: AbsolutePath, |  | ||||||
|     val dir: AbsolutePath, |  | ||||||
|     @SerialName("extra_perms") val extraPerms: List<ExtraPermConfig>? = null, |  | ||||||
| 
 |  | ||||||
|     val identity: Int, |  | ||||||
|     val groups: List<String>, |  | ||||||
| 
 |  | ||||||
|     val container: ContainerConfig |  | ||||||
| ) { |  | ||||||
|     @Serializable |  | ||||||
|     data class DbusConfig( |  | ||||||
|         val see: List<String>? = null, |  | ||||||
|         val talk: List<String>? = null, |  | ||||||
|         val own: List<String>? = null, |  | ||||||
|         val call: Map<String, String>? = null, |  | ||||||
|         val broadcast: Map<String, String>? = null, |  | ||||||
| 
 |  | ||||||
|         val log: Boolean? = null, |  | ||||||
|         val filter: Boolean |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * ExtraPermConfig describes an ACL update op. |  | ||||||
|      */ |  | ||||||
|     @Serializable |  | ||||||
|     data class ExtraPermConfig( |  | ||||||
|         val ensure: Boolean? = null, |  | ||||||
|         val path: AbsolutePath, |  | ||||||
|         @SerialName("r") val read: Boolean? = null, |  | ||||||
|         @SerialName("w") val write: Boolean? = null, |  | ||||||
|         @SerialName("x") val execute: Boolean? = null |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * ContainerConfig describes the container configuration baseline to which the app implementation adds upon. |  | ||||||
|      * @param hostname container hostname |  | ||||||
|      * @param waitDelay duration to wait for after interrupting a container's initial process in nanoseconds; a negative value causes the container to be terminated immediately on cancellation |  | ||||||
|      * @param seccompFlags extra seccomp flags |  | ||||||
|      * @param seccompPresets extra seccomp presets |  | ||||||
|      * @param seccompCompat disable project-specific filter extensions |  | ||||||
|      * @param devel allow ptrace and friends |  | ||||||
|      * @param userns allow userns creation in container |  | ||||||
|      * @param net share host net namespace |  | ||||||
|      * @param tty allow dangerous terminal I/O |  | ||||||
|      * @param multiarch allow multiarch |  | ||||||
|      * |  | ||||||
|      * @param env initial process environment variables |  | ||||||
|      * @param mapRealUid map target user uid to privileged user uid in the user namespace |  | ||||||
|      * |  | ||||||
|      * @param device pass through all devices |  | ||||||
|      * @param filesystem container host filesystem bind mounts |  | ||||||
|      * @param link create symlinks inside container filesystem |  | ||||||
|      * |  | ||||||
|      * @param autoRoot automatically bind mount top-level directories to container root; the zero value disables this behaviour |  | ||||||
|      * @param rootFlags extra flags for AutoRoot |  | ||||||
|      * |  | ||||||
|      * @param etc read-only /etc directory |  | ||||||
|      * @param autoEtc automatically set up /etc symlinks |  | ||||||
|      */ |  | ||||||
|     @Serializable |  | ||||||
|     data class ContainerConfig( |  | ||||||
|         val hostname: String?, |  | ||||||
|         @SerialName("wait_delay") val waitDelay: Int? = null, |  | ||||||
|         @SerialName("seccomp_flags") val seccompFlags: Int, |  | ||||||
|         @SerialName("seccomp_presets") val seccompPresets: Int, |  | ||||||
|         @SerialName("seccomp_compat") val seccompCompat: Boolean? = null, |  | ||||||
| 
 |  | ||||||
|         val devel: Boolean? = null, |  | ||||||
|         val userns: Boolean? = null, |  | ||||||
|         val net: Boolean? = null, |  | ||||||
|         val tty: Boolean? = null, |  | ||||||
|         val multiarch: Boolean? = null, |  | ||||||
| 
 |  | ||||||
|         val env: Map<String, String>, |  | ||||||
|         @SerialName("map_real_uid") val mapRealUid: Boolean, |  | ||||||
| 
 |  | ||||||
|         val device: Boolean?, |  | ||||||
|         val filesystem: List<FilesystemConfig>, |  | ||||||
|         @SerialName("symlink") val link: List<LinkConfig>, |  | ||||||
| 
 |  | ||||||
|         @SerialName("auto_root") val autoRoot: AbsolutePath, |  | ||||||
|         @SerialName("root_flags") val rootFlags: Int, |  | ||||||
| 
 |  | ||||||
|         val etc: AbsolutePath?, |  | ||||||
| 
 |  | ||||||
|         @SerialName("auto_etc") val autoEtc: Boolean, |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * FilesystemConfig is an abstract representation of a bind mount. |  | ||||||
|      * @param dst mount point in container, same as src if empty |  | ||||||
|      * @param src host filesystem path to make available to the container |  | ||||||
|      * @param write do not mount filesystem read-only |  | ||||||
|      * @param device do not disable device files |  | ||||||
|      * @param must fail if the bind mount cannot be established for any reason |  | ||||||
|      */ |  | ||||||
|     @Serializable |  | ||||||
|     data class FilesystemConfig( |  | ||||||
|         val dst: AbsolutePath? = null, |  | ||||||
|         val src: AbsolutePath, |  | ||||||
|         val write: Boolean? = null, |  | ||||||
|         @SerialName("dev") val device: Boolean? = null, |  | ||||||
|         @SerialName("require") val must: Boolean? = null |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @param target symlink target in container |  | ||||||
|      * @param linkname linkname the symlink points to; prepend '*' to dereference an absolute pathname on host |  | ||||||
|      */ |  | ||||||
|     @Serializable |  | ||||||
|     data class LinkConfig( |  | ||||||
|         val target: AbsolutePath, |  | ||||||
|         val linkname: String |  | ||||||
|     ) |  | ||||||
|     enum class Enablement(val value: Int) { |  | ||||||
|         Wayland(1 shl 0), |  | ||||||
|         X11(1 shl 1), |  | ||||||
|         DBus(1 shl 2), |  | ||||||
|         Pulse(1 shl 3); |  | ||||||
| 
 |  | ||||||
|         companion object { |  | ||||||
|             fun enablements(vararg enablements: Enablement): Byte { |  | ||||||
|                 return enablements.orOf(Enablement::value).toByte() |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     enum class SeccompFilterPreset(val value: Int) { |  | ||||||
|         Ext(1 shl 0), |  | ||||||
|         DenyNS(1 shl 1), |  | ||||||
|         DenyTTY(1 shl 2), |  | ||||||
|         DenyDevel(1 shl 3), |  | ||||||
|         Linux32(1 shl 4); |  | ||||||
| 
 |  | ||||||
|         companion object { |  | ||||||
|             fun filterPresets(vararg filterPresets: SeccompFilterPreset): Int { |  | ||||||
|                 return filterPresets.orOf(SeccompFilterPreset::value) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     enum class HakureiExportFlag(val value: Int) { |  | ||||||
|         Multiarch(1 shl 0), |  | ||||||
|         CAN(1 shl 1), |  | ||||||
|         Bluetooth(1 shl 2); |  | ||||||
| 
 |  | ||||||
|         companion object { |  | ||||||
|             fun exportFlags(vararg exportFlags: HakureiExportFlag): Int { |  | ||||||
|                 return exportFlags.orOf(HakureiExportFlag::value) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     enum class RootFlag(val value: Int) { |  | ||||||
|         Optional(1 shl 0), |  | ||||||
|         Writable(1 shl 1), |  | ||||||
|         Device(1 shl 2); |  | ||||||
| 
 |  | ||||||
|         companion object { |  | ||||||
|             fun rootFlags(vararg rootFlags: RootFlag): Int { |  | ||||||
|                 return rootFlags.orOf(RootFlag::value) |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,35 +0,0 @@ | |||||||
| package app.hakurei.planterette.api |  | ||||||
| 
 |  | ||||||
| import kotlinx.serialization.Serializable |  | ||||||
| 
 |  | ||||||
| @Serializable |  | ||||||
| data class PackageManifest( |  | ||||||
|     var hakureiConfig: HakureiConfig, |  | ||||||
|     var id: String, |  | ||||||
|     var version: Version, |  | ||||||
|     var name: String, |  | ||||||
|     var description: String, |  | ||||||
|     var architecture: MutableList<Architecture>, |  | ||||||
|     var baseImage: BaseImage |  | ||||||
| ) { |  | ||||||
|     @Serializable |  | ||||||
|     data class Version(val version: String, val canonicalVersion: UInt) |  | ||||||
| 
 |  | ||||||
|     @Serializable |  | ||||||
|     enum class Architecture { |  | ||||||
|         X86_64, |  | ||||||
|         ARM64, |  | ||||||
|     } |  | ||||||
|     @Serializable |  | ||||||
|     data class BaseImage(val type: Type) { |  | ||||||
|         @Serializable |  | ||||||
|         enum class Type { |  | ||||||
|             DEBIAN, |  | ||||||
|             CHIMERA, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     companion object { |  | ||||||
|         val fileList = listOf("planterette.json", "icon.png", "image.tar") |  | ||||||
|         val signatureFile = "package.sig" |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,48 +0,0 @@ | |||||||
| package app.hakurei.planterette.api |  | ||||||
| 
 |  | ||||||
| @Suppress("unused") |  | ||||||
| object Paths { |  | ||||||
|     const val FHS_ROOT = "/" |  | ||||||
|     const val FHS_ETC = "/etc/" |  | ||||||
|     const val FHS_TMP = "/tmp/" |  | ||||||
| 
 |  | ||||||
|     const val FHS_RUN = "/run/" |  | ||||||
|     const val FHS_RUN_USER = FHS_RUN + "user/" |  | ||||||
| 
 |  | ||||||
|     const val FHS_USR = "/usr/" |  | ||||||
|     const val FHS_USR_BIN = FHS_USR + "bin/" |  | ||||||
| 
 |  | ||||||
|     const val FHS_VAR = "/var/" |  | ||||||
|     const val FHS_VAR_LIB = FHS_VAR + "lib/" |  | ||||||
|     const val FHS_VAR_EMPTY = FHS_VAR + "empty/" |  | ||||||
| 
 |  | ||||||
|     const val FHS_DEV = "/dev/" |  | ||||||
|     const val FHS_PROC = "/proc/" |  | ||||||
|     const val FHS_PROC_SYS = FHS_PROC + "sys/" |  | ||||||
|     const val FHS_SYS = "/sys/" |  | ||||||
| 
 |  | ||||||
|     const val NONEXISTENT = FHS_PROC + "nonexistent" |  | ||||||
|     const val SYSROOT_DIR = "sysroot" |  | ||||||
|     const val HOST_DIR = "host" |  | ||||||
|     const val HOST_PATH = FHS_ROOT + HOST_DIR |  | ||||||
|     const val SYSROOT_PATH = FHS_ROOT + SYSROOT_DIR |  | ||||||
| 
 |  | ||||||
|     val AbsFHSRoot = AbsolutePath(FHS_ROOT) |  | ||||||
|     val AbsFHSEtc = AbsolutePath(FHS_ETC) |  | ||||||
|     val AbsFHSTmp = AbsolutePath(FHS_TMP) |  | ||||||
| 
 |  | ||||||
|     val AbsFHSRun = AbsolutePath(FHS_RUN) |  | ||||||
|     val AbsFHSRunUser = AbsolutePath(FHS_RUN_USER) |  | ||||||
| 
 |  | ||||||
|     val AbsFHSUsrBin = AbsolutePath(FHS_USR_BIN) |  | ||||||
| 
 |  | ||||||
|     val AbsFHSVar = AbsolutePath(FHS_VAR) |  | ||||||
|     val AbsFHSVarLib = AbsolutePath(FHS_VAR_LIB) |  | ||||||
| 
 |  | ||||||
|     val AbsFHSDev = AbsolutePath(FHS_DEV) |  | ||||||
|     val AbsFHSProc = AbsolutePath(FHS_PROC) |  | ||||||
|     val AbsFHSSys = AbsolutePath(FHS_SYS) |  | ||||||
| 
 |  | ||||||
|     val AbsNonexistent = AbsolutePath(NONEXISTENT) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| @ -1,9 +0,0 @@ | |||||||
| package app.hakurei.planterette.api |  | ||||||
| 
 |  | ||||||
| inline fun <T> Array<out T>.orOf(selector: (T) -> Int): Int { |  | ||||||
|     var sum = 0 |  | ||||||
|     for (element in this) { |  | ||||||
|         sum = sum or selector(element) |  | ||||||
|     } |  | ||||||
|     return sum |  | ||||||
| } |  | ||||||
| @ -1,26 +0,0 @@ | |||||||
| package app.hakurei.planterette.api |  | ||||||
| 
 |  | ||||||
| import kotlin.test.Test |  | ||||||
| import kotlin.test.assertEquals |  | ||||||
| import kotlin.test.assertFailsWith |  | ||||||
| import kotlin.test.assertIs |  | ||||||
| 
 |  | ||||||
| class AbsolutePathTest { |  | ||||||
|     @Test |  | ||||||
|     fun testAbsolutePath() { |  | ||||||
|         val path = AbsolutePath("/dev/null") |  | ||||||
|         assertIs<AbsolutePath>(path) |  | ||||||
|     } |  | ||||||
|     @Test |  | ||||||
|     fun testRelativePath() { |  | ||||||
|         assertFailsWith(AbsolutePathException::class) { |  | ||||||
|             AbsolutePath("./test") |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     @Test |  | ||||||
|     fun testPathJoin() { |  | ||||||
|         val want = AbsolutePath("/dev/null") |  | ||||||
|         val base = AbsolutePath("/dev/") |  | ||||||
|         assertEquals(want, base + "null") |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,333 +0,0 @@ | |||||||
| package app.hakurei.planterette.api |  | ||||||
| 
 |  | ||||||
| import app.hakurei.planterette.api.HakureiConfig.Enablement.* |  | ||||||
| import app.hakurei.planterette.api.HakureiConfig.SeccompFilterPreset.* |  | ||||||
| import app.hakurei.planterette.api.HakureiConfig.HakureiExportFlag.* |  | ||||||
| import app.hakurei.planterette.api.HakureiConfig.RootFlag.* |  | ||||||
| 
 |  | ||||||
| import kotlinx.serialization.ExperimentalSerializationApi |  | ||||||
| import kotlinx.serialization.encodeToString |  | ||||||
| import kotlinx.serialization.json.Json |  | ||||||
| import kotlin.test.Test |  | ||||||
| import kotlin.test.assertEquals |  | ||||||
| 
 |  | ||||||
| class HakureiConfigTest { |  | ||||||
|     @OptIn(ExperimentalSerializationApi::class) |  | ||||||
|     val format = Json { |  | ||||||
|         explicitNulls = false |  | ||||||
|         prettyPrint = true |  | ||||||
|     } |  | ||||||
|     val want: String = """ |  | ||||||
|         { |  | ||||||
|         	"id": "org.chromium.Chromium", |  | ||||||
|         	"path": "/run/current-system/sw/bin/chromium", |  | ||||||
|         	"args": [ |  | ||||||
|         		"chromium", |  | ||||||
|         		"--ignore-gpu-blocklist", |  | ||||||
|         		"--disable-smooth-scrolling", |  | ||||||
|         		"--enable-features=UseOzonePlatform", |  | ||||||
|         		"--ozone-platform=wayland" |  | ||||||
|         	], |  | ||||||
|         	"enablements": 13, |  | ||||||
|         	"session_bus": { |  | ||||||
|         		"see": null, |  | ||||||
|         		"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.*": "*" |  | ||||||
|         		}, |  | ||||||
|         		"broadcast": { |  | ||||||
|         			"org.freedesktop.portal.*": "@/org/freedesktop/portal/*" |  | ||||||
|         		}, |  | ||||||
|         		"filter": true |  | ||||||
|         	}, |  | ||||||
|         	"system_bus": { |  | ||||||
|         		"see": null, |  | ||||||
|         		"talk": [ |  | ||||||
|         			"org.bluez", |  | ||||||
|         			"org.freedesktop.Avahi", |  | ||||||
|         			"org.freedesktop.UPower" |  | ||||||
|         		], |  | ||||||
|         		"own": null, |  | ||||||
|         		"call": null, |  | ||||||
|         		"broadcast": null, |  | ||||||
|         		"filter": true |  | ||||||
|         	}, |  | ||||||
|         	"username": "chronos", |  | ||||||
|         	"shell": "/run/current-system/sw/bin/zsh", |  | ||||||
|         	"data": "/var/lib/hakurei/u0/org.chromium.Chromium", |  | ||||||
|         	"dir": "/data/data/org.chromium.Chromium", |  | ||||||
|         	"extra_perms": [ |  | ||||||
|         		{ |  | ||||||
|         			"ensure": true, |  | ||||||
|         			"path": "/var/lib/hakurei/u0", |  | ||||||
|         			"x": true |  | ||||||
|         		}, |  | ||||||
|         		{ |  | ||||||
|         			"path": "/var/lib/hakurei/u0/org.chromium.Chromium", |  | ||||||
|         			"r": true, |  | ||||||
|         			"w": true, |  | ||||||
|         			"x": true |  | ||||||
|         		} |  | ||||||
|         	], |  | ||||||
|         	"identity": 9, |  | ||||||
|         	"groups": [ |  | ||||||
|         		"video", |  | ||||||
|         		"dialout", |  | ||||||
|         		"plugdev" |  | ||||||
|         	], |  | ||||||
|         	"container": { |  | ||||||
|         		"hostname": "localhost", |  | ||||||
|         		"wait_delay": -1, |  | ||||||
|         		"seccomp_flags": 1, |  | ||||||
|         		"seccomp_presets": 1, |  | ||||||
|         		"seccomp_compat": true, |  | ||||||
|         		"devel": true, |  | ||||||
|         		"userns": true, |  | ||||||
|         		"net": true, |  | ||||||
|         		"tty": true, |  | ||||||
|         		"multiarch": true, |  | ||||||
|         		"env": { |  | ||||||
|         			"GOOGLE_API_KEY": "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", |  | ||||||
|         			"GOOGLE_DEFAULT_CLIENT_ID": "77185425430.apps.googleusercontent.com", |  | ||||||
|         			"GOOGLE_DEFAULT_CLIENT_SECRET": "OTJgUOQcT7lO7GsGZq2G4IlT" |  | ||||||
|         		}, |  | ||||||
|         		"map_real_uid": true, |  | ||||||
|         		"device": true, |  | ||||||
|         		"filesystem": [ |  | ||||||
|         			{ |  | ||||||
|         				"dst": "/tmp/", |  | ||||||
|         				"src": "/proc/nonexistent", |  | ||||||
|         				"write": true |  | ||||||
|         			}, |  | ||||||
|         			{ |  | ||||||
|         				"src": "/nix/store" |  | ||||||
|         			}, |  | ||||||
|         			{ |  | ||||||
|         				"src": "/run/current-system" |  | ||||||
|         			}, |  | ||||||
|         			{ |  | ||||||
|         				"src": "/run/opengl-driver" |  | ||||||
|         			}, |  | ||||||
|         			{ |  | ||||||
|         				"src": "/var/db/nix-channels" |  | ||||||
|         			}, |  | ||||||
|         			{ |  | ||||||
|         				"dst": "/data/data/org.chromium.Chromium", |  | ||||||
|         				"src": "/var/lib/hakurei/u0/org.chromium.Chromium", |  | ||||||
|         				"write": true, |  | ||||||
|         				"require": true |  | ||||||
|         			}, |  | ||||||
|         			{ |  | ||||||
|         				"src": "/dev/dri", |  | ||||||
|         				"dev": true |  | ||||||
|         			} |  | ||||||
|         		], |  | ||||||
|         		"symlink": [ |  | ||||||
|         			{ |  | ||||||
|         				"target": "/run/user/65534", |  | ||||||
|         				"linkname": "/run/user/150" |  | ||||||
|         			} |  | ||||||
|         		], |  | ||||||
|         		"auto_root": "/var/lib/hakurei/base/org.debian", |  | ||||||
|         		"root_flags": 2, |  | ||||||
|         		"etc": "/etc/", |  | ||||||
|         		"auto_etc": true |  | ||||||
|         	} |  | ||||||
|         } |  | ||||||
|     """.trimIndent() |  | ||||||
|     val testConfig = HakureiConfig( |  | ||||||
|         id = "org.chromium.Chromium", |  | ||||||
|         path = Paths.AbsFHSRun + "current-system/sw/bin/chromium", |  | ||||||
|         args = listOf( |  | ||||||
|             "chromium", |  | ||||||
|             "--ignore-gpu-blocklist", |  | ||||||
|             "--disable-smooth-scrolling", |  | ||||||
|             "--enable-features=UseOzonePlatform", |  | ||||||
|             "--ozone-platform=wayland", |  | ||||||
|         ), |  | ||||||
|         enablements = HakureiConfig.Enablement.enablements(Wayland, DBus, Pulse), |  | ||||||
|         sessionBus = HakureiConfig.DbusConfig( |  | ||||||
|             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/*"), |  | ||||||
|             log = false, |  | ||||||
|             filter = true, |  | ||||||
|         ), |  | ||||||
|         systemBus = HakureiConfig.DbusConfig( |  | ||||||
|             talk = listOf("org.bluez", "org.freedesktop.Avahi", "org.freedesktop.UPower"), |  | ||||||
|             log = false, |  | ||||||
|             filter = true, |  | ||||||
|         ), |  | ||||||
|         directWayland = false, |  | ||||||
| 
 |  | ||||||
|         username = "chronos", |  | ||||||
|         shell = Paths.AbsFHSRun + "current-system/sw/bin/zsh", |  | ||||||
|         data = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium", |  | ||||||
|         dir = AbsolutePath("/data/data/org.chromium.Chromium"), |  | ||||||
|         extraPerms = listOf( |  | ||||||
|             HakureiConfig.ExtraPermConfig(path = Paths.AbsFHSVarLib + "hakurei/u0", ensure = true, execute = true), |  | ||||||
|             HakureiConfig.ExtraPermConfig(path = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium", read = true, write = true, execute = true) |  | ||||||
|         ), |  | ||||||
|         identity = 9, |  | ||||||
|         groups = listOf("video", "dialout", "plugdev"), |  | ||||||
|         container = HakureiConfig.ContainerConfig( |  | ||||||
|             hostname = "localhost", |  | ||||||
|             devel = true, |  | ||||||
|             userns = true, |  | ||||||
|             net = true, |  | ||||||
|             device = true, |  | ||||||
|             waitDelay = -1, |  | ||||||
|             seccompFlags = HakureiConfig.HakureiExportFlag.exportFlags(Multiarch), |  | ||||||
|             seccompPresets = HakureiConfig.SeccompFilterPreset.filterPresets(Ext), |  | ||||||
|             seccompCompat = true, |  | ||||||
|             tty = true, |  | ||||||
|             multiarch = true, |  | ||||||
|             mapRealUid = true, |  | ||||||
|             env = mapOf( |  | ||||||
|                 "GOOGLE_API_KEY" to "AIzaSyBHDrl33hwRp4rMQY0ziRbj8K9LPA6vUCY", |  | ||||||
|                 "GOOGLE_DEFAULT_CLIENT_ID" to "77185425430.apps.googleusercontent.com", |  | ||||||
|                 "GOOGLE_DEFAULT_CLIENT_SECRET" to "OTJgUOQcT7lO7GsGZq2G4IlT" |  | ||||||
|             ), |  | ||||||
|             filesystem = listOf( |  | ||||||
|                 HakureiConfig.FilesystemConfig(dst = Paths.AbsFHSTmp, src = Paths.AbsNonexistent, write = true), |  | ||||||
|                 HakureiConfig.FilesystemConfig(src = AbsolutePath("/nix/store")), |  | ||||||
|                 HakureiConfig.FilesystemConfig(src = Paths.AbsFHSRun + "current-system"), |  | ||||||
|                 HakureiConfig.FilesystemConfig(src = Paths.AbsFHSRun + "opengl-driver"), |  | ||||||
|                 HakureiConfig.FilesystemConfig(src = Paths.AbsFHSVar + "db/nix-channels"), |  | ||||||
|                 HakureiConfig.FilesystemConfig(src = Paths.AbsFHSVarLib + "hakurei/u0/org.chromium.Chromium", dst = AbsolutePath("/data/data/org.chromium.Chromium"), write = true, must = true), |  | ||||||
|                 HakureiConfig.FilesystemConfig(src = Paths.AbsFHSDev + "dri", device = true) |  | ||||||
|             ), |  | ||||||
|             link = listOf(HakureiConfig.LinkConfig(Paths.AbsFHSRunUser + "65534", Paths.FHS_RUN_USER + "150")), |  | ||||||
|             autoRoot = Paths.AbsFHSVarLib + "hakurei/base/org.debian", |  | ||||||
|             rootFlags = HakureiConfig.RootFlag.rootFlags(Writable), |  | ||||||
|             etc = Paths.AbsFHSEtc, |  | ||||||
|             autoEtc = true |  | ||||||
|         ) |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     internal fun compare(want: HakureiConfig, test: HakureiConfig) { |  | ||||||
|         assertEquals(want.id, test.id, "id") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.path, test.path, "path") |  | ||||||
|         assertEquals(want.args, test.args, "args") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.enablements, test.enablements, "enablements") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.sessionBus?.see, test.sessionBus?.see, "sessionBus.see") |  | ||||||
|         assertEquals(want.sessionBus?.talk, test.sessionBus?.talk, "sessionBus.talk") |  | ||||||
|         assertEquals(want.sessionBus?.own, test.sessionBus?.own, "sessionBus.own") |  | ||||||
|         assertEquals(want.sessionBus?.broadcast, test.sessionBus?.broadcast, "sessionBus.broadcast") |  | ||||||
|         assertEquals(want.sessionBus?.log ?: false, test.sessionBus?.log, "sessionBus.log") |  | ||||||
|         assertEquals(want.sessionBus?.filter, test.sessionBus?.filter, "sessionBus.filter") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.systemBus?.see, test.systemBus?.see, "systemBus.see") |  | ||||||
|         assertEquals(want.systemBus?.talk, test.systemBus?.talk, "systemBus.talk") |  | ||||||
|         assertEquals(want.systemBus?.own, test.systemBus?.own, "systemBus.own") |  | ||||||
|         assertEquals(want.systemBus?.broadcast, test.systemBus?.broadcast, "systemBus.broadcast") |  | ||||||
|         assertEquals(want.systemBus?.log ?: false, test.systemBus?.log, "systemBus.log") |  | ||||||
|         assertEquals(want.systemBus?.filter, test.systemBus?.filter, "systemBus.filter") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.directWayland ?: false, test.directWayland, "directWayland") |  | ||||||
|         assertEquals(want.username, test.username, "username") |  | ||||||
|         assertEquals(want.shell, test.shell, "shell") |  | ||||||
|         assertEquals(want.data, test.data, "data") |  | ||||||
|         assertEquals(want.dir, test.dir, "dir") |  | ||||||
|         assertEquals(want.extraPerms, test.extraPerms, "extraPerms") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.identity, test.identity, "identity") |  | ||||||
|         assertEquals(want.groups, test.groups, "groups") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.container.waitDelay, test.container.waitDelay, "waitDelay") |  | ||||||
|         assertEquals(want.container.seccompFlags, test.container.seccompFlags, "seccompFlags") |  | ||||||
|         assertEquals(want.container.seccompPresets, test.container.seccompPresets, "seccompPresets") |  | ||||||
|         assertEquals(want.container.seccompCompat, test.container.seccompCompat, "seccompCompat") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.container.devel, test.container.devel, "devel") |  | ||||||
|         assertEquals(want.container.userns, test.container.userns, "userns") |  | ||||||
|         assertEquals(want.container.net, test.container.net, "net") |  | ||||||
|         assertEquals(want.container.tty, test.container.tty, "tty") |  | ||||||
|         assertEquals(want.container.multiarch, test.container.multiarch, "multiarch") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.container.env, test.container.env, "env") |  | ||||||
|         assertEquals(want.container.mapRealUid, test.container.mapRealUid, "mapRealUid") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.container.device, test.container.device, "device") |  | ||||||
|         assertEquals(want.container.filesystem.size, test.container.filesystem.size, "filesystem.size") |  | ||||||
|         for(i in 0 until want.container.filesystem.size) { |  | ||||||
|             val w = want.container.filesystem[i] |  | ||||||
|             val t = test.container.filesystem[i] |  | ||||||
| 
 |  | ||||||
|             assertEquals(w.src, t.src, "filesystem[$i].src") |  | ||||||
|             assertEquals(w.dst, t.dst, "filesystem[$i].dst") |  | ||||||
|             assertEquals(w.write, t.write, "filesystem[$i].write") |  | ||||||
|             assertEquals(w.device, t.device, "filesystem[$i].device") |  | ||||||
|             assertEquals(w.must, t.must, "filesystem[$i].must") |  | ||||||
|         } |  | ||||||
|         assertEquals(want.container.link, test.container.link, "link") |  | ||||||
|         assertEquals(want.container.autoRoot, test.container.autoRoot, "autoroot") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.container.etc, test.container.etc, "etc") |  | ||||||
| 
 |  | ||||||
|         assertEquals(want.container.autoEtc, test.container.autoEtc, "autoEtc") |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     fun testHakureiConfigSerialization() { |  | ||||||
|         val json = format.encodeToString(testConfig) |  | ||||||
|         val test = format.decodeFromString<HakureiConfig>(json) |  | ||||||
|         assertEquals(testConfig, test) |  | ||||||
|         val want = format.decodeFromString<HakureiConfig>(want) |  | ||||||
| 
 |  | ||||||
|         compare(want, test) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     fun testEnablements() { |  | ||||||
|         val enablement = HakureiConfig.Enablement.enablements(Wayland, X11, DBus, Pulse) |  | ||||||
|         assertEquals(enablement, 0b1111) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     fun testSeccompFilterPresets() { |  | ||||||
|         val presets = HakureiConfig.SeccompFilterPreset.filterPresets(Ext, DenyNS, DenyTTY, DenyDevel, Linux32) |  | ||||||
|         assertEquals(presets, 0b11111) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     fun testExportFlags() { |  | ||||||
|         val flags = HakureiConfig.HakureiExportFlag.exportFlags(Multiarch, CAN, Bluetooth) |  | ||||||
|         assertEquals(flags, 0b111) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     @Test |  | ||||||
|     fun testRootFlags() { |  | ||||||
|         val flags = HakureiConfig.RootFlag.rootFlags(Optional, Writable, Device) |  | ||||||
|         assertEquals(flags, 0b111) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,30 +0,0 @@ | |||||||
| plugins { |  | ||||||
|     kotlin("multiplatform") |  | ||||||
| } |  | ||||||
| kotlin { |  | ||||||
|     jvm() |  | ||||||
|     val nativeTarget = if(System.getProperty("os.arch") == "aarch64") { |  | ||||||
|         linuxArm64("native") |  | ||||||
|     } else { |  | ||||||
|         linuxX64("native") |  | ||||||
|     } |  | ||||||
|     nativeTarget.binaries { |  | ||||||
|         executable() |  | ||||||
|     } |  | ||||||
|     sourceSets { |  | ||||||
|         nativeMain.dependencies { |  | ||||||
|             implementation(project(":libplt")) |  | ||||||
|         } |  | ||||||
|         nativeTest.dependencies { |  | ||||||
|             implementation(project(":libplt")) |  | ||||||
|             implementation(kotlin("test")) |  | ||||||
|         } |  | ||||||
|         jvmMain.dependencies { |  | ||||||
|             implementation(project(":libplt")) |  | ||||||
|         } |  | ||||||
|         jvmTest.dependencies { |  | ||||||
|             implementation(project(":libplt")) |  | ||||||
|             implementation(kotlin("test")) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,16 +0,0 @@ | |||||||
| package app.hakurei.planterette.api |  | ||||||
| 
 |  | ||||||
| interface Task { |  | ||||||
|     val execute: String |  | ||||||
| } |  | ||||||
| class AptInstallTask(val packages: List<String>) : Task { |  | ||||||
|     private fun getPackageList(): String { |  | ||||||
|         var string = "" |  | ||||||
|         packages.forEach { p -> |  | ||||||
|             string += "$p " |  | ||||||
|         } |  | ||||||
|         return string |  | ||||||
|     } |  | ||||||
|     override val execute: String |  | ||||||
|         get() = "sudo apt install ${getPackageList()}" |  | ||||||
| } |  | ||||||
| @ -1,22 +0,0 @@ | |||||||
| plugins { |  | ||||||
|     kotlin("multiplatform") |  | ||||||
| } |  | ||||||
| kotlin { |  | ||||||
|     val nativeTarget = if(System.getProperty("os.arch") == "aarch64") { |  | ||||||
|         linuxArm64("native") |  | ||||||
|     } else { |  | ||||||
|         linuxX64("native") |  | ||||||
|     } |  | ||||||
|     nativeTarget.binaries { |  | ||||||
|         executable() |  | ||||||
|     } |  | ||||||
|     sourceSets { |  | ||||||
|         nativeMain.dependencies { |  | ||||||
|             implementation(project(":libplt")) |  | ||||||
|         } |  | ||||||
|         nativeTest.dependencies { |  | ||||||
|             implementation(project(":libplt")) |  | ||||||
|             implementation(kotlin("test")) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,22 +0,0 @@ | |||||||
| plugins { |  | ||||||
|     kotlin("multiplatform") |  | ||||||
| } |  | ||||||
| kotlin { |  | ||||||
|     val nativeTarget = if(System.getProperty("os.arch") == "aarch64") { |  | ||||||
|         linuxArm64("native") |  | ||||||
|     } else { |  | ||||||
|         linuxX64("native") |  | ||||||
|     } |  | ||||||
|     nativeTarget.binaries { |  | ||||||
|         executable() |  | ||||||
|     } |  | ||||||
|     sourceSets { |  | ||||||
|         nativeMain.dependencies { |  | ||||||
|             implementation(project(":libplt")) |  | ||||||
|         } |  | ||||||
|         nativeTest.dependencies { |  | ||||||
|             implementation(project(":libplt")) |  | ||||||
|             implementation(kotlin("test")) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,22 +0,0 @@ | |||||||
| plugins { |  | ||||||
|     kotlin("multiplatform") |  | ||||||
| } |  | ||||||
| kotlin { |  | ||||||
|     val nativeTarget = if(System.getProperty("os.arch") == "aarch64") { |  | ||||||
|         linuxArm64("native") |  | ||||||
|     } else { |  | ||||||
|         linuxX64("native") |  | ||||||
|     } |  | ||||||
|     nativeTarget.binaries { |  | ||||||
|             executable() |  | ||||||
|     } |  | ||||||
|     sourceSets { |  | ||||||
|         nativeMain.dependencies { |  | ||||||
|             implementation(project(":libplt")) |  | ||||||
|         } |  | ||||||
|         nativeTest.dependencies { |  | ||||||
|             implementation(project(":libplt")) |  | ||||||
|             implementation(kotlin("test")) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,18 +1,4 @@ | |||||||
| dependencyResolutionManagement { |  | ||||||
|     @Suppress("UnstableApiUsage") |  | ||||||
|     repositories { |  | ||||||
|         mavenCentral() |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| plugins { | plugins { | ||||||
|     id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" |     id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0" | ||||||
| } | } | ||||||
| 
 | rootProject.name = "planterette" | ||||||
| rootProject.name = "planterette" |  | ||||||
| 
 |  | ||||||
| include("plt") |  | ||||||
| include("libplt") |  | ||||||
| include("plt-build") |  | ||||||
| include("plt-fetch") |  | ||||||
| include("plt-pkg") |  | ||||||
							
								
								
									
										5
									
								
								src/main/go/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/main/go/main.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | func main() { | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								src/main/go/main_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/main/go/main_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 	_ "testing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestHelloWorld(t *testing.T) { | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										2
									
								
								src/main/kotlin/moe/rosa/planterette/Main.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								src/main/kotlin/moe/rosa/planterette/Main.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | |||||||
|  | package moe.rosa.planterette | ||||||
|  | 
 | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | package moe.rosa.planterette | ||||||
|  | 
 | ||||||
|  | import moe.rosa.planterette.hakurei.HakureiConfig | ||||||
|  | 
 | ||||||
|  | data class PlanteretteConfig(var hakurei: HakureiConfig?) | ||||||
							
								
								
									
										12
									
								
								src/main/kotlin/moe/rosa/planterette/dsl/DSL.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/main/kotlin/moe/rosa/planterette/dsl/DSL.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | |||||||
|  | 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) | ||||||
|  | } | ||||||
							
								
								
									
										392
									
								
								src/main/kotlin/moe/rosa/planterette/dsl/HakureiDSL.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										392
									
								
								src/main/kotlin/moe/rosa/planterette/dsl/HakureiDSL.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,392 @@ | |||||||
|  | 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 | ||||||
|  | } | ||||||
							
								
								
									
										85
									
								
								src/main/kotlin/moe/rosa/planterette/hakurei/Filesystem.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/main/kotlin/moe/rosa/planterette/hakurei/Filesystem.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | |||||||
|  | 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 | ||||||
							
								
								
									
										91
									
								
								src/main/kotlin/moe/rosa/planterette/hakurei/Hakurei.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/main/kotlin/moe/rosa/planterette/hakurei/Hakurei.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | |||||||
|  | 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, | ||||||
|  | ) | ||||||
							
								
								
									
										111
									
								
								src/test/kotlin/DSLTest.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/test/kotlin/DSLTest.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,111 @@ | |||||||
|  | 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) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										194
									
								
								src/test/kotlin/HakureiTest.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								src/test/kotlin/HakureiTest.kt
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,194 @@ | |||||||
|  | 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()) | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										0
									
								
								src/test/resources/ChromiumExample.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/test/resources/ChromiumExample.kts
									
									
									
									
									
										Normal file
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user