Skip to content

Commit

Permalink
Merge pull request #35 from devchat-ai/new-installation-process
Browse files Browse the repository at this point in the history
New installation process
  • Loading branch information
pplam authored Dec 19, 2023
2 parents 72297dc + cad73ee commit 8f15810
Show file tree
Hide file tree
Showing 19 changed files with 197 additions and 105 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
[submodule "gui"]
path = gui
url = https://github.com/devchat-ai/devchat-gui.git
[submodule "tools"]
path = tools
url = https://github.com/devchat-ai/devchat-vscode-tools.git
1 change: 1 addition & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,24 @@ intellij {
plugins.set(listOf(/* Plugin Dependencies */))
}

tasks.register<Copy>("copyTools") {
from(layout.projectDirectory.dir("tools")) { exclude(".git/**", ".gitignore") }
into(layout.buildDirectory.dir("tmp/copyTools/tools"))
}

//tasks.register<Exec>("buildGUI") {
// commandLine("yarn", "idea")
// workingDir(layout.projectDirectory.dir("gui"))
//}

sourceSets {
main {
resources {
srcDir(layout.buildDirectory.dir("tmp/copyTools"))
}
}
}

tasks {
// Set the JVM compatibility versions
withType<JavaCompile> {
Expand All @@ -49,6 +67,10 @@ tasks {
password.set(System.getenv("PRIVATE_KEY_PASSWORD"))
}

processResources {
dependsOn("copyTools")
}

publishPlugin {
token.set(System.getenv("INTELLIJ_PUBLISH_TOKEN"))
channels.set(listOf("stable", "eap"))
Expand Down
8 changes: 4 additions & 4 deletions src/main/kotlin/ai/devchat/cli/DevChatWrapper.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ai.devchat.cli

import ai.devchat.common.DevChatPathUtil
import ai.devchat.common.PathUtils
import ai.devchat.common.Log
import ai.devchat.common.Settings
import ai.devchat.idea.balloon.DevChatNotifier
Expand Down Expand Up @@ -42,7 +42,6 @@ suspend fun executeCommand(
}

class DevChatWrapper(
private val command: String = DevChatPathUtil.devchatBinPath,
private var apiBase: String? = null,
private var apiKey: String? = null,
private var defaultModel: String? = null
Expand All @@ -66,6 +65,7 @@ class DevChatWrapper(
env["OPENAI_API_KEY"] = it
Log.info("api_key: ${it.substring(0, 5)}...${it.substring(it.length - 4)}")
}
env["PYTHONPATH"] = PathUtils.pythonPath
return env
}

Expand Down Expand Up @@ -160,7 +160,7 @@ class DevChatWrapper(


fun runCommand(subCommands: List<String>?, flags: List<Pair<String, String?>>? = null, callback: ((String) -> Unit)? = null): String? {
val cmd: MutableList<String> = mutableListOf(command)
val cmd: MutableList<String> = mutableListOf(PathUtils.pythonCommand, "-m", "devchat")
cmd.addAll(subCommands.orEmpty())
flags?.forEach { (name, value) ->
cmd.add("--$name")
Expand All @@ -175,7 +175,7 @@ class DevChatWrapper(
}

private fun subCommand(subCommands: List<String>): (MutableList<Pair<String, String?>>?, ((String) -> Unit)?) -> String? {
val cmd: MutableList<String> = mutableListOf(command)
val cmd: MutableList<String> = mutableListOf(PathUtils.pythonCommand, "-m", "devchat")
cmd.addAll(subCommands)
return {flags: List<Pair<String, String?>>?, callback: ((String) -> Unit)? ->
flags?.forEach { (name, value) ->
Expand Down
57 changes: 15 additions & 42 deletions src/main/kotlin/ai/devchat/cli/PythonEnvManager.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package ai.devchat.cli

import ai.devchat.common.Log
import ai.devchat.common.OSInfo
import java.io.File
import java.io.IOException
import java.util.*
import java.nio.file.Paths

/**
* DevChat represents for the DevChat Python CLI
*/


class PythonEnvManager(private val workDir: String) {
private val mambaWorkDir: String = "$workDir/mamba"
private val mambaBinPath: String = "$mambaWorkDir/micromamba"
private val mambaWorkDir = Paths.get(workDir, "mamba").toString()
private val mambaBinPath = Paths.get(mambaWorkDir, "micromamba").toString()

init {
try {
Expand All @@ -32,7 +32,7 @@ class PythonEnvManager(private val workDir: String) {
val dstDir = dstFile.parentFile
dstDir.exists() || dstDir.mkdirs() || throw RuntimeException("Unable to create directory: $dstDir")
javaClass.getResource(
"/tool/mamba/micromamba-$platform/bin/micromamba"
"/tools/micromamba-${OSInfo.platform}/bin/micromamba"
)!!.openStream().buffered().use { input ->
dstFile.outputStream().buffered().use { output ->
input.copyTo(output)
Expand All @@ -48,8 +48,8 @@ class PythonEnvManager(private val workDir: String) {
fun createEnv(name: String, version: String = "3.11.4"): PythonEnv {
Log.info("Python environment is creating.")
val errPrefix = "Error occurred during Python environment creation:"
val pyenv = PythonEnv("$mambaWorkDir/envs/$name")
val pythonBinPath = pyenv.pythonPath
val pyenv = PythonEnv(Paths.get(mambaWorkDir, "envs", name).toString())
val pythonBinPath = pyenv.pythonCommand
if (File(pythonBinPath).exists()) {
Log.info("Python environment already exists.")
return pyenv
Expand All @@ -69,7 +69,7 @@ class PythonEnvManager(private val workDir: String) {
process.inputStream.bufferedReader().forEachLine { Log.info("[Mamba installation] $it") }
val exitCode = process.waitFor()
if (exitCode != 0) throw RuntimeException(
"Command execution failed with exit code: ${exitCode}"
"Command execution failed with exit code: $exitCode"
)
}
} catch (e: IOException) {
Expand All @@ -81,43 +81,16 @@ class PythonEnvManager(private val workDir: String) {

Log.info("Python is installed in: $pythonBinPath")
return pyenv

}

private val platform: String
get() {
val unsupportedArchMessage = { "Unsupported architecture: $OS_ARCH" }
return when {
OS_NAME.contains("win") ->
if (OS_ARCH.contains("64")) "win-64"
else throw RuntimeException(unsupportedArchMessage())

OS_NAME.contains("darwin") || OS_NAME.contains("mac") -> when {
OS_ARCH.contains("arm") -> "osx-arm64"
OS_ARCH.contains("64") -> "osx-64"
else -> throw RuntimeException(unsupportedArchMessage())
}

OS_NAME.contains("linux") -> when {
OS_ARCH.contains("x64") -> "linux-64"
OS_ARCH.contains("ppc64le") -> "linux-ppc64le"
OS_ARCH.contains("aarch64") -> "linux-aarch64"
else -> throw RuntimeException(unsupportedArchMessage())
}

else -> throw RuntimeException("Unsupported operating system: $OS_NAME")
}
}

companion object {
private val OS_NAME: String = System.getProperty("os.name").lowercase(Locale.getDefault())
private val OS_ARCH: String = System.getProperty("os.arch")
}
}


class PythonEnv(private val workDir: String) {
val pythonPath = "$workDir/bin/python${if (OS_NAME.contains("win")) ".exe" else ""}"
val pythonCommand = Paths.get(
workDir,
"bin",
"python${if (OSInfo.isWindows) ".exe" else ""}"
).toString()
private var sourceIndex = 0
fun installPackage(packageName: String, packageVersion: String) {
pipInstall("$packageName==$packageVersion")
Expand All @@ -132,7 +105,7 @@ class PythonEnv(private val workDir: String) {
repeat(MAX_RETRIES) {
try {
ProcessBuilder(
pythonPath, "-m", "pip", "install", "--index-url",
pythonCommand, "-m", "pip", "install", "--index-url",
SOURCES[sourceIndex], *things
).apply {
val cmd = this.command().joinToString(" ")
Expand Down Expand Up @@ -160,8 +133,8 @@ class PythonEnv(private val workDir: String) {
else Log.info("Python package installation succeeded.")
}


companion object {
private val OS_NAME: String = System.getProperty("os.name").lowercase(Locale.getDefault())
private const val MAX_RETRIES = 3
private val SOURCES = arrayOf("https://pypi.org/simple", "https://pypi.tuna.tsinghua.edu.cn/simple")
}
Expand Down
9 changes: 0 additions & 9 deletions src/main/kotlin/ai/devchat/common/DevChatPathUtil.kt

This file was deleted.

30 changes: 30 additions & 0 deletions src/main/kotlin/ai/devchat/common/OSInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ai.devchat.common

import java.util.*

object OSInfo {
val OS_NAME: String = System.getProperty("os.name").lowercase(Locale.getDefault())
val OS_ARCH: String = System.getProperty("os.arch")

val isWindows: Boolean = OS_NAME.contains("win")
val platform: String = when {
OS_NAME.contains("win") ->
if (OS_ARCH.contains("64")) "win-64"
else throw RuntimeException("Unsupported architecture: $OS_ARCH")

OS_NAME.contains("darwin") || OS_NAME.contains("mac") -> when {
OS_ARCH.contains("arm") -> "osx-arm64"
OS_ARCH.contains("64") -> "osx-64"
else -> throw RuntimeException("Unsupported architecture: $OS_ARCH")
}

OS_NAME.contains("linux") -> when {
OS_ARCH.contains("x64") -> "linux-64"
OS_ARCH.contains("ppc64le") -> "linux-ppc64le"
OS_ARCH.contains("aarch64") -> "linux-aarch64"
else -> throw RuntimeException("Unsupported architecture: $OS_ARCH")
}

else -> throw RuntimeException("Unsupported operating system: $OS_NAME")
}
}
45 changes: 45 additions & 0 deletions src/main/kotlin/ai/devchat/common/PathUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ai.devchat.common

import java.io.IOException
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes

object PathUtils {
val workPath: String = Paths.get(System.getProperty("user.home"), ".chat").toString()
var pythonCommand: String = "python"
val pythonPath: String = Paths.get(workPath, "site-packages").toString()

fun copyResourceDirToPath(resourceDir: String, outputPath: String) {
val uri = javaClass.getResource(resourceDir)!!.toURI()
val path = if (uri.scheme == "jar") {
val fileSystem = FileSystems.newFileSystem(uri, emptyMap<String, Any>())
fileSystem.getPath("/$resourceDir")
} else {
Paths.get(uri)
}

Files.walkFileTree(path, object : SimpleFileVisitor<Path>() {
@Throws(IOException::class)
override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
val relativeDir = dir.toString().substring(path.toString().length)
val targetPath = Paths.get(outputPath, relativeDir)
return if (!Files.exists(targetPath)) {
Files.createDirectory(targetPath)
FileVisitResult.CONTINUE
} else {
if (relativeDir == "") FileVisitResult.CONTINUE else FileVisitResult.SKIP_SUBTREE
}
}

@Throws(IOException::class)
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
val relativePath = file.toString().substring(path.toString().length)
val targetFilePath = Paths.get(outputPath, relativePath)
if (!Files.exists(targetFilePath)) {
Files.copy(file, targetFilePath)
}
return FileVisitResult.CONTINUE
}
})
}
}
2 changes: 0 additions & 2 deletions src/main/kotlin/ai/devchat/devchat/DevChatActionHandler.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package ai.devchat.devchat

import ai.devchat.cli.DevChatWrapper
import ai.devchat.common.DevChatPathUtil
import ai.devchat.common.Log
import com.alibaba.fastjson.JSONObject
import com.intellij.openapi.project.Project
Expand Down
Loading

0 comments on commit 8f15810

Please sign in to comment.