Skip to content

Commit

Permalink
proper command line output for Build/Sync task
Browse files Browse the repository at this point in the history
  • Loading branch information
mkurnikov committed Nov 9, 2024
1 parent 9795f3d commit 765dbea
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 81 deletions.
140 changes: 84 additions & 56 deletions src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import com.intellij.build.events.BuildEventsNls
import com.intellij.build.events.MessageEvent
import com.intellij.build.progress.BuildProgress
import com.intellij.build.progress.BuildProgressDescriptor
import com.intellij.execution.process.ProcessAdapter
import com.intellij.execution.process.ProcessEvent
import com.intellij.icons.AllIcons
import com.intellij.notification.NotificationType.ERROR
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.ActionUpdateThread
Expand All @@ -30,13 +28,10 @@ import com.intellij.openapi.util.Key
import com.intellij.openapi.util.NlsContexts
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.util.concurrency.annotations.RequiresReadLock
import io.ktor.http.*
import org.move.cli.MoveProject.UpdateStatus
import org.move.cli.manifest.MoveToml
import org.move.cli.settings.getAptosCli
import org.move.cli.settings.moveSettings
import org.move.ide.notifications.showBalloon
import org.move.ide.notifications.showDebugBalloon
import org.move.lang.toNioPathOrNull
import org.move.lang.toTomlFile
import org.move.openapiext.TaskResult
Expand Down Expand Up @@ -142,17 +137,27 @@ class MoveProjectsSyncTask(
runReadAction {
val tomlFile = moveTomlFile.toTomlFile(project)!!
val rootMoveToml = MoveToml.fromTomlFile(tomlFile)
// val rootMoveToml = MoveToml.fromTomlFile(tomlFile, projectRoot)
val rootPackage = MovePackage.fromMoveToml(rootMoveToml)
val rootProject = MoveProject(project, rootPackage, emptyList())
rootProject to rootMoveToml
}

val result = fetchDependencyPackages(context, projectRoot)
if (result is TaskResult.Err) {
moveProject =
moveProject.copy(fetchDepsStatus = UpdateStatus.UpdateFailed("Failed to fetch dependency packages"))
if (!project.moveSettings.fetchAptosDeps) {
context.syncProgress.info(
"Fetching dependencies (disabled)",
"Fetching dependencies disabled in the plugin settings",
)
} else {
context.runWithChildProgress("Fetching dependencies") { childContext ->
val result = fetchDependencyPackages(childContext, projectRoot)
if (result is TaskResult.Err) {
moveProject =
moveProject.copy(fetchDepsStatus = UpdateStatus.UpdateFailed("Failed to fetch dependency packages"))
}
result
}
}
context.checkCanceled()

val deps =
(context.runWithChildProgress("Loading dependencies") { childContext ->
Expand All @@ -167,7 +172,7 @@ class MoveProjectsSyncTask(
deps,
visitedDepIds,
true,
childContext.progress
childContext
)
TaskResult.Ok(deps)
}
Expand All @@ -176,30 +181,31 @@ class MoveProjectsSyncTask(
projects.add(moveProject.copy(dependencies = deps))
}

private fun fetchDependencyPackages(context: SyncContext, projectRoot: Path): TaskResult<Unit> =
context.runWithChildProgress("Synchronize dependencies") { childContext ->
val listener = SyncProcessAdapter(childContext)

val skipLatest = project.moveSettings.skipFetchLatestGitDeps
val aptos = project.getAptosCli(parentDisposable = this)
when {
aptos == null -> TaskResult.Err("Invalid Aptos CLI configuration")
else -> {
aptos.fetchPackageDependencies(
project,
projectRoot,
skipLatest,
processListener = listener
).unwrapOrElse {
return@runWithChildProgress TaskResult.Err(
"Failed to fetch / update dependencies",
it.message
)
}
TaskResult.Ok(Unit)
private fun fetchDependencyPackages(childContext: SyncContext, projectRoot: Path): TaskResult<Unit> {
val taskResult: TaskResult<Unit>? = null
val syncListener =
SyncProcessListener(childContext) { event ->
childContext.syncProgress.output(event.text, true)
}
val skipLatest = project.moveSettings.skipFetchLatestGitDeps
val aptos = project.getAptosCli(parentDisposable = this)
return when {
aptos == null -> TaskResult.Err("Invalid Aptos CLI configuration")
else -> {
aptos.fetchPackageDependencies(
projectRoot,
skipLatest,
processListener = syncListener
).unwrapOrElse {
return TaskResult.Err(
"Failed to fetch / update dependencies",
it.message
)
}
TaskResult.Ok(Unit)
}
}
}

private fun createSyncProgressDescriptor(progress: ProgressIndicator): BuildProgressDescriptor {
val buildContentDescriptor = BuildContentDescriptor(
Expand Down Expand Up @@ -244,6 +250,8 @@ class MoveProjectsSyncTask(

val id: Any get() = syncProgress.id

fun checkCanceled() = progress.checkCanceled()

fun <T> runWithChildProgress(
@NlsContexts.ProgressText title: String,
action: (SyncContext) -> TaskResult<T>
Expand Down Expand Up @@ -289,26 +297,26 @@ class MoveProjectsSyncTask(
deps: MutableList<Pair<MovePackage, RawAddressMap>>,
visitedIds: MutableSet<DepId>,
isRoot: Boolean,
progress: ProgressIndicator
syncContext: SyncContext,
) {
// checks for the cancel() of the whole SyncTask
progress.checkCanceled()
syncContext.checkCanceled()

var parsedDeps = rootMoveToml.deps
if (isRoot) {
parsedDeps = parsedDeps.withExtended(rootMoveToml.dev_deps)
}
for ((dep, addressMap) in parsedDeps) {
val depRoot = dep.rootDirectory() ?: continue
// if (depRoot == null) {
// // root does not exist
// project.showDebugBalloon(
// title = "Cannot resolve the ${dep.name.quote()} dependency.",
// content = "Root directory does not exist.",
// ERROR
// )
// continue
// }
val depRoot = dep.rootDirectory()
.unwrapOrElse {
syncContext.syncProgress.message(
"Failed to load ${dep.name}.",
"Error when resolving dependency root, \n${it.message}",
MessageEvent.Kind.ERROR,
null
)
null
} ?: continue

val depId = DepId(depRoot.path)
if (depId in visitedIds) continue
Expand All @@ -322,19 +330,28 @@ class MoveProjectsSyncTask(

// parse all nested dependencies with their address maps
visitedIds.add(depId)
loadDependencies(project, depMoveToml, deps, visitedIds, false, progress)

loadDependencies(project, depMoveToml, deps, visitedIds, false, syncContext)
deps.add(Pair(depPackage, addressMap))

syncContext.syncProgress.output(
"${dep.name} dependency loaded successfully, " +
"\npackage directory: \n${depRoot.presentableUrl}",
true,
)
syncContext.syncProgress.newline()
syncContext.syncProgress.newline()
}
}
}
}

private class SyncProcessAdapter(
private val context: MoveProjectsSyncTask.SyncContext
): ProcessAdapter(),
ProcessProgressListener {
private class SyncProcessListener(
private val context: MoveProjectsSyncTask.SyncContext,
private val onTextAvailable: (ProcessEvent) -> Unit,
): ProcessProgressListener {
override fun onTextAvailable(event: ProcessEvent, outputType: Key<Any>) {
onTextAvailable(event)
// show progress bars for the long operations
val text = event.text.trim { it <= ' ' }
if (text.startsWith("FETCHING GIT DEPENDENCY")) {
val gitRepo = text.substring("FETCHING GIT DEPENDENCY".length)
Expand All @@ -346,8 +363,8 @@ private class SyncProcessAdapter(
}
}

override fun error(title: String, message: String) = context.error(title, message)
override fun warning(title: String, message: String) = context.warning(title, message)
override fun error(title: String, message: String) = context.syncProgress.error(title, message)
override fun warning(title: String, message: String) = context.syncProgress.warning(title, message)
}

private fun <T, R> BuildProgress<BuildProgressDescriptor>.runWithChildProgress(
Expand All @@ -372,18 +389,29 @@ private fun <T, R> BuildProgress<BuildProgressDescriptor>.runWithChildProgress(
}
}

private fun MoveProjectsSyncTask.SyncContext.error(
private fun BuildProgress<BuildProgressDescriptor>.error(
@BuildEventsNls.Title title: String,
@BuildEventsNls.Message message: String
) {
message(title, message, MessageEvent.Kind.ERROR, null)
}

private fun BuildProgress<BuildProgressDescriptor>.warning(
@BuildEventsNls.Title title: String,
@BuildEventsNls.Message message: String
) {
syncProgress.message(title, message, com.intellij.build.events.MessageEvent.Kind.ERROR, null)
message(title, message, MessageEvent.Kind.WARNING, null)
}

private fun MoveProjectsSyncTask.SyncContext.warning(
private fun BuildProgress<BuildProgressDescriptor>.info(
@BuildEventsNls.Title title: String,
@BuildEventsNls.Message message: String
) {
syncProgress.message(title, message, com.intellij.build.events.MessageEvent.Kind.WARNING, null)
message(title, message, MessageEvent.Kind.INFO, null)
}

private fun BuildProgress<BuildProgressDescriptor>.newline() {
output("\n", true)
}


40 changes: 31 additions & 9 deletions src/main/kotlin/org/move/cli/manifest/TomlDependency.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,66 @@ package org.move.cli.manifest
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.findDirectory
import com.intellij.util.SystemProperties
import com.intellij.util.concurrency.annotations.RequiresReadLock
import org.move.stdext.RsResult
import org.move.stdext.RsResult.Err
import org.move.stdext.RsResult.Ok
import org.move.stdext.exists
import org.move.stdext.toPath
import java.nio.file.Path

sealed class TomlDependency {
abstract val name: String

@RequiresReadLock
abstract fun rootDirectory(): VirtualFile?
abstract fun rootDirectory(): RsResult<VirtualFile, DependencyError>

data class Local(
override val name: String,
private val localPath: Path,
) : TomlDependency() {
): TomlDependency() {

@RequiresReadLock
override fun rootDirectory(): VirtualFile? = VfsUtil.findFile(localPath, true)
override fun rootDirectory(): RsResult<VirtualFile, DependencyError> {
val vFile = VfsUtil.findFile(localPath, true)
if (vFile == null) {
return Err(DependencyError("Cannot find dependency folder: $localPath"))
}
return Ok(vFile)
}
}

data class Git(
override val name: String,
private val repo: String,
private val rev: String,
private val subdir: String,
) : TomlDependency() {
): TomlDependency() {

@RequiresReadLock
override fun rootDirectory(): VirtualFile? {
val userHome = VfsUtil.getUserHomeDir() ?: return null
val sourceDirName = dirNameAptos(repo, rev)
return userHome.findDirectory(".move/$sourceDirName/$subdir")
override fun rootDirectory(): RsResult<VirtualFile, DependencyError> {
val moveHomePath = SystemProperties.getUserHome().toPath().resolve(".move")
if (!moveHomePath.exists()) {
return Err(DependencyError("$moveHomePath directory does not exist"))
}
val depDirName = dependencyDirName(repo, rev)
val depRoot = moveHomePath.resolve(depDirName).resolve(subdir)
val depRootFile = VfsUtil.findFile(depRoot, true)
if (depRootFile == null) {
return Err(DependencyError("cannot find folder: $depRoot"))
}
return Ok(depRootFile)
}

companion object {
fun dirNameAptos(repo: String, rev: String): String {
fun dependencyDirName(repo: String, rev: String): String {
val sanitizedRepoName = repo.replace(Regex("[/:.@]"), "_")
val aptosRevName = rev.replace("/", "_")
return "${sanitizedRepoName}_$aptosRevName"
}
}
}
}

data class DependencyError(val message: String)
26 changes: 11 additions & 15 deletions src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,25 +63,21 @@ data class Aptos(val cliLocation: Path, val parentDisposable: Disposable?): Disp
}

fun fetchPackageDependencies(
project: Project,
projectDir: Path,
skipLatest: Boolean,
processListener: ProcessListener
): RsProcessResult<Unit> {
if (project.moveSettings.fetchAptosDeps) {
val commandLine =
AptosCommandLine(
subCommand = "move compile",
arguments = listOfNotNull(
"--skip-fetch-latest-git-deps".takeIf { skipLatest }
),
workingDirectory = projectDir
)
// TODO: as Aptos does not yet support fetching dependencies without compiling, ignore errors here,
// TODO: still better than no call at all
executeCommandLine(commandLine, listener = processListener)
// .unwrapOrElse { return Err(it) }
}
val commandLine =
AptosCommandLine(
subCommand = "move compile",
arguments = listOfNotNull(
"--skip-fetch-latest-git-deps".takeIf { skipLatest }
),
workingDirectory = projectDir
)
commandLine
.toColoredCommandLine(this.cliLocation)
.execute(innerDisposable, listener = processListener)
return Ok(Unit)
}

Expand Down
5 changes: 5 additions & 0 deletions src/main/kotlin/org/move/stdext/RsResult.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ sealed class RsResult<out T, out E> {
}
}

fun unwrapErr(): E = when (this) {
is Err -> err
is Ok -> throw IllegalStateException("called `RsResult.unwrapErr()` on an `Ok` value")
}

fun unwrapOrNull(): T? = when (this) {
is Ok -> ok
is Err -> null
Expand Down
Loading

0 comments on commit 765dbea

Please sign in to comment.