mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 16:53:04 +08:00
refactor(partnerctl-init): extract source build/install flow into reusable support helper
This commit is contained in:
@@ -1,100 +1,42 @@
|
|||||||
package work.slhaf.partner.ctl.commands.init
|
package work.slhaf.partner.ctl.commands.init
|
||||||
|
|
||||||
import work.slhaf.partner.ctl.support.CommandInterrupted
|
import work.slhaf.partner.ctl.support.SourceBuildInstallSpec
|
||||||
import work.slhaf.partner.ctl.support.inheritCommand
|
import work.slhaf.partner.ctl.support.buildAndInstallFromSource
|
||||||
import work.slhaf.partner.ctl.support.runCommand
|
|
||||||
import work.slhaf.partner.ctl.ui.Prompt
|
import work.slhaf.partner.ctl.ui.Prompt
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.Paths
|
||||||
import kotlin.io.path.isDirectory
|
import kotlin.io.path.isDirectory
|
||||||
import kotlin.io.path.name
|
import kotlin.io.path.name
|
||||||
|
|
||||||
|
private const val PARTNER_REPO_URL = "https://gitea.slhaf.work/slhaf/Partner.git"
|
||||||
|
|
||||||
fun buildFromSource(home: Path, prompt: Prompt) {
|
fun buildFromSource(home: Path, prompt: Prompt) {
|
||||||
checkTool()
|
buildAndInstallFromSource(
|
||||||
|
home = home,
|
||||||
val tempDir = Files.createTempDirectory("partnerctl-build-")
|
prompt = prompt,
|
||||||
val sourceDir = tempDir.resolve("Partner")
|
spec = SourceBuildInstallSpec(
|
||||||
val repoUrl = "https://gitea.slhaf.work/slhaf/Partner.git"
|
displayName = "Partner runtime",
|
||||||
val targetJar = home.resolve("resource").resolve("partner-core.jar")
|
repoUrl = PARTNER_REPO_URL,
|
||||||
|
sourceDirName = "Partner",
|
||||||
try {
|
buildCommand = listOf("mvn", "-pl", "Partner-Core", "-am", "package", "-DskipTests=true"),
|
||||||
prompt.info("Cloning Partner source from $repoUrl")
|
artifactDirectory = Paths.get("Partner-Core", "target"),
|
||||||
val cloneExitCode = inheritCommand(
|
artifactSelector = ::findLargestJar,
|
||||||
command = listOf("git", "clone", "--depth", "1", repoUrl, sourceDir.toString()),
|
installRelativePath = Paths.get("resource", "partner-core.jar"),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
if (cloneExitCode != 0) {
|
|
||||||
throw CommandInterrupted("Failed to clone Partner source from $repoUrl")
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt.info("Building Partner-Core and required modules")
|
|
||||||
val buildExitCode = inheritCommand(
|
|
||||||
command = listOf("mvn", "-pl", "Partner-Core", "-am", "package", "-DskipTests=true"),
|
|
||||||
workingDirectory = sourceDir,
|
|
||||||
)
|
|
||||||
if (buildExitCode != 0) {
|
|
||||||
throw CommandInterrupted("Failed to build Partner runtime. Please make sure Maven is using JDK 21.")
|
|
||||||
}
|
|
||||||
|
|
||||||
val builtJar = findPartnerCoreJar(sourceDir)
|
|
||||||
Files.createDirectories(targetJar.parent)
|
|
||||||
Files.copy(builtJar, targetJar, StandardCopyOption.REPLACE_EXISTING)
|
|
||||||
prompt.success("Partner runtime installed at $targetJar")
|
|
||||||
} finally {
|
|
||||||
tempDir.toFile().deleteRecursively()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkTool() {
|
private fun findLargestJar(directory: Path): Path? {
|
||||||
|
if (!directory.isDirectory()) return null
|
||||||
|
|
||||||
fun buildToolLackedMessage(lack: Map<String, String>): String {
|
return Files.list(directory).use { stream ->
|
||||||
return buildString {
|
|
||||||
appendLine("Missing required build tools:")
|
|
||||||
appendLine()
|
|
||||||
lack.forEach { (tool, reason) ->
|
|
||||||
appendLine("$tool:")
|
|
||||||
appendLine(" $reason")
|
|
||||||
}
|
|
||||||
appendLine()
|
|
||||||
appendLine("Install the missing tools, then rerun:")
|
|
||||||
appendLine(" partnerctl init")
|
|
||||||
}.trimEnd()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val lack = mutableMapOf<String, String>()
|
|
||||||
if (runCommand(listOf("java", "--version")).exitCode != 0) {
|
|
||||||
lack["java"] = "Required to run Maven and Partner runtime. Command failed: java --version"
|
|
||||||
}
|
|
||||||
if (runCommand(listOf("javac", "--version")).exitCode != 0) {
|
|
||||||
lack["javac"] =
|
|
||||||
"Required to compile Partner from source. Install a JDK, not just a JRE. Command failed: javac --version"
|
|
||||||
}
|
|
||||||
if (runCommand(listOf("git", "--version")).exitCode != 0) {
|
|
||||||
lack["git"] = "Required to clone Partner source. Command failed: git --version"
|
|
||||||
}
|
|
||||||
if (runCommand(listOf("mvn", "--version")).exitCode != 0) {
|
|
||||||
lack["mvn"] = "Required to build Partner from source. Command failed: mvn --version"
|
|
||||||
}
|
|
||||||
if (lack.isNotEmpty()) {
|
|
||||||
throw CommandInterrupted(buildToolLackedMessage(lack))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findPartnerCoreJar(sourceDir: Path): Path {
|
|
||||||
val targetDir = sourceDir.resolve("Partner-Core").resolve("target")
|
|
||||||
if (!targetDir.isDirectory()) {
|
|
||||||
throw CommandInterrupted("Partner-Core target directory does not exist: $targetDir")
|
|
||||||
}
|
|
||||||
|
|
||||||
return Files.list(targetDir).use { stream ->
|
|
||||||
stream
|
stream
|
||||||
.filter { it.name.endsWith(".jar") }
|
.filter { it.name.endsWith(".jar") }
|
||||||
.filter { !it.name.startsWith("original-") }
|
.filter { !it.name.startsWith("original-") }
|
||||||
.filter { !it.name.endsWith("-sources.jar") }
|
.filter { !it.name.endsWith("-sources.jar") }
|
||||||
.filter { !it.name.endsWith("-javadoc.jar") }
|
.filter { !it.name.endsWith("-javadoc.jar") }
|
||||||
.max(Comparator.comparingLong { Files.size(it) })
|
.max(Comparator.comparingLong { Files.size(it) })
|
||||||
.orElseThrow { CommandInterrupted("Could not find built Partner Core jar in $targetDir") }
|
.orElse(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
package work.slhaf.partner.ctl.support
|
||||||
|
|
||||||
|
import work.slhaf.partner.ctl.ui.Prompt
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.StandardCopyOption
|
||||||
|
|
||||||
|
data class SourceBuildInstallSpec(
|
||||||
|
val displayName: String,
|
||||||
|
val repoUrl: String,
|
||||||
|
val sourceDirName: String,
|
||||||
|
val buildCommand: List<String>,
|
||||||
|
val artifactDirectory: Path,
|
||||||
|
val artifactSelector: (Path) -> Path?,
|
||||||
|
val installRelativePath: Path,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun buildAndInstallFromSource(
|
||||||
|
home: Path,
|
||||||
|
prompt: Prompt,
|
||||||
|
spec: SourceBuildInstallSpec,
|
||||||
|
) {
|
||||||
|
require(spec.sourceDirName.isNotBlank()) { "sourceDirName must not be blank" }
|
||||||
|
require(spec.buildCommand.isNotEmpty()) { "buildCommand must not be empty" }
|
||||||
|
|
||||||
|
checkTool()
|
||||||
|
|
||||||
|
val tempDir = Files.createTempDirectory("partnerctl-build-")
|
||||||
|
val sourceDir = tempDir.resolve(spec.sourceDirName)
|
||||||
|
val targetPath = home.resolve(spec.installRelativePath)
|
||||||
|
|
||||||
|
try {
|
||||||
|
prompt.info("Cloning ${spec.displayName} source from ${spec.repoUrl}")
|
||||||
|
val cloneExitCode = inheritCommand(
|
||||||
|
command = listOf("git", "clone", "--depth", "1", spec.repoUrl, sourceDir.toString()),
|
||||||
|
)
|
||||||
|
if (cloneExitCode != 0) {
|
||||||
|
throw CommandInterrupted("Failed to clone ${spec.displayName} source from ${spec.repoUrl}")
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt.info("Building ${spec.displayName}")
|
||||||
|
val buildExitCode = inheritCommand(
|
||||||
|
command = spec.buildCommand,
|
||||||
|
workingDirectory = sourceDir,
|
||||||
|
)
|
||||||
|
if (buildExitCode != 0) {
|
||||||
|
throw CommandInterrupted("Failed to build ${spec.displayName}.")
|
||||||
|
}
|
||||||
|
|
||||||
|
val artifactDir = sourceDir.resolve(spec.artifactDirectory)
|
||||||
|
val artifact = spec.artifactSelector(artifactDir)
|
||||||
|
?: throw CommandInterrupted("Could not find built ${spec.displayName} artifact in $artifactDir")
|
||||||
|
|
||||||
|
Files.createDirectories(targetPath.parent)
|
||||||
|
Files.copy(artifact, targetPath, StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
prompt.success("${spec.displayName} installed at $targetPath")
|
||||||
|
} finally {
|
||||||
|
runCatching { tempDir.toFile().deleteRecursively() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkTool() {
|
||||||
|
fun buildToolLackedMessage(lack: Map<String, String>): String {
|
||||||
|
return buildString {
|
||||||
|
appendLine("Missing required build tools:")
|
||||||
|
appendLine()
|
||||||
|
lack.forEach { (tool, reason) ->
|
||||||
|
appendLine("$tool:")
|
||||||
|
appendLine(" $reason")
|
||||||
|
}
|
||||||
|
appendLine()
|
||||||
|
appendLine("Install the missing tools, then rerun:")
|
||||||
|
appendLine(" partnerctl init")
|
||||||
|
}.trimEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
val lack = mutableMapOf<String, String>()
|
||||||
|
if (runCommand(listOf("java", "--version")).exitCode != 0) {
|
||||||
|
lack["java"] = "Required to run Maven. Command failed: java --version"
|
||||||
|
}
|
||||||
|
if (runCommand(listOf("javac", "--version")).exitCode != 0) {
|
||||||
|
lack["javac"] =
|
||||||
|
"Required to compile Partner from source. Install a JDK, not just a JRE. Command failed: javac --version"
|
||||||
|
}
|
||||||
|
if (runCommand(listOf("git", "--version")).exitCode != 0) {
|
||||||
|
lack["git"] = "Required to clone Partner source. Command failed: git --version"
|
||||||
|
}
|
||||||
|
if (runCommand(listOf("mvn", "--version")).exitCode != 0) {
|
||||||
|
lack["mvn"] = "Required to build Partner from source. Command failed: mvn --version"
|
||||||
|
}
|
||||||
|
if (lack.isNotEmpty()) {
|
||||||
|
throw CommandInterrupted(buildToolLackedMessage(lack))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user