feat: environment config patcher

This commit is contained in:
2025-01-02 11:13:12 +07:00
parent 5836b23a29
commit 37b3feea3a
5 changed files with 481 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

View File

@@ -0,0 +1,32 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="minikura-environment-config-patcher" type="MavenRunConfiguration" factoryName="Maven" nameIsGenerated="true">
<MavenSettings>
<option name="myGeneralSettings" />
<option name="myRunnerSettings" />
<option name="myRunnerParameters">
<MavenRunnerParameters>
<option name="cmdOptions" />
<option name="profiles">
<set />
</option>
<option name="goals">
<list>
<option value="package" />
</list>
</option>
<option name="multimoduleDir" />
<option name="pomFileName" />
<option name="profilesMap">
<map />
</option>
<option name="projectsCmdOptionValues">
<list />
</option>
<option name="resolveToWorkspace" value="false" />
<option name="workingDirPath" value="$PROJECT_DIR$" />
</MavenRunnerParameters>
</option>
</MavenSettings>
<method v="2" />
</configuration>
</component>

View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cafe.kirameki</groupId>
<artifactId>minikura-environment-config-patcher</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>2.0.20</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>MainKt</mainClass>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<archive>
<manifest>
<mainClass>MainKt</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<transformers>
<transformer>
<mainClass>MainKt</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>mavenCentral</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>2.0.20</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>kotlin-test</artifactId>
<groupId>org.jetbrains.kotlin</groupId>
</exclusion>
<exclusion>
<artifactId>junit-jupiter-api</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
<exclusion>
<artifactId>junit-jupiter-engine</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
<exclusion>
<artifactId>junit-platform-launcher</artifactId>
<groupId>org.junit.platform</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>junit-jupiter-params</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
<exclusion>
<artifactId>junit-jupiter-api</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
<exclusion>
<artifactId>junit-jupiter-engine</artifactId>
<groupId>org.junit.jupiter</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<properties>
<kotlin.code.style>official</kotlin.code.style>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
</properties>
</project>

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cafe.kirameki</groupId>
<artifactId>minikura-environment-config-patcher</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.code.style>official</kotlin.code.style>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
</properties>
<repositories>
<repository>
<id>mavenCentral</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>2.0.20</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>MainKt</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<archive>
<manifest>
<mainClass>MainKt</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>true</createDependencyReducedPom>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>MainKt</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<version>2.0.20</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>2.0.20</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>2.0</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,147 @@
import org.yaml.snakeyaml.DumperOptions
import org.yaml.snakeyaml.LoaderOptions
import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.constructor.Constructor
import java.io.File
const val PREFIX = "minikura-environment-config-patcher"
fun main() {
val envVars = System.getenv()
val patchConfigVars = envVars.filter { it.key.startsWith("PATCH_YAML_CONFIG_") }
for ((key, value) in patchConfigVars) {
println("[${getCurrentTime()} INFO] [$PREFIX]: Processing $key")
val lines = splitWithEscapedPipe(value)
if (lines.size < 2) {
println("[${getCurrentTime()} INFO] [$PREFIX]: Skipping. Insufficient data.")
continue
}
val yamlFilePath = lines[0].trim()
val yamlFile = File(yamlFilePath)
if (!yamlFile.exists()) {
println("[${getCurrentTime()} INFO] [$PREFIX]: Skipping. File does not exist.")
continue
}
val replacements = mutableMapOf<String, Any>()
lines.drop(1).forEach { line ->
val keyValue = line.split("=", limit = 2)
if (keyValue.size == 2) {
val key = keyValue[0].trim()
val value = parseValue(keyValue[1].trim())
val (baseKey, index, subKey) = parseKeyWithIndex(key)
if (index != null) {
val arrayData = replacements.computeIfAbsent(baseKey) { mutableListOf<MutableMap<String, Any>>() } as MutableList<MutableMap<String, Any>>
while (arrayData.size <= index) {
arrayData.add(mutableMapOf())
}
val arrayItem = arrayData[index]
arrayItem[subKey] = value
println("[${getCurrentTime()} INFO] [$PREFIX]: Editing key: $baseKey[$index].$subKey")
} else {
replacements[baseKey] = value
println("[${getCurrentTime()} INFO] [$PREFIX]: Editing key: $baseKey")
}
} else {
println("[${getCurrentTime()} WARN] [$PREFIX]: Skipping malformed line.")
}
}
try {
val updatedYaml = updateYaml(yamlFile, replacements)
yamlFile.writeText(updatedYaml)
println("[${getCurrentTime()} INFO] [$PREFIX]: Successfully updated $yamlFilePath")
} catch (e: Exception) {
println("[${getCurrentTime()} ERROR] [$PREFIX]: Error processing $yamlFilePath. ${e.message}")
}
}
println("[${getCurrentTime()} INFO] [$PREFIX]: Patch completed")
}
fun getCurrentTime(): String {
val currentDate = java.time.LocalTime.now()
return currentDate.toString().substring(0, 8)
}
fun splitWithEscapedPipe(value: String): List<String> {
val regex = "(?<!\\\\)\\|".toRegex()
return value.split(regex).map { it.replace("\\|", "|") }
}
fun parseValue(value: String): Any {
return when {
value.equals("true", ignoreCase = true) -> true
value.equals("false", ignoreCase = true) -> false
value.startsWith("\"") && value.endsWith("\"") -> value.substring(1, value.length - 1)
value.startsWith("'") && value.endsWith("'") -> value.substring(1, value.length - 1)
value.toIntOrNull() != null -> value.toInt()
value.startsWith("[") && value.endsWith("]") -> parseArray(value)
else -> value
}
}
fun parseArray(value: String): List<Map<String, Any>> {
val arrayContent = value.substring(1, value.length - 1).trim()
return arrayContent.split(",").map { item ->
val keyValue = item.split("=")
if (keyValue.size == 2) {
keyValue[0].trim() to parseValue(keyValue[1].trim())
} else {
keyValue[0].trim() to parseValue(keyValue[0].trim())
}
}.map { mapOf(it) }
}
fun parseKeyWithIndex(key: String): Triple<String, Int?, String> {
val regex = """([^\[]+)(?:\[(\d+)\])?(\.[^\[]+)*""".toRegex()
val matchResult = regex.matchEntire(key)
return if (matchResult != null) {
val (baseKey, indexStr, subKey) = matchResult.destructured
val index = indexStr.toIntOrNull()
val finalKey = subKey.trimStart('.')
Triple(baseKey, index, finalKey)
} else {
Triple(key, null, "")
}
}
fun updateYaml(file: File, replacements: Map<String, Any>): String {
val loaderOptions = LoaderOptions()
val yaml = Yaml(Constructor(loaderOptions))
val options = DumperOptions().apply {
defaultFlowStyle = DumperOptions.FlowStyle.BLOCK
isPrettyFlow = true
}
val yamlDumper = Yaml(options)
val yamlData = yaml.load<Map<String, Any>>(file.readText()).toMutableMap()
for ((keyPath, value) in replacements) {
applyReplacement(yamlData, keyPath.split("."), value)
}
return yamlDumper.dump(yamlData)
}
fun applyReplacement(data: MutableMap<String, Any>, keys: List<String>, value: Any) {
val key = keys.first()
if (keys.size == 1) {
println("[${getCurrentTime()} INFO] [$PREFIX]: Editing key: $key")
data[key] = value
} else {
val nestedData = data[key] as? MutableMap<String, Any>
?: mutableMapOf<String, Any>().also { data[key] = it }
applyReplacement(nestedData, keys.drop(1), value)
}
}